From c3c92d96ad501fe80898680884b74c2dbdb493ee Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Tue, 31 Mar 2026 19:48:09 +0200 Subject: [PATCH] Introduce taggedEvent + taggedAuthor... --- .../1.json | 26 ++++++++-- .../aux/compose/database/model/NostrEvent.kt | 6 +++ .../model/intermdiate/LocalNostrEvent.kt | 12 +++++ .../composable/widgets/feed/EventListView.kt | 50 ++++++++++++++----- .../ui/view/model/NavigationViewModel.kt | 13 ++++- 5 files changed, 90 insertions(+), 17 deletions(-) diff --git a/composeApp/schemas/ac.aux.compose.database.AuxDatabase/1.json b/composeApp/schemas/ac.aux.compose.database.AuxDatabase/1.json index e7b48687..81124740 100644 --- a/composeApp/schemas/ac.aux.compose.database.AuxDatabase/1.json +++ b/composeApp/schemas/ac.aux.compose.database.AuxDatabase/1.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "6c8c4b9a4b44bed3c7fde29fcf54d7df", + "identityHash": "a9975543a4ca1505dcf4ee74321b3e69", "entities": [ { "tableName": "BroadcastNostrEventReceipt", @@ -181,7 +181,7 @@ }, { "tableName": "NostrEvent", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `pubKey` TEXT NOT NULL, `kind` INTEGER NOT NULL, `tags` TEXT NOT NULL, `content` TEXT NOT NULL, `sig` TEXT NOT NULL, `unsignedNostrEventId` INTEGER, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`id`))", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `pubKey` TEXT NOT NULL, `kind` INTEGER NOT NULL, `tags` TEXT NOT NULL, `content` TEXT NOT NULL, `sig` TEXT NOT NULL, `taggedNostrEventId` TEXT, `taggedNostrEventRelayUrl` TEXT, `taggedPublicKey` TEXT, `taggedPublicKeyRelayUrl` TEXT, `unsignedNostrEventId` INTEGER, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, `savedAt` INTEGER NOT NULL, `deletedAt` INTEGER, `broadcastedAt` INTEGER, PRIMARY KEY(`id`))", "fields": [ { "fieldPath": "id", @@ -219,6 +219,26 @@ "affinity": "TEXT", "notNull": true }, + { + "fieldPath": "taggedNostrEventId", + "columnName": "taggedNostrEventId", + "affinity": "TEXT" + }, + { + "fieldPath": "taggedNostrEventRelayUrl", + "columnName": "taggedNostrEventRelayUrl", + "affinity": "TEXT" + }, + { + "fieldPath": "taggedPublicKey", + "columnName": "taggedPublicKey", + "affinity": "TEXT" + }, + { + "fieldPath": "taggedPublicKeyRelayUrl", + "columnName": "taggedPublicKeyRelayUrl", + "affinity": "TEXT" + }, { "fieldPath": "unsignedNostrEventId", "columnName": "unsignedNostrEventId", @@ -1342,7 +1362,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, '6c8c4b9a4b44bed3c7fde29fcf54d7df')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a9975543a4ca1505dcf4ee74321b3e69')" ] } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/aux/compose/database/model/NostrEvent.kt b/composeApp/src/commonMain/kotlin/ac/aux/compose/database/model/NostrEvent.kt index f091dc73..15dd1e10 100644 --- a/composeApp/src/commonMain/kotlin/ac/aux/compose/database/model/NostrEvent.kt +++ b/composeApp/src/commonMain/kotlin/ac/aux/compose/database/model/NostrEvent.kt @@ -38,6 +38,12 @@ data class NostrEvent( val content: String, val sig: HexKey, + val taggedNostrEventId: HexKey? = null, + val taggedNostrEventRelayUrl: String? = null, + + val taggedPublicKey: HexKey? = null, + val taggedPublicKeyRelayUrl: String? = null, + override val unsignedNostrEventId: Long? = null, override val createdAt: Instant = Clock.System.now(), diff --git a/composeApp/src/commonMain/kotlin/ac/aux/compose/database/model/intermdiate/LocalNostrEvent.kt b/composeApp/src/commonMain/kotlin/ac/aux/compose/database/model/intermdiate/LocalNostrEvent.kt index daf0b5e9..4335e815 100644 --- a/composeApp/src/commonMain/kotlin/ac/aux/compose/database/model/intermdiate/LocalNostrEvent.kt +++ b/composeApp/src/commonMain/kotlin/ac/aux/compose/database/model/intermdiate/LocalNostrEvent.kt @@ -21,4 +21,16 @@ data class LocalNostrEvent( entityColumn = "nostrEventId" ) val repost: Repost? = null, + + @Relation( + parentColumn = "taggedNostrEventId", + entityColumn = "id" + ) + val taggedNostrEvent: NostrEvent? = null, + + @Relation( + parentColumn = "taggedPublicKey", + entityColumn = "publicKey" + ) + val taggedAuthor: Profile? = null, ) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/widgets/feed/EventListView.kt b/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/widgets/feed/EventListView.kt index 79cc3b32..44c2ec7d 100644 --- a/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/widgets/feed/EventListView.kt +++ b/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/composable/widgets/feed/EventListView.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip18Reposts.RepostEvent +import kotlin.time.Instant @Composable fun LocalNostrEvent.ListView( @@ -57,21 +58,43 @@ fun LocalNostrEvent.ListView( style = MaterialTheme.typography.labelSmall) } - if (repost != null) { - RepostFeedItem( - repost, - this@ListView, + if (taggedNostrEvent != null) { + TextNoteFeedItem( + nostrEvent = taggedNostrEvent, + profile = taggedAuthor, onNavigateToEvent = onNavigateToEvent ) } else { - Text("Missing note") + + val containedPost = RepostEvent( + id = nostrEvent.id, + pubKey = nostrEvent.pubKey, + createdAt = nostrEvent.createdAt.epochSeconds, + content = nostrEvent.content, + sig = nostrEvent.sig, + tags = nostrEvent.tags + ).containedPost() + + if (containedPost != null) { + Text("Loading author information...") + Text( + containedPost.content + ) + Text( + text = Instant.fromEpochSeconds(containedPost.createdAt).toFormattedTimeAndDateString() + ) + } else { + Text("Malformed Note") + } + } } } // TODO: Support other kinds... else -> { TextNoteFeedItem( - this, + nostrEvent = nostrEvent, + profile = profile, onNavigateToEvent = onNavigateToEvent ) } @@ -80,7 +103,8 @@ fun LocalNostrEvent.ListView( @Composable private fun TextNoteFeedItem( - localNostrEvent: LocalNostrEvent, + nostrEvent: NostrEvent, + profile: Profile?, onNavigateToEvent: (NostrEvent) -> Unit ) { ElevatedCard( @@ -88,7 +112,7 @@ private fun TextNoteFeedItem( horizontal = 10.dp ), onClick = { - onNavigateToEvent.invoke(localNostrEvent.nostrEvent) + onNavigateToEvent.invoke(nostrEvent) } ) { Column( @@ -107,12 +131,12 @@ private fun TextNoteFeedItem( ) Text( modifier = Modifier.weight(1f), - text = localNostrEvent.profile.displayName ?: localNostrEvent.profile.userName ?: localNostrEvent.profile.publicKey, + text = profile?.displayName ?: profile?.userName ?: nostrEvent.pubKey, style = MaterialTheme.typography.bodyMedium ) } Text( - text = localNostrEvent.nostrEvent.content, + text = nostrEvent.content, ) LazyRow( @@ -126,7 +150,7 @@ private fun TextNoteFeedItem( AssistChip( label = { Text( - text = localNostrEvent.nostrEvent.kind.toString() // TODO: Kind by name... + text = nostrEvent.kind.toString() // TODO: Kind by name... ) }, onClick = { @@ -136,7 +160,7 @@ private fun TextNoteFeedItem( } items( - items = localNostrEvent.nostrEvent.tags, + items = nostrEvent.tags, ) { tag -> SuggestionChip( label = { @@ -152,7 +176,7 @@ private fun TextNoteFeedItem( } Text( - text = localNostrEvent.nostrEvent.createdAt.toFormattedTimeAndDateString() + text = nostrEvent.createdAt.toFormattedTimeAndDateString() ) } } diff --git a/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/view/model/NavigationViewModel.kt b/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/view/model/NavigationViewModel.kt index 1e02688b..a43e565a 100644 --- a/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/view/model/NavigationViewModel.kt +++ b/composeApp/src/commonMain/kotlin/ac/aux/compose/ui/view/model/NavigationViewModel.kt @@ -17,6 +17,8 @@ import com.vitorpamplona.quartz.nip01Core.core.toHexKey import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.ReqCmd import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync +import com.vitorpamplona.quartz.nip01Core.tags.events.firstTaggedEvent +import com.vitorpamplona.quartz.nip01Core.tags.people.firstTaggedUser import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent import com.vitorpamplona.quartz.utils.text import io.ktor.client.HttpClient @@ -197,6 +199,9 @@ class NavigationViewModel( // TODO: Do profile specific cache (to avoid wiping an updated profile) syncEventCache.getOrPut(event.id, { + val taggedEvent = event.firstTaggedEvent() + val taggedUser = event.firstTaggedUser() + val nostrEvent = NostrEvent( id = event.id, pubKey = event.pubKey, @@ -205,7 +210,13 @@ class NavigationViewModel( tags = event.tags, createdAt = Instant.fromEpochSeconds(event.createdAt), unsignedNostrEventId = synchronizeNostrEventRequest.unsignedNostrEventId, - sig = event.sig + sig = event.sig, + + taggedNostrEventId = taggedEvent?.eventId, + taggedNostrEventRelayUrl = taggedEvent?.relay?.url, + + taggedPublicKey = taggedUser?.pubKey, + taggedPublicKeyRelayUrl = taggedUser?.relayHint?.url ) nostrRepository.saveNostrEvent(