From 9ddf571b1139d2ff9523f39beac9550b2bcde8d9 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Tue, 21 Jul 2026 14:09:59 -0400 Subject: [PATCH] fix: persist the Concord community list so cold boot doesn't refetch it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AccountSettings has held backupConcordList and saved it on change since the feature landed, but the field was never wired into LocalPreferences: it was neither written to nor read back from the encrypted prefs. So it only ever existed for the lifetime of the process, concordList() returned null on every cold boot, and the kind-13302 joined-communities list had to be refetched from relays before a single Concord plane could be subscribed. Every sibling list — channel, community, hashtag, geohash, ephemeral chat, relay group, trust provider — is persisted this way; Concord was the one that was missed. That made it the only chat type whose rooms could not appear until the network answered, which is the bulk of the cold-boot delay: the joined list gates the control-plane REQ, the control plane gates the fold, and the fold gates the channels. Measured on device, boot -> first Concord plane wrap: - without the backup: liveCommunities sat empty for ~56 s waiting on the 13302 fetch (first arrival from nostr.mom), first wrap at +45 s - with the backup restored: list decoded 1.6 s after boot (30 ms), first wrap at +6 s Wired the same five sites the other lists use (pref key, save, read, parse, restore). Prefs are encrypted and backupCashuWallet already sets the precedent for persisting a secret-bearing event, so the community roots in the 13302 content are stored no differently than the wallet's. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../java/com/vitorpamplona/amethyst/LocalPreferences.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt index 9603f78862..f489142661 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt @@ -38,6 +38,7 @@ import com.vitorpamplona.amethyst.model.UiSettings import com.vitorpamplona.amethyst.service.checkNotInMainThread import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName +import com.vitorpamplona.quartz.concord.cord02Community.ConcordCommunityListEvent import com.vitorpamplona.quartz.experimental.ephemChat.list.EphemeralChatListEvent import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent import com.vitorpamplona.quartz.nip01Core.core.Event @@ -168,6 +169,7 @@ private object PrefKeys { const val LATEST_GEOHASH_LIST = "latestGeohashList" const val LATEST_EPHEMERAL_LIST = "latestEphemeralChatList" const val LATEST_RELAY_GROUP_LIST = "latestRelayGroupList" + const val LATEST_CONCORD_LIST = "latestConcordList" const val LATEST_TRUST_PROVIDER_LIST = "latestTrustProviderList" const val CALLS_ENABLED = "calls_enabled" const val HIDE_DELETE_REQUEST_DIALOG = "hide_delete_request_dialog" @@ -577,6 +579,7 @@ object LocalPreferences { putOrRemove(PrefKeys.LATEST_GEOHASH_LIST, settings.backupGeohashList) putOrRemove(PrefKeys.LATEST_EPHEMERAL_LIST, settings.backupEphemeralChatList) putOrRemove(PrefKeys.LATEST_RELAY_GROUP_LIST, settings.backupRelayGroupList) + putOrRemove(PrefKeys.LATEST_CONCORD_LIST, settings.backupConcordList) putOrRemove(PrefKeys.LATEST_TRUST_PROVIDER_LIST, settings.backupTrustProviderList) putOrRemove(PrefKeys.LATEST_PAYMENT_TARGETS, settings.backupNipA3PaymentTargets) putOrRemove(PrefKeys.LATEST_CASHU_WALLET, settings.backupCashuWallet) @@ -763,6 +766,7 @@ object LocalPreferences { val latestGeohashListStr = getString(PrefKeys.LATEST_GEOHASH_LIST, null) val latestEphemeralListStr = getString(PrefKeys.LATEST_EPHEMERAL_LIST, null) val latestRelayGroupListStr = getString(PrefKeys.LATEST_RELAY_GROUP_LIST, null) + val latestConcordListStr = getString(PrefKeys.LATEST_CONCORD_LIST, null) val latestTrustProviderListStr = getString(PrefKeys.LATEST_TRUST_PROVIDER_LIST, null) val latestPaymentTargetsStr = getString(PrefKeys.LATEST_PAYMENT_TARGETS, null) val latestCashuWalletStr = getString(PrefKeys.LATEST_CASHU_WALLET, null) @@ -823,6 +827,7 @@ object LocalPreferences { val latestGeohashList = async { parseEventOrNull(latestGeohashListStr) } val latestEphemeralList = async { parseEventOrNull(latestEphemeralListStr) } val latestRelayGroupList = async { parseEventOrNull(latestRelayGroupListStr) } + val latestConcordList = async { parseEventOrNull(latestConcordListStr) } val latestTrustProviderList = async { parseEventOrNull(latestTrustProviderListStr) } val latestPaymentTargets = async { parseEventOrNull(latestPaymentTargetsStr) } val latestCashuWallet = @@ -875,6 +880,7 @@ object LocalPreferences { val latestGeohashListResolved = latestGeohashList.await() val latestEphemeralListResolved = latestEphemeralList.await() val latestRelayGroupListResolved = latestRelayGroupList.await() + val latestConcordListResolved = latestConcordList.await() val latestTrustProviderListResolved = latestTrustProviderList.await() val latestPaymentTargetsResolved = latestPaymentTargets.await() val latestCashuWalletResolved = latestCashuWallet.await() @@ -969,6 +975,7 @@ object LocalPreferences { backupGeohashList = latestGeohashListResolved, backupEphemeralChatList = latestEphemeralListResolved, backupRelayGroupList = latestRelayGroupListResolved, + backupConcordList = latestConcordListResolved, backupTrustProviderList = latestTrustProviderListResolved, lastReadPerRoute = MutableStateFlow(lastReadPerRouteResolved), hasDonatedInVersion = MutableStateFlow(hasDonatedInVersion),