From fb22009ec4799a624f00c228fbd7435b44c1cbac Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 10 Jan 2022 16:04:53 -0300 Subject: deposit design from belen, feature missing: kyc --- .../src/wallet/AddNewActionView.stories.tsx | 33 +++ .../src/wallet/AddNewActionView.tsx | 64 ++++++ .../src/wallet/Balance.stories.tsx | 177 ++++++++++------ .../src/wallet/BalancePage.tsx | 89 +++++--- .../src/wallet/CreateManualWithdraw.tsx | 12 +- .../src/wallet/DepositPage.tsx | 104 ++++++---- .../src/wallet/History.stories.tsx | 119 ++++++++--- .../src/wallet/History.tsx | 227 ++++++++++++++------- .../src/wallet/LastActivityPage.stories.tsx | 33 +++ .../src/wallet/LastActivityPage.tsx | 35 ++++ .../src/wallet/ManualWithdrawPage.tsx | 16 +- .../src/wallet/Transaction.tsx | 13 +- .../src/wallet/index.stories.tsx | 19 +- 13 files changed, 684 insertions(+), 257 deletions(-) create mode 100644 packages/taler-wallet-webextension/src/wallet/AddNewActionView.stories.tsx create mode 100644 packages/taler-wallet-webextension/src/wallet/AddNewActionView.tsx create mode 100644 packages/taler-wallet-webextension/src/wallet/LastActivityPage.stories.tsx create mode 100644 packages/taler-wallet-webextension/src/wallet/LastActivityPage.tsx (limited to 'packages/taler-wallet-webextension/src/wallet') diff --git a/packages/taler-wallet-webextension/src/wallet/AddNewActionView.stories.tsx b/packages/taler-wallet-webextension/src/wallet/AddNewActionView.stories.tsx new file mode 100644 index 000000000..54e4eb1f2 --- /dev/null +++ b/packages/taler-wallet-webextension/src/wallet/AddNewActionView.stories.tsx @@ -0,0 +1,33 @@ +/* + This file is part of GNU Taler + (C) 2021 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 + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { createExample } from "../test-utils"; +import { AddNewActionView as TestedComponent } from "./AddNewActionView"; + +export default { + title: "wallet/add new action", + component: TestedComponent, + argTypes: { + setDeviceName: () => Promise.resolve(), + }, +}; + +export const Initial = createExample(TestedComponent, {}); diff --git a/packages/taler-wallet-webextension/src/wallet/AddNewActionView.tsx b/packages/taler-wallet-webextension/src/wallet/AddNewActionView.tsx new file mode 100644 index 000000000..d4158973e --- /dev/null +++ b/packages/taler-wallet-webextension/src/wallet/AddNewActionView.tsx @@ -0,0 +1,64 @@ +import { classifyTalerUri, TalerUriType } from "@gnu-taler/taler-util"; +import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { Button, ButtonSuccess, InputWithLabel } from "../components/styled"; +import { actionForTalerUri } from "../utils/index"; + +export interface Props { + onCancel: () => void; +} + +function buttonLabelByTalerType(type: TalerUriType): string { + switch (type) { + case TalerUriType.TalerNotifyReserve: + return "Open reserve page"; + case TalerUriType.TalerPay: + return "Open pay page"; + case TalerUriType.TalerRefund: + return "Open refund page"; + case TalerUriType.TalerTip: + return "Open tip page"; + case TalerUriType.TalerWithdraw: + return "Open withdraw page"; + } + return ""; +} + +export function AddNewActionView({ onCancel }: Props): VNode { + const [url, setUrl] = useState(""); + const uriType = classifyTalerUri(url); + + return ( + +
+ + +
+ setUrl(e.currentTarget.value)} + /> +
+
+
+
+ + {uriType !== TalerUriType.Unknown && ( + { + // eslint-disable-next-line no-undef + chrome.tabs.create({ url: actionForTalerUri(uriType, url) }); + }} + > + {buttonLabelByTalerType(uriType)} + + )} +
+
+ ); +} diff --git a/packages/taler-wallet-webextension/src/wallet/Balance.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Balance.stories.tsx index 2432c31eb..6c670b019 100644 --- a/packages/taler-wallet-webextension/src/wallet/Balance.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Balance.stories.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample, NullLink } from "../test-utils"; +import { createExample } from "../test-utils"; import { BalanceView as TestedComponent } from "./BalancePage"; export default { @@ -28,83 +28,124 @@ export default { argTypes: {}, }; -export const NotYetLoaded = createExample(TestedComponent, {}); - -export const GotError = createExample(TestedComponent, { - balance: { - hasError: true, - message: "Network error", - }, - Linker: NullLink, +export const EmptyBalance = createExample(TestedComponent, { + balances: [], }); -export const EmptyBalance = createExample(TestedComponent, { - balance: { - hasError: false, - response: { - balances: [], +export const SomeCoins = createExample(TestedComponent, { + balances: [ + { + available: "USD:10.5", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, }, - }, - Linker: NullLink, + ], }); -export const SomeCoins = createExample(TestedComponent, { - balance: { - hasError: false, - response: { - balances: [ - { - available: "USD:10.5", - hasPendingTransactions: false, - pendingIncoming: "USD:0", - pendingOutgoing: "USD:0", - requiresUserInput: false, - }, - ], +export const SomeCoinsInTreeCurrencies = createExample(TestedComponent, { + balances: [ + { + available: "EUR:1", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, + }, + { + available: "TESTKUDOS:2000", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, }, - }, - Linker: NullLink, + { + available: "JPY:4", + hasPendingTransactions: false, + pendingIncoming: "EUR:15", + pendingOutgoing: "EUR:0", + requiresUserInput: false, + }, + ], }); -export const SomeCoinsAndIncomingMoney = createExample(TestedComponent, { - balance: { - hasError: false, - response: { - balances: [ - { - available: "USD:2.23", - hasPendingTransactions: false, - pendingIncoming: "USD:5.11", - pendingOutgoing: "USD:0", - requiresUserInput: false, - }, - ], +export const NoCoinsInTreeCurrencies = createExample(TestedComponent, { + balances: [ + { + available: "EUR:3", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, + }, + { + available: "USD:2", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, }, - }, - Linker: NullLink, + { + available: "ARS:1", + hasPendingTransactions: false, + pendingIncoming: "EUR:15", + pendingOutgoing: "EUR:0", + requiresUserInput: false, + }, + ], }); -export const SomeCoinsInTwoCurrencies = createExample(TestedComponent, { - balance: { - hasError: false, - response: { - balances: [ - { - available: "USD:2", - hasPendingTransactions: false, - pendingIncoming: "USD:5", - pendingOutgoing: "USD:0", - requiresUserInput: false, - }, - { - available: "EUR:4", - hasPendingTransactions: false, - pendingIncoming: "EUR:5", - pendingOutgoing: "EUR:0", - requiresUserInput: false, - }, - ], +export const SomeCoinsInFiveCurrencies = createExample(TestedComponent, { + balances: [ + { + available: "USD:0", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, + }, + { + available: "ARS:13451", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, + }, + { + available: "EUR:202.02", + hasPendingTransactions: false, + pendingIncoming: "EUR:0", + pendingOutgoing: "EUR:0", + requiresUserInput: false, + }, + { + available: "JPY:0", + hasPendingTransactions: false, + pendingIncoming: "EUR:0", + pendingOutgoing: "EUR:0", + requiresUserInput: false, + }, + { + available: "JPY:51223233", + hasPendingTransactions: false, + pendingIncoming: "EUR:0", + pendingOutgoing: "EUR:0", + requiresUserInput: false, + }, + { + available: "DEMOKUDOS:6", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, + }, + { + available: "TESTKUDOS:6", + hasPendingTransactions: false, + pendingIncoming: "USD:5", + pendingOutgoing: "USD:0", + requiresUserInput: false, }, - }, - Linker: NullLink, + ], }); diff --git a/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx b/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx index 33182a38d..5fa08f8a6 100644 --- a/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx @@ -14,68 +14,87 @@ TALER; see the file COPYING. If not, see */ -import { BalancesResponse, i18n } from "@gnu-taler/taler-util"; +import { Amounts, Balance, i18n } from "@gnu-taler/taler-util"; import { Fragment, h, VNode } from "preact"; import { BalanceTable } from "../components/BalanceTable"; -import { ButtonPrimary, Centered, ErrorBox } from "../components/styled"; -import { HookResponse, useAsyncAsHook } from "../hooks/useAsyncAsHook"; +import { Loading } from "../components/Loading"; +import { MultiActionButton } from "../components/MultiActionButton"; +import { + ButtonPrimary, + Centered, + ErrorBox, + SuccessBox, +} from "../components/styled"; +import { useAsyncAsHook } from "../hooks/useAsyncAsHook"; import { PageLink } from "../renderHtml"; import * as wxApi from "../wxApi"; +interface Props { + goToWalletDeposit: (currency: string) => void; + goToWalletHistory: (currency: string) => void; + goToWalletManualWithdraw: () => void; +} + export function BalancePage({ goToWalletManualWithdraw, goToWalletDeposit, -}: { - goToWalletDeposit: (currency: string) => void; - goToWalletManualWithdraw: () => void; -}): VNode { + goToWalletHistory, +}: Props): VNode { const state = useAsyncAsHook(wxApi.getBalance); + + const balances = !state || state.hasError ? [] : state.response.balances; + + if (!state) { + return ; + } + + if (state.hasError) { + return ( + + {state.message} +

+ Click here for help and + diagnostics. +

+
+ ); + } + return ( ); } export interface BalanceViewProps { - balance: HookResponse; - Linker: typeof PageLink; + balances: Balance[]; goToWalletManualWithdraw: () => void; goToWalletDeposit: (currency: string) => void; + goToWalletHistory: (currency: string) => void; } export function BalanceView({ - balance, - Linker, + balances, goToWalletManualWithdraw, goToWalletDeposit, + goToWalletHistory, }: BalanceViewProps): VNode { - if (!balance) { - return
Loading...
; - } + const currencyWithNonZeroAmount = balances + .filter((b) => !Amounts.isZero(b.available)) + .map((b) => b.available.split(":")[0]); - if (balance.hasError) { - return ( - - {balance.message} -

- Click here for help and - diagnostics. -

-
- ); - } - if (balance.response.balances.length === 0) { + if (balances.length === 0) { return (

You have no balance to show. Need some{" "} - help getting started? + help getting started?

@@ -93,15 +112,21 @@ export function BalanceView({
-
Withdraw + {currencyWithNonZeroAmount.length > 0 && ( + `Deposit ${s}`} + actions={currencyWithNonZeroAmount} + onClick={(c) => goToWalletDeposit(c)} + /> + )}
); diff --git a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx index 36feeb76e..f32a2aa5c 100644 --- a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx +++ b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx @@ -41,12 +41,14 @@ export interface Props { exchangeList: Record; onCreate: (exchangeBaseUrl: string, amount: AmountJson) => Promise; onAddExchange: () => void; + initialCurrency?: string; } export function CreateManualWithdraw({ initialAmount, exchangeList, error, + initialCurrency, onCreate, onAddExchange, }: Props): VNode { @@ -61,8 +63,16 @@ export function CreateManualWithdraw({ {} as Record, ); + const foundExchangeForCurrency = exchangeSelectList.findIndex( + (e) => exchangeList[e] === initialCurrency, + ); + const initialExchange = - exchangeSelectList.length > 0 ? exchangeSelectList[0] : ""; + foundExchangeForCurrency !== -1 + ? exchangeSelectList[foundExchangeForCurrency] + : exchangeSelectList.length > 0 + ? exchangeSelectList[0] + : ""; const [exchange, setExchange] = useState(initialExchange || ""); const [currency, setCurrency] = useState(exchangeList[initialExchange] ?? ""); diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx b/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx index 5c931394d..9e15daa97 100644 --- a/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx @@ -23,23 +23,24 @@ import { import { DepositFee } from "@gnu-taler/taler-wallet-core/src/operations/deposits"; import { Fragment, h, VNode } from "preact"; import { useEffect, useState } from "preact/hooks"; -import { Part } from "../components/Part"; +import { Loading } from "../components/Loading"; import { SelectList } from "../components/SelectList"; import { + ButtonBoxWarning, ButtonPrimary, ErrorText, Input, InputWithLabel, + WarningBox, } from "../components/styled"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook"; import * as wxApi from "../wxApi"; interface Props { currency: string; + onSuccess: (currency: string) => void; } -export function DepositPage({ currency }: Props): VNode { - const [success, setSuccess] = useState(false); - +export function DepositPage({ currency, onSuccess }: Props): VNode { const state = useAsyncAsHook(async () => { const balance = await wxApi.getBalance(); const bs = balance.balances.filter((b) => b.available.startsWith(currency)); @@ -63,7 +64,7 @@ export function DepositPage({ currency }: Props): VNode { async function doSend(account: string, amount: AmountString): Promise { await wxApi.createDepositGroup(account, amount); - setSuccess(true); + onSuccess(currency); } async function getFeeForAmount( @@ -73,8 +74,8 @@ export function DepositPage({ currency }: Props): VNode { return await wxApi.getFeeForDeposit(account, amount); } - if (accounts.length === 0) return
loading..
; - if (success) return
deposit created
; + if (accounts.length === 0) return ; + return ( (undefined); const [fee, setFee] = useState(undefined); + function updateAmount(num: number | undefined) { + setAmount(num); + setFee(undefined); + } + const feeHasBeenCalculated = fee !== undefined; const currency = balance.currency; const amountStr: AmountString = `${currency}:${amount}`; + const feeSum = + fee !== undefined + ? Amounts.sum([fee.wire, fee.coin, fee.refresh]).amount + : Amounts.getZero(currency); const account = knownBankAccounts.length ? knownBankAccounts[accountIdx] @@ -126,7 +136,12 @@ export function View({ return
no balance
; } if (!knownBankAccounts || !knownBankAccounts.length) { - return
there is no known bank account to send money to
; + return ( + +

There is no known bank account to send money to

+ Withdraw +
+ ); } const parsedAmount = amount === undefined ? undefined : Amounts.parse(amountStr); @@ -136,9 +151,16 @@ export function View({ : !parsedAmount ? "Invalid amount" : Amounts.cmp(balance, parsedAmount) === -1 - ? `To much, your current balance is ${balance.value}` + ? `To much, your current balance is ${Amounts.stringifyValue(balance)}` : undefined; + const totalToDeposit = parsedAmount + ? Amounts.sub(parsedAmount, feeSum).amount + : Amounts.getZero(currency); + + const unableToDeposit = + Amounts.isZero(totalToDeposit) && feeHasBeenCalculated; + return (

Send {currency} to your account

@@ -153,7 +175,7 @@ export function View({ /> - +
{currency} { const num = parseFloat(e.currentTarget.value); - console.log(num); if (!Number.isNaN(num)) { - setAmount(num); + updateAmount(num); } else { - setAmount(undefined); + updateAmount(undefined); setFee(undefined); } }} @@ -173,40 +194,41 @@ export function View({
{error && {error}}
- {!error && fee && ( -
- - - {parsedAmount && ( - - )} -
- )} + { + + + +
+ {currency} + +
+
+ + + +
+ {currency} + +
+
+
+ }
onSend(accountURI, amountStr)} > - Send + Deposit {Amounts.stringifyValue(totalToDeposit)} {currency}
diff --git a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx index ce4b0fb74..3f550175d 100644 --- a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx @@ -35,7 +35,7 @@ import { HistoryView as TestedComponent } from "./History"; import { createExample } from "../test-utils"; export default { - title: "wallet/history/list", + title: "wallet/balance/history", component: TestedComponent, }; @@ -114,8 +114,13 @@ const exampleData = { } as TransactionRefund, }; -export const Empty = createExample(TestedComponent, { - list: [], +export const NoBalance = createExample(TestedComponent, { + transactions: [], + balances: [], +}); + +export const SomeBalanceWithNoTransactions = createExample(TestedComponent, { + transactions: [], balances: [ { available: "TESTKUDOS:10", @@ -127,16 +132,24 @@ export const Empty = createExample(TestedComponent, { ], }); -export const EmptyWithNoBalance = createExample(TestedComponent, { - list: [], - balances: [], +export const OneSimpleTransaction = createExample(TestedComponent, { + transactions: [exampleData.withdraw], + balances: [ + { + available: "USD:10", + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + hasPendingTransactions: false, + requiresUserInput: false, + }, + ], }); -export const One = createExample(TestedComponent, { - list: [exampleData.withdraw], +export const TwoTransactionsAndZeroBalance = createExample(TestedComponent, { + transactions: [exampleData.withdraw, exampleData.deposit], balances: [ { - available: "USD:10", + available: "USD:0", pendingIncoming: "USD:0", pendingOutgoing: "USD:0", hasPendingTransactions: false, @@ -145,8 +158,8 @@ export const One = createExample(TestedComponent, { ], }); -export const OnePending = createExample(TestedComponent, { - list: [ +export const OneTransactionPending = createExample(TestedComponent, { + transactions: [ { ...exampleData.withdraw, pending: true, @@ -163,8 +176,8 @@ export const OnePending = createExample(TestedComponent, { ], }); -export const Several = createExample(TestedComponent, { - list: [ +export const SomeTransactions = createExample(TestedComponent, { + transactions: [ exampleData.withdraw, exampleData.payment, exampleData.withdraw, @@ -183,38 +196,82 @@ export const Several = createExample(TestedComponent, { ], balances: [ { - available: "TESTKUDOS:10", - pendingIncoming: "TESTKUDOS:0", - pendingOutgoing: "TESTKUDOS:0", + available: "USD:10", + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", hasPendingTransactions: false, requiresUserInput: false, }, ], }); -export const SeveralWithTwoCurrencies = createExample(TestedComponent, { - list: [ - exampleData.withdraw, - exampleData.payment, - exampleData.withdraw, - exampleData.payment, - exampleData.refresh, - exampleData.refund, - exampleData.tip, - exampleData.deposit, - ], +export const SomeTransactionsWithTwoCurrencies = createExample( + TestedComponent, + { + transactions: [ + exampleData.withdraw, + exampleData.payment, + exampleData.withdraw, + exampleData.payment, + exampleData.refresh, + exampleData.refund, + exampleData.tip, + exampleData.deposit, + ], + balances: [ + { + available: "USD:0", + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + hasPendingTransactions: false, + requiresUserInput: false, + }, + { + available: "TESTKUDOS:10", + pendingIncoming: "TESTKUDOS:0", + pendingOutgoing: "TESTKUDOS:0", + hasPendingTransactions: false, + requiresUserInput: false, + }, + ], + }, +); + +export const FiveOfficialCurrencies = createExample(TestedComponent, { + transactions: [exampleData.withdraw], balances: [ { - available: "TESTKUDOS:10", + available: "USD:1000", + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + hasPendingTransactions: false, + requiresUserInput: false, + }, + { + available: "EUR:881", pendingIncoming: "TESTKUDOS:0", pendingOutgoing: "TESTKUDOS:0", hasPendingTransactions: false, requiresUserInput: false, }, { - available: "USD:10", - pendingIncoming: "USD:0", - pendingOutgoing: "USD:0", + available: "COL:4043000.5", + pendingIncoming: "TESTKUDOS:0", + pendingOutgoing: "TESTKUDOS:0", + hasPendingTransactions: false, + requiresUserInput: false, + }, + { + available: "JPY:11564450.6", + pendingIncoming: "TESTKUDOS:0", + pendingOutgoing: "TESTKUDOS:0", + hasPendingTransactions: false, + requiresUserInput: false, + }, + { + available: "GBP:736", + pendingIncoming: "TESTKUDOS:0", + pendingOutgoing: "TESTKUDOS:0", hasPendingTransactions: false, requiresUserInput: false, }, diff --git a/packages/taler-wallet-webextension/src/wallet/History.tsx b/packages/taler-wallet-webextension/src/wallet/History.tsx index 58db0360b..7912d169a 100644 --- a/packages/taler-wallet-webextension/src/wallet/History.tsx +++ b/packages/taler-wallet-webextension/src/wallet/History.tsx @@ -15,21 +15,38 @@ */ import { - AmountString, + Amounts, Balance, NotificationType, Transaction, } from "@gnu-taler/taler-util"; import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; -import { ButtonPrimary, DateSeparator } from "../components/styled"; +import { Loading } from "../components/Loading"; +import { + ButtonBoxPrimary, + ButtonBoxWarning, + ButtonPrimary, + DateSeparator, + ErrorBox, + NiceSelect, + WarningBox, +} from "../components/styled"; import { Time } from "../components/Time"; import { TransactionItem } from "../components/TransactionItem"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook"; -import { AddNewActionView } from "../popup/AddNewActionView"; import * as wxApi from "../wxApi"; -export function HistoryPage(): VNode { +interface Props { + currency?: string; + goToWalletDeposit: (currency: string) => void; + goToWalletManualWithdraw: (currency?: string) => void; +} +export function HistoryPage({ + currency, + goToWalletManualWithdraw, + goToWalletDeposit, +}: Props): VNode { const balance = useAsyncAsHook(wxApi.getBalance); const balanceWithoutError = balance?.hasError ? [] @@ -39,110 +56,166 @@ export function HistoryPage(): VNode { NotificationType.WithdrawGroupFinished, ]); - const [addingAction, setAddingAction] = useState(false); - - if (addingAction) { - return setAddingAction(false)} />; + if (!transactionQuery || !balance) { + return ; } - if (!transactionQuery) { - return
Loading ...
; - } if (transactionQuery.hasError) { - return
There was an error loading the transactions.
; + return ( + + {transactionQuery.message} +

There was an error loading the transactions.

+
+ ); } return ( setAddingAction(true)} + defaultCurrency={currency} + goToWalletManualWithdraw={goToWalletManualWithdraw} + goToWalletDeposit={goToWalletDeposit} + transactions={[...transactionQuery.response.transactions].reverse()} /> ); } -function amountToString(c: AmountString): string { - const idx = c.indexOf(":"); - return `${c.substring(idx + 1)} ${c.substring(0, idx)}`; -} - const term = 1000 * 60 * 60 * 24; function normalizeToDay(x: number): number { return Math.round(x / term) * term; } export function HistoryView({ - list, + defaultCurrency, + transactions, balances, - onAddNewAction, + goToWalletManualWithdraw, + goToWalletDeposit, }: { - list: Transaction[]; + goToWalletDeposit: (currency: string) => void; + goToWalletManualWithdraw: (currency?: string) => void; + defaultCurrency?: string; + transactions: Transaction[]; balances: Balance[]; - onAddNewAction: () => void; }): VNode { - const byDate = list.reduce((rv, x) => { - const theDate = - x.timestamp.t_ms === "never" ? 0 : normalizeToDay(x.timestamp.t_ms); - if (theDate) { - (rv[theDate] = rv[theDate] || []).push(x); - } + const currencies = balances.map((b) => b.available.split(":")[0]); - return rv; - }, {} as { [x: string]: Transaction[] }); + const defaultCurrencyIndex = currencies.findIndex( + (c) => c === defaultCurrency, + ); + const [currencyIndex, setCurrencyIndex] = useState( + defaultCurrencyIndex === -1 ? 0 : defaultCurrencyIndex, + ); + const selectedCurrency = + currencies.length > 0 ? currencies[currencyIndex] : undefined; + + const currencyAmount = balances[currencyIndex] + ? Amounts.jsonifyAmount(balances[currencyIndex].available) + : undefined; + + const byDate = transactions + .filter((t) => t.amountRaw.split(":")[0] === selectedCurrency) + .reduce((rv, x) => { + const theDate = + x.timestamp.t_ms === "never" ? 0 : normalizeToDay(x.timestamp.t_ms); + if (theDate) { + (rv[theDate] = rv[theDate] || []).push(x); + } + + return rv; + }, {} as { [x: string]: Transaction[] }); + const datesWithTransaction = Object.keys(byDate); const multiCurrency = balances.length > 1; + if (balances.length === 0 || !selectedCurrency) { + return ( + +

+ You have no balance. Withdraw some founds into your wallet +

+ goToWalletManualWithdraw()}> + Withdraw + +
+ ); + } return ( -
- {balances.length > 0 ? ( - - {balances.length === 1 && ( -
- Balance: {amountToString(balances[0].available)} -
- )} - {balances.length > 1 && ( -
- Balance:{" "} -
    - {balances.map((b, i) => ( -
  • {b.available}
  • - ))} -
-
- )} -
- ) : ( -
- )} -
- - + +
+

+ {currencies.length === 1 ? ( +

{selectedCurrency}
+ ) : ( + + + + )} + {currencyAmount && ( +

+ {Amounts.stringifyValue(currencyAmount)} +

+ )} +

+
+ goToWalletManualWithdraw(selectedCurrency)} + > + Withdraw + {currencyAmount && Amounts.isNonZero(currencyAmount) && ( + goToWalletDeposit(selectedCurrency)} + > + Deposit + + )}
-
-
- {Object.keys(byDate).map((d, i) => { - return ( - - - - {byDate[d].map((tx, i) => ( - - ))} - - ); - })}
+ {datesWithTransaction.length === 0 ? ( +
There is no history for this currency
+ ) : ( +
+ {datesWithTransaction.map((d, i) => { + return ( + + + + {byDate[d].map((tx, i) => ( + + ))} + + ); + })} +
+ )}
); } diff --git a/packages/taler-wallet-webextension/src/wallet/LastActivityPage.stories.tsx b/packages/taler-wallet-webextension/src/wallet/LastActivityPage.stories.tsx new file mode 100644 index 000000000..e729c2982 --- /dev/null +++ b/packages/taler-wallet-webextension/src/wallet/LastActivityPage.stories.tsx @@ -0,0 +1,33 @@ +/* + This file is part of GNU Taler + (C) 2021 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 + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { createExample } from "../test-utils"; +import { queryToSlashKeys } from "../utils/index"; +import { LastActivityPage as TestedComponent } from "./LastActivityPage"; + +export default { + title: "wallet/last activity", + component: TestedComponent, +}; + +export const InitialState = createExample(TestedComponent, { + onVerify: queryToSlashKeys, +}); diff --git a/packages/taler-wallet-webextension/src/wallet/LastActivityPage.tsx b/packages/taler-wallet-webextension/src/wallet/LastActivityPage.tsx new file mode 100644 index 000000000..8ec4c8759 --- /dev/null +++ b/packages/taler-wallet-webextension/src/wallet/LastActivityPage.tsx @@ -0,0 +1,35 @@ +/* + This file is part of TALER + (C) 2016 GNUnet e.V. + + 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. + + 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 + TALER; see the file COPYING. If not, see +*/ + +import { h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { ButtonPrimary } from "../components/styled"; +import { AddNewActionView } from "./AddNewActionView"; + +export function LastActivityPage(): VNode { + const [addingAction, setAddingAction] = useState(false); + + if (addingAction) { + return setAddingAction(false)} />; + } + + return ( +
+
+ setAddingAction(true)}>+ +
+ ); +} diff --git a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx index b3e8a2c25..c7958eb8a 100644 --- a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx @@ -14,7 +14,7 @@ TALER; see the file COPYING. If not, see */ -import { VNode, h } from "preact"; +import { VNode, h, Fragment } from "preact"; import { useState } from "preact/hooks"; import { CreateManualWithdraw } from "./CreateManualWithdraw"; import * as wxApi from "../wxApi"; @@ -29,8 +29,10 @@ import { route } from "preact-router"; import { Pages } from "../NavigationBar"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook"; import { ExchangeAddPage } from "./ExchangeAddPage"; +import { Loading } from "../components/Loading"; +import { ErrorBox } from "../components/styled"; -export function ManualWithdrawPage(): VNode { +export function ManualWithdrawPage({ currency }: { currency?: string }): VNode { const [success, setSuccess] = useState< | { response: AcceptManualWithdrawalResult; @@ -86,10 +88,15 @@ export function ManualWithdrawPage(): VNode { } if (!state) { - return
loading...
; + return ; } if (state.hasError) { - return
There was an error getting the known exchanges
; + return ( + + {state.message} +

There was an error getting the known exchanges

+
+ ); } const exchangeList = state.response.exchanges.reduce( (p, c) => ({ @@ -105,6 +112,7 @@ export function ManualWithdrawPage(): VNode { error={error} exchangeList={exchangeList} onCreate={doCreate} + initialCurrency={currency} /> ); } diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx index 8172e02a2..21bfc943d 100644 --- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx @@ -73,7 +73,7 @@ export function TransactionPage({ tid }: { tid: string }): VNode { } if (state.hasError) { - route(Pages.history); + route(Pages.balance); return (
@@ -84,7 +84,16 @@ export function TransactionPage({ tid }: { tid: string }): VNode { } function goToHistory(): void { - route(Pages.history); + const currency = + state !== undefined && !state.hasError + ? Amounts.parseOrThrow(state.response.amountRaw).currency + : undefined; + + if (currency) { + route(Pages.balance_history.replace(":currency", currency)); + } else { + route(Pages.balance); + } } return ( diff --git a/packages/taler-wallet-webextension/src/wallet/index.stories.tsx b/packages/taler-wallet-webextension/src/wallet/index.stories.tsx index 644ab1c59..55f350d46 100644 --- a/packages/taler-wallet-webextension/src/wallet/index.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/index.stories.tsx @@ -33,5 +33,22 @@ import * as a11 from "./ReserveCreated.stories"; import * as a12 from "./Settings.stories"; import * as a13 from "./Transaction.stories"; import * as a14 from "./Welcome.stories"; +import * as a15 from "./AddNewActionView.stories"; -export default [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14]; +export default [ + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9, + a10, + a11, + a12, + a13, + a14, + a15, +]; -- cgit v1.2.3