From 2bdb5bc3bf484aa5b6faf4d3c40da5244bae9dca Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Mon, 4 May 2026 22:29:22 +0200 Subject: [PATCH] Save nostrEventRelay association --- .../1.json | 65 ++++++++++++++++++- .../auxiliary/compose/database/AuxDatabase.kt | 7 ++ .../compose/database/dao/NostrDao.kt | 13 +++- .../database/dao/NostrEventRelayDao.kt | 16 +++++ .../compose/database/model/NostrEventRelay.kt | 44 +++++++++++++ .../repository/DatabaseNostrRepository.kt | 5 +- 6 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrEventRelayDao.kt create mode 100644 composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/NostrEventRelay.kt diff --git a/composeApp/schemas/ac.cord.auxiliary.compose.database.AuxDatabase/1.json b/composeApp/schemas/ac.cord.auxiliary.compose.database.AuxDatabase/1.json index 059071f1..a875ce1f 100644 --- a/composeApp/schemas/ac.cord.auxiliary.compose.database.AuxDatabase/1.json +++ b/composeApp/schemas/ac.cord.auxiliary.compose.database.AuxDatabase/1.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "52ac6d568351e25cbde397f09d3fecd5", + "identityHash": "5df12f022a01cc28ca4f7e06383826a4", "entities": [ { "tableName": "BroadcastNostrEventReceipt", @@ -806,6 +806,67 @@ } ] }, + { + "tableName": "NostrEventRelay", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`relayURL` TEXT NOT NULL, `nostrEventId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, PRIMARY KEY(`nostrEventId`, `relayURL`), FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "relayURL", + "columnName": "relayURL", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "nostrEventId", + "columnName": "nostrEventId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "nostrEventId", + "relayURL" + ] + }, + "indices": [ + { + "name": "index_NostrEventRelay_relayURL", + "unique": false, + "columnNames": [ + "relayURL" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_NostrEventRelay_relayURL` ON `${TABLE_NAME}` (`relayURL`)" + } + ], + "foreignKeys": [ + { + "table": "NostrEvent", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "nostrEventId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, { "tableName": "Post", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `repostId` TEXT, `quote` TEXT, `content` TEXT NOT NULL, `nostrEventId` TEXT NOT NULL, `replyToId` TEXT, `profilePublicKey` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `viewedAt` INTEGER, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`profilePublicKey`) REFERENCES `Profile`(`publicKey`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`replyToId`) REFERENCES `Post`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`repostId`) REFERENCES `Post`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", @@ -1987,7 +2048,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, '52ac6d568351e25cbde397f09d3fecd5')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5df12f022a01cc28ca4f7e06383826a4')" ] } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/AuxDatabase.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/AuxDatabase.kt index f8b5f495..89a822cd 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/AuxDatabase.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/AuxDatabase.kt @@ -10,6 +10,7 @@ import ac.cord.auxiliary.compose.database.dao.NegentropySynchronizeRequestDao import ac.cord.auxiliary.compose.database.dao.NegentropySynchronizeResultDao import ac.cord.auxiliary.compose.database.dao.NostrDao import ac.cord.auxiliary.compose.database.dao.NostrEventDao +import ac.cord.auxiliary.compose.database.dao.NostrEventRelayDao import ac.cord.auxiliary.compose.database.dao.PostDao import ac.cord.auxiliary.compose.database.dao.ProfileDao import ac.cord.auxiliary.compose.database.dao.QuotedRelationDao @@ -29,6 +30,7 @@ import ac.cord.auxiliary.compose.database.model.Mention import ac.cord.auxiliary.compose.database.model.NegentropySynchronizeRequest import ac.cord.auxiliary.compose.database.model.NegentropySynchronizeResult import ac.cord.auxiliary.compose.database.model.NostrEvent +import ac.cord.auxiliary.compose.database.model.NostrEventRelay import ac.cord.auxiliary.compose.database.model.Post import ac.cord.auxiliary.compose.database.model.Profile import ac.cord.auxiliary.compose.database.model.QuotedRelation @@ -59,6 +61,7 @@ val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L) NegentropySynchronizeRequest::class, NegentropySynchronizeResult::class, NostrEvent::class, + NostrEventRelay::class, Post::class, Profile::class, QuotedRelation::class, @@ -90,7 +93,11 @@ abstract class AuxDatabase: RoomDatabase() { abstract fun nostrDao(): NostrDao abstract fun nostrEventDao(): NostrEventDao + + abstract fun nostrEventRelayDao(): NostrEventRelayDao + abstract fun postDao(): PostDao + abstract fun profileDao(): ProfileDao abstract fun quotedRelationDao(): QuotedRelationDao diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrDao.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrDao.kt index 7d5629de..69b5c2eb 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrDao.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrDao.kt @@ -6,6 +6,7 @@ import ac.cord.auxiliary.compose.database.model.BroadcastNostrEventRequest import ac.cord.auxiliary.compose.database.model.Connection import ac.cord.auxiliary.compose.database.model.Mention import ac.cord.auxiliary.compose.database.model.NostrEvent +import ac.cord.auxiliary.compose.database.model.NostrEventRelay import ac.cord.auxiliary.compose.database.model.Post import ac.cord.auxiliary.compose.database.model.Profile import ac.cord.auxiliary.compose.database.model.SynchronizeNostrEventRequest @@ -100,17 +101,18 @@ abstract class NostrDao( @Transaction open suspend fun storeNostrEvent( nostrEvent: NostrEvent, + relayURL: String, synchronizationRelayURLs: List, level: Int ) { - - val storedNostrEvent = database.nostrEventDao().getNostrEventById(nostrEvent.id) if (storedNostrEvent == null) { // Add new nostr event logger.i("Store Nostr Event: $nostrEvent") database.nostrEventDao().insert(nostrEvent) + + } else if (nostrEvent.createdAt > storedNostrEvent.createdAt) { // Update nostr event` logger.i("Update Nostr Event: $nostrEvent") @@ -126,6 +128,13 @@ abstract class NostrDao( } } + database.nostrEventRelayDao().upsert( + NostrEventRelay( + relayURL = relayURL, + nostrEventId = nostrEvent.id + ) + ) + // Index nostrEvent logger.i("Index Nostr Event: ${nostrEvent.id}") indexNostrEvent( diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrEventRelayDao.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrEventRelayDao.kt new file mode 100644 index 00000000..5667633c --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrEventRelayDao.kt @@ -0,0 +1,16 @@ +package ac.cord.auxiliary.compose.database.dao + +import ac.cord.auxiliary.compose.database.model.NostrEventRelay +import ac.cord.auxiliary.compose.database.model.Relay +import androidx.room3.Dao +import androidx.room3.Query +import androidx.room3.Upsert +import kotlinx.coroutines.flow.Flow + +@Dao +interface NostrEventRelayDao { + + @Upsert + suspend fun upsert(nostrEventRelay: NostrEventRelay) + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/NostrEventRelay.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/NostrEventRelay.kt new file mode 100644 index 00000000..9b290e5c --- /dev/null +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/NostrEventRelay.kt @@ -0,0 +1,44 @@ +package ac.cord.auxiliary.compose.database.model + +import ac.cord.auxiliary.compose.database.model.traits.NostrEventEntity +import ac.cord.auxiliary.compose.database.model.traits.OptionalNostrEventEntity +import ac.cord.auxiliary.compose.database.model.traits.TimestampedEntity +import ac.cord.auxiliary.compose.database.model.traits.UnsignedNostrEventEntity +import ac.cord.auxiliary.compose.database.model.typealiases.SynchronizationFilterArray +import ac.cord.auxiliary.compose.database.model.types.SynchronizationFilter +import androidx.room3.Entity +import androidx.room3.ForeignKey +import androidx.room3.Index +import androidx.room3.PrimaryKey +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import kotlin.time.Clock +import kotlin.time.Instant +import kotlin.uuid.ExperimentalUuidApi +import kotlin.uuid.Uuid + +@OptIn(ExperimentalUuidApi::class) +@Entity( + foreignKeys = [ + ForeignKey( + entity = NostrEvent::class, + parentColumns = ["id"], + childColumns = ["nostrEventId"], + onDelete = ForeignKey.CASCADE, + ), + ], + indices = [ + Index("relayURL"), + ], + primaryKeys = ["nostrEventId","relayURL"] +) +data class NostrEventRelay( + val relayURL: String, + + override val nostrEventId: HexKey, + + override val createdAt: Instant = Clock.System.now(), + override val updatedAt: Instant = createdAt, +): NostrEventEntity, TimestampedEntity { + + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/repository/DatabaseNostrRepository.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/repository/DatabaseNostrRepository.kt index 9bc591e0..ac8c55ee 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/repository/DatabaseNostrRepository.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/repository/DatabaseNostrRepository.kt @@ -7,6 +7,7 @@ import ac.cord.auxiliary.compose.database.model.BroadcastNostrEventRequest import ac.cord.auxiliary.compose.database.model.Connection import ac.cord.auxiliary.compose.database.model.NegentropySynchronizeRequest import ac.cord.auxiliary.compose.database.model.NostrEvent +import ac.cord.auxiliary.compose.database.model.NostrEventRelay import ac.cord.auxiliary.compose.database.model.Profile import ac.cord.auxiliary.compose.database.model.RecentSearch import ac.cord.auxiliary.compose.database.model.Relay @@ -433,9 +434,10 @@ class DatabaseNostrRepository( logger.d("saveNostrEvent: $nostrEvent") database.nostrDao().storeNostrEvent( nostrEvent.copy( - broadcastedAt = nostrEvent.createdAt + broadcastedAt = nostrEvent.createdAt, ), synchronizationRelayURLs = synchronizationRelayURLs, + relayURL = synchronizeNostrEventRequest.relayURL, level = synchronizeNostrEventRequest.level ) @@ -456,6 +458,7 @@ class DatabaseNostrRepository( logger.d("saveNostrEvent: $nostrEvent") database.nostrDao().storeNostrEvent( nostrEvent, + relayURL = negentropySynchronizeRequest.relayURL, synchronizationRelayURLs = synchronizationRelayURLs, level = negentropySynchronizeRequest.level )