diff options
author | Iván Ávalos <avalos@disroot.org> | 2024-04-02 11:35:07 -0600 |
---|---|---|
committer | Iván Ávalos <avalos@disroot.org> | 2024-04-05 10:24:56 -0600 |
commit | 7d0e8300eb7e42ec215285730769d35af83c94cc (patch) | |
tree | 1a6e421fbdf939c4ce608c3b0142403e48063f89 | |
parent | 316015230d47bac6f1b9cab164d1927754bf94e7 (diff) | |
download | taler-android-7d0e8300eb7e42ec215285730769d35af83c94cc.tar.gz taler-android-7d0e8300eb7e42ec215285730769d35af83c94cc.tar.bz2 taler-android-7d0e8300eb7e42ec215285730769d35af83c94cc.zip |
[wallet] Implement financial loss transactions
bug 0008694
5 files changed, 242 insertions, 0 deletions
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionLossFragment.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionLossFragment.kt new file mode 100644 index 0000000..9138345 --- /dev/null +++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionLossFragment.kt @@ -0,0 +1,163 @@ +/* + * This file is part of GNU Taler + * (C) 2024 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +package net.taler.wallet.transactions + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import net.taler.common.Amount +import net.taler.common.CurrencySpecification +import net.taler.common.Timestamp +import net.taler.common.toAbsoluteTime +import net.taler.wallet.R +import net.taler.wallet.backend.TalerErrorCode +import net.taler.wallet.backend.TalerErrorInfo +import net.taler.wallet.compose.TalerSurface +import net.taler.wallet.transactions.LossEventType.DenomExpired +import net.taler.wallet.transactions.LossEventType.DenomUnoffered +import net.taler.wallet.transactions.LossEventType.DenomVanished +import net.taler.wallet.transactions.TransactionAction.Abort +import net.taler.wallet.transactions.TransactionAction.Retry +import net.taler.wallet.transactions.TransactionAction.Suspend +import net.taler.wallet.transactions.TransactionMajorState.Pending + +class TransactionLossFragment: TransactionDetailFragment() { + val scope get() = transactionManager.selectedScope + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View = ComposeView(requireContext()).apply { + setContent { + val t = transactionManager.selectedTransaction.observeAsState().value + val spec = scope?.let { balanceManager.getSpecForScopeInfo(it) } + + TalerSurface { + if (t is TransactionDenomLoss) { + TransitionLossComposable(t, devMode, spec) { + onTransitionButtonClicked(t, it) + } + } + } + } + } +} + +@Composable +fun TransitionLossComposable( + t: TransactionDenomLoss, + devMode: Boolean, + spec: CurrencySpecification?, + onTransition: (t: TransactionAction) -> Unit, +) { + val scrollState = rememberScrollState() + val context = LocalContext.current + + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(scrollState), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + modifier = Modifier.padding(16.dp), + text = t.timestamp.ms.toAbsoluteTime(context).toString(), + style = MaterialTheme.typography.bodyLarge, + ) + + TransactionAmountComposable( + label = stringResource(id = R.string.loss_amount), + amount = t.amountEffective.withSpec(spec), + amountType = AmountType.Negative, + ) + + TransactionInfoComposable( + label = stringResource(id = R.string.loss_reason), + info = stringResource( + when(t.lossEventType) { + DenomExpired -> R.string.loss_reason_expired + DenomVanished -> R.string.loss_reason_vanished + DenomUnoffered -> R.string.loss_reason_unoffered + } + ) + ) + + TransitionsComposable(t, devMode, onTransition) + + if (devMode && t.error != null) { + ErrorTransactionButton(error = t.error) + } + } +} + +fun previewLossTransaction(lossEventType: LossEventType) = + TransactionDenomLoss( + transactionId = "transactionId", + timestamp = Timestamp.fromMillis(System.currentTimeMillis() - 360 * 60 * 1000), + txState = TransactionState(Pending), + txActions = listOf(Retry, Suspend, Abort), + amountRaw = Amount.fromString("TESTKUDOS", "0.3"), + amountEffective = Amount.fromString("TESTKUDOS", "0.3"), + error = TalerErrorInfo(code = TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED), + lossEventType = lossEventType, + ) + +@Composable +@Preview +fun TransitionLossComposableExpiredPreview() { + val t = previewLossTransaction(DenomExpired) + Surface { + TransitionLossComposable(t, true, null) {} + } +} + +@Composable +@Preview +fun TransitionLossComposableVanishedPreview() { + val t = previewLossTransaction(DenomVanished) + Surface { + TransitionLossComposable(t, true, null) {} + } +} + +@Composable +@Preview +fun TransactionLossComposableUnofferedPreview() { + val t = previewLossTransaction(DenomUnoffered) + Surface { + TransitionLossComposable(t, true, null) {} + } +} diff --git a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt index be36a13..f43db5f 100644 --- a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt +++ b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt @@ -506,6 +506,47 @@ class TransactionPeerPushCredit( } /** + * A transaction to indicate financial loss due to denominations + * that became unusable for deposits. + */ +@Serializable +@SerialName("denom-loss") +class TransactionDenomLoss( + override val transactionId: String, + override val timestamp: Timestamp, + override val txState: TransactionState, + override val txActions: List<TransactionAction>, + override val error: TalerErrorInfo? = null, + override val amountRaw: Amount, + override val amountEffective: Amount, + val lossEventType: LossEventType, +): Transaction() { + override val icon: Int = R.drawable.transaction_loss + override val detailPageNav = R.id.nav_transactions_detail_loss + + @Transient + override val amountType: AmountType = AmountType.Negative + + override fun getTitle(context: Context): String { + return context.getString(R.string.transaction_denom_loss) + } + + override val generalTitleRes: Int = R.string.transaction_denom_loss +} + +@Serializable +enum class LossEventType { + @SerialName("denom-expired") + DenomExpired, + + @SerialName("denom-vanished") + DenomVanished, + + @SerialName("denom-unoffered") + DenomUnoffered +} + +/** * This represents a transaction that we can not parse for some reason. */ class DummyTransaction( diff --git a/wallet/src/main/res/drawable/transaction_loss.xml b/wallet/src/main/res/drawable/transaction_loss.xml new file mode 100644 index 0000000..ffc9a2e --- /dev/null +++ b/wallet/src/main/res/drawable/transaction_loss.xml @@ -0,0 +1,26 @@ +<!-- + ~ This file is part of GNU Taler + ~ (C) 2024 Taler Systems S.A. + ~ + ~ GNU Taler is free software; you can redistribute it and/or modify it under the + ~ terms of the GNU General Public License as published by the Free Software + ~ Foundation; either version 3, or (at your option) any later version. + ~ + ~ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + ~ A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License along with + ~ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:tint="?attr/colorControlNormal" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FF000000" + android:pathData="M17.12,9.88C16.56,9.32 15.8,9 15,9S13.44,9.32 12.88,9.88C12.32,10.44 12,11.2 12,12S12.32,13.56 12.88,14.12 14.2,15 15,15 16.56,14.68 17.12,14.12 18,12.8 18,12 17.68,10.44 17.12,9.88M7,6V18H23V6H7M21,14C20.47,14 19.96,14.21 19.59,14.59C19.21,14.96 19,15.47 19,16H11C11,15.47 10.79,14.96 10.41,14.59C10.04,14.21 9.53,14 9,14V10C9.53,10 10.04,9.79 10.41,9.41C10.79,9.04 11,8.53 11,8H19C19,8.53 19.21,9.04 19.59,9.41C19.96,9.79 20.47,10 21,10V14M5,8H3C2.45,8 2,7.55 2,7C2,6.45 2.45,6 3,6H5V8M5,13H2C1.45,13 1,12.55 1,12C1,11.45 1.45,11 2,11H5V13M5,18H1C0.448,18 0,17.55 0,17C0,16.45 0.448,16 1,16H5V18Z"/> +</vector> diff --git a/wallet/src/main/res/navigation/nav_graph.xml b/wallet/src/main/res/navigation/nav_graph.xml index bc2e871..f6c90ab 100644 --- a/wallet/src/main/res/navigation/nav_graph.xml +++ b/wallet/src/main/res/navigation/nav_graph.xml @@ -324,6 +324,11 @@ android:label="@string/transactions_detail_title" /> <fragment + android:id="@+id/nav_transactions_detail_loss" + android:name="net.taler.wallet.transactions.TransactionLossFragment" + android:label="@string/transactions_detail_title" /> + + <fragment android:id="@+id/nav_transactions_detail_dummy" android:name="net.taler.wallet.transactions.TransactionDummyFragment" android:label="@string/transactions_detail_title" /> diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml index 64a0d7b..f0a30f1 100644 --- a/wallet/src/main/res/values/strings.xml +++ b/wallet/src/main/res/values/strings.xml @@ -128,6 +128,7 @@ GNU Taler is immune against many types of fraud, such as phishing of credit card <string name="transaction_peer_pull_debit">Invoice paid</string> <string name="transaction_peer_push_credit">Push payment</string> <string name="transaction_action_kyc">Complete KYC</string> + <string name="transaction_denom_loss">Loss of funds</string> <string name="transaction_dummy_title">Unknown Transaction</string> <string name="payment_title">Payment</string> @@ -269,6 +270,12 @@ GNU Taler is immune against many types of fraud, such as phishing of credit card <string name="exchange_tos_accept">Accept Terms of Service</string> <string name="exchange_tos_error">Error showing Terms of Service: %s</string> + <string name="loss_amount">Amount lost</string> + <string name="loss_reason">Reason</string> + <string name="loss_reason_expired">Denomination expired</string> + <string name="loss_reason_vanished">Denomination vanished</string> + <string name="loss_reason_unoffered">Denomination ceased to be offered</string> + <string name="pending_operations_title">Pending Operations</string> <string name="pending_operations_refuse">Refuse Proposal</string> <string name="pending_operations_no_action">(no action)</string> |