Merge pull request #3054 from vitorpamplona/claude/pensive-brown-UVDZf

Replace wallet reorder buttons with drag-and-drop UI
This commit is contained in:
Vitor Pamplona
2026-05-26 20:33:59 -04:00
committed by GitHub
8 changed files with 54 additions and 48 deletions
@@ -285,9 +285,9 @@ fun BuildNavigation(
composable<Route.Chess> { ChessLobbyScreen(accountViewModel, nav) }
composableFromEnd<Route.Wallet> { WalletScreen(accountViewModel, nav) }
composableFromEnd<Route.WalletSend> { WalletSendScreen(accountViewModel, nav) }
composableFromEnd<Route.WalletReceive> { WalletReceiveScreen(accountViewModel, nav) }
composableFromEnd<Route.WalletTransactions> { WalletTransactionsScreen(accountViewModel, nav) }
composableFromEndArgs<Route.WalletSend> { WalletSendScreen(it.walletId, accountViewModel, nav) }
composableFromEndArgs<Route.WalletReceive> { WalletReceiveScreen(it.walletId, accountViewModel, nav) }
composableFromEndArgs<Route.WalletTransactions> { WalletTransactionsScreen(it.walletId, accountViewModel, nav) }
composableFromEnd<Route.OnchainTransactions> { OnchainTransactionsScreen(accountViewModel, nav) }
composableFromEndArgs<Route.WalletDetail> { WalletDetailScreen(it.walletId, accountViewModel, nav) }
composableFromEnd<Route.WalletAdd> { AddWalletScreen(accountViewModel, nav) }
@@ -146,11 +146,20 @@ sealed class Route {
@Serializable object Wallet : Route()
@Serializable object WalletSend : Route()
@Serializable
data class WalletSend(
val walletId: String,
) : Route()
@Serializable object WalletReceive : Route()
@Serializable
data class WalletReceive(
val walletId: String,
) : Route()
@Serializable object WalletTransactions : Route()
@Serializable
data class WalletTransactions(
val walletId: String,
) : Route()
@Serializable object OnchainTransactions : Route()
@@ -149,7 +149,7 @@ fun WalletDetailScreen(
horizontalArrangement = Arrangement.spacedBy(16.dp),
) {
Button(
onClick = { nav.nav(Route.WalletReceive) },
onClick = { nav.nav(Route.WalletReceive(walletId)) },
modifier =
Modifier
.weight(1f)
@@ -171,7 +171,7 @@ fun WalletDetailScreen(
}
Button(
onClick = { nav.nav(Route.WalletSend) },
onClick = { nav.nav(Route.WalletSend(walletId)) },
modifier =
Modifier
.weight(1f)
@@ -192,7 +192,7 @@ fun WalletDetailScreen(
// Transactions button
OutlinedButton(
onClick = { nav.nav(Route.WalletTransactions) },
onClick = { nav.nav(Route.WalletTransactions(walletId)) },
modifier =
Modifier
.fillMaxWidth()
@@ -74,13 +74,15 @@ import java.text.NumberFormat
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun WalletReceiveScreen(
walletId: String,
accountViewModel: AccountViewModel,
nav: INav,
) {
val walletViewModel: WalletViewModel = viewModel()
LaunchedEffect(accountViewModel) {
LaunchedEffect(accountViewModel, walletId) {
walletViewModel.init(accountViewModel)
walletViewModel.selectWallet(walletId)
}
DisposableEffect(Unit) {
@@ -73,6 +73,10 @@ import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.RelayDragState
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.draggableRelayItem
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relayDragHandle
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.rememberRelayDragState
import com.vitorpamplona.amethyst.ui.stringRes
import kotlinx.coroutines.launch
import java.text.NumberFormat
@@ -197,6 +201,12 @@ private fun MultiWalletHomeContent(
}
}
val dragState =
rememberRelayDragState(
onMove = { from, to -> walletViewModel.moveWallet(from, to) },
itemCount = { walletInfoList.size },
)
LazyColumn(
state = listState,
modifier =
@@ -204,6 +214,7 @@ private fun MultiWalletHomeContent(
.fillMaxSize()
.padding(horizontal = 16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
userScrollEnabled = !dragState.isDragging,
) {
item {
Spacer(modifier = Modifier.height(8.dp))
@@ -218,7 +229,7 @@ private fun MultiWalletHomeContent(
WalletCard(
walletInfo = walletInfo,
index = index,
totalCount = walletInfoList.size,
dragState = if (walletInfoList.size > 1) dragState else null,
onSelect = {
walletViewModel.selectWallet(walletInfo.walletId)
nav.nav(Route.WalletDetail(walletInfo.walletId))
@@ -229,12 +240,6 @@ private fun MultiWalletHomeContent(
onRename = { newName ->
walletViewModel.renameWallet(walletInfo.walletId, newName)
},
onMoveUp = {
walletViewModel.moveWallet(index, index - 1)
},
onMoveDown = {
walletViewModel.moveWallet(index, index + 1)
},
onRemove = {
walletViewModel.removeWallet(walletInfo.walletId)
},
@@ -264,12 +269,10 @@ private fun MultiWalletHomeContent(
private fun WalletCard(
walletInfo: WalletInfo,
index: Int,
totalCount: Int,
dragState: RelayDragState?,
onSelect: () -> Unit,
onSetDefault: () -> Unit,
onRename: (String) -> Unit,
onMoveUp: () -> Unit,
onMoveDown: () -> Unit,
onRemove: () -> Unit,
) {
var showRemoveDialog by remember { mutableStateOf(false) }
@@ -311,6 +314,7 @@ private fun WalletCard(
modifier =
Modifier
.fillMaxWidth()
.let { if (dragState != null) it.draggableRelayItem(index, dragState) else it }
.clickable(onClick = onSelect),
shape = RoundedCornerShape(16.dp),
border =
@@ -363,32 +367,18 @@ private fun WalletCard(
}
}
// Reorder buttons
if (totalCount > 1) {
Column {
IconButton(
onClick = onMoveUp,
enabled = index > 0,
modifier = Modifier.size(28.dp),
) {
Icon(
MaterialSymbols.KeyboardArrowUp,
contentDescription = stringRes(R.string.wallet_move_up),
modifier = Modifier.size(20.dp),
)
}
IconButton(
onClick = onMoveDown,
enabled = index < totalCount - 1,
modifier = Modifier.size(28.dp),
) {
Icon(
MaterialSymbols.KeyboardArrowDown,
contentDescription = stringRes(R.string.wallet_move_down),
modifier = Modifier.size(20.dp),
)
}
}
// Drag handle for reordering
if (dragState != null) {
Icon(
MaterialSymbols.DragIndicator,
contentDescription = stringRes(R.string.wallet_reorder),
modifier =
Modifier
.size(24.dp)
.relayDragHandle(index, dragState),
tint = MaterialTheme.colorScheme.onSurfaceVariant,
)
Spacer(modifier = Modifier.width(8.dp))
}
// Balance
@@ -64,13 +64,15 @@ import com.vitorpamplona.amethyst.ui.stringRes
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun WalletSendScreen(
walletId: String,
accountViewModel: AccountViewModel,
nav: INav,
) {
val walletViewModel: WalletViewModel = viewModel()
LaunchedEffect(accountViewModel) {
LaunchedEffect(accountViewModel, walletId) {
walletViewModel.init(accountViewModel)
walletViewModel.selectWallet(walletId)
}
DisposableEffect(Unit) {
@@ -73,13 +73,15 @@ import java.util.Locale
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun WalletTransactionsScreen(
walletId: String,
accountViewModel: AccountViewModel,
nav: INav,
) {
val walletViewModel: WalletViewModel = viewModel()
LaunchedEffect(accountViewModel) {
LaunchedEffect(accountViewModel, walletId) {
walletViewModel.init(accountViewModel)
walletViewModel.selectWallet(walletId)
walletViewModel.fetchTransactions()
}
+1
View File
@@ -1870,6 +1870,7 @@
<string name="wallet_invalid_uri">Invalid NWC connection URI</string>
<string name="wallet_move_up">Move Up</string>
<string name="wallet_move_down">Move Down</string>
<string name="wallet_reorder">Reorder wallet</string>
<string name="wallet_onchain_transactions">Onchain Transactions</string>
<string name="wallet_onchain_no_address">No on-chain address available for this account.</string>
<string name="wallet_onchain_no_backend">No chain backend is configured.</string>