diff --git a/composeApp/schemas/at.torch.compose.database.TorchDatabase/1.json b/composeApp/schemas/at.torch.compose.database.TorchDatabase/1.json index da2b9037..2606f620 100644 --- a/composeApp/schemas/at.torch.compose.database.TorchDatabase/1.json +++ b/composeApp/schemas/at.torch.compose.database.TorchDatabase/1.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "f0a498de098c8c2c2007d4309022282e", + "identityHash": "6a176cdf1ee35e271c2a38b5955a7440", "entities": [ { "tableName": "BroadcastNostrEventReceipt", @@ -645,7 +645,7 @@ }, { "tableName": "ChatRoom", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `userPublicKey` TEXT NOT NULL, `subject` TEXT, `mlsGroupState` TEXT, `initialGiftWrapPayloadId` TEXT, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`userPublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`initialGiftWrapPayloadId`) REFERENCES `GiftWrapPayload`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `userPublicKey` TEXT NOT NULL, `subject` TEXT, `description` TEXT, `mlsGroupState` TEXT, `initialGiftWrapPayloadId` TEXT, `leftGroupAt` INTEGER, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`userPublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`initialGiftWrapPayloadId`) REFERENCES `GiftWrapPayload`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", "fields": [ { "fieldPath": "id", @@ -664,6 +664,11 @@ "columnName": "subject", "affinity": "TEXT" }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + }, { "fieldPath": "mlsGroupState", "columnName": "mlsGroupState", @@ -674,6 +679,11 @@ "columnName": "initialGiftWrapPayloadId", "affinity": "TEXT" }, + { + "fieldPath": "leftGroupAt", + "columnName": "leftGroupAt", + "affinity": "INTEGER" + }, { "fieldPath": "createdAt", "columnName": "createdAt", @@ -3452,7 +3462,7 @@ ], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f0a498de098c8c2c2007d4309022282e')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6a176cdf1ee35e271c2a38b5955a7440')" ] } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/ChatRoomDao.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/ChatRoomDao.kt index a6f627de..ddd4c9a1 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/ChatRoomDao.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/ChatRoomDao.kt @@ -1,26 +1,32 @@ package at.torch.compose.database.dao import androidx.room3.Dao +import androidx.room3.Delete import androidx.room3.Query import androidx.room3.Transaction import androidx.room3.Upsert +import at.torch.compose.database.model.ChatRoom +import at.torch.compose.database.model.intermdiate.LocalChatRoom import kotlinx.coroutines.flow.Flow @Dao interface ChatRoomDao { @Transaction @Query("SELECT * FROM ChatRoom WHERE id = :id") - fun findChatRoomById(id: String): at.torch.compose.database.model.intermdiate.LocalChatRoom? + fun findChatRoomById(id: String): LocalChatRoom? @Transaction @Query("SELECT * FROM ChatRoom WHERE userPublicKey = :userPublicKey") - fun getChatRoomListByUserPublicKey(userPublicKey: String): List + fun getChatRoomListByUserPublicKey(userPublicKey: String): List @Transaction @Query("SELECT * FROM ChatRoom WHERE userPublicKey = :userPublicKey") - fun observeChatRoomListByUserPublicKey(userPublicKey: String): Flow> + fun observeChatRoomListByUserPublicKey(userPublicKey: String): Flow> @Upsert - suspend fun upsert(chatRoom: at.torch.compose.database.model.ChatRoom) + suspend fun upsert(chatRoom: ChatRoom) + + @Delete + suspend fun delete(chatRoom: ChatRoom) } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/MarmotOutboundDao.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/MarmotOutboundDao.kt index db67ebd8..07fba71e 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/MarmotOutboundDao.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/MarmotOutboundDao.kt @@ -56,6 +56,8 @@ abstract class MarmotOutboundDao( @Transaction open suspend fun createMlsDirectMessageChatRoom( + name: String? = null, + description: String? = null, userPublicKey: HexKey, peerPublicKey: HexKey, peerKeyPackage: MarmotKeyPackage, @@ -78,7 +80,8 @@ abstract class MarmotOutboundDao( id = nostrGroupId, userPublicKey = userPublicKey, mlsGroupState = mlsGroup.saveState().encodeTls().toHex(), - subject = null, + subject = name, + description = description ) database.chatRoomDao().upsert( chatRoom @@ -275,7 +278,6 @@ abstract class MarmotOutboundDao( ) } ) - // TODO: Save chatMessage } } diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/NostrDao.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/NostrDao.kt index d38b1149..8d98c88c 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/NostrDao.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/NostrDao.kt @@ -510,6 +510,7 @@ abstract class NostrDao( id = decryptedGiftWrapPayload.chatRoomId, userPublicKey = userPublicKey, subject = decryptedGiftWrapPayload.parseSubject(), + description = null, createdAt = decryptedGiftWrapPayload.createdAt, initialGiftWrapPayloadId = decryptedGiftWrapPayload.id, mlsGroupState = null @@ -736,6 +737,7 @@ abstract class NostrDao( id = nostrGroupId, userPublicKey = userPublicKey, subject = group.currentMarmotData()?.name?.ifBlank { null }, + description = group.currentMarmotData()?.description?.ifBlank { null }, initialGiftWrapPayloadId = decryptedGiftWrapPayload.id, createdAt = decryptedGiftWrapPayload.createdAt, mlsGroupState = group.saveState().encodeTls().toHex(), diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/NostrNip17Dao.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/NostrNip17Dao.kt index 4f987c1d..b354412a 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/NostrNip17Dao.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/NostrNip17Dao.kt @@ -28,7 +28,14 @@ abstract class NostrNip17Dao( val logger = Logger.withTag(TAG) @Transaction - open suspend fun getOrCreateChatRoom(chatRoomId: String, activeUserPublicKey: HexKey, relayHint: String?, defaultSubject: String? = null, mlsGroupState: String? = null): LocalChatRoom? { + open suspend fun getOrCreateChatRoom( + chatRoomId: HexKey, + activeUserPublicKey: HexKey, + relayHint: String?, + defaultSubject: String? = null, + description: String? = null, + mlsGroupState: String? = null + ): LocalChatRoom? { logger.d("getOrCreateChatRoom: $chatRoomId") @@ -51,7 +58,8 @@ abstract class NostrNip17Dao( id = chatRoomId, userPublicKey = activeUserPublicKey, subject = defaultSubject, - mlsGroupState = mlsGroupState + mlsGroupState = mlsGroupState, + description = description ) database.chatRoomDao().upsert(chatRoom) diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/database/model/ChatRoom.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/database/model/ChatRoom.kt index 7edeb7c6..7fb7d8a1 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/database/model/ChatRoom.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/database/model/ChatRoom.kt @@ -58,6 +58,8 @@ data class ChatRoom( */ val subject: String?, + val description: String?, + val mlsGroupState: String?, /** @@ -75,6 +77,8 @@ data class ChatRoom( // Might want to focus on the other last messages... val initialGiftWrapPayloadId: HexKey? = null, + val leftGroupAt: Instant? = null, + override val createdAt: Instant = Clock.System.now(), override val updatedAt: Instant = createdAt, override val savedAt: Instant = createdAt, diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/database/repository/DatabaseChatRepository.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/database/repository/DatabaseChatRepository.kt index cc637ab9..469a5f13 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/database/repository/DatabaseChatRepository.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/database/repository/DatabaseChatRepository.kt @@ -3,6 +3,7 @@ package at.torch.compose.database.repository import at.torch.compose.database.GENESIS_AT import at.torch.compose.database.TorchDatabase import at.torch.compose.database.model.ChatMessage +import at.torch.compose.database.model.ChatRoom import at.torch.compose.database.model.GiftWrapPayload import at.torch.compose.database.model.MarmotInnerEvent import at.torch.compose.database.model.MarmotKeyPackage @@ -14,7 +15,6 @@ import co.touchlab.kermit.Logger import com.vitorpamplona.quartz.marmot.mip02Welcome.WelcomeEvent import com.vitorpamplona.quartz.marmot.mls.group.MlsGroup import com.vitorpamplona.quartz.marmot.mls.group.MlsGroupState -import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.Kind import com.vitorpamplona.quartz.nip01Core.crypto.EventHasher @@ -69,6 +69,7 @@ class DatabaseChatRepository( activeUserPublicKey: HexKey, relayHint: String?, defaultSubject: String?, + description: String?, mlsGroupState: String? ): LocalChatRoom? = try { return database.nostrNip17Dao().getOrCreateChatRoom( @@ -76,6 +77,7 @@ class DatabaseChatRepository( activeUserPublicKey = activeUserPublicKey, relayHint = relayHint, defaultSubject = defaultSubject, + description = description, mlsGroupState = mlsGroupState ) } catch (e: Throwable) { @@ -120,11 +122,15 @@ class DatabaseChatRepository( } override suspend fun createMlsDirectMessage( + name: String?, + description: String?, userPublicKey: HexKey, peerPublicKey: HexKey, peerKeyPackage: MarmotKeyPackage ): String { return database.marmotOutboundDao().createMlsDirectMessageChatRoom( + name = name, + description = description, userPublicKey = userPublicKey, peerPublicKey = peerPublicKey, peerKeyPackage = peerKeyPackage @@ -155,6 +161,7 @@ class DatabaseChatRepository( ) ) } + if (mlsGroup != null) { // Create ChatRumor... val createdAt = Clock.System.now().epochSeconds @@ -345,6 +352,112 @@ class DatabaseChatRepository( } } + override suspend fun leaveChatRoom(localChatRoom: LocalChatRoom) { + val text = "Left group." + + val mlsGroup = localChatRoom.chatRoom.mlsGroupState?.let { mlsGroupState -> + MlsGroup.restore( + MlsGroupState.decodeTls( + mlsGroupState.hexToByteArray() + ) + ) + } + + if (mlsGroup != null) { + // Create ChatRumor... + val createdAt = Clock.System.now().epochSeconds + val marmotInnerEventKind = ChatEvent.KIND + + val marmotInnerEventId = EventHasher.hashId( + pubKey = localChatRoom.chatRoom.userPublicKey, + createdAt = createdAt, + tags = emptyArray(), + content = text, + kind = marmotInnerEventKind + ) + + database.marmotInnerEventDao().upsert( + MarmotInnerEvent( + id = marmotInnerEventId, + publicKey = localChatRoom.chatRoom.userPublicKey, + createdAt = Instant.fromEpochSeconds(createdAt), + tags = emptyArray(), + content = text, + chatRoomId = localChatRoom.chatRoom.id, + kind = marmotInnerEventKind + ) + ) + + database.chatMessageDao().upsert( + ChatMessage( + content = text, + chatRoomId = localChatRoom.chatRoom.id, + senderPublicKey = localChatRoom.chatRoom.userPublicKey, + isUserMessage = true, + giftWrapPayloadId = null, + marmotGroupEventId = null, + marmotInnerEventId = marmotInnerEventId, + ) + ) + } else { + val innerEventKind = ChatMessageEvent.KIND + // Send GiftWrapMessage + val receiverTags = localChatRoom.localParticipants.filter { participant -> + participant.participant.participantPublicKey != localChatRoom.chatRoom.userPublicKey // TODO: Allow this where it's notes to self... + }.map { localParticipant -> + PTag.assemble( + localParticipant.participant.participantPublicKey, + localParticipant.participant.relayHint?.let { NormalizedRelayUrl(it) } + ) + } + + val createdAt = Clock.System.now().epochSeconds + val giftWrapPayloadId = EventHasher.hashId( + pubKey = localChatRoom.chatRoom.userPublicKey, + createdAt = createdAt, + tags = receiverTags.toTypedArray(), + content = text, + kind = innerEventKind + ) + + database.giftWrapPayloadDao().upsert( + GiftWrapPayload( + id = giftWrapPayloadId, + kind = innerEventKind, + tags = receiverTags.toTypedArray(), + createdAt = Instant.fromEpochSeconds(createdAt), + content = text, + chatRoomId = localChatRoom.chatRoom.id, + publicKey = localChatRoom.chatRoom.userPublicKey + ) + ) + + database.chatMessageDao().upsert( + ChatMessage( + content = text, + chatRoomId = localChatRoom.chatRoom.id, + senderPublicKey = localChatRoom.chatRoom.userPublicKey, + isUserMessage = true, + giftWrapPayloadId = giftWrapPayloadId, + marmotGroupEventId = null, + marmotInnerEventId = null + ) + ) + } + } + + override suspend fun softDeleteChatRoom(chatRoom: ChatRoom) { + database.chatRoomDao().upsert( + chatRoom.copy( + deletedAt = Clock.System.now() + ) + ) + } + + override suspend fun deleteChatRoom(chatRoom: ChatRoom) { + database.chatRoomDao().delete(chatRoom) + } + companion object { const val TAG = "DatabaseChatRepository" } diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/repository/ChatRepository.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/repository/ChatRepository.kt index deb23ee8..f0a5bd32 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/repository/ChatRepository.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/repository/ChatRepository.kt @@ -7,13 +7,11 @@ import at.torch.compose.database.model.Participant import at.torch.compose.database.model.Profile import at.torch.compose.database.model.intermdiate.LocalChatMessage import at.torch.compose.database.model.intermdiate.LocalChatRoom -import at.torch.compose.database.model.intermdiate.LocalProfile import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.Kind import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent -import fr.acinq.secp256k1.Hex import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -35,6 +33,7 @@ interface ChatRepository { activeUserPublicKey: HexKey, relayHint: String?, defaultSubject: String?, + description: String?, mlsGroupState: String? ): LocalChatRoom? @@ -45,6 +44,8 @@ interface ChatRepository { suspend fun getProfileWithPublicKey(publicKey: HexKey): Profile? suspend fun createMlsDirectMessage( + name: String? = null, + description: String? = null, userPublicKey: HexKey, peerPublicKey: HexKey, peerKeyPackage: MarmotKeyPackage @@ -68,6 +69,12 @@ interface ChatRepository { suspend fun sealGiftWrapPayload(giftWrapPayload: GiftWrapPayload, nostrSignerSync: NostrSignerSync) + suspend fun leaveChatRoom(localChatRoom: LocalChatRoom) + + suspend fun softDeleteChatRoom(chatRoom: ChatRoom) + + suspend fun deleteChatRoom(chatRoom: ChatRoom) + companion object { val NO_OP_CHAT_REPOSITORY = object: ChatRepository { override suspend fun observeChatRoomListByPublicKey(publicKey: String): Flow> { @@ -100,6 +107,7 @@ interface ChatRepository { activeUserPublicKey: HexKey, relayHint: String?, defaultSubject: String?, + description: String?, mlsGroupState: String? ): LocalChatRoom? { return null @@ -118,6 +126,8 @@ interface ChatRepository { } override suspend fun createMlsDirectMessage( + name: String?, + description: String?, userPublicKey: HexKey, peerPublicKey: HexKey, peerKeyPackage: MarmotKeyPackage @@ -155,6 +165,18 @@ interface ChatRepository { ) { TODO("Not yet implemented") } + + override suspend fun leaveChatRoom(localChatRoom: LocalChatRoom) { + TODO("Not yet implemented") + } + + override suspend fun softDeleteChatRoom(chatRoom: ChatRoom) { + TODO("Not yet implemented") + } + + override suspend fun deleteChatRoom(chatRoom: ChatRoom) { + TODO("Not yet implemented") + } } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/AddMemberToChatRoomConfirmationScreen.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/AddMemberToChatRoomConfirmationScreen.kt index 6d7d576e..bb243963 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/AddMemberToChatRoomConfirmationScreen.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/AddMemberToChatRoomConfirmationScreen.kt @@ -219,6 +219,7 @@ private fun ChatRoomDetailScreenPreview() { id = "hex", userPublicKey = "hex", subject = "Room Title", + description = "See something... say something.", initialGiftWrapPayloadId = "sdfaer", mlsGroupState = null ), diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/ChatRoomDetailScreen.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/ChatRoomDetailScreen.kt index b791071f..63003ed4 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/ChatRoomDetailScreen.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/ChatRoomDetailScreen.kt @@ -206,12 +206,12 @@ fun ChatRoomDetailScreen( colors = ButtonDefaults.textButtonColors( contentColor = MaterialTheme.colorScheme.secondary ), - enabled = true, // Disable after we leave group... + enabled = chatRoomDetailUIState.localChatRoom.chatRoom.leftGroupAt == null, // Disable after we leave group... onClick = { - onNavigateToRoute.invoke( - ImplementationPendingRoute( - "Leave Group" - ) + // Leave group logic + chatRoomDetailViewModel.leaveGroup( + localChatRoom = chatRoomDetailUIState.localChatRoom, + onNavigateToRoute = onNavigateToRoute ) } ) { @@ -233,7 +233,7 @@ fun ChatRoomDetailScreen( colors = ButtonDefaults.textButtonColors( contentColor = MaterialTheme.colorScheme.error ), - enabled = true, // TODO: Only enabled after we leave group... + enabled = chatRoomDetailUIState.localChatRoom.chatRoom.leftGroupAt != null, onClick = { onNavigateToRoute.invoke( ImplementationPendingRoute( @@ -316,7 +316,8 @@ private fun ChatRoomMessagingScreenPreview() { chatRoom = ChatRoom( id = "", userPublicKey = "", - subject = "Message title", + subject = "Message Title", + description = "Description", initialGiftWrapPayloadId = "sdfaer", mlsGroupState = null ), diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/ChatRoomMessagingScreen.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/ChatRoomMessagingScreen.kt index a000605b..6b62e37f 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/ChatRoomMessagingScreen.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/ChatRoomMessagingScreen.kt @@ -340,6 +340,7 @@ private fun ChatRoomMessagingScreenPreview() { id = "", userPublicKey = "", subject = "Message title", + description = "See something. Say somethin", initialGiftWrapPayloadId = "sdfaer", mlsGroupState = null ), diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/SearchMemberToAddToChatRoomScreen.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/SearchMemberToAddToChatRoomScreen.kt index a9cda57d..86a0666b 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/SearchMemberToAddToChatRoomScreen.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/SearchMemberToAddToChatRoomScreen.kt @@ -227,6 +227,7 @@ private fun ChatRoomDetailScreenPreview() { id = "", userPublicKey = "", subject = "Message title", + description = "See something... say something.", initialGiftWrapPayloadId = "sdfaer", mlsGroupState = null ), diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomCreationViewModel.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomCreationViewModel.kt index 4ba14822..e4156d61 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomCreationViewModel.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomCreationViewModel.kt @@ -84,6 +84,7 @@ class ChatRoomCreationViewModel( activeUserPublicKey = keyPair.pubKey.toHexKey(), relayHint = null, defaultSubject = name.toString(), + description = description.toString(), mlsGroupState = group.saveState().encodeTls().toHex() ) diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomDetailViewModel.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomDetailViewModel.kt index b7f773d7..68a79c5d 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomDetailViewModel.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomDetailViewModel.kt @@ -9,8 +9,12 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory +import at.torch.compose.database.model.ChatRoom +import at.torch.compose.database.model.intermdiate.LocalChatRoom import at.torch.compose.repository.ChatRepository import at.torch.compose.repository.NostrRepository +import at.torch.compose.ui.composable.navigation.routes.ImplementationPendingRoute +import at.torch.compose.ui.composable.navigation.routes.Route import at.torch.compose.ui.view.state.ChatRoomDetailUIState import co.touchlab.kermit.Logger import com.vitorpamplona.quartz.nip01Core.core.HexKey @@ -53,6 +57,22 @@ class ChatRoomDetailViewModel( } } + fun leaveGroup( + localChatRoom: LocalChatRoom, + onNavigateToRoute: (Route) -> Unit + ) { + viewModelScope.launch(Dispatchers.IO) { + chatRepository.leaveChatRoom(localChatRoom) + + viewModelScope.launch(Dispatchers.Main) { + onNavigateToRoute.invoke( + ImplementationPendingRoute( + "Leave Group" + ) + ) + } + } + } companion object { private const val TAG = "ChatRoomDetailViewModel"