Faster logout processing without closing the account switcher dialog.

This commit is contained in:
Vitor Pamplona
2024-10-08 13:59:51 -04:00
parent f51f929079
commit 67ea0dcf7f
3 changed files with 49 additions and 25 deletions
@@ -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<AccountInfo>? = null
private var savedAccounts: MutableStateFlow<List<AccountInfo>?> = MutableStateFlow(null)
private var cachedAccounts: MutableMap<String, AccountSettings?> = mutableMapOf()
suspend fun currentAccount(): String? {
@@ -156,7 +157,7 @@ object LocalPreferences {
}
private suspend fun savedAccounts(): List<AccountInfo> {
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<AccountInfo>) =
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)
@@ -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<User?>(
decodePublicKeyAsHexOrNull(acc.npub)?.let {
LocalCache.getUserIfExists(it)
@@ -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)
}
}
}
}