TransactionLossFragment.kt (6231B)
1 /* 2 * This file is part of GNU Taler 3 * (C) 2024 Taler Systems S.A. 4 * 5 * GNU Taler is free software; you can redistribute it and/or modify it under the 6 * terms of the GNU General Public License as published by the Free Software 7 * Foundation; either version 3, or (at your option) any later version. 8 * 9 * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY 10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License along with 14 * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 package net.taler.wallet.transactions 18 19 import android.os.Bundle 20 import android.view.LayoutInflater 21 import android.view.View 22 import android.view.ViewGroup 23 import androidx.compose.foundation.layout.Column 24 import androidx.compose.foundation.layout.fillMaxWidth 25 import androidx.compose.foundation.layout.padding 26 import androidx.compose.foundation.rememberScrollState 27 import androidx.compose.foundation.verticalScroll 28 import androidx.compose.material3.MaterialTheme 29 import androidx.compose.material3.Surface 30 import androidx.compose.material3.Text 31 import androidx.compose.runtime.Composable 32 import androidx.compose.runtime.getValue 33 import androidx.compose.runtime.remember 34 import androidx.compose.ui.Alignment 35 import androidx.compose.ui.Modifier 36 import androidx.compose.ui.platform.ComposeView 37 import androidx.compose.ui.platform.LocalContext 38 import androidx.compose.ui.res.stringResource 39 import androidx.compose.ui.tooling.preview.Preview 40 import androidx.compose.ui.unit.dp 41 import net.taler.common.Amount 42 import net.taler.common.CurrencySpecification 43 import net.taler.common.Timestamp 44 import net.taler.common.toAbsoluteTime 45 import net.taler.wallet.BottomInsetsSpacer 46 import net.taler.wallet.R 47 import net.taler.wallet.backend.TalerErrorCode 48 import net.taler.wallet.backend.TalerErrorInfo 49 import net.taler.wallet.balances.ScopeInfo 50 import net.taler.wallet.compose.TalerSurface 51 import net.taler.wallet.compose.collectAsStateLifecycleAware 52 import net.taler.wallet.transactions.LossEventType.DenomExpired 53 import net.taler.wallet.transactions.LossEventType.DenomUnoffered 54 import net.taler.wallet.transactions.LossEventType.DenomVanished 55 import net.taler.wallet.transactions.TransactionAction.Abort 56 import net.taler.wallet.transactions.TransactionAction.Retry 57 import net.taler.wallet.transactions.TransactionAction.Suspend 58 import net.taler.wallet.transactions.TransactionMajorState.Pending 59 60 class TransactionLossFragment: TransactionDetailFragment() { 61 62 override fun onCreateView( 63 inflater: LayoutInflater, 64 container: ViewGroup?, 65 savedInstanceState: Bundle? 66 ): View = ComposeView(requireContext()).apply { 67 setContent { 68 val t by transactionManager.selectedTransaction.collectAsStateLifecycleAware() 69 70 TalerSurface { 71 (t as? TransactionDenomLoss)?.let { tx -> 72 val spec = remember(tx.amountRaw.currency, tx.scopes) { 73 exchangeManager.getSpecForCurrency(tx.amountRaw.currency, tx.scopes) 74 } 75 TransitionLossComposable(tx, devMode, spec) { 76 onTransitionButtonClicked(tx, it) 77 } 78 } 79 } 80 } 81 } 82 } 83 84 @Composable 85 fun TransitionLossComposable( 86 t: TransactionDenomLoss, 87 devMode: Boolean, 88 spec: CurrencySpecification?, 89 onTransition: (t: TransactionAction) -> Unit, 90 ) { 91 val scrollState = rememberScrollState() 92 val context = LocalContext.current 93 94 Column( 95 modifier = Modifier 96 .fillMaxWidth() 97 .verticalScroll(scrollState), 98 horizontalAlignment = Alignment.CenterHorizontally, 99 ) { 100 TransactionStateComposable(state = t.txState) 101 102 Text( 103 modifier = Modifier.padding(16.dp), 104 text = t.timestamp.ms.toAbsoluteTime(context).toString(), 105 style = MaterialTheme.typography.bodyLarge, 106 ) 107 108 TransactionAmountComposable( 109 label = stringResource(id = R.string.amount_lost), 110 amount = t.amountEffective.withSpec(spec), 111 amountType = AmountType.Negative, 112 ) 113 114 TransactionInfoComposable( 115 label = stringResource(id = R.string.loss_reason), 116 info = stringResource( 117 when(t.lossEventType) { 118 DenomExpired -> R.string.loss_reason_expired 119 DenomVanished -> R.string.loss_reason_vanished 120 DenomUnoffered -> R.string.loss_reason_unoffered 121 } 122 ) 123 ) 124 125 TransitionsComposable(t, devMode, onTransition) 126 127 if (devMode && t.error != null) { 128 ErrorTransactionButton(error = t.error) 129 } 130 131 BottomInsetsSpacer() 132 } 133 } 134 135 fun previewLossTransaction(lossEventType: LossEventType) = 136 TransactionDenomLoss( 137 transactionId = "transactionId", 138 timestamp = Timestamp.fromMillis(System.currentTimeMillis() - 360 * 60 * 1000), 139 txState = TransactionState(Pending), 140 txActions = listOf(Retry, Suspend, Abort), 141 amountRaw = Amount.fromString("TESTKUDOS", "0.3"), 142 amountEffective = Amount.fromString("TESTKUDOS", "0.3"), 143 error = TalerErrorInfo(code = TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED), 144 lossEventType = lossEventType, 145 scopes = listOf(ScopeInfo.Exchange( 146 currency = "TESTKUDOS", 147 url = "exchange.test.taler.net", 148 )) 149 ) 150 151 @Composable 152 @Preview 153 fun TransitionLossComposableExpiredPreview() { 154 val t = previewLossTransaction(DenomExpired) 155 Surface { 156 TransitionLossComposable(t, true, null) {} 157 } 158 } 159 160 @Composable 161 @Preview 162 fun TransitionLossComposableVanishedPreview() { 163 val t = previewLossTransaction(DenomVanished) 164 Surface { 165 TransitionLossComposable(t, true, null) {} 166 } 167 } 168 169 @Composable 170 @Preview 171 fun TransactionLossComposableUnofferedPreview() { 172 val t = previewLossTransaction(DenomUnoffered) 173 Surface { 174 TransitionLossComposable(t, true, null) {} 175 } 176 }