diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt index 968a6e75c..a61c4f374 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt @@ -25,9 +25,31 @@ import android.content.Context import android.content.SharedPreferences import androidx.compose.runtime.Immutable import androidx.core.content.edit +import coil3.util.CoilUtils.result import com.vitorpamplona.amethyst.model.AccountSettings import com.vitorpamplona.amethyst.model.TopFilter import com.vitorpamplona.amethyst.model.UiSettings +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.defaultDiscoveryFollowList +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.defaultFileServer +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.defaultHomeFollowList +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.defaultNotificationFollowList +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.defaultStoriesFollowList +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.hasDonatedInVersion +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.hideBlockAlertDialog +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.hideDeleteRequestDialog +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.latestAppSpecificData +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.latestBlockedRelayList +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.latestChannelList +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.latestCommunityList +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.latestContactList +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.latestGeohashList +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.latestHashtagList +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.latestMuteList +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.latestPrivateHomeRelayList +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.latestSearchRelayList +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.latestTrustedRelayList +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.latestUserMetadata +import com.vitorpamplona.amethyst.model.preferences.AccountPreferenceStores.Companion.localRelayServers import com.vitorpamplona.amethyst.service.checkNotInMainThread import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName @@ -62,6 +84,7 @@ import com.vitorpamplona.quartz.nip78AppData.AppSpecificDataEvent import com.vitorpamplona.quartz.utils.Log import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -448,96 +471,128 @@ object LocalPreferences { Log.d("LocalPreferences", "Load account from file $npub") val result = withContext(Dispatchers.IO) { - checkNotInMainThread() - return@withContext with(encryptedPreferences(npub)) { + Log.d("LocalPreferences", "Load account from file $npub - opened file") val privKey = getString(PrefKeys.NOSTR_PRIVKEY, null) val pubKey = getString(PrefKeys.NOSTR_PUBKEY, null) ?: return@with null - val externalSignerPackageName = - getString(PrefKeys.SIGNER_PACKAGE_NAME, null) - ?: if (getBoolean(PrefKeys.LOGIN_WITH_EXTERNAL_SIGNER, false)) "com.greenart7c3.nostrsigner" else null + val externalSignerPackageName = getString(PrefKeys.SIGNER_PACKAGE_NAME, null) ?: if (getBoolean(PrefKeys.LOGIN_WITH_EXTERNAL_SIGNER, false)) "com.greenart7c3.nostrsigner" else null - val defaultHomeFollowList = parseOrNull(PrefKeys.DEFAULT_HOME_FOLLOW_LIST) ?: TopFilter.AllFollows - val defaultStoriesFollowList = parseOrNull(PrefKeys.DEFAULT_STORIES_FOLLOW_LIST) ?: TopFilter.Global - val defaultNotificationFollowList = parseOrNull(PrefKeys.DEFAULT_NOTIFICATION_FOLLOW_LIST) ?: TopFilter.Global - val defaultDiscoveryFollowList = parseOrNull(PrefKeys.DEFAULT_DISCOVERY_FOLLOW_LIST) ?: TopFilter.Global + val keyPair = KeyPair(privKey = privKey?.hexToByteArray(), pubKey = pubKey.hexToByteArray()) + + Log.d("LocalPreferences", "Load account from file $npub - keys ready") - val zapPaymentRequestServer = parseOrNull(PrefKeys.ZAP_PAYMENT_REQUEST_SERVER) - val defaultFileServer = parseOrNull(PrefKeys.DEFAULT_FILE_SERVER) ?: DEFAULT_MEDIA_SERVERS[0] val stripLocationOnUpload = getBoolean(PrefKeys.STRIP_LOCATION_ON_UPLOAD, true) - - val pendingAttestations = parseOrNull>(PrefKeys.PENDING_ATTESTATIONS) ?: mapOf() - val localRelayServers = getStringSet(PrefKeys.LOCAL_RELAY_SERVERS, null) ?: setOf() - - val latestUserMetadata = parseEventOrNull(PrefKeys.LATEST_USER_METADATA) - val latestContactList = parseEventOrNull(PrefKeys.LATEST_CONTACT_LIST) - val latestDmRelayList = parseEventOrNull(PrefKeys.LATEST_DM_RELAY_LIST) - val latestNip65RelayList = parseEventOrNull(PrefKeys.LATEST_NIP65_RELAY_LIST) - val latestSearchRelayList = parseEventOrNull(PrefKeys.LATEST_SEARCH_RELAY_LIST) - val latestIndexRelayList = parseEventOrNull(PrefKeys.LATEST_INDEX_RELAY_LIST) - val latestRelayFeedsList = parseEventOrNull(PrefKeys.LATEST_RELAY_FEEDS_LIST) - val latestBlockedRelayList = parseEventOrNull(PrefKeys.LATEST_BLOCKED_RELAY_LIST) - val latestTrustedRelayList = parseEventOrNull(PrefKeys.LATEST_TRUSTED_RELAY_LIST) - val latestMuteList = parseEventOrNull(PrefKeys.LATEST_MUTE_LIST) - val latestPrivateHomeRelayList = parseEventOrNull(PrefKeys.LATEST_PRIVATE_HOME_RELAY_LIST) - val latestAppSpecificData = parseEventOrNull(PrefKeys.LATEST_APP_SPECIFIC_DATA) - val latestChannelList = parseEventOrNull(PrefKeys.LATEST_CHANNEL_LIST) - val latestCommunityList = parseEventOrNull(PrefKeys.LATEST_COMMUNITY_LIST) - val latestHashtagList = parseEventOrNull(PrefKeys.LATEST_HASHTAG_LIST) - val latestGeohashList = parseEventOrNull(PrefKeys.LATEST_GEOHASH_LIST) - val latestEphemeralList = parseEventOrNull(PrefKeys.LATEST_EPHEMERAL_LIST) - val latestTrustProviderList = parseEventOrNull(PrefKeys.LATEST_TRUST_PROVIDER_LIST) - val latestPaymentTargets = parseEventOrNull(PrefKeys.LATEST_PAYMENT_TARGETS) - val hideDeleteRequestDialog = getBoolean(PrefKeys.HIDE_DELETE_REQUEST_DIALOG, false) val hideBlockAlertDialog = getBoolean(PrefKeys.HIDE_BLOCK_ALERT_DIALOG, false) val hideNIP17WarningDialog = getBoolean(PrefKeys.HIDE_NIP_17_WARNING_DIALOG, false) + val hasDonatedInVersion = getStringSet(PrefKeys.HAS_DONATED_IN_VERSION, null) ?: setOf() + val localRelayServers = getStringSet(PrefKeys.LOCAL_RELAY_SERVERS, null) ?: setOf() + + val defaultHomeFollowListStr = getString(PrefKeys.DEFAULT_HOME_FOLLOW_LIST, null) + val defaultStoriesFollowListStr = getString(PrefKeys.DEFAULT_STORIES_FOLLOW_LIST, null) + val defaultNotificationFollowListStr = getString(PrefKeys.DEFAULT_NOTIFICATION_FOLLOW_LIST, null) + val defaultDiscoveryFollowListStr = getString(PrefKeys.DEFAULT_DISCOVERY_FOLLOW_LIST, null) + val zapPaymentRequestServerStr = getString(PrefKeys.ZAP_PAYMENT_REQUEST_SERVER, null) + val defaultFileServerStr = getString(PrefKeys.DEFAULT_FILE_SERVER, null) + + val pendingAttestationsStr = getString(PrefKeys.PENDING_ATTESTATIONS, null) + val latestUserMetadataStr = getString(PrefKeys.LATEST_USER_METADATA, null) + val latestContactListStr = getString(PrefKeys.LATEST_CONTACT_LIST, null) + val latestDmRelayListStr = getString(PrefKeys.LATEST_DM_RELAY_LIST, null) + val latestNip65RelayListStr = getString(PrefKeys.LATEST_NIP65_RELAY_LIST, null) + val latestSearchRelayListStr = getString(PrefKeys.LATEST_SEARCH_RELAY_LIST, null) + val latestIndexRelayListStr = getString(PrefKeys.LATEST_INDEX_RELAY_LIST, null) + val latestRelayFeedsListStr = getString(PrefKeys.LATEST_RELAY_FEEDS_LIST, null) + val latestBlockedRelayListStr = getString(PrefKeys.LATEST_BLOCKED_RELAY_LIST, null) + val latestTrustedRelayListStr = getString(PrefKeys.LATEST_TRUSTED_RELAY_LIST, null) + val latestMuteListStr = getString(PrefKeys.LATEST_MUTE_LIST, null) + val latestPrivateHomeRelayListStr = getString(PrefKeys.LATEST_PRIVATE_HOME_RELAY_LIST, null) + val latestAppSpecificDataStr = getString(PrefKeys.LATEST_APP_SPECIFIC_DATA, null) + val latestChannelListStr = getString(PrefKeys.LATEST_CHANNEL_LIST, null) + val latestCommunityListStr = getString(PrefKeys.LATEST_COMMUNITY_LIST, null) + val latestHashtagListStr = getString(PrefKeys.LATEST_HASHTAG_LIST, null) + val latestGeohashListStr = getString(PrefKeys.LATEST_GEOHASH_LIST, null) + val latestEphemeralListStr = getString(PrefKeys.LATEST_EPHEMERAL_LIST, null) + val latestTrustProviderListStr = getString(PrefKeys.LATEST_TRUST_PROVIDER_LIST, null) + val latestPaymentTargetsStr = getString(PrefKeys.LATEST_PAYMENT_TARGETS, null) + val lastReadPerRouteStr = getString(PrefKeys.LAST_READ_PER_ROUTE, null) + + Log.d("LocalPreferences", "Load account from file $npub - before parsing events") + + val defaultHomeFollowList = async { parseOrNull(defaultHomeFollowListStr) ?: TopFilter.AllFollows } + val defaultStoriesFollowList = async { parseOrNull(defaultStoriesFollowListStr) ?: TopFilter.Global } + val defaultNotificationFollowList = async { parseOrNull(defaultNotificationFollowListStr) ?: TopFilter.Global } + val defaultDiscoveryFollowList = async { parseOrNull(defaultDiscoveryFollowListStr) ?: TopFilter.Global } + val zapPaymentRequestServer = async { parseOrNull(zapPaymentRequestServerStr) } + val defaultFileServer = async { parseOrNull(defaultFileServerStr) ?: DEFAULT_MEDIA_SERVERS[0] } + + val pendingAttestations = async { parseOrNull>(pendingAttestationsStr) ?: mapOf() } + val latestUserMetadata = async { parseEventOrNull(latestUserMetadataStr) } + val latestContactList = async { parseEventOrNull(latestContactListStr) } + val latestDmRelayList = async { parseEventOrNull(latestDmRelayListStr) } + val latestNip65RelayList = async { parseEventOrNull(latestNip65RelayListStr) } + val latestSearchRelayList = async { parseEventOrNull(latestSearchRelayListStr) } + val latestIndexRelayList = async { parseEventOrNull(latestIndexRelayListStr) } + val latestRelayFeedsList = async { parseEventOrNull(latestRelayFeedsListStr) } + val latestBlockedRelayList = async { parseEventOrNull(latestBlockedRelayListStr) } + val latestTrustedRelayList = async { parseEventOrNull(latestTrustedRelayListStr) } + val latestMuteList = async { parseEventOrNull(latestMuteListStr) } + val latestPrivateHomeRelayList = async { parseEventOrNull(latestPrivateHomeRelayListStr) } + val latestAppSpecificData = async { parseEventOrNull(latestAppSpecificDataStr) } + val latestChannelList = async { parseEventOrNull(latestChannelListStr) } + val latestCommunityList = async { parseEventOrNull(latestCommunityListStr) } + val latestHashtagList = async { parseEventOrNull(latestHashtagListStr) } + val latestGeohashList = async { parseEventOrNull(latestGeohashListStr) } + val latestEphemeralList = async { parseEventOrNull(latestEphemeralListStr) } + val latestTrustProviderList = async { parseEventOrNull(latestTrustProviderListStr) } + val latestPaymentTargets = async { parseEventOrNull(latestPaymentTargetsStr) } val lastReadPerRoute = - parseOrNull>(PrefKeys.LAST_READ_PER_ROUTE)?.mapValues { - MutableStateFlow(it.value) - } ?: mapOf() + async { + parseOrNull>(lastReadPerRouteStr)?.mapValues { + MutableStateFlow(it.value) + } ?: mapOf() + } - val keyPair = KeyPair(privKey = privKey?.hexToByteArray(), pubKey = pubKey.hexToByteArray()) - val hasDonatedInVersion = getStringSet(PrefKeys.HAS_DONATED_IN_VERSION, null) ?: setOf() + Log.d("LocalPreferences", "Load account from file $npub - asyncs created") return@with AccountSettings( keyPair = keyPair, transientAccount = false, externalSignerPackageName = externalSignerPackageName, localRelayServers = MutableStateFlow(localRelayServers), - defaultFileServer = defaultFileServer, + defaultFileServer = defaultFileServer.await(), stripLocationOnUpload = stripLocationOnUpload, - defaultHomeFollowList = MutableStateFlow(defaultHomeFollowList), - defaultStoriesFollowList = MutableStateFlow(defaultStoriesFollowList), - defaultNotificationFollowList = MutableStateFlow(defaultNotificationFollowList), - defaultDiscoveryFollowList = MutableStateFlow(defaultDiscoveryFollowList), - zapPaymentRequest = MutableStateFlow(zapPaymentRequestServer?.normalize()), + defaultHomeFollowList = MutableStateFlow(defaultHomeFollowList.await()), + defaultStoriesFollowList = MutableStateFlow(defaultStoriesFollowList.await()), + defaultNotificationFollowList = MutableStateFlow(defaultNotificationFollowList.await()), + defaultDiscoveryFollowList = MutableStateFlow(defaultDiscoveryFollowList.await()), + zapPaymentRequest = MutableStateFlow(zapPaymentRequestServer.await()?.normalize()), hideDeleteRequestDialog = hideDeleteRequestDialog, hideBlockAlertDialog = hideBlockAlertDialog, hideNIP17WarningDialog = hideNIP17WarningDialog, - backupUserMetadata = latestUserMetadata, - backupContactList = latestContactList, - backupNIP65RelayList = latestNip65RelayList, - backupDMRelayList = latestDmRelayList, - backupSearchRelayList = latestSearchRelayList, - backupIndexRelayList = latestIndexRelayList, - backupRelayFeedsList = latestRelayFeedsList, - backupBlockedRelayList = latestBlockedRelayList, - backupTrustedRelayList = latestTrustedRelayList, - backupPrivateHomeRelayList = latestPrivateHomeRelayList, - backupMuteList = latestMuteList, - backupAppSpecificData = latestAppSpecificData, - backupChannelList = latestChannelList, - backupCommunityList = latestCommunityList, - backupHashtagList = latestHashtagList, - backupGeohashList = latestGeohashList, - backupEphemeralChatList = latestEphemeralList, - backupTrustProviderList = latestTrustProviderList, - lastReadPerRoute = MutableStateFlow(lastReadPerRoute), + backupUserMetadata = latestUserMetadata.await(), + backupContactList = latestContactList.await(), + backupNIP65RelayList = latestNip65RelayList.await(), + backupDMRelayList = latestDmRelayList.await(), + backupSearchRelayList = latestSearchRelayList.await(), + backupIndexRelayList = latestIndexRelayList.await(), + backupRelayFeedsList = latestRelayFeedsList.await(), + backupBlockedRelayList = latestBlockedRelayList.await(), + backupTrustedRelayList = latestTrustedRelayList.await(), + backupPrivateHomeRelayList = latestPrivateHomeRelayList.await(), + backupMuteList = latestMuteList.await(), + backupAppSpecificData = latestAppSpecificData.await(), + backupChannelList = latestChannelList.await(), + backupCommunityList = latestCommunityList.await(), + backupHashtagList = latestHashtagList.await(), + backupGeohashList = latestGeohashList.await(), + backupEphemeralChatList = latestEphemeralList.await(), + backupTrustProviderList = latestTrustProviderList.await(), + lastReadPerRoute = MutableStateFlow(lastReadPerRoute.await()), hasDonatedInVersion = MutableStateFlow(hasDonatedInVersion), - pendingAttestations = MutableStateFlow(pendingAttestations), - backupNipA3PaymentTargets = latestPaymentTargets, + pendingAttestations = MutableStateFlow(pendingAttestations.await()), + backupNipA3PaymentTargets = latestPaymentTargets.await(), ) } } @@ -545,8 +600,7 @@ object LocalPreferences { return result } - private inline fun SharedPreferences.parseOrNull(key: String): T? { - val value = getString(key, null) + private inline fun parseOrNull(value: String?): T? { if (value.isNullOrEmpty() || value == "null") { return null } @@ -558,13 +612,12 @@ object LocalPreferences { } } catch (e: Throwable) { if (e is CancellationException) throw e - Log.w("LocalPreferences", "Error Decoding $key from Preferences with value $value", e) + Log.w("LocalPreferences", "Error Decoding ${T::class.java} from Preferences with value $value", e) null } } - private inline fun SharedPreferences.parseEventOrNull(key: String): T? { - val value = getString(key, null) + private inline fun parseEventOrNull(value: String?): T? { if (value.isNullOrEmpty() || value == "null") { return null } @@ -572,7 +625,7 @@ object LocalPreferences { Event.fromJson(value) as T? } catch (e: Throwable) { if (e is CancellationException) throw e - Log.w("LocalPreferences", "Error Decoding $key from Preferences with value $value", e) + Log.w("LocalPreferences", "Error Decoding ${T::class.java} from Preferences with value $value", e) null } }