From 67ea0dcf7fd791d970eaeb64b248d06ea2a587e3 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Tue, 8 Oct 2024 13:59:51 -0400 Subject: [PATCH] Faster logout processing without closing the account switcher dialog. --- .../amethyst/LocalPreferences.kt | 24 +++++++++-------- .../ui/navigation/AccountSwitchBottomSheet.kt | 24 +++++++++-------- .../ui/screen/AccountStateViewModel.kt | 26 ++++++++++++++++--- 3 files changed, 49 insertions(+), 25 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt index 06f2af1f2..04a82a0c7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt @@ -57,6 +57,7 @@ import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext @@ -74,7 +75,7 @@ data class AccountInfo( val npub: String, val hasPrivKey: Boolean, val loggedInWithExternalSigner: Boolean, - val isTransient: Boolean = false, + val isTransient: Boolean, ) private object PrefKeys { @@ -126,7 +127,7 @@ object LocalPreferences { private const val COMMA = "," private var currentAccount: String? = null - private var savedAccounts: List? = null + private var savedAccounts: MutableStateFlow?> = MutableStateFlow(null) private var cachedAccounts: MutableMap = mutableMapOf() suspend fun currentAccount(): String? { @@ -156,7 +157,7 @@ object LocalPreferences { } private suspend fun savedAccounts(): List { - if (savedAccounts == null) { + if (savedAccounts.value == null) { withContext(Dispatchers.IO) { with(encryptedPreferences()) { val newSystemOfAccounts = @@ -165,7 +166,7 @@ object LocalPreferences { } if (!newSystemOfAccounts.isNullOrEmpty()) { - savedAccounts = newSystemOfAccounts + savedAccounts.emit(newSystemOfAccounts) } else { val oldAccounts = getString(PrefKeys.SAVED_ACCOUNTS, null)?.split(COMMA) ?: listOf() @@ -174,27 +175,28 @@ object LocalPreferences { AccountInfo( npub, encryptedPreferences(npub).getBoolean(PrefKeys.LOGIN_WITH_EXTERNAL_SIGNER, false), - (encryptedPreferences(npub).getString(PrefKeys.NOSTR_PRIVKEY, "") ?: "") - .isNotBlank(), + (encryptedPreferences(npub).getString(PrefKeys.NOSTR_PRIVKEY, "") ?: "").isNotBlank(), + false, ) } - savedAccounts = migrated + savedAccounts.emit(migrated) edit().apply { putString(PrefKeys.ALL_ACCOUNT_INFO, Event.mapper.writeValueAsString(savedAccounts)) }.apply() } } } } - return savedAccounts!! + // it's always not null when it gets here. + return savedAccounts.value!! } - fun cachedAccounts() = savedAccounts + fun accountsFlow() = savedAccounts private suspend fun updateSavedAccounts(accounts: List) = withContext(Dispatchers.IO) { if (savedAccounts != accounts) { - savedAccounts = accounts + savedAccounts.emit(accounts) encryptedPreferences() .edit() @@ -269,7 +271,7 @@ object LocalPreferences { */ @SuppressLint("ApplySharedPref") suspend fun updatePrefsForLogout(accountInfo: AccountInfo) { - Log.d("LocalPreferences", "Saving to encrypted storage updatePrefsForLogout") + Log.d("LocalPreferences", "Saving to encrypted storage updatePrefsForLogout ${accountInfo.npub}") withContext(Dispatchers.IO) { encryptedPreferences(accountInfo.npub).edit().clear().commit() removeAccount(accountInfo) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AccountSwitchBottomSheet.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AccountSwitchBottomSheet.kt index fdf671472..6aeb227f2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AccountSwitchBottomSheet.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AccountSwitchBottomSheet.kt @@ -47,7 +47,6 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -55,6 +54,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.AccountInfo import com.vitorpamplona.amethyst.LocalPreferences import com.vitorpamplona.amethyst.R @@ -73,6 +73,7 @@ import com.vitorpamplona.amethyst.ui.theme.Size10dp import com.vitorpamplona.amethyst.ui.theme.Size55dp import com.vitorpamplona.quartz.encoders.decodePublicKeyAsHexOrNull import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.forEach import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @@ -81,14 +82,6 @@ fun AccountSwitchBottomSheet( accountViewModel: AccountViewModel, accountStateViewModel: AccountStateViewModel, ) { - @Suppress("ProduceStateDoesNotAssignValue") - val accounts by - produceState(initialValue = LocalPreferences.cachedAccounts()) { - if (value == null) { - value = LocalPreferences.allSavedAccounts() - } - } - var popupExpanded by remember { mutableStateOf(false) } val scrollState = rememberScrollState() @@ -103,7 +96,7 @@ fun AccountSwitchBottomSheet( ) { Text(stringRes(R.string.account_switch_select_account), fontWeight = FontWeight.Bold) } - accounts?.forEach { acc -> DisplayAccount(acc, accountViewModel, accountStateViewModel) } + DisplayAllAccounts(accountViewModel, accountStateViewModel) Row( modifier = Modifier @@ -123,13 +116,22 @@ fun AccountSwitchBottomSheet( } } +@Composable +private fun DisplayAllAccounts( + accountViewModel: AccountViewModel, + accountStateViewModel: AccountStateViewModel, +) { + val accounts by LocalPreferences.accountsFlow().collectAsStateWithLifecycle() + accounts?.forEach { acc -> DisplayAccount(acc, accountViewModel, accountStateViewModel) } +} + @Composable fun DisplayAccount( acc: AccountInfo, accountViewModel: AccountViewModel, accountStateViewModel: AccountStateViewModel, ) { - var baseUser by remember { + var baseUser by remember(acc) { mutableStateOf( decodePublicKeyAsHexOrNull(acc.npub)?.let { LocalCache.getUserIfExists(it) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountStateViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountStateViewModel.kt index 0c9538c81..78f375bee 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountStateViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountStateViewModel.kt @@ -27,6 +27,7 @@ import androidx.lifecycle.viewModelScope import com.vitorpamplona.amethyst.AccountInfo import com.vitorpamplona.amethyst.Amethyst import com.vitorpamplona.amethyst.LocalPreferences +import com.vitorpamplona.amethyst.LocalPreferences.currentAccount import com.vitorpamplona.amethyst.model.AccountSettings import com.vitorpamplona.amethyst.model.DefaultChannels import com.vitorpamplona.amethyst.model.DefaultDMRelayList @@ -321,11 +322,30 @@ class AccountStateViewModel : ViewModel() { } } + fun currentAccount() = + when (val state = _accountContent.value) { + is AccountState.LoggedIn -> + state.accountSettings.keyPair.pubKey + .toNpub() + + is AccountState.LoggedInViewOnly -> + state.accountSettings.keyPair.pubKey + .toNpub() + + else -> null + } + fun logOff(accountInfo: AccountInfo) { viewModelScope.launch(Dispatchers.IO) { - prepareLogoutOrSwitch() - LocalPreferences.updatePrefsForLogout(accountInfo) - tryLoginExistingAccount() + if (accountInfo.npub == currentAccount()) { + // log off and relogin with the 0 account + prepareLogoutOrSwitch() + LocalPreferences.updatePrefsForLogout(accountInfo) + tryLoginExistingAccount() + } else { + // delete without login off + LocalPreferences.updatePrefsForLogout(accountInfo) + } } } }