Bug fix for resyncing

This commit is contained in:
Kgothatso Ngako
2026-07-08 21:19:02 +02:00
parent 284ced17c9
commit c39b26c2e1
12 changed files with 60 additions and 35 deletions

View File

@@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "ec367eb293e2be375d50f8d1b8133e00",
"identityHash": "f0a498de098c8c2c2007d4309022282e",
"entities": [
{
"tableName": "BroadcastNostrEventReceipt",
@@ -1252,7 +1252,7 @@
},
{
"tableName": "MarmotGroupEvent",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `userPublicKey` TEXT NOT NULL, `publicKey` TEXT NOT NULL, `chatRoomId` TEXT NOT NULL, `signature` TEXT NOT NULL, `encryptedContent` TEXT NOT NULL, `expiresAt` INTEGER, `nostrEventId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `userPublicKey` TEXT NOT NULL, `publicKey` TEXT NOT NULL, `chatRoomId` TEXT NOT NULL, `signature` TEXT NOT NULL, `encryptedContent` TEXT NOT NULL, `expiresAt` INTEGER, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`id`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
@@ -1295,12 +1295,6 @@
"columnName": "expiresAt",
"affinity": "INTEGER"
},
{
"fieldPath": "nostrEventId",
"columnName": "nostrEventId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "createdAt",
"columnName": "createdAt",
@@ -1342,7 +1336,7 @@
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"nostrEventId"
"id"
],
"referencedColumns": [
"id"
@@ -3458,7 +3452,7 @@
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ec367eb293e2be375d50f8d1b8133e00')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f0a498de098c8c2c2007d4309022282e')"
]
}
}

View File

@@ -4,6 +4,7 @@ import androidx.room3.Dao
import androidx.room3.Insert
import androidx.room3.Query
import androidx.room3.Upsert
import at.torch.compose.database.model.BroadcastNostrEventRequest
import at.torch.compose.database.model.intermdiate.LocalBroadcastNostrEventRequest
import kotlinx.coroutines.flow.Flow
import kotlin.time.Clock
@@ -12,17 +13,20 @@ import kotlin.time.Instant
@Dao
interface BroadcastNostrEventRequestDao {
@Query("SELECT * FROM BroadcastNostrEventRequest")
fun getAllBroadcastNostrEventRequests(): List<at.torch.compose.database.model.BroadcastNostrEventRequest>
fun getAllBroadcastNostrEventRequests(): List<BroadcastNostrEventRequest>
@Query("SELECT * FROM BroadcastNostrEventRequest WHERE nostrEventId = :nostrEventId ORDER BY createdAt ASC")
fun getFirstBroadcastNostrEventRequestByNostrEventId(nostrEventId: String): BroadcastNostrEventRequest?
@Query("SELECT * FROM BroadcastNostrEventRequest WHERE status = :status AND createdAt > :createdAt")
fun observeBroadcastNostrEventRequestsByStatus(status: String, createdAt: Instant = Clock.System.now()): Flow<LocalBroadcastNostrEventRequest?>
@Query("SELECT * FROM BroadcastNostrEventRequest WHERE nostrEventId = :nostrEventId")
fun observeBroadcastNostrEventRequestByNostrEventId(nostrEventId: String): Flow<at.torch.compose.database.model.BroadcastNostrEventRequest?>
fun observeBroadcastNostrEventRequestByNostrEventId(nostrEventId: String): Flow<BroadcastNostrEventRequest?>
@Upsert
suspend fun upsert(broadcastNostrEventRequest: at.torch.compose.database.model.BroadcastNostrEventRequest)
suspend fun upsert(broadcastNostrEventRequest: BroadcastNostrEventRequest): Long
@Insert
suspend fun insert(broadcastNostrEventRequests: List<at.torch.compose.database.model.BroadcastNostrEventRequest>): LongArray
suspend fun insert(broadcastNostrEventRequests: List<BroadcastNostrEventRequest>): LongArray
}

View File

@@ -3,12 +3,13 @@ package at.torch.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Upsert
import at.torch.compose.database.model.ChatMessageBroadcastNostrEventRequestRelation
@Dao
interface ChatMessageBroadcastNostrEventRequestRelationDao {
@Query("SELECT * FROM ChatMessageBroadcastNostrEventRequestRelation WHERE broadcastNostrEventRequestId = :broadcastNostrEventRequestId")
suspend fun getChatMessageBroadcastNostrEventRequestRelationByBroadcastRequest(broadcastNostrEventRequestId: Long): at.torch.compose.database.model.ChatMessageBroadcastNostrEventRequestRelation?
suspend fun getChatMessageBroadcastNostrEventRequestRelationByBroadcastRequest(broadcastNostrEventRequestId: Long): ChatMessageBroadcastNostrEventRequestRelation?
@Upsert
suspend fun upsert(chatMessageBroadcastNostrEventRequestRelation: at.torch.compose.database.model.ChatMessageBroadcastNostrEventRequestRelation)
suspend fun upsert(chatMessageBroadcastNostrEventRequestRelation: ChatMessageBroadcastNostrEventRequestRelation)
}

View File

@@ -24,6 +24,9 @@ interface ChatMessageDao {
@Query("SELECT * FROM ChatMessage WHERE marmotInnerEventId = :marmotInnerEventId ORDER BY createdAt DESC")
fun getChatMessagesByMarmotInnerEventId(marmotInnerEventId: HexKey): ChatMessage?
@Query("SELECT * FROM ChatMessage WHERE marmotGroupEventId = :marmotGroupEventId ORDER BY createdAt DESC")
fun getChatMessagesByMarmotGroupEventId(marmotGroupEventId: HexKey): ChatMessage?
@Query("SELECT COUNT(*) FROM ChatMessage WHERE chatRoomId = :chatRoomId AND senderPublicKey = :senderPublicKey")
fun countChatMessagesBySenderPublicKey(chatRoomId: HexKey, senderPublicKey: HexKey): Int

View File

@@ -394,7 +394,6 @@ abstract class MarmotOutboundDao(
expiresAt = groupEvent.expiration()?.let { Instant.fromEpochSeconds(it) },
encryptedContent = groupEvent.encryptedContent(),
chatRoomId = localChatRoom.chatRoom.id,
nostrEventId = groupEvent.id,
userPublicKey = groupEvent.pubKey,
publicKey = groupEvent.pubKey,
signature = groupEvent.sig,

View File

@@ -6,6 +6,7 @@ import androidx.room3.Transaction
import at.torch.compose.database.GENESIS_AT
import at.torch.compose.database.model.BroadcastNostrEventRequest
import at.torch.compose.database.model.ChatMessage
import at.torch.compose.database.model.ChatMessageBroadcastNostrEventRequestRelation
import at.torch.compose.database.model.ChatRoom
import at.torch.compose.database.model.Connection
import at.torch.compose.database.model.MarmotGroupEvent
@@ -384,7 +385,6 @@ abstract class NostrDao(
publicKey = groupEvent.pubKey,
encryptedContent = groupEvent.encryptedContent(),
chatRoomId = chatRoomId,
nostrEventId = nostrEvent.id,
createdAt = Instant.fromEpochSeconds(groupEvent.createdAt),
expiresAt = groupEvent.expiration()?.let { Instant.fromEpochSeconds(it) },
signature = groupEvent.sig
@@ -1170,4 +1170,30 @@ abstract class NostrDao(
}
}
}
/**
* Persist the rescheduling using a transaction so that we are assured of the existence of the
* [ChatMessageBroadcastNostrEventRequestRelation] when we produce the [at.torch.compose.database.model.BroadcastNostrEventReceipt]
*/
@Transaction
open suspend fun rescheduleBroadcastNostrEventRequests(broadcastNostrEventRequests: List<BroadcastNostrEventRequest>) {
// Check if any previous chatMessageBroadcastNostrEventRequestRelationDao exists for this nostrEventId...
broadcastNostrEventRequests.forEach { broadcastNostrEventRequest ->
val broadcastNostrEventRequestId = database.broadcastNostrEventRequestDao().upsert(
broadcastNostrEventRequest
)
database.chatMessageDao().getChatMessagesByMarmotGroupEventId(
marmotGroupEventId = broadcastNostrEventRequest.nostrEventId
)?.let { chatMessage ->
database.chatMessageBroadcastNostrEventRequestRelationDao().upsert(
ChatMessageBroadcastNostrEventRequestRelation(
broadcastNostrEventRequestId = broadcastNostrEventRequestId,
chatMessageId = chatMessage.id
)
)
}
}
}
}

View File

@@ -109,7 +109,7 @@ interface NostrEventDao {
): List<NostrEvent>
@Transaction
@Query("SELECT * FROM MarmotGroupEvent WHERE nostrEventId IS NOT NULL AND chatRoomId in (:chatRoomIds) AND createdAt > :since AND (expiresAt IS NULL OR expiresAt < :expiresAt) ORDER BY createdAt ASC LIMIT :limit")
@Query("SELECT * FROM MarmotGroupEvent WHERE chatRoomId in (:chatRoomIds) AND createdAt > :since AND (expiresAt IS NULL OR expiresAt < :expiresAt) ORDER BY createdAt ASC LIMIT :limit")
fun getMarmotGroupEvents(
chatRoomIds: Array<HexKey>,
since: Instant,

View File

@@ -28,6 +28,7 @@ data class ChatMessageBroadcastNostrEventRequestRelation(
val chatMessageId: Long,
val broadcastNostrEventRequestId: Long,
// Add nostrEventId here...
override val createdAt: Instant = Clock.System.now(),
override val updatedAt: Instant = Clock.System.now(),

View File

@@ -25,7 +25,7 @@ import kotlin.time.Instant
ForeignKey(
entity = NostrEvent::class,
parentColumns = ["id"],
childColumns = ["nostrEventId"],
childColumns = ["id"],
onDelete = ForeignKey.CASCADE,
),
],
@@ -48,17 +48,12 @@ data class MarmotGroupEvent(
val encryptedContent: String,
val expiresAt: Instant?,
/**
* What gets broadcasts?
*/
override val nostrEventId: String,
override val createdAt: Instant = Clock.System.now(),
override val updatedAt: Instant = createdAt,
override val savedAt: Instant = Clock.System.now(),
override val deletedAt: Instant? = null,
override val broadcastedAt: Instant? = null,
) : NostrEventEntity, TimestampedEntity, LocalStoreEntity, BroadcastableEntity, SoftDeletableEntity {
) : TimestampedEntity, LocalStoreEntity, BroadcastableEntity, SoftDeletableEntity {
companion object {
const val KIND = GroupEvent.KIND

View File

@@ -2,6 +2,7 @@ package at.torch.compose.database.repository
import at.torch.compose.database.GENESIS_AT
import at.torch.compose.database.model.BroadcastNostrEventRequest
import at.torch.compose.database.model.ChatMessageBroadcastNostrEventRequestRelation
import at.torch.compose.database.model.NostrEvent
import at.torch.compose.database.model.UnsignedNostrEvent
import at.torch.compose.database.model.intermdiate.LocalAccount
@@ -698,7 +699,7 @@ class DatabaseNostrRepository(
}
).map { marmotGroupEvent ->
NostrEvent(
id = marmotGroupEvent.nostrEventId,
id = marmotGroupEvent.id,
content = marmotGroupEvent.encryptedContent,
kind = GroupEvent.KIND,
tags = emptyArray(),
@@ -774,8 +775,8 @@ class DatabaseNostrRepository(
return database.relayDao().observePublicKeyRelays(publicKey)
}
override suspend fun scheduleBroadcastNostrEventRequests(broadcastNostrEventRequests: List<BroadcastNostrEventRequest>) {
database.broadcastNostrEventRequestDao().insert(
override suspend fun rescheduleBroadcastNostrEventRequests(broadcastNostrEventRequests: List<BroadcastNostrEventRequest>) {
database.nostrDao().rescheduleBroadcastNostrEventRequests(
broadcastNostrEventRequests
)
}

View File

@@ -134,7 +134,7 @@ interface NostrRepository {
suspend fun saveSearchQuery(query: String, synchronizationFilterArray: at.torch.compose.database.model.typealiases.SynchronizationFilterArray? = null)
suspend fun scheduleBroadcastNostrEventRequests(broadcastNostrEventRequests: List<BroadcastNostrEventRequest>)
suspend fun rescheduleBroadcastNostrEventRequests(broadcastNostrEventRequests: List<BroadcastNostrEventRequest>)
companion object {
val NO_OP_NOSTR_REPOSITORY = object : NostrRepository {
@@ -323,7 +323,7 @@ interface NostrRepository {
TODO("Not yet implemented")
}
override suspend fun scheduleBroadcastNostrEventRequests(broadcastNostrEventRequests: List<BroadcastNostrEventRequest>) {
override suspend fun rescheduleBroadcastNostrEventRequests(broadcastNostrEventRequests: List<BroadcastNostrEventRequest>) {
TODO("Not yet implemented")
}
}

View File

@@ -338,10 +338,11 @@ class SynchronizationViewModel(
)
}
logger.d("broadcastNostrEventRequests: $broadcastNostrEventRequests")
// TODO: Schedule broadcastNostrEventRequests
// nostrRepository.scheduleBroadcastNostrEventRequests(
// broadcastNostrEventRequests
// )
// Schedule broadcastNostrEventRequests
nostrRepository.rescheduleBroadcastNostrEventRequests(
broadcastNostrEventRequests
)
relaysSocketManager.closeNegentropySync(
negCloseCmd,