Faster logout processing without closing the account switcher dialog.
This commit is contained in:
@@ -57,6 +57,7 @@ import kotlinx.collections.immutable.toImmutableList
|
|||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -74,7 +75,7 @@ data class AccountInfo(
|
|||||||
val npub: String,
|
val npub: String,
|
||||||
val hasPrivKey: Boolean,
|
val hasPrivKey: Boolean,
|
||||||
val loggedInWithExternalSigner: Boolean,
|
val loggedInWithExternalSigner: Boolean,
|
||||||
val isTransient: Boolean = false,
|
val isTransient: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
private object PrefKeys {
|
private object PrefKeys {
|
||||||
@@ -126,7 +127,7 @@ object LocalPreferences {
|
|||||||
private const val COMMA = ","
|
private const val COMMA = ","
|
||||||
|
|
||||||
private var currentAccount: String? = null
|
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()
|
private var cachedAccounts: MutableMap<String, AccountSettings?> = mutableMapOf()
|
||||||
|
|
||||||
suspend fun currentAccount(): String? {
|
suspend fun currentAccount(): String? {
|
||||||
@@ -156,7 +157,7 @@ object LocalPreferences {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun savedAccounts(): List<AccountInfo> {
|
private suspend fun savedAccounts(): List<AccountInfo> {
|
||||||
if (savedAccounts == null) {
|
if (savedAccounts.value == null) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
with(encryptedPreferences()) {
|
with(encryptedPreferences()) {
|
||||||
val newSystemOfAccounts =
|
val newSystemOfAccounts =
|
||||||
@@ -165,7 +166,7 @@ object LocalPreferences {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!newSystemOfAccounts.isNullOrEmpty()) {
|
if (!newSystemOfAccounts.isNullOrEmpty()) {
|
||||||
savedAccounts = newSystemOfAccounts
|
savedAccounts.emit(newSystemOfAccounts)
|
||||||
} else {
|
} else {
|
||||||
val oldAccounts = getString(PrefKeys.SAVED_ACCOUNTS, null)?.split(COMMA) ?: listOf()
|
val oldAccounts = getString(PrefKeys.SAVED_ACCOUNTS, null)?.split(COMMA) ?: listOf()
|
||||||
|
|
||||||
@@ -174,27 +175,28 @@ object LocalPreferences {
|
|||||||
AccountInfo(
|
AccountInfo(
|
||||||
npub,
|
npub,
|
||||||
encryptedPreferences(npub).getBoolean(PrefKeys.LOGIN_WITH_EXTERNAL_SIGNER, false),
|
encryptedPreferences(npub).getBoolean(PrefKeys.LOGIN_WITH_EXTERNAL_SIGNER, false),
|
||||||
(encryptedPreferences(npub).getString(PrefKeys.NOSTR_PRIVKEY, "") ?: "")
|
(encryptedPreferences(npub).getString(PrefKeys.NOSTR_PRIVKEY, "") ?: "").isNotBlank(),
|
||||||
.isNotBlank(),
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
savedAccounts = migrated
|
savedAccounts.emit(migrated)
|
||||||
|
|
||||||
edit().apply { putString(PrefKeys.ALL_ACCOUNT_INFO, Event.mapper.writeValueAsString(savedAccounts)) }.apply()
|
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>) =
|
private suspend fun updateSavedAccounts(accounts: List<AccountInfo>) =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
if (savedAccounts != accounts) {
|
if (savedAccounts != accounts) {
|
||||||
savedAccounts = accounts
|
savedAccounts.emit(accounts)
|
||||||
|
|
||||||
encryptedPreferences()
|
encryptedPreferences()
|
||||||
.edit()
|
.edit()
|
||||||
@@ -269,7 +271,7 @@ object LocalPreferences {
|
|||||||
*/
|
*/
|
||||||
@SuppressLint("ApplySharedPref")
|
@SuppressLint("ApplySharedPref")
|
||||||
suspend fun updatePrefsForLogout(accountInfo: AccountInfo) {
|
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) {
|
withContext(Dispatchers.IO) {
|
||||||
encryptedPreferences(accountInfo.npub).edit().clear().commit()
|
encryptedPreferences(accountInfo.npub).edit().clear().commit()
|
||||||
removeAccount(accountInfo)
|
removeAccount(accountInfo)
|
||||||
|
|||||||
+13
-11
@@ -47,7 +47,6 @@ import androidx.compose.runtime.derivedStateOf
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.produceState
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
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.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.vitorpamplona.amethyst.AccountInfo
|
import com.vitorpamplona.amethyst.AccountInfo
|
||||||
import com.vitorpamplona.amethyst.LocalPreferences
|
import com.vitorpamplona.amethyst.LocalPreferences
|
||||||
import com.vitorpamplona.amethyst.R
|
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.amethyst.ui.theme.Size55dp
|
||||||
import com.vitorpamplona.quartz.encoders.decodePublicKeyAsHexOrNull
|
import com.vitorpamplona.quartz.encoders.decodePublicKeyAsHexOrNull
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.forEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -81,14 +82,6 @@ fun AccountSwitchBottomSheet(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
accountStateViewModel: AccountStateViewModel,
|
accountStateViewModel: AccountStateViewModel,
|
||||||
) {
|
) {
|
||||||
@Suppress("ProduceStateDoesNotAssignValue")
|
|
||||||
val accounts by
|
|
||||||
produceState(initialValue = LocalPreferences.cachedAccounts()) {
|
|
||||||
if (value == null) {
|
|
||||||
value = LocalPreferences.allSavedAccounts()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var popupExpanded by remember { mutableStateOf(false) }
|
var popupExpanded by remember { mutableStateOf(false) }
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
|
|
||||||
@@ -103,7 +96,7 @@ fun AccountSwitchBottomSheet(
|
|||||||
) {
|
) {
|
||||||
Text(stringRes(R.string.account_switch_select_account), fontWeight = FontWeight.Bold)
|
Text(stringRes(R.string.account_switch_select_account), fontWeight = FontWeight.Bold)
|
||||||
}
|
}
|
||||||
accounts?.forEach { acc -> DisplayAccount(acc, accountViewModel, accountStateViewModel) }
|
DisplayAllAccounts(accountViewModel, accountStateViewModel)
|
||||||
Row(
|
Row(
|
||||||
modifier =
|
modifier =
|
||||||
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
|
@Composable
|
||||||
fun DisplayAccount(
|
fun DisplayAccount(
|
||||||
acc: AccountInfo,
|
acc: AccountInfo,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
accountStateViewModel: AccountStateViewModel,
|
accountStateViewModel: AccountStateViewModel,
|
||||||
) {
|
) {
|
||||||
var baseUser by remember {
|
var baseUser by remember(acc) {
|
||||||
mutableStateOf<User?>(
|
mutableStateOf<User?>(
|
||||||
decodePublicKeyAsHexOrNull(acc.npub)?.let {
|
decodePublicKeyAsHexOrNull(acc.npub)?.let {
|
||||||
LocalCache.getUserIfExists(it)
|
LocalCache.getUserIfExists(it)
|
||||||
|
|||||||
+23
-3
@@ -27,6 +27,7 @@ import androidx.lifecycle.viewModelScope
|
|||||||
import com.vitorpamplona.amethyst.AccountInfo
|
import com.vitorpamplona.amethyst.AccountInfo
|
||||||
import com.vitorpamplona.amethyst.Amethyst
|
import com.vitorpamplona.amethyst.Amethyst
|
||||||
import com.vitorpamplona.amethyst.LocalPreferences
|
import com.vitorpamplona.amethyst.LocalPreferences
|
||||||
|
import com.vitorpamplona.amethyst.LocalPreferences.currentAccount
|
||||||
import com.vitorpamplona.amethyst.model.AccountSettings
|
import com.vitorpamplona.amethyst.model.AccountSettings
|
||||||
import com.vitorpamplona.amethyst.model.DefaultChannels
|
import com.vitorpamplona.amethyst.model.DefaultChannels
|
||||||
import com.vitorpamplona.amethyst.model.DefaultDMRelayList
|
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) {
|
fun logOff(accountInfo: AccountInfo) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
prepareLogoutOrSwitch()
|
if (accountInfo.npub == currentAccount()) {
|
||||||
LocalPreferences.updatePrefsForLogout(accountInfo)
|
// log off and relogin with the 0 account
|
||||||
tryLoginExistingAccount()
|
prepareLogoutOrSwitch()
|
||||||
|
LocalPreferences.updatePrefsForLogout(accountInfo)
|
||||||
|
tryLoginExistingAccount()
|
||||||
|
} else {
|
||||||
|
// delete without login off
|
||||||
|
LocalPreferences.updatePrefsForLogout(accountInfo)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user