Merge pull request #45 from dmnyc/feat/multi-account-switcher

feat(accounts): multi-account switcher + cancel add-account flow
This commit is contained in:
Barry Deen
2026-06-30 17:38:41 -04:00
committed by GitHub
4 changed files with 92 additions and 12 deletions
@@ -330,6 +330,7 @@ fun WispNavHost(
val onAddAccount: () -> Unit = {
if (anonMode != null) feedViewModel.exitAnonMode()
authViewModel.previousAccountPubkey = authViewModel.keyRepo.getPubkeyHex()
authViewModel.isAddingAccount = true
feedViewModel.resetForAccountSwitch()
walletViewModel.suspendForAccountSwitch() // disconnect only, preserve credentials
@@ -703,7 +704,28 @@ fun WispNavHost(
onLogIn = {
navController.navigate(Routes.AUTH)
},
onToggleTor = { feedViewModel.setTorEnabled(it) }
onToggleTor = { feedViewModel.setTorEnabled(it) },
onCancel = if (authViewModel.isAddingAccount) {
{
val prev = authViewModel.previousAccountPubkey
authViewModel.isAddingAccount = false
authViewModel.previousAccountPubkey = null
if (anonMode != null) feedViewModel.exitAnonMode()
if (prev != null) {
authViewModel.keyRepo.switchToAccount(prev)
authViewModel.keyRepo.reloadPrefs(prev)
}
feedViewModel.reloadForNewAccount()
relayViewModel.reload()
blossomServersViewModel.reload()
composeViewModel.reloadBlossomRepo()
feedViewModel.initRelays()
walletViewModel.refreshState()
navController.navigate(Routes.LOADING) {
popUpTo(Routes.SPLASH) { inclusive = true }
}
}
} else null
)
}
@@ -3,6 +3,7 @@ package com.darkwisp.app.ui.component
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.material3.HorizontalDivider
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -155,6 +156,54 @@ fun WispDrawerContent(
}) {
ProfilePicture(url = profile?.picture, size = 64)
}
Spacer(modifier = Modifier.width(8.dp))
val otherAccountCount = (accounts.size - 1).coerceAtLeast(0)
if (otherAccountCount == 0) {
Box(
modifier = Modifier
.size(26.dp)
.clip(CircleShape)
.background(MaterialTheme.colorScheme.surfaceVariant)
.clickable { onAddAccount() },
contentAlignment = Alignment.Center
) {
Icon(
Icons.Filled.Add,
contentDescription = "Add account",
modifier = Modifier.size(16.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
}
} else {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.clickable { accountPickerExpanded = !accountPickerExpanded }
) {
Box(
modifier = Modifier
.height(26.dp)
.clip(CircleShape)
.background(MaterialTheme.colorScheme.surfaceVariant)
.padding(horizontal = 10.dp),
contentAlignment = Alignment.Center
) {
Text(
text = "+ $otherAccountCount",
style = MaterialTheme.typography.labelSmall,
fontWeight = androidx.compose.ui.text.font.FontWeight.Bold,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Spacer(modifier = Modifier.width(2.dp))
Icon(
if (accountPickerExpanded) Icons.Outlined.KeyboardArrowDown
else Icons.Outlined.KeyboardArrowRight,
contentDescription = null,
modifier = Modifier.size(18.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
Spacer(modifier = Modifier.weight(1f))
IconButton(onClick = onToggleTheme) {
Icon(
@@ -176,7 +225,7 @@ fun WispDrawerContent(
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(enabled = accounts.size > 1 || accounts.isNotEmpty()) {
.clickable(enabled = accounts.isNotEmpty()) {
accountPickerExpanded = !accountPickerExpanded
},
verticalAlignment = Alignment.CenterVertically
@@ -187,15 +236,6 @@ fun WispDrawerContent(
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.weight(1f, fill = false)
)
if (accounts.size > 1 || accounts.isNotEmpty()) {
Icon(
if (accountPickerExpanded) Icons.Outlined.KeyboardArrowDown
else Icons.Outlined.KeyboardArrowRight,
contentDescription = stringResource(R.string.cd_switch_account),
modifier = Modifier.size(20.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
Spacer(Modifier.height(2.dp))
if (!profile?.nip05.isNullOrBlank()) {
@@ -1,6 +1,7 @@
package com.darkwisp.app.ui.screen
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
@@ -59,7 +60,8 @@ fun SplashScreen(
viewModel: SplashViewModel,
onSignUp: () -> Unit,
onLogIn: () -> Unit,
onToggleTor: (Boolean) -> Unit = {}
onToggleTor: (Boolean) -> Unit = {},
onCancel: (() -> Unit)? = null
) {
val profilePictures by viewModel.profilePictures.collectAsState()
val liveMetrics by viewModel.liveMetrics.collectAsState()
@@ -123,6 +125,21 @@ fun SplashScreen(
)
)
if (onCancel != null) {
Text(
text = "Cancel",
style = MaterialTheme.typography.labelLarge,
color = androidx.compose.ui.graphics.Color.White,
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 120.dp)
.clip(androidx.compose.foundation.shape.CircleShape)
.background(androidx.compose.ui.graphics.Color.White.copy(alpha = 0.15f))
.clickable(onClick = onCancel)
.padding(horizontal = 28.dp, vertical = 12.dp)
)
}
// Logo, tagline, and action buttons pinned to bottom
Column(
horizontalAlignment = Alignment.CenterHorizontally,
@@ -31,6 +31,7 @@ class AuthViewModel(app: Application) : AndroidViewModel(app) {
val accountsFlow: StateFlow<List<AccountInfo>> = keyRepo.accountsFlow
var isAddingAccount: Boolean = false
var previousAccountPubkey: String? = null
val isLoggedIn: Boolean get() = keyRepo.isLoggedIn()