From f1011c0ae87f0afa3c1247ba5e6de30911185342 Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Fri, 1 May 2026 18:11:40 +0200 Subject: [PATCH] Introduce an atrocious bug when syncing. --- .../compose/database/dao/NostrDao.kt | 13 +++++++ .../compose/database/dao/NostrEventDao.kt | 12 +++---- .../compose/database/model/NostrEvent.kt | 19 ++++++++-- .../model/intermdiate/LocalNostrEvent.kt | 2 +- .../intermdiate/LocalQuotedNostrEvent.kt | 8 ++--- .../repository/DatabaseNostrRepository.kt | 35 +++++++++++++++---- .../ui/composable/navigation/AuxNavHost.kt | 24 +++++++------ .../composable/widgets/content/RichContent.kt | 13 ++++--- .../composable/widgets/feed/EventListView.kt | 32 ++++++++--------- 9 files changed, 107 insertions(+), 51 deletions(-) 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 04c057b3..71c73738 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 @@ -14,6 +14,7 @@ import ac.cord.auxiliary.compose.database.model.types.SynchronizationFilter import ac.cord.auxiliary.compose.nostr.Relays import androidx.room.Dao import androidx.room.Transaction +import androidx.room.useWriterConnection import co.touchlab.kermit.Logger import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents @@ -289,6 +290,18 @@ abstract class NostrDao( nostrEvent.tags.taggedEvents().take(4).forEach { taggedEvent -> // We only look at the first 3 tagged events... val taggedNostrEvent = database.nostrEventDao().getNostrEventById(taggedEvent.eventId) if (taggedNostrEvent == null) { + database.nostrEventDao().insert( + NostrEvent( + id = taggedEvent.eventId, + content = "Pending Sync... ", + createdAt = GENESIS_AT, + pubKey = nostrEvent.pubKey, + sig = "", + kind = nostrEvent.kind, + tags = emptyArray() + ) + ) + val recommendRelayUrl = taggedEvent.relay?.url val synchronizeNostrEventRequests = if (recommendRelayUrl != null) { diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrEventDao.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrEventDao.kt index e93542ac..c344a6db 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrEventDao.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/dao/NostrEventDao.kt @@ -16,28 +16,28 @@ import kotlinx.coroutines.flow.Flow @Dao interface NostrEventDao { - @Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) ORDER BY createdAt DESC") + @Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) AND savedAt < strftime('%s', 'now') ORDER BY createdAt DESC") fun observeNostrEvents(kinds: Array): Flow> - @Query("SELECT * FROM NostrEvent WHERE content LIKE '%' || :search || '%' AND kind in (:kinds) ORDER BY createdAt DESC") + @Query("SELECT * FROM NostrEvent WHERE content LIKE '%' || :search || '%' AND kind in (:kinds) AND savedAt < strftime('%s', 'now') ORDER BY createdAt DESC") fun observeFilteredNostrEvents( kinds: Array, search: String ): Flow> - @Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) AND id in (:ids) ORDER BY createdAt DESC") + @Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) AND id in (:ids) AND savedAt < strftime('%s', 'now') ORDER BY createdAt DESC") fun observeFilteredNostrEvents( kinds: Array, ids: Array, ): Flow> - @Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) AND pubKey in (:authors) ORDER BY createdAt DESC LIMIT 50") + @Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) AND pubKey in (:authors) AND savedAt < strftime('%s', 'now') ORDER BY createdAt DESC LIMIT 50") fun observeAuthoredNostrEvents( kinds: Array, authors: Array, ): Flow> - @Query("SELECT * FROM NostrEvent WHERE tags LIKE '%' || :publicKey || '%' AND kind in (:kinds) ORDER BY createdAt DESC") + @Query("SELECT * FROM NostrEvent WHERE tags LIKE '%' || :publicKey || '%' AND kind in (:kinds) AND savedAt < strftime('%s', 'now') ORDER BY createdAt DESC") fun observePublicKeyMentionedNostrEvents( kinds: Array, publicKey: HexKey @@ -48,7 +48,7 @@ interface NostrEventDao { query: RoomRawQuery ): Flow> - @Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) ORDER BY savedAt DESC") + @Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) AND savedAt < strftime('%s', 'now') ORDER BY savedAt DESC") fun getAllNostrEvents(kinds: Array): List @Query("SELECT * FROM NostrEvent WHERE id = :id") diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/NostrEvent.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/NostrEvent.kt index ae45f077..ed110b4f 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/NostrEvent.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/NostrEvent.kt @@ -6,6 +6,8 @@ import ac.cord.auxiliary.compose.database.model.traits.SoftDeletableEntity 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.types.SynchronizationFilter +import ac.cord.auxiliary.compose.ui.composable.widgets.content.ContentParser +import ac.cord.auxiliary.compose.ui.composable.widgets.content.ContentSegment import androidx.room.Entity import androidx.room.Ignore import androidx.room.Index @@ -275,9 +277,22 @@ data class NostrEvent( } } - // TODO: Get quoted event from content... - return null + // TODO: Bring in quoted text +// val quotedNotes = ContentParser.parse(content.trimEnd('\n', '\r'), emptyMap(), emptyMap(), trimBlankLines = true) +// .filterIsInstance() +// .firstOrNull() +// +// return quotedNotes?.let { quotedNote -> +// QuotedRelation( +// quotedNostrEventId = quotedNote.eventId, +// quotedProfilePublicKey = null +// , +// +// quotingNostrEventId = id, +// createdAt = createdAt +// ) +// } } catch (e: Throwable) { logger.e("Failed to return Repost: ", e) return null diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalNostrEvent.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalNostrEvent.kt index 0f74a313..30070925 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalNostrEvent.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalNostrEvent.kt @@ -80,7 +80,7 @@ data class LocalNostrEvent( parentColumn = "id", entityColumn = "nostrEventId", ) - val mentionedProfiles: List = emptyList(), + val mentionedProfiles: List = emptyList(), ) { @Composable fun RenderNotePreview() { diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalQuotedNostrEvent.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalQuotedNostrEvent.kt index 490771c0..fba50a49 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalQuotedNostrEvent.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/database/model/intermdiate/LocalQuotedNostrEvent.kt @@ -35,15 +35,11 @@ data class LocalQuotedNostrEvent( val profile: Profile?, @Relation( + entity = Mention::class, parentColumn = "id", entityColumn = "nostrEventId", - associateBy = Junction( - value = Mention::class, - parentColumn = "publicKey", - entityColumn = "publicKey" - ) ) - val mentionedProfiles: List = emptyList() + val mentionedProfiles: List = emptyList(), ) { @Composable fun RenderNotePreview() { 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 2dc943a3..15f94322 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 @@ -23,14 +23,12 @@ import ac.cord.auxiliary.compose.nostr.Relays import ac.cord.auxiliary.compose.repository.NostrRepository import ac.cord.auxiliary.compose.repository.RelayRepository import co.touchlab.kermit.Logger -import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.Kind import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags import com.vitorpamplona.quartz.nip01Core.tags.people.PTag -import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers import com.vitorpamplona.quartz.nip01Core.tags.references.references import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent import com.vitorpamplona.quartz.nip02FollowList.RelaySet @@ -39,14 +37,12 @@ import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import com.vitorpamplona.quartz.nip10Notes.content.findHashtags import com.vitorpamplona.quartz.nip10Notes.content.findNostrUris import com.vitorpamplona.quartz.nip10Notes.content.findURLs -import com.vitorpamplona.quartz.nip10Notes.tags.markedETag import com.vitorpamplona.quartz.nip10Notes.tags.markedETags import com.vitorpamplona.quartz.nip10Notes.tags.notify import com.vitorpamplona.quartz.nip10Notes.tags.prepareETagsAsReplyTo import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent import com.vitorpamplona.quartz.nip18Reposts.RepostEvent import com.vitorpamplona.quartz.nip18Reposts.quotes.QEventTag -import com.vitorpamplona.quartz.nip18Reposts.quotes.QTag import com.vitorpamplona.quartz.nip18Reposts.quotes.quotes import com.vitorpamplona.quartz.nip21UriScheme.toNostrUri import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent @@ -58,15 +54,20 @@ import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent import com.vitorpamplona.quartz.nip51Lists.tags.RelayTag import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.launch import kotlin.collections.emptyMap import kotlin.collections.plus import kotlin.time.Clock +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds import kotlin.time.Instant class DatabaseNostrRepository( - private val database: AuxDatabase + private val database: AuxDatabase, + private val scope: CoroutineScope ): NostrRepository, RelayRepository { companion object { const val TAG = "DatabaseNostrRepository" @@ -398,8 +399,17 @@ class DatabaseNostrRepository( synchronizeNostrEventRequest: SynchronizeNostrEventRequest, synchronizationRelayURLs: List ) { + val triggerDelay = 5.seconds + + val delayedFlowTriggerNostrEvent = nostrEvent.copy( + savedAt = Instant.fromEpochSeconds( + Clock.System.now().epochSeconds + triggerDelay.inWholeSeconds + ) + ) + logger.d("savedAt: ${nostrEvent.savedAt}") + logger.d("SavedAt: ${delayedFlowTriggerNostrEvent.savedAt}") database.nostrDao().storeNostrEvent( - nostrEvent, + delayedFlowTriggerNostrEvent, synchronizationRelayURLs = synchronizationRelayURLs, level = synchronizeNostrEventRequest.level ) @@ -410,6 +420,19 @@ class DatabaseNostrRepository( status = "processed" ) ) + + // Might not even need this delay +// scope.launch { +// delay(triggerDelay) +// database.nostrEventDao().upsert( +// nostrEvent +// ) +// } + // Trigger the nostrEvent to appear in the feed... + delay(triggerDelay) + database.nostrEventDao().upsert( + nostrEvent + ) } override suspend fun queueSynchronizeNostrEvent( diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/navigation/AuxNavHost.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/navigation/AuxNavHost.kt index e9e86b37..4b0eb473 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/navigation/AuxNavHost.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/navigation/AuxNavHost.kt @@ -70,16 +70,6 @@ fun AuxNavHost( navController: NavHostController ) { val logger = Logger.withTag("AuxNavHost") - val lifecycleOwner = LocalLifecycleOwner.current - - val databaseManager = DatabaseManager(auxGlobal) - - val databaseNostrRepository = DatabaseNostrRepository( - database = databaseManager.auxDatabase - ) - val searchRepository = DatabaseSearchRepository( - database = databaseManager.auxDatabase - ) val exceptionHandler = CoroutineExceptionHandler { _, throwable -> @@ -89,6 +79,20 @@ fun AuxNavHost( val applicationIOScope = CoroutineScope(Dispatchers.IO + SupervisorJob() + exceptionHandler) val applicationMainScope = CoroutineScope(Dispatchers.Main) + val lifecycleOwner = LocalLifecycleOwner.current + + val databaseManager = DatabaseManager(auxGlobal) + + val databaseNostrRepository = DatabaseNostrRepository( + database = databaseManager.auxDatabase, + applicationIOScope + ) + val searchRepository = DatabaseSearchRepository( + database = databaseManager.auxDatabase + ) + + + val navigationViewModel: NavigationViewModel = viewModel ( factory = NavigationViewModel.factory( diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/RichContent.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/RichContent.kt index 072883b0..051db797 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/RichContent.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/RichContent.kt @@ -101,14 +101,19 @@ fun RichContent( .filterIsInstance() .map { it.pubkey } + Logger.withTag("RichContent").d("profilePubKey: $profilePubkeys") val profileNames = remember(profilePubkeys) { val names = mutableMapOf() - for (pubkey in profilePubkeys) { - val mentionedProfile = localNostrEvent.mentionedProfiles.find { mentionedProfile -> mentionedProfile.profile?.publicKey == pubkey } - names[pubkey] = mentionedProfile?.profile?.humanReadableNameOrPubkey() - ?: "${pubkey.take(8)}...${pubkey.takeLast(4)}" + if (localNostrEvent.mentionedProfiles.isNotEmpty()) { + Logger.withTag("RichContent").d("mentionedProfiles: ${localNostrEvent.mentionedProfiles}") + for (pubkey in profilePubkeys) { + val mentionedProfile = localNostrEvent.mentionedProfiles.find { mentionedProfile -> mentionedProfile?.profile?.publicKey == pubkey } + names[pubkey] = mentionedProfile?.profile?.humanReadableNameOrPubkey() + ?: "${pubkey.take(8)}...${pubkey.takeLast(4)}" + } } + names } // TODO Queue fetches for any missing profiles diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/feed/EventListView.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/feed/EventListView.kt index 2c03455f..58fd4bdf 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/feed/EventListView.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/feed/EventListView.kt @@ -202,22 +202,22 @@ private fun TextNoteFeedItem( ) } - item { - SuggestionChip( - label = { - Text( - text = if (localNostrEvent.mentionedProfiles.isNotEmpty()) { - localNostrEvent.mentionedProfiles.joinToString(",") { it.profile?.humanReadableNameOrPubkey() ?: "Find user information" } - } else { - ":-(" - } - ) - }, - onClick = { - - } - ) - } +// item { +// SuggestionChip( +// label = { +// Text( +// text = if (localNostrEvent.mentionedProfiles.isNotEmpty()) { +// localNostrEvent.mentionedProfiles.joinToString(",") { it?.profile?.humanReadableNameOrPubkey() ?: "Find user information" } +// } else { +// ":-(" +// } +// ) +// }, +// onClick = { +// +// } +// ) +// } items( items = nostrEvent.tags,