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 633ac1ef..a8453348 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 @@ -20,6 +20,7 @@ import at.torch.compose.exceptions.GiftWrapImpersonationException import at.torch.compose.exceptions.GiftWrapSealDecryptionException import at.torch.compose.exceptions.GiftWrapUnsealException import at.torch.compose.exceptions.MarmotMissingKeyPackageBundleException +import at.torch.compose.exceptions.MarmotMissingNostrGroupDataExtension import at.torch.compose.exceptions.MarmotWelcomeEventMissingKeyPackageEventIdException import at.torch.compose.extensions.toHex import at.torch.compose.network.serialization.encodeToJsonString @@ -615,7 +616,7 @@ abstract class NostrDao( ) logger.d("Group: $group") - val nostrGroupId = group.groupId.toHex() + val nostrGroupId = group.currentMarmotData()?.nostrGroupId ?: throw MarmotMissingNostrGroupDataExtension("Didn't find group data for $group") logger.d("nostrGroupId: $nostrGroupId") 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 e1f5757a..9fe8369b 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 @@ -78,8 +78,8 @@ data class ChatRoom( ): at.torch.compose.database.model.traits.TimestampedEntity, at.torch.compose.database.model.traits.LocalStoreEntity, at.torch.compose.database.model.traits.UserViewableEntity, - at.torch.compose.database.model.traits.SoftDeletableEntity { - + at.torch.compose.database.model.traits.SoftDeletableEntity +{ companion object { const val TAG = "ChatRoom" diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/HomeScreen.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/HomeScreen.kt index 43edbd75..76f5cba2 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/HomeScreen.kt @@ -185,10 +185,6 @@ fun HomeScreen( factory = at.torch.compose.ui.view.model.ChatRoomListViewModel.factory( initialChatRoomListUIState = at.torch.compose.ui.view.state.ChatRoomListUIState.Loading, publicKey = homeScreenUIState.profileWithFollowing.profile.publicKey, - synchronizationFilter = homeScreenViewModel.getSynchronizationFilter( - homeScreenTypeType, - profileWithFollowing = homeScreenUIState.profileWithFollowing - ), nostrRepository = nostrRepository, chatRepository = chatRepository ), diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/ShareProfileScreen.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/ShareProfileScreen.kt index 4a9fc699..44be1a12 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/ShareProfileScreen.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/composable/ShareProfileScreen.kt @@ -164,7 +164,7 @@ fun ShareProfileScreen( clipboardManager.setText( buildAnnotatedString { - activeUserPublicKey.hexToNpubHrp() + append(activeUserPublicKey.hexToNpubHrp()) } ) } diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomListViewModel.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomListViewModel.kt index 9722cfd2..998d84d0 100755 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomListViewModel.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/ChatRoomListViewModel.kt @@ -25,15 +25,23 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory +import at.torch.compose.database.model.NegentropySynchronizeRequest +import at.torch.compose.database.model.types.SynchronizationFilter +import at.torch.compose.nostr.Relays import co.touchlab.kermit.Logger +import com.vitorpamplona.quartz.marmot.mip03GroupMessages.GroupEvent +import com.vitorpamplona.quartz.marmot.mls.group.MlsGroup +import com.vitorpamplona.quartz.marmot.mls.group.MlsGroupState +import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent +import io.ktor.utils.io.core.toByteArray import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.IO import kotlinx.coroutines.launch +import kotlin.math.log class ChatRoomListViewModel( initialChatRoomListUIState: at.torch.compose.ui.view.state.ChatRoomListUIState, val publicKey: String, - val synchronizationFilter: at.torch.compose.database.model.types.SynchronizationFilter, val nostrRepository: at.torch.compose.repository.NostrRepository, val chatRepository: at.torch.compose.repository.ChatRepository ): ViewModel() { @@ -65,20 +73,78 @@ class ChatRoomListViewModel( logger.d("scheduleSynchronization") viewModelScope.launch(Dispatchers.IO) { // Sync Notifications... might want to also run this in the background + val chatRequestFilter = SynchronizationFilter( + kinds = arrayOf( + GiftWrapEvent.KIND, + ), + tags = mapOf( + Pair("p", listOf(publicKey)) + ), + limit = 50 + ) nostrRepository.queueNegentropySynchronizeRequest( at.torch.compose.nostr.Relays.DefaultDMRelayList.shuffled().map { normalizedRelayUrl -> - _root_ide_package_.at.torch.compose.database.model.NegentropySynchronizeRequest( - id = at.torch.compose.database.model.NegentropySynchronizeRequest.computeId( + NegentropySynchronizeRequest( + id = NegentropySynchronizeRequest.computeId( relayURL = normalizedRelayUrl.url, - synchronizationFilter = synchronizationFilter + synchronizationFilter = chatRequestFilter ), purpose = "chat", - synchronizationFilter = synchronizationFilter, + synchronizationFilter = chatRequestFilter, relayURL = normalizedRelayUrl.url, level = 0 ) } ) + + // Get/observe groupIds for publicKey + val chatRooms = chatRepository.getChatRoomListByPublicKey(publicKey) + val relayChatRoomMaps = Relays.DefaultDMRelayList.map { dmRelay -> + dmRelay.url to chatRooms.map { localChatRoom -> + // TODO: Just return localChatRoom.chatRoom.id once we have nostrGroupId stored as the chatRoom primary id + if (localChatRoom.chatRoom.mlsGroupState != null) { + + if (localChatRoom.chatRoom.id.length == 64) { + localChatRoom.chatRoom.id + } else { + val group = MlsGroup.restore( + MlsGroupState.decodeTls( + localChatRoom.chatRoom.mlsGroupState.hexToByteArray() + ) + ) + + logger.d("chatRoomId: ${localChatRoom.chatRoom.id} vs nostrGroupId (${group.currentMarmotData()?.nostrGroupId})") + group.currentMarmotData()?.nostrGroupId ?: localChatRoom.chatRoom.id + } + } else { + localChatRoom.chatRoom.id + } + } + } + // TODO: Get relay with chatGroups... + val negentropySyncRequests = relayChatRoomMaps.map { relayChatRoomMap -> + val mlsGroupMessageFilter = SynchronizationFilter( + kinds = arrayOf(GroupEvent.KIND), + tags = mapOf("h" to relayChatRoomMap.second), + ) + logger.d("mlsGroupMessageFilter: $mlsGroupMessageFilter") + + NegentropySynchronizeRequest( + id = NegentropySynchronizeRequest.computeId( + relayURL = relayChatRoomMap.first, + synchronizationFilter = mlsGroupMessageFilter + ), + purpose = "messages", + synchronizationFilter = mlsGroupMessageFilter, + relayURL = relayChatRoomMap.first, + level = 0 + ) + } + + logger.d("negentropySyncRequests: ${negentropySyncRequests.map { it.synchronizationFilter }}") + nostrRepository.queueNegentropySynchronizeRequest( + negentropySyncRequests + ) } } @@ -178,7 +244,6 @@ class ChatRoomListViewModel( fun factory( initialChatRoomListUIState: at.torch.compose.ui.view.state.ChatRoomListUIState = at.torch.compose.ui.view.state.ChatRoomListUIState.Loading, publicKey: String, - synchronizationFilter: at.torch.compose.database.model.types.SynchronizationFilter, nostrRepository: at.torch.compose.repository.NostrRepository, chatRepository: at.torch.compose.repository.ChatRepository ): ViewModelProvider.Factory = viewModelFactory { @@ -186,7 +251,6 @@ class ChatRoomListViewModel( ChatRoomListViewModel( initialChatRoomListUIState = initialChatRoomListUIState, publicKey = publicKey, - synchronizationFilter = synchronizationFilter, nostrRepository = nostrRepository, chatRepository = chatRepository ) diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/HomeViewModel.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/HomeViewModel.kt index ae5247f7..43fe5e1c 100755 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/HomeViewModel.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/HomeViewModel.kt @@ -9,7 +9,9 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory +import at.torch.compose.database.model.types.SynchronizationFilter import co.touchlab.kermit.Logger +import com.vitorpamplona.quartz.marmot.mip03GroupMessages.GroupEvent import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent import kotlinx.coroutines.Dispatchers @@ -56,29 +58,6 @@ class HomeViewModel( } } - fun getSynchronizationFilter( - homeScreenType: HomeScreenType, - profileWithFollowing: at.torch.compose.database.model.intermdiate.LocalProfileWithFollowing, - ): at.torch.compose.database.model.types.SynchronizationFilter { - - Clock.System.now().minus(12.hours) - Clock.System.now() - - return when (homeScreenType) { - HomeScreenType.Messages -> { - _root_ide_package_.at.torch.compose.database.model.types.SynchronizationFilter( - kinds = arrayOf( - GiftWrapEvent.KIND, - ), - tags = mapOf( - Pair("p", listOf(activeUserPublicKey)) - ), - limit = 50 - ) - } - } - } - companion object { const val TAG = "HomeViewModel" diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/NavigationViewModel.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/NavigationViewModel.kt index d6e8b0c1..935ab9f0 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/NavigationViewModel.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/NavigationViewModel.kt @@ -75,18 +75,18 @@ class NavigationViewModel( } } } else { - if (startupRoute != null) { - val localAccount = nostrRepository.observeProfile( - publicKey = activeUserPublicKey - ).firstOrNull() - - processLocalAccount(localAccount) - - } else { - _navigationUIState.getAndUpdate { - NavigationUIState.StartupPhoenix - } - } +// if (startupRoute != null) { +// val localAccount = nostrRepository.observeProfile( +// publicKey = activeUserPublicKey +// ).firstOrNull() +// +// processLocalAccount(localAccount) +// +// } else { +// _navigationUIState.getAndUpdate { +// NavigationUIState.StartupPhoenix +// } +// } } }