diff --git a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/GiftWrapPayloadDao.kt b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/GiftWrapPayloadDao.kt index d2a39873..1ff79587 100644 --- a/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/GiftWrapPayloadDao.kt +++ b/composeApp/src/commonMain/kotlin/at/torch/compose/database/dao/GiftWrapPayloadDao.kt @@ -1,18 +1,23 @@ package at.torch.compose.database.dao import androidx.room3.Dao +import androidx.room3.Insert import androidx.room3.Query import androidx.room3.Upsert +import at.torch.compose.database.model.GiftWrapPayload import kotlinx.coroutines.flow.Flow @Dao interface GiftWrapPayloadDao { @Query("SELECT * FROM GiftWrapPayload") - fun getAllGiftWrapUnsigned(): List + fun getAllGiftWrapUnsigned(): List @Query("SELECT * FROM GiftWrapPayload WHERE publicKey = :publicKey AND giftWrapSealId IS NULL") - fun observeUnsealedGiftWrapPayloads(publicKey: String): Flow + fun observeUnsealedGiftWrapPayloads(publicKey: String): Flow @Upsert - suspend fun upsert(giftWrapPayload: at.torch.compose.database.model.GiftWrapPayload) + suspend fun upsert(giftWrapPayload: GiftWrapPayload) + + @Insert + suspend fun insert(giftWrapPayload: GiftWrapPayload) } \ 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 797e3664..db67ebd8 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 @@ -45,6 +45,7 @@ import com.vitorpamplona.quartz.nip59Giftwrap.rumors.RumorAssembler import com.vitorpamplona.quartz.utils.RandomInstance import com.vitorpamplona.quartz.utils.TimeUtils import kotlin.io.encoding.Base64 +import kotlin.time.Clock import kotlin.time.Instant @Dao @@ -121,7 +122,7 @@ abstract class MarmotOutboundDao( } @Transaction - open suspend fun inviteMember( + open suspend fun inviteMemberToChatRoom( localChatRoom: LocalChatRoom, peerPublicKey: HexKey, peerKeyPackage: MarmotKeyPackage, @@ -204,6 +205,7 @@ abstract class MarmotOutboundDao( welcomeBytes = welcomeBytes, peerKeyPackageEventId = peerKeyPackage.id, relays = relays, + createdAt = Clock.System.now() ) } } else { @@ -283,51 +285,64 @@ abstract class MarmotOutboundDao( welcomeBytes: ByteArray, peerKeyPackageEventId: HexKey, relays: List, + createdAt: Instant ) { - val welcomeBase64 = Base64.encode( - source = welcomeBytes - ) + try { + 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 - ) - - database.giftWrapPayloadDao().upsert( - GiftWrapPayload( - id = welcomeEventId, - publicKey = userPublicKey, - chatRoomId = nostrGroupId, + // GiftWrap... + val welcomeEvent = WelcomeEvent.build( + welcomeBase64 = welcomeBase64, + createdAt = createdAt.epochSeconds, + keyPackageEventId = peerKeyPackageEventId, + relays = relays.map { NormalizedRelayUrl(it) }, + nostrGroupId = nostrGroupId, + ) + // Compute welcomeEventId + val welcomeEventId = EventHasher.hashId( + pubKey = userPublicKey, kind = welcomeEvent.kind, tags = welcomeEvent.tags, - content = welcomeEvent.content, - createdAt = Instant.fromEpochSeconds(welcomeEvent.createdAt) + createdAt = welcomeEvent.createdAt, + content = welcomeEvent.content ) - ) + logger.d("Welcome Event: ${welcomeEvent.toJson()}") + logger.d("Welcome Event Id: $welcomeEventId") - // 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 + + database.giftWrapPayloadDao().insert( + GiftWrapPayload( + id = welcomeEventId, + publicKey = userPublicKey, + chatRoomId = nostrGroupId, + kind = welcomeEvent.kind, + tags = welcomeEvent.tags, + content = welcomeEvent.content, + createdAt = Instant.fromEpochSeconds(welcomeEvent.createdAt) + ) ) - ) + + database.marmotKeyPackageDao().getMarmotKeyPackageById(peerKeyPackageEventId)?.let { marmotKeyPackage -> + database.profileDao().getProfileByPublicKey(marmotKeyPackage.publicKey)?.let { profile -> + // Save chatMessage for the invite... + database.chatMessageDao().upsert( + ChatMessage( + content = "Invited ${profile.humanReadableNameOrPubkey() ?: "participant"} to chat", // use profile.humanReadable... + chatRoomId = nostrGroupId, + senderPublicKey = userPublicKey, + isUserMessage = true, // TODO: This is information message... + giftWrapPayloadId = welcomeEventId, + marmotGroupEventId = null, + marmotInnerEventId = null + ) + ) + } + } + } catch (e: Throwable) { + logger.e("Failed to deliver welcome:", e) + } } private fun encryptedCommitEvent( 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 5a5d98ab..cc637ab9 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 @@ -136,7 +136,7 @@ class DatabaseChatRepository( peerPublicKey: HexKey, peerKeyPackage: MarmotKeyPackage ) { - database.marmotOutboundDao().inviteMember( + database.marmotOutboundDao().inviteMemberToChatRoom( localChatRoom = localChatRoom, peerPublicKey = peerPublicKey, peerKeyPackage = peerKeyPackage 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 3ad84758..ae9d3144 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 @@ -7,6 +7,7 @@ import at.torch.compose.database.model.UnsignedNostrEvent import at.torch.compose.database.model.intermdiate.LocalAccount import at.torch.compose.database.model.intermdiate.LocalBroadcastNostrEventRequest import at.torch.compose.database.model.types.SynchronizationFilter +import at.torch.compose.extensions.toHex import at.torch.compose.nostr.Relays import co.touchlab.kermit.Logger import com.vitorpamplona.quartz.marmot.mip00KeyPackages.KeyPackageRelayListEvent @@ -406,15 +407,20 @@ class DatabaseNostrRepository( database.marmotCommitResultDao().getMarmotCommitRequestById( nostrEventId = broadcastNostrEventRequest.nostrEventId )?.let { marmotCommitResult -> + logger.d("marmotCommitResult: $marmotCommitResult") if (marmotCommitResult.isOneMemberInitialGroupCreation.not()) { + logger.d("Deliver Welcome") database.chatRoomDao().findChatRoomById(marmotCommitResult.chatRoomId)?.let { localChatRoom -> // TODO: Update status of participant Invitation.PENDING -> Invitation.SENT + logger.d("localChatRoom: $localChatRoom") marmotCommitResult.welcomeBytes?.let { welcomeBytes -> + logger.d("welcomeBytes: ${welcomeBytes.toHex()}") database.marmotOutboundDao().deliveryWelcome( nostrGroupId = marmotCommitResult.chatRoomId, userPublicKey = marmotCommitResult.userPublicKey, welcomeBytes = welcomeBytes, peerKeyPackageEventId = marmotCommitResult.peerKeyPackageEventId, + createdAt = marmotCommitResult.createdAt, relays = Relays.DefaultDMRelayList.map { it.url } // TODO: Get localChatRoom relays... ) }