taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

commit 2b307902d1b77f16a90e095931206d59f33578d4
parent bd4e0958bfa9c4ac42d92f2feb710efc98ad53ec
Author: Iván Ávalos <avalos@disroot.org>
Date:   Fri, 25 Oct 2024 21:31:18 +0200

[wallet] Disable actions when wallet is empty

Diffstat:
Mwallet/src/main/java/net/taler/wallet/MainFragment.kt | 22++++++++++++++++------
Mwallet/src/main/java/net/taler/wallet/balances/BalancesComposable.kt | 56++++++++++++++++++++++++++++++++++++--------------------
Mwallet/src/main/res/values/strings.xml | 2++
3 files changed, 54 insertions(+), 26 deletions(-)

diff --git a/wallet/src/main/java/net/taler/wallet/MainFragment.kt b/wallet/src/main/java/net/taler/wallet/MainFragment.kt @@ -103,6 +103,12 @@ class MainFragment: Fragment() { val settingsFragmentState = rememberFragmentState() + val context = LocalContext.current + val balanceState by model.balanceManager.state.observeAsState(BalanceState.None) + val selectedScope by model.transactionManager.selectedScope.collectAsStateLifecycleAware() + val txResult by remember(selectedScope) { model.transactionManager.transactionsFlow(selectedScope) }.collectAsStateLifecycleAware() + val selectedSpec = remember(selectedScope) { selectedScope?.let { model.balanceManager.getSpecForScopeInfo(it) } } + Scaffold( bottomBar = { NavigationBar { @@ -149,12 +155,6 @@ class MainFragment: Fragment() { } } ) { innerPadding -> - val context = LocalContext.current - val balanceState by model.balanceManager.state.observeAsState(BalanceState.None) - val selectedScope by model.transactionManager.selectedScope.collectAsStateLifecycleAware() - val txResult by remember(selectedScope) { model.transactionManager.transactionsFlow(selectedScope) }.collectAsStateLifecycleAware() - val selectedSpec = remember(selectedScope) { selectedScope?.let { model.balanceManager.getSpecForScopeInfo(it) } } - LaunchedEffect(tab, selectedScope) { setTitle(tab, selectedScope) } @@ -197,10 +197,15 @@ class MainFragment: Fragment() { } } + val disableActions = remember(balanceState) { + (balanceState as? BalanceState.Success)?.balances?.isEmpty() ?: true + } + TalerActionsModal( showSheet = showSheet, sheetState = sheetState, onDismiss = { showSheet = false }, + disableActions = disableActions, onSend = this@MainFragment::onSend, onReceive = this@MainFragment::onReceive, onScanQr = this@MainFragment::onScanQr, @@ -281,6 +286,7 @@ fun SettingsView( fun TalerActionsModal( showSheet: Boolean, sheetState: SheetState, + disableActions: Boolean, onDismiss: () -> Unit, onSend: () -> Unit, onReceive: () -> Unit, @@ -308,12 +314,14 @@ fun TalerActionsModal( icon = R.drawable.transaction_p2p_outgoing, title = R.string.transactions_send_funds, onClick = onSend, + enabled = !disableActions, ) GridMenuItem( icon = R.drawable.transaction_p2p_incoming, title = R.string.transactions_receive_funds, onClick = onReceive, + enabled = !disableActions, ) GridMenuItem( @@ -326,12 +334,14 @@ fun TalerActionsModal( icon = R.drawable.transaction_deposit, title = R.string.send_deposit_button_label, onClick = onDeposit, + enabled = !disableActions ) GridMenuItem( icon = R.drawable.transaction_withdrawal, title = R.string.withdraw_button_label, onClick = onWithdraw, + enabled = !disableActions, ) GridMenuItem( diff --git a/wallet/src/main/java/net/taler/wallet/balances/BalancesComposable.kt b/wallet/src/main/java/net/taler/wallet/balances/BalancesComposable.kt @@ -19,12 +19,15 @@ package net.taler.wallet.balances import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Button import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedCard @@ -34,6 +37,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign @@ -48,6 +52,7 @@ import net.taler.wallet.balances.ScopeInfo.Global import net.taler.wallet.cleanExchange import net.taler.wallet.compose.LoadingScreen import net.taler.wallet.compose.TalerSurface +import net.taler.wallet.launchInAppBrowser import net.taler.wallet.transactions.Transaction import net.taler.wallet.transactions.TransactionsComposable import net.taler.wallet.transactions.TransactionsResult @@ -68,8 +73,8 @@ fun BalancesComposable( is BalanceState.None -> {} is BalanceState.Loading -> LoadingScreen() is BalanceState.Error -> WithdrawalError(state.error) - is BalanceState.Success -> if (selectedScope == null) { - if (state.balances.isNotEmpty()) { + is BalanceState.Success -> if (state.balances.isNotEmpty()) { + if (selectedScope == null) { LazyColumn(Modifier.fillMaxSize()) { items(state.balances, key = { it.scopeInfo.hashCode() }) { balance -> BalanceRow(balance) { @@ -78,23 +83,23 @@ fun BalancesComposable( } } } else { - EmptyBalancesComposable() + val balance = remember(state.balances, selectedScope) { + state.balances.find { it.scopeInfo == selectedScope } + } + + balance?.let { + TransactionsComposable( + balance = it, + currencySpec = selectedCurrencySpec, + txResult = txResult, + onTransactionClick = onTransactionClicked, + onTransactionsDelete = onTransactionsDelete, + onShowBalancesClicked = onShowBalancesClicked, + ) + } ?: error("no balance matching scopeInfo") } } else { - val balance = remember(state.balances, selectedScope) { - state.balances.find { it.scopeInfo == selectedScope } - } - - balance?.let { - TransactionsComposable( - balance = it, - currencySpec = selectedCurrencySpec, - txResult = txResult, - onTransactionClick = onTransactionClicked, - onTransactionsDelete = onTransactionsDelete, - onShowBalancesClicked = onShowBalancesClicked, - ) - } ?: error("no balance matching scopeInfo") + EmptyBalancesComposable() } } } @@ -173,16 +178,27 @@ fun BalanceRow( @Composable fun EmptyBalancesComposable() { - Box( + Column( modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center, + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, ) { + val context = LocalContext.current + // TODO: render hyperlink! Text( stringResource(R.string.balances_empty_state), textAlign = TextAlign.Center, style = MaterialTheme.typography.bodyMedium, ) + + Spacer(Modifier.height(32.dp)) + + Button(onClick = { + launchInAppBrowser(context, context.getString(R.string.balances_empty_demo_url)) + }) { + Text(stringResource(R.string.balances_empty_get_money)) + } } } diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml @@ -115,6 +115,8 @@ GNU Taler is immune against many types of fraud, such as phishing of credit card <string name="balance_scope_auditor">Auditor: %1$s</string> <string name="balance_scope_exchange">From %1$s</string> <string name="balances_empty_state">There is no digital cash in your wallet.\n\nYou can get test money from the demo bank:\n\nhttps://bank.demo.taler.net</string> + <string name="balances_empty_demo_url">https://bank.demo.taler.net</string> + <string name="balances_empty_get_money">Get test money</string> <string name="balances_inbound_amount">+%1$s inbound</string> <string name="balances_outbound_amount">-%1$s outbound</string> <string name="balances_title">Balances</string>