Leave and delete group logic
This commit is contained in:
1 parent
947ae439f0
commit
30db090de8
6 files changed
+106
-123
No files matched your search
+27
-92
@@ -8,7 +8,6 @@ import at.torch.compose.database.model.GiftWrapPayload
|
|||||||
import at.torch.compose.database.model.MarmotInnerEvent
|
import at.torch.compose.database.model.MarmotInnerEvent
|
||||||
import at.torch.compose.database.model.MarmotKeyPackage
|
import at.torch.compose.database.model.MarmotKeyPackage
|
||||||
import at.torch.compose.database.model.Participant
|
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.LocalChatMessage
|
||||||
import at.torch.compose.database.model.intermdiate.LocalChatRoom
|
import at.torch.compose.database.model.intermdiate.LocalChatRoom
|
||||||
import co.touchlab.kermit.Logger
|
import co.touchlab.kermit.Logger
|
||||||
@@ -348,110 +347,46 @@ class DatabaseChatRepository(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun leaveChatRoom(localChatRoom: LocalChatRoom) {
|
override suspend fun leaveChatRoom(
|
||||||
val text = "Left group."
|
localChatRoom: LocalChatRoom,
|
||||||
|
onCompletion: () -> Unit
|
||||||
val mlsGroup = localChatRoom.chatRoom.mlsGroupState?.let { mlsGroupState ->
|
) {
|
||||||
MlsGroup.restore(
|
database.chatRoomDao().upsert(
|
||||||
MlsGroupState.decodeTls(
|
chatRoom = localChatRoom.chatRoom.copy(
|
||||||
mlsGroupState.hexToByteArray()
|
leftGroupAt = Clock.System.now()
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
|
|
||||||
if (mlsGroup != null) {
|
// TODO: Process this in the notary...
|
||||||
// Create ChatRumor...
|
sendChatMessage(
|
||||||
val createdAt = Clock.System.now().epochSeconds
|
text = "Left Group",
|
||||||
val marmotInnerEventKind = ChatEvent.KIND
|
localChatRoom = localChatRoom,
|
||||||
|
// TODO: Add Information/LeaveChatGroup Message Type...
|
||||||
|
)
|
||||||
|
|
||||||
val marmotInnerEventId = EventHasher.hashId(
|
onCompletion.invoke()
|
||||||
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) {
|
override suspend fun softDeleteChatRoom(
|
||||||
|
chatRoom: ChatRoom,
|
||||||
|
onCompletion: () -> Unit
|
||||||
|
) {
|
||||||
database.chatRoomDao().upsert(
|
database.chatRoomDao().upsert(
|
||||||
chatRoom.copy(
|
chatRoom.copy(
|
||||||
deletedAt = Clock.System.now()
|
deletedAt = Clock.System.now()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
onCompletion.invoke()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun deleteChatRoom(chatRoom: ChatRoom) {
|
override suspend fun deleteChatRoom(
|
||||||
|
chatRoom: ChatRoom,
|
||||||
|
onCompletion: () -> Unit
|
||||||
|
) {
|
||||||
database.chatRoomDao().delete(chatRoom)
|
database.chatRoomDao().delete(chatRoom)
|
||||||
|
|
||||||
|
onCompletion.invoke()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import at.torch.compose.database.model.ChatRoom
|
|||||||
import at.torch.compose.database.model.GiftWrapPayload
|
import at.torch.compose.database.model.GiftWrapPayload
|
||||||
import at.torch.compose.database.model.MarmotKeyPackage
|
import at.torch.compose.database.model.MarmotKeyPackage
|
||||||
import at.torch.compose.database.model.Participant
|
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.LocalChatMessage
|
||||||
import at.torch.compose.database.model.intermdiate.LocalChatRoom
|
import at.torch.compose.database.model.intermdiate.LocalChatRoom
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
@@ -67,11 +66,20 @@ interface ChatRepository {
|
|||||||
|
|
||||||
suspend fun sealGiftWrapPayload(giftWrapPayload: GiftWrapPayload, nostrSignerSync: NostrSignerSync)
|
suspend fun sealGiftWrapPayload(giftWrapPayload: GiftWrapPayload, nostrSignerSync: NostrSignerSync)
|
||||||
|
|
||||||
suspend fun leaveChatRoom(localChatRoom: LocalChatRoom)
|
suspend fun leaveChatRoom(
|
||||||
|
localChatRoom: LocalChatRoom,
|
||||||
|
onCompletion: () -> Unit
|
||||||
|
)
|
||||||
|
|
||||||
suspend fun softDeleteChatRoom(chatRoom: ChatRoom)
|
suspend fun softDeleteChatRoom(
|
||||||
|
chatRoom: ChatRoom,
|
||||||
|
onCompletion: () -> Unit
|
||||||
|
)
|
||||||
|
|
||||||
suspend fun deleteChatRoom(chatRoom: ChatRoom)
|
suspend fun deleteChatRoom(
|
||||||
|
chatRoom: ChatRoom,
|
||||||
|
onCompletion: () -> Unit
|
||||||
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val NO_OP_CHAT_REPOSITORY = object: ChatRepository {
|
val NO_OP_CHAT_REPOSITORY = object: ChatRepository {
|
||||||
@@ -160,15 +168,24 @@ interface ChatRepository {
|
|||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun leaveChatRoom(localChatRoom: LocalChatRoom) {
|
override suspend fun leaveChatRoom(
|
||||||
|
localChatRoom: LocalChatRoom,
|
||||||
|
onCompletion: () -> Unit
|
||||||
|
) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun softDeleteChatRoom(chatRoom: ChatRoom) {
|
override suspend fun softDeleteChatRoom(
|
||||||
|
chatRoom: ChatRoom,
|
||||||
|
onCompletion: () -> Unit
|
||||||
|
) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun deleteChatRoom(chatRoom: ChatRoom) {
|
override suspend fun deleteChatRoom(
|
||||||
|
chatRoom: ChatRoom,
|
||||||
|
onCompletion: () -> Unit
|
||||||
|
) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-7
@@ -63,6 +63,7 @@ fun ChatRoomDetailScreen(
|
|||||||
nostrRepository: NostrRepository,
|
nostrRepository: NostrRepository,
|
||||||
chatRepository: ChatRepository,
|
chatRepository: ChatRepository,
|
||||||
onNavigateToRoute: (Route) -> Unit,
|
onNavigateToRoute: (Route) -> Unit,
|
||||||
|
onPopBackToRoute: (Route) -> Unit
|
||||||
) {
|
) {
|
||||||
val chatRoomDetailViewModel: ChatRoomDetailViewModel = viewModel(
|
val chatRoomDetailViewModel: ChatRoomDetailViewModel = viewModel(
|
||||||
factory = ChatRoomDetailViewModel.factory(
|
factory = ChatRoomDetailViewModel.factory(
|
||||||
@@ -213,10 +214,9 @@ fun ChatRoomDetailScreen(
|
|||||||
),
|
),
|
||||||
enabled = chatRoomDetailUIState.localChatRoom.chatRoom.leftGroupAt == null, // Disable after we leave group...
|
enabled = chatRoomDetailUIState.localChatRoom.chatRoom.leftGroupAt == null, // Disable after we leave group...
|
||||||
onClick = {
|
onClick = {
|
||||||
// Leave group logic
|
|
||||||
chatRoomDetailViewModel.leaveGroup(
|
chatRoomDetailViewModel.leaveGroup(
|
||||||
localChatRoom = chatRoomDetailUIState.localChatRoom,
|
localChatRoom = chatRoomDetailUIState.localChatRoom,
|
||||||
onNavigateToRoute = onNavigateToRoute
|
onPopBackToRoute = onPopBackToRoute
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -240,10 +240,9 @@ fun ChatRoomDetailScreen(
|
|||||||
),
|
),
|
||||||
enabled = chatRoomDetailUIState.localChatRoom.chatRoom.leftGroupAt != null,
|
enabled = chatRoomDetailUIState.localChatRoom.chatRoom.leftGroupAt != null,
|
||||||
onClick = {
|
onClick = {
|
||||||
onNavigateToRoute.invoke(
|
chatRoomDetailViewModel.softDeleteGroup(
|
||||||
ImplementationPendingRoute(
|
localChatRoom = chatRoomDetailUIState.localChatRoom,
|
||||||
"Delete Group"
|
onPopBackToRoute = onPopBackToRoute
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -345,7 +344,8 @@ private fun ChatRoomMessagingScreenPreview() {
|
|||||||
),
|
),
|
||||||
nostrRepository = NostrRepository.NO_OP_NOSTR_REPOSITORY,
|
nostrRepository = NostrRepository.NO_OP_NOSTR_REPOSITORY,
|
||||||
chatRepository = ChatRepository.NO_OP_CHAT_REPOSITORY,
|
chatRepository = ChatRepository.NO_OP_CHAT_REPOSITORY,
|
||||||
onNavigateToRoute = {}
|
onNavigateToRoute = {},
|
||||||
|
onPopBackToRoute = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-4
@@ -51,7 +51,7 @@ import at.torch.compose.ui.composable.navigation.routes.ChatRoomCreationRoute
|
|||||||
import at.torch.compose.ui.composable.navigation.routes.ChatRoomDetailRoute
|
import at.torch.compose.ui.composable.navigation.routes.ChatRoomDetailRoute
|
||||||
import at.torch.compose.ui.composable.navigation.routes.ChatRoomMessagingRoute
|
import at.torch.compose.ui.composable.navigation.routes.ChatRoomMessagingRoute
|
||||||
import at.torch.compose.ui.composable.navigation.routes.CreateProfileRoute
|
import at.torch.compose.ui.composable.navigation.routes.CreateProfileRoute
|
||||||
import at.torch.compose.ui.composable.navigation.routes.FeedRoute
|
import at.torch.compose.ui.composable.navigation.routes.HomeRoute
|
||||||
import at.torch.compose.ui.composable.navigation.routes.ImplementationPendingRoute
|
import at.torch.compose.ui.composable.navigation.routes.ImplementationPendingRoute
|
||||||
import at.torch.compose.ui.composable.navigation.routes.KeyPackageManagementRoute
|
import at.torch.compose.ui.composable.navigation.routes.KeyPackageManagementRoute
|
||||||
import at.torch.compose.ui.composable.navigation.routes.LandingRoute
|
import at.torch.compose.ui.composable.navigation.routes.LandingRoute
|
||||||
@@ -444,7 +444,7 @@ fun TorchNavHost(
|
|||||||
SocialPreconditionScreen(
|
SocialPreconditionScreen(
|
||||||
onNavigateToSkipForNow = {
|
onNavigateToSkipForNow = {
|
||||||
navController.navigate(
|
navController.navigate(
|
||||||
route = FeedRoute(
|
route = HomeRoute(
|
||||||
activeUserPublicKey = route.activeUserPubkey
|
activeUserPublicKey = route.activeUserPubkey
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -465,8 +465,8 @@ fun TorchNavHost(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable<FeedRoute> { backStackEntry ->
|
composable<HomeRoute> { backStackEntry ->
|
||||||
val route = backStackEntry.toRoute<FeedRoute>()
|
val route = backStackEntry.toRoute<HomeRoute>()
|
||||||
HomeScreen(
|
HomeScreen(
|
||||||
activeUserPublicKey = route.activeUserPublicKey,
|
activeUserPublicKey = route.activeUserPublicKey,
|
||||||
onNavigateToRoute = { eventRoute ->
|
onNavigateToRoute = { eventRoute ->
|
||||||
@@ -594,6 +594,12 @@ fun TorchNavHost(
|
|||||||
navController.navigate(
|
navController.navigate(
|
||||||
route = actionRoute
|
route = actionRoute
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
onPopBackToRoute = { popRoute ->
|
||||||
|
navController.popBackStack(
|
||||||
|
route = popRoute,
|
||||||
|
inclusive = false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -3,6 +3,6 @@ package at.torch.compose.ui.composable.navigation.routes
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class FeedRoute(
|
data class HomeRoute(
|
||||||
val activeUserPublicKey: String
|
val activeUserPublicKey: String
|
||||||
): Route()
|
): Route()
|
||||||
+37
-12
@@ -9,11 +9,10 @@ import androidx.lifecycle.ViewModelProvider
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.initializer
|
import androidx.lifecycle.viewmodel.initializer
|
||||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||||
import at.torch.compose.database.model.ChatRoom
|
|
||||||
import at.torch.compose.database.model.intermdiate.LocalChatRoom
|
import at.torch.compose.database.model.intermdiate.LocalChatRoom
|
||||||
import at.torch.compose.repository.ChatRepository
|
import at.torch.compose.repository.ChatRepository
|
||||||
import at.torch.compose.repository.NostrRepository
|
import at.torch.compose.repository.NostrRepository
|
||||||
import at.torch.compose.ui.composable.navigation.routes.ImplementationPendingRoute
|
import at.torch.compose.ui.composable.navigation.routes.HomeRoute
|
||||||
import at.torch.compose.ui.composable.navigation.routes.Route
|
import at.torch.compose.ui.composable.navigation.routes.Route
|
||||||
import at.torch.compose.ui.view.state.ChatRoomDetailUIState
|
import at.torch.compose.ui.view.state.ChatRoomDetailUIState
|
||||||
import co.touchlab.kermit.Logger
|
import co.touchlab.kermit.Logger
|
||||||
@@ -46,6 +45,7 @@ class ChatRoomDetailViewModel(
|
|||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val localChatRoom = chatRepository.getChatRoomByIdentifier(chatRoomId)
|
val localChatRoom = chatRepository.getChatRoomByIdentifier(chatRoomId)
|
||||||
|
|
||||||
|
logger.d("localChatRoom: $localChatRoom")
|
||||||
chatRoomDetailUIState = if (localChatRoom == null) {
|
chatRoomDetailUIState = if (localChatRoom == null) {
|
||||||
// Initiate new chat... and navigate to new chat...
|
// Initiate new chat... and navigate to new chat...
|
||||||
ChatRoomDetailUIState.Error("Missing chat room")
|
ChatRoomDetailUIState.Error("Missing chat room")
|
||||||
@@ -59,20 +59,45 @@ class ChatRoomDetailViewModel(
|
|||||||
|
|
||||||
fun leaveGroup(
|
fun leaveGroup(
|
||||||
localChatRoom: LocalChatRoom,
|
localChatRoom: LocalChatRoom,
|
||||||
onNavigateToRoute: (Route) -> Unit
|
onPopBackToRoute: (Route) -> Unit
|
||||||
) {
|
) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
chatRepository.leaveChatRoom(localChatRoom)
|
chatRepository.leaveChatRoom(
|
||||||
|
localChatRoom,
|
||||||
viewModelScope.launch(Dispatchers.Main) {
|
onCompletion = {
|
||||||
onNavigateToRoute.invoke(
|
viewModelScope.launch(Dispatchers.Main) {
|
||||||
ImplementationPendingRoute(
|
onPopBackToRoute.invoke(
|
||||||
"Leave Group"
|
HomeRoute(
|
||||||
)
|
activeUserPublicKey = activeUserPublicKey,
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun softDeleteGroup(
|
||||||
|
localChatRoom: LocalChatRoom,
|
||||||
|
onPopBackToRoute: (Route) -> Unit
|
||||||
|
) {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
chatRepository.softDeleteChatRoom(
|
||||||
|
chatRoom = localChatRoom.chatRoom,
|
||||||
|
onCompletion = {
|
||||||
|
viewModelScope.launch(Dispatchers.Main) {
|
||||||
|
onPopBackToRoute.invoke(
|
||||||
|
HomeRoute(
|
||||||
|
activeUserPublicKey = activeUserPublicKey,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "ChatRoomDetailViewModel"
|
private const val TAG = "ChatRoomDetailViewModel"
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user