From 4fe04766fbf5328d0816f7cd862228a71690fd1c Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 6 Sep 2022 18:18:47 -0300 Subject: [wallet] implement prototype for outgoing peer transactions --- .../wallet/transactions/TransactionPeerFragment.kt | 148 +++++++++++++++++++++ .../net/taler/wallet/transactions/Transactions.kt | 113 ++++++++++++++++ .../wallet/transactions/TransactionsFragment.kt | 6 + 3 files changed, 267 insertions(+) create mode 100644 wallet/src/main/java/net/taler/wallet/transactions/TransactionPeerFragment.kt (limited to 'wallet/src/main/java/net/taler/wallet/transactions') diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionPeerFragment.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionPeerFragment.kt new file mode 100644 index 0000000..f1afb41 --- /dev/null +++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionPeerFragment.kt @@ -0,0 +1,148 @@ +/* + * This file is part of GNU Taler + * (C) 2022 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 + */ + +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.Row +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.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment.Companion.CenterHorizontally +import androidx.compose.ui.Alignment.Companion.CenterVertically +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.google.android.material.composethemeadapter.MdcTheme +import net.taler.common.Amount +import net.taler.common.toAbsoluteTime +import net.taler.wallet.R +import net.taler.wallet.peer.TransactionPeerPullCreditComposable +import net.taler.wallet.peer.TransactionPeerPushDebitComposable + +class TransactionPeerFragment : TransactionDetailFragment() { + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View = ComposeView(requireContext()).apply { + setContent { + MdcTheme { + Surface { + val t = transaction ?: error("No transaction") + TransactionPeerComposable(t) { + onDeleteButtonClicked(t) + } + } + } + } + } +} + +@Composable +fun TransactionPeerComposable(t: Transaction, onDelete: () -> Unit) { + val scrollState = rememberScrollState() + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(scrollState), + horizontalAlignment = CenterHorizontally, + ) { + val context = LocalContext.current + Text( + modifier = Modifier.padding(16.dp), + text = t.timestamp.ms.toAbsoluteTime(context).toString(), + style = MaterialTheme.typography.body1, + ) + when (t) { + is TransactionPeerPullCredit -> TransactionPeerPullCreditComposable(t) + is TransactionPeerPushCredit -> TODO() + is TransactionPeerPullDebit -> TODO() + is TransactionPeerPushDebit -> TransactionPeerPushDebitComposable(t) + else -> error("unexpected transaction: ${t::class.simpleName}") + } + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(backgroundColor = colorResource(R.color.red)), + onClick = onDelete, + ) { + Row(verticalAlignment = CenterVertically) { + Icon( + painter = painterResource(id = R.drawable.ic_delete), + contentDescription = null, + tint = Color.White, + ) + Text( + modifier = Modifier.padding(start = 8.dp), + text = stringResource(R.string.transactions_delete), + color = Color.White, + ) + } + } + } +} + +@Composable +fun TransactionAmountComposable(label: String, amount: Amount, amountType: AmountType) { + Text( + modifier = Modifier.padding(top = 16.dp, start = 16.dp, end = 16.dp), + text = label, + style = MaterialTheme.typography.body2, + ) + Text( + modifier = Modifier.padding(top = 8.dp, start = 16.dp, end = 16.dp, bottom = 16.dp), + text = if (amountType == AmountType.Negative) "-$amount" else amount.toString(), + fontSize = 24.sp, + color = when (amountType) { + AmountType.Positive -> colorResource(R.color.green) + AmountType.Negative -> colorResource(R.color.red) + AmountType.Neutral -> Color.Unspecified + }, + ) +} + +@Composable +fun TransactionInfoComposable(label: String, info: String) { + Text( + modifier = Modifier.padding(top = 16.dp, start = 16.dp, end = 16.dp), + text = label, + style = MaterialTheme.typography.body2, + ) + Text( + modifier = Modifier.padding(top = 8.dp, start = 16.dp, end = 16.dp, bottom = 16.dp), + text = info, + fontSize = 24.sp, + ) +} 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 ca01501..6f72567 100644 --- a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt +++ b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt @@ -252,3 +252,116 @@ class TransactionRefresh( override val generalTitleRes = R.string.transaction_refresh } + +@Serializable +data class PeerInfoShort( + val expiration: Timestamp? = null, + val summary: String? = null, +) + +/** + * Debit because we paid someone's invoice. + */ +@Serializable +@SerialName("peer-pull-debit") +class TransactionPeerPullDebit( + override val transactionId: String, + override val timestamp: Timestamp, + override val pending: Boolean, + val exchangeBaseUrl: String, + override val error: TalerErrorInfo? = null, + override val amountRaw: Amount, + override val amountEffective: Amount, + val info: PeerInfoShort, +) : Transaction() { + override val icon = R.drawable.ic_cash_usd_outline + override val detailPageNav = R.id.nav_transactions_detail_peer + + @Transient + override val amountType = AmountType.Negative + override fun getTitle(context: Context): String { + return context.getString(R.string.transaction_peer_push_debit) + } + override val generalTitleRes = R.string.payment_title +} + +/** + * Credit because someone paid for an invoice we created. + */ +@Serializable +@SerialName("peer-pull-credit") +class TransactionPeerPullCredit( + override val transactionId: String, + override val timestamp: Timestamp, + override val pending: Boolean, + val exchangeBaseUrl: String, + override val error: TalerErrorInfo? = null, + override val amountRaw: Amount, + override val amountEffective: Amount, + val info: PeerInfoShort, + val talerUri: String, + // val completed: Boolean, maybe +) : Transaction() { + override val icon = R.drawable.transaction_withdrawal + override val detailPageNav = R.id.nav_transactions_detail_peer + + override val amountType get() = AmountType.Positive + override fun getTitle(context: Context): String { + return context.getString(R.string.transaction_peer_pull_credit) + } + override val generalTitleRes = R.string.transaction_peer_pull_credit +} + +/** + * Debit because we sent money to someone. + */ +@Serializable +@SerialName("peer-push-debit") +class TransactionPeerPushDebit( + override val transactionId: String, + override val timestamp: Timestamp, + override val pending: Boolean, + val exchangeBaseUrl: String, + override val error: TalerErrorInfo? = null, + override val amountRaw: Amount, + override val amountEffective: Amount, + val info: PeerInfoShort, + val talerUri: String, + // val completed: Boolean, definitely +) : Transaction() { + override val icon = R.drawable.ic_cash_usd_outline + override val detailPageNav = R.id.nav_transactions_detail_peer + + @Transient + override val amountType = AmountType.Negative + override fun getTitle(context: Context): String { + return context.getString(R.string.transaction_peer_push_debit) + } + override val generalTitleRes = R.string.payment_title +} + +/** + * We received money via a peer payment. + */ +@Serializable +@SerialName("peer-push-credit") +class TransactionPeerPushCredit( + override val transactionId: String, + override val timestamp: Timestamp, + override val pending: Boolean, + val exchangeBaseUrl: String, + override val error: TalerErrorInfo? = null, + override val amountRaw: Amount, + override val amountEffective: Amount, + val info: PeerInfoShort, +) : Transaction() { + override val icon = R.drawable.transaction_withdrawal + override val detailPageNav = R.id.nav_transactions_detail_peer + + @Transient + override val amountType = AmountType.Positive + override fun getTitle(context: Context): String { + return context.getString(R.string.transaction_peer_push_debit) + } + override val generalTitleRes = R.string.withdraw_title +} diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt index f5840ab..50f95c0 100644 --- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt @@ -115,6 +115,12 @@ class TransactionsFragment : Fragment(), OnTransactionClickListener, ActionMode. transactionManager.transactions.observe(viewLifecycleOwner) { result -> onTransactionsResult(result) } + ui.sendButton.setOnClickListener { + findNavController().navigate(R.id.sendFunds) + } + ui.receiveButton.setOnClickListener { + findNavController().navigate(R.id.receiveFunds) + } ui.mainFab.setOnClickListener { model.scanCode() } -- cgit v1.2.3