Use unique constraint when saving to the database.

This commit is contained in:
Kgothatso Ngako
2026-07-12 21:15:27 +02:00
parent 9331669aea
commit 7e1be3c3f6
4 changed files with 69 additions and 43 deletions

View File

@@ -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<at.torch.compose.database.model.GiftWrapPayload>
fun getAllGiftWrapUnsigned(): List<GiftWrapPayload>
@Query("SELECT * FROM GiftWrapPayload WHERE publicKey = :publicKey AND giftWrapSealId IS NULL")
fun observeUnsealedGiftWrapPayloads(publicKey: String): Flow<at.torch.compose.database.model.GiftWrapPayload?>
fun observeUnsealedGiftWrapPayloads(publicKey: String): Flow<GiftWrapPayload?>
@Upsert
suspend fun upsert(giftWrapPayload: at.torch.compose.database.model.GiftWrapPayload)
suspend fun upsert(giftWrapPayload: GiftWrapPayload)
@Insert
suspend fun insert(giftWrapPayload: GiftWrapPayload)
}

View File

@@ -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<String>,
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(

View File

@@ -136,7 +136,7 @@ class DatabaseChatRepository(
peerPublicKey: HexKey,
peerKeyPackage: MarmotKeyPackage
) {
database.marmotOutboundDao().inviteMember(
database.marmotOutboundDao().inviteMemberToChatRoom(
localChatRoom = localChatRoom,
peerPublicKey = peerPublicKey,
peerKeyPackage = peerKeyPackage

View File

@@ -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...
)
}