diff --git a/shared/src/commonMain/kotlin/su/reya/coop/nostr/Nostr.kt b/shared/src/commonMain/kotlin/su/reya/coop/nostr/Nostr.kt index 913c8e0..4483ba0 100644 --- a/shared/src/commonMain/kotlin/su/reya/coop/nostr/Nostr.kt +++ b/shared/src/commonMain/kotlin/su/reya/coop/nostr/Nostr.kt @@ -79,6 +79,8 @@ class Nostr { // Initialize configurations for nostr client val lmdb = NostrDatabase.lmdb(dbPath) val gossip = NostrGossip.inMemory() + + // Initialize the authenticator val authenticator = SignerAuthenticator(signer) val idleTimeout = Duration.parse("5m") @@ -208,7 +210,7 @@ class Nostr { if (isSignedByUser(event = event)) { val pubkeys = event.tags().publicKeys() // Get mutual contacts - profiles.getMutualContacts(pubkeys) + profiles.syncMutualContacts(pubkeys) // Emit contact list update onContactListUpdate(pubkeys) } diff --git a/shared/src/commonMain/kotlin/su/reya/coop/nostr/ProfileManager.kt b/shared/src/commonMain/kotlin/su/reya/coop/nostr/ProfileManager.kt index 778a693..889e79b 100644 --- a/shared/src/commonMain/kotlin/su/reya/coop/nostr/ProfileManager.kt +++ b/shared/src/commonMain/kotlin/su/reya/coop/nostr/ProfileManager.kt @@ -74,22 +74,13 @@ class ProfileManager(private val nostr: Nostr) { } } - suspend fun getMutualContacts(pubkeys: List) { + suspend fun syncMutualContacts(pubkeys: List) { try { val kind = Kind.fromStd(KindStandard.CONTACT_LIST) val filter = Filter().kind(kind).authors(pubkeys).limit(pubkeys.size.toULong()) - val opts = SubscribeAutoCloseOptions().exitPolicy(ReqExitPolicy.ExitOnEose) + val relays = NostrManager.BOOTSTRAP_RELAYS.map { RelayUrl.parse(it) } - val target = mutableMapOf>() - NostrManager.BOOTSTRAP_RELAYS.forEach { relay -> - target[RelayUrl.parse(relay)] = listOf(filter) - } - - client?.subscribe( - target = ReqTarget.manual(target), - id = "mutual-contacts", - closeOn = opts, - ) + client?.sync(filter, relays) } catch (e: Exception) { throw IllegalStateException("Failed to fetch mutual contacts: ${e.message}", e) } diff --git a/shared/src/commonMain/kotlin/su/reya/coop/nostr/RelayManager.kt b/shared/src/commonMain/kotlin/su/reya/coop/nostr/RelayManager.kt index 7c5569d..d763018 100644 --- a/shared/src/commonMain/kotlin/su/reya/coop/nostr/RelayManager.kt +++ b/shared/src/commonMain/kotlin/su/reya/coop/nostr/RelayManager.kt @@ -134,8 +134,9 @@ class RelayManager(private val nostr: Nostr) { val kind = Kind.fromStd(KindStandard.INBOX_RELAYS) val filter = Filter().kind(kind).author(publicKey).limit(1u) val events = client?.database()?.query(filter) + val event = events?.first() ?: return emptyList() - return nip17ExtractRelayList(events?.toVec()?.firstOrNull() ?: return emptyList()) + return nip17ExtractRelayList(event) } catch (e: Exception) { throw IllegalStateException("Failed to get msg relays: ${e.message}", e) } diff --git a/shared/src/commonMain/kotlin/su/reya/coop/viewmodel/NostrViewModel.kt b/shared/src/commonMain/kotlin/su/reya/coop/viewmodel/NostrViewModel.kt index 5d46875..5fb8c89 100644 --- a/shared/src/commonMain/kotlin/su/reya/coop/viewmodel/NostrViewModel.kt +++ b/shared/src/commonMain/kotlin/su/reya/coop/viewmodel/NostrViewModel.kt @@ -11,6 +11,8 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf @@ -159,8 +161,7 @@ class NostrViewModel(private val nostr: Nostr) : BaseViewModel() { // Wait until the client is ready nostr.waitUntilInitialized() - val results = nostr.profiles.getAllCacheMetadata() - results.forEach { (pubkey, metadata) -> + nostr.profiles.getAllCacheMetadata().forEach { (pubkey, metadata) -> // Update the metadata state updateMetadata(pubkey, Profile(pubkey, metadata)) // Update seenPublicKeys to avoid duplicate requests @@ -171,25 +172,21 @@ class NostrViewModel(private val nostr: Nostr) : BaseViewModel() { private fun observeSignerAndCheckRelays() { viewModelScope.launch { - while (true) { - val currentUser = nostr.signer.getPublicKeyAsync() + // Wait until the client is ready + nostr.waitUntilInitialized() - if (currentUser != null) { - // Get all metadata for the current user - nostr.profiles.getUserMetadata() + // Wait until a signer is explicitly set (which updates publicKeyFlow) + val currentUser = nostr.signer.publicKeyFlow.filterNotNull().first() - // Small delay to ensure all relays are connected - delay(2.seconds) + // Get all metadata for the current user + nostr.profiles.getUserMetadata() - // Check if the relay list is empty - val relays = nostr.relays.getMsgRelays(currentUser) - if (relays.isEmpty()) _appState.update { it.copy(isRelayListEmpty = true) } + // Small delay to ensure all relays are connected + delay(2.seconds) - break - } - - delay(500.milliseconds) - } + // Check if the relay list is empty + val relays = nostr.relays.getMsgRelays(currentUser) + if (relays.isEmpty()) _appState.update { it.copy(isRelayListEmpty = true) } } }