Introduce an atrocious bug when syncing.

This commit is contained in:
Kgothatso Ngako
2026-05-01 18:11:40 +02:00
parent 64183ff45a
commit f1011c0ae8
9 changed files with 107 additions and 51 deletions

View File

@@ -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) {

View File

@@ -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<Kind>): Flow<List<LocalNostrEvent>>
@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<Kind>,
search: String
): Flow<List<LocalNostrEvent>>
@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<Kind>,
ids: Array<HexKey>,
): Flow<List<LocalNostrEvent>>
@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<Kind>,
authors: Array<HexKey>,
): Flow<List<LocalNostrEvent>>
@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<Kind>,
publicKey: HexKey
@@ -48,7 +48,7 @@ interface NostrEventDao {
query: RoomRawQuery
): Flow<List<LocalNostrEvent>>
@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<Kind>): List<NostrEvent>
@Query("SELECT * FROM NostrEvent WHERE id = :id")

View File

@@ -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<ContentSegment.NostrNoteSegment>()
// .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

View File

@@ -80,7 +80,7 @@ data class LocalNostrEvent(
parentColumn = "id",
entityColumn = "nostrEventId",
)
val mentionedProfiles: List<LocalMention> = emptyList(),
val mentionedProfiles: List<LocalMention?> = emptyList(),
) {
@Composable
fun RenderNotePreview() {

View File

@@ -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<Profile> = emptyList()
val mentionedProfiles: List<LocalMention?> = emptyList(),
) {
@Composable
fun RenderNotePreview() {

View File

@@ -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<String>
) {
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(

View File

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

View File

@@ -101,14 +101,19 @@ fun RichContent(
.filterIsInstance<ContentSegment.NostrProfileSegment>()
.map { it.pubkey }
Logger.withTag("RichContent").d("profilePubKey: $profilePubkeys")
val profileNames = remember(profilePubkeys) {
val names = mutableMapOf<String, String>()
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

View File

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