diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/MarmotCommitResultDao.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/MarmotCommitResultDao.kt index 6c2c8a53..aa8a0213 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/MarmotCommitResultDao.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/MarmotCommitResultDao.kt @@ -10,6 +10,9 @@ import fr.acinq.bitcoin.PublicKey @Dao interface MarmotCommitResultDao { + @Query("SELECT * FROM MarmotCommitResult WHERE id = :nostrEventId") + suspend fun getMarmotCommitRequestById(nostrEventId: HexKey): MarmotCommitResult? + @Upsert suspend fun upsert(marmotCommitResult: MarmotCommitResult) } \ 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 057216db..3e05b12d 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 @@ -206,6 +206,7 @@ abstract class MarmotOutboundDao( framedCommitBytes = commitResult.preCommitExporterSecret, groupInfoBytes = commitResult.groupInfoBytes, userPublicKey = userPublicKey, + peerKeyPackageEventId = peerKeyPackage.id, createdAt = Instant.fromEpochSeconds(commitEvent.createdAt) ) ) @@ -222,55 +223,66 @@ abstract class MarmotOutboundDao( if (isOneMemberInitialGroupCreation) { // Send welcome event to participant commitResult.welcomeBytes?.let { welcomeBytes -> - val welcomeBase64 = Base64.encode( - source = welcomeBytes - ) - - // GiftWrap... - val welcomeEvent = WelcomeEvent.build( - welcomeBase64 = welcomeBase64, - keyPackageEventId = peerKeyPackage.id, - relays = relays.map { NormalizedRelayUrl(it) }, + deliveryWelcome( nostrGroupId = nostrGroupId, - ) - // Compute welcomeEventId - val welcomeEventId = EventHasher.hashId( - pubKey = userPublicKey, - kind = welcomeEvent.kind, - tags = welcomeEvent.tags, - createdAt = welcomeEvent.createdAt, - content = welcomeEvent.content - ) - - GiftWrapPayload( - id = welcomeEventId, - publicKey = userPublicKey, - chatRoomId = nostrGroupId, - kind = welcomeEvent.kind, - tags = welcomeEvent.tags, - content = welcomeEvent.content, - createdAt = Instant.fromEpochSeconds(welcomeEvent.createdAt) - ) - - // Save chatMessage for the invite... - database.chatMessageDao().upsert( - ChatMessage( - content = "Invited participant to chat", // use profile.humanReadable... - chatRoomId = nostrGroupId, - senderPublicKey = userPublicKey, - isUserMessage = true, // TODO: This is information message... - giftWrapPayloadId = welcomeEventId, - marmotGroupEventId = null, - marmotInnerEventId = null - ) + userPublicKey = userPublicKey, + welcomeBytes = welcomeBytes, + peerKeyPackageEventId = peerKeyPackage.id, + relays = relays, ) } } + } + suspend fun deliveryWelcome( + nostrGroupId: HexKey, + userPublicKey: HexKey, + welcomeBytes: ByteArray, + peerKeyPackageEventId: HexKey, + relays: List, + ) { + val welcomeBase64 = Base64.encode( + source = welcomeBytes + ) + // GiftWrap... + val welcomeEvent = WelcomeEvent.build( + welcomeBase64 = welcomeBase64, + keyPackageEventId = peerKeyPackageEventId, + relays = relays.map { NormalizedRelayUrl(it) }, + nostrGroupId = nostrGroupId, + ) + // Compute welcomeEventId + val welcomeEventId = EventHasher.hashId( + pubKey = userPublicKey, + kind = welcomeEvent.kind, + tags = welcomeEvent.tags, + createdAt = welcomeEvent.createdAt, + content = welcomeEvent.content + ) + GiftWrapPayload( + id = welcomeEventId, + publicKey = userPublicKey, + chatRoomId = nostrGroupId, + kind = welcomeEvent.kind, + tags = welcomeEvent.tags, + content = welcomeEvent.content, + createdAt = Instant.fromEpochSeconds(welcomeEvent.createdAt) + ) - + // Save chatMessage for the invite... + database.chatMessageDao().upsert( + ChatMessage( + content = "Invited participant to chat", // use profile.humanReadable... + chatRoomId = nostrGroupId, + senderPublicKey = userPublicKey, + isUserMessage = true, // TODO: This is information message... + giftWrapPayloadId = welcomeEventId, + marmotGroupEventId = null, + marmotInnerEventId = null + ) + ) } private fun encryptedCommitEvent( diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/database/model/MarmotCommitResult.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/database/model/MarmotCommitResult.kt index b97a7661..962a8c51 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/database/model/MarmotCommitResult.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/database/model/MarmotCommitResult.kt @@ -41,6 +41,8 @@ data class MarmotCommitResult( // TODO: Rename this to GiftWrapPayload... val userPublicKey: HexKey, + val peerKeyPackageEventId: HexKey, + val chatRoomId: HexKey, val isOneMemberInitialGroupCreation: Boolean, diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/database/repository/DatabaseNostrRepository.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/database/repository/DatabaseNostrRepository.kt index 62b0f89b..d0e6890f 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/database/repository/DatabaseNostrRepository.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/database/repository/DatabaseNostrRepository.kt @@ -1,5 +1,6 @@ package at.torch.compose.database.repository +import at.torch.compose.database.model.BroadcastNostrEventRequest import at.torch.compose.database.model.UnsignedNostrEvent import at.torch.compose.database.model.intermdiate.LocalAccount import at.torch.compose.nostr.Relays @@ -384,7 +385,7 @@ class DatabaseNostrRepository( } override suspend fun broadcastProcessed( - broadcastNostrEventRequest: at.torch.compose.database.model.BroadcastNostrEventRequest, + broadcastNostrEventRequest: BroadcastNostrEventRequest, status: String ) { database.broadcastNostrEventRequestDao().upsert( @@ -414,6 +415,25 @@ class DatabaseNostrRepository( ) ) } + + database.marmotCommitResultDao().getMarmotCommitRequestById( + nostrEventId = broadcastNostrEventRequest.nostrEventId + )?.let { marmotCommitResult -> + if (marmotCommitResult.isOneMemberInitialGroupCreation.not()) { + database.chatRoomDao().findChatRoomById(marmotCommitResult.chatRoomId)?.let { localChatRoom -> + // TODO: Update status of participant Invitation.PENDING -> Invitation.SENT + marmotCommitResult.welcomeBytes?.let { welcomeBytes -> + database.marmotOutboundDao().deliveryWelcome( + nostrGroupId = marmotCommitResult.chatRoomId, + userPublicKey = marmotCommitResult.userPublicKey, + welcomeBytes = welcomeBytes, + peerKeyPackageEventId = marmotCommitResult.peerKeyPackageEventId, + relays = Relays.DefaultDMRelayList.map { it.url } // TODO: Get localChatRoom relays... + ) + } + } + } + } } } @@ -742,7 +762,7 @@ class DatabaseNostrRepository( return database.relayDao().observePublicKeyRelays(publicKey) } - override suspend fun scheduleBroadcastNostrEventRequests(broadcastNostrEventRequests: List) { + override suspend fun scheduleBroadcastNostrEventRequests(broadcastNostrEventRequests: List) { database.broadcastNostrEventRequestDao().insert( broadcastNostrEventRequests ) diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/SynchronizationViewModel.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/SynchronizationViewModel.kt index 564aebc7..61b8c3bb 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/SynchronizationViewModel.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/ui/view/model/SynchronizationViewModel.kt @@ -7,7 +7,13 @@ import androidx.lifecycle.viewmodel.viewModelFactory import at.torch.compose.database.model.SynchronizeNostrEventRequest import at.torch.compose.database.model.types.SynchronizationFilter import at.torch.compose.network.dto.toRelayDTO +import at.torch.compose.network.relays.RelaysSocketManager import at.torch.compose.network.sockets.NostrIncomingMessage +import at.torch.compose.network.sockets.NostrSocketClientFactory +import at.torch.compose.repository.CachingImportRepository +import at.torch.compose.repository.ChatRepository +import at.torch.compose.repository.NostrRepository +import at.torch.compose.repository.RelayRepository import co.touchlab.kermit.Logger import com.vitorpamplona.negentropy.Negentropy import com.vitorpamplona.negentropy.storage.StorageVector @@ -35,16 +41,16 @@ import kotlin.time.Duration.Companion.milliseconds class SynchronizationViewModel( val activeWalletStateFlow: StateFlow, - val nostrRepository: at.torch.compose.repository.NostrRepository, - val chatRepository: at.torch.compose.repository.ChatRepository, - val relayRepository: at.torch.compose.repository.RelayRepository, + val nostrRepository: NostrRepository, + val chatRepository: ChatRepository, + val relayRepository: RelayRepository, val scope: CoroutineScope, ): ViewModel() { - val relaysSocketManager = at.torch.compose.network.relays.RelaysSocketManager( + val relaysSocketManager = RelaysSocketManager( activeWalletStateFlow = activeWalletStateFlow, - nostrSocketClientFactory = at.torch.compose.network.sockets.NostrSocketClientFactory, - cachingImportRepository = at.torch.compose.repository.CachingImportRepository.NO_OP_CACHING_IMPORT_REPOSITORY, + nostrSocketClientFactory = NostrSocketClientFactory, + cachingImportRepository = CachingImportRepository.NO_OP_CACHING_IMPORT_REPOSITORY, relayRepository = relayRepository, ) @@ -55,9 +61,9 @@ class SynchronizationViewModel( fun factory( activeWalletStateFlow: StateFlow, - nostrRepository: at.torch.compose.repository.NostrRepository, - chatRepository: at.torch.compose.repository.ChatRepository, - relayRepository: at.torch.compose.repository.RelayRepository, + nostrRepository: NostrRepository, + chatRepository: ChatRepository, + relayRepository: RelayRepository, scope: CoroutineScope, ): ViewModelProvider.Factory = viewModelFactory { initializer {