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/NavigationBar.tsx | 15 +- .../src/components/BalanceTable.tsx | 28 +- .../src/components/Loading.tsx | 20 ++ .../src/components/MultiActionButton.tsx | 95 +++++++ .../src/components/styled/index.tsx | 64 +++-- .../src/context/devContext.ts | 1 + .../src/cta/Deposit.stories.tsx | 168 ++++++++++++ .../taler-wallet-webextension/src/cta/Deposit.tsx | 251 ++++++++++++++++++ packages/taler-wallet-webextension/src/cta/Pay.tsx | 29 +- .../src/popup/AddNewActionView.stories.tsx | 33 --- .../src/popup/AddNewActionView.tsx | 64 ----- .../src/popup/Balance.stories.tsx | 293 ++++++++------------- .../src/popup/BalancePage.tsx | 73 +++-- .../src/popup/DeveloperPage.tsx | 7 +- .../src/popup/History.stories.tsx | 213 --------------- .../src/popup/History.tsx | 148 ----------- .../src/popup/index.stories.tsx | 5 +- .../src/popupEntryPoint.tsx | 30 ++- .../taler-wallet-webextension/src/renderHtml.tsx | 7 +- .../taler-wallet-webextension/src/test-utils.ts | 3 +- .../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 +- .../src/walletEntryPoint.tsx | 285 ++++++++++---------- packages/taler-wallet-webextension/src/wxApi.ts | 2 +- 35 files changed, 1614 insertions(+), 1161 deletions(-) create mode 100644 packages/taler-wallet-webextension/src/components/Loading.tsx create mode 100644 packages/taler-wallet-webextension/src/components/MultiActionButton.tsx create mode 100644 packages/taler-wallet-webextension/src/cta/Deposit.stories.tsx create mode 100644 packages/taler-wallet-webextension/src/cta/Deposit.tsx delete mode 100644 packages/taler-wallet-webextension/src/popup/AddNewActionView.stories.tsx delete mode 100644 packages/taler-wallet-webextension/src/popup/AddNewActionView.tsx delete mode 100644 packages/taler-wallet-webextension/src/popup/History.stories.tsx delete mode 100644 packages/taler-wallet-webextension/src/popup/History.tsx 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') diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx b/packages/taler-wallet-webextension/src/NavigationBar.tsx index c02e48983..44e8af78e 100644 --- a/packages/taler-wallet-webextension/src/NavigationBar.tsx +++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx @@ -28,18 +28,18 @@ import { i18n } from "@gnu-taler/taler-util"; import { ComponentChildren, h, VNode } from "preact"; import Match from "preact-router/match"; import { PopupNavigation } from "./components/styled"; -import { useDevContext } from "./context/devContext"; export enum Pages { welcome = "/welcome", balance = "/balance", - manual_withdraw = "/manual-withdraw", + balance_history = "/balance/history/:currency", + manual_withdraw = "/manual-withdraw/:currency?", deposit = "/deposit/:currency", settings = "/settings", dev = "/dev", cta = "/cta/:action", backup = "/backup", - history = "/history", + last_activity = "/last-activity", transaction = "/transaction/:tid", provider_detail = "/provider/:pid", provider_add = "/provider/add", @@ -78,7 +78,10 @@ export function NavBar({ devMode, path }: { path: string; devMode: boolean }) {
{i18n.str`Balance`} - {i18n.str`History`} + {i18n.str`Last Activity`} {i18n.str`Backup`} {i18n.str`Settings`} {devMode && {i18n.str`Dev`}} @@ -87,8 +90,8 @@ export function NavBar({ devMode, path }: { path: string; devMode: boolean }) { ); } -export function WalletNavBar() { - const { devMode } = useDevContext(); +export function WalletNavBar({ devMode }: { devMode: boolean }) { + // const { devMode } = useDevContext(); return ( {({ path }: any) => { diff --git a/packages/taler-wallet-webextension/src/components/BalanceTable.tsx b/packages/taler-wallet-webextension/src/components/BalanceTable.tsx index 05a7d28dd..c69625cd2 100644 --- a/packages/taler-wallet-webextension/src/components/BalanceTable.tsx +++ b/packages/taler-wallet-webextension/src/components/BalanceTable.tsx @@ -14,31 +14,28 @@ TALER; see the file COPYING. If not, see */ -import { amountFractionalBase, Amounts, Balance } from "@gnu-taler/taler-util"; +import { Amounts, amountToPretty, Balance } from "@gnu-taler/taler-util"; import { h, VNode } from "preact"; -import { - ButtonPrimary, - TableWithRoundRows as TableWithRoundedRows, -} from "./styled"; +import { TableWithRoundRows as TableWithRoundedRows } from "./styled"; export function BalanceTable({ balances, - goToWalletDeposit, + goToWalletHistory, }: { balances: Balance[]; - goToWalletDeposit: (currency: string) => void; + goToWalletHistory: (currency: string) => void; }): VNode { - const currencyFormatter = new Intl.NumberFormat("en-US"); return ( {balances.map((entry, idx) => { const av = Amounts.parseOrThrow(entry.available); - const v = currencyFormatter.format( - av.value + av.fraction / amountFractionalBase, - ); return ( - + goToWalletHistory(av.currency)} + style={{ cursor: "pointer" }} + > {av.currency} - {v} - - - goToWalletDeposit(av.currency)}> - Deposit - + {Amounts.stringifyValue(av)} ); diff --git a/packages/taler-wallet-webextension/src/components/Loading.tsx b/packages/taler-wallet-webextension/src/components/Loading.tsx new file mode 100644 index 000000000..34edac551 --- /dev/null +++ b/packages/taler-wallet-webextension/src/components/Loading.tsx @@ -0,0 +1,20 @@ +/* + 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"; + +export function Loading(): VNode { + return
Loading...
; +} diff --git a/packages/taler-wallet-webextension/src/components/MultiActionButton.tsx b/packages/taler-wallet-webextension/src/components/MultiActionButton.tsx new file mode 100644 index 000000000..70d53640d --- /dev/null +++ b/packages/taler-wallet-webextension/src/components/MultiActionButton.tsx @@ -0,0 +1,95 @@ +import { h, VNode } from "preact"; +import arrowDown from "../../static/img/chevron-down.svg"; +import { ButtonBoxPrimary, ButtonPrimary, ParagraphClickable } from "./styled"; +import { useState } from "preact/hooks"; + +export interface Props { + label: (s: string) => string; + actions: string[]; + onClick: (s: string) => void; +} + +/** + * functionality: it will receive a list of actions, take the first actions as + * the first chosen action + * the user may change the chosen action + * when the user click the button it will call onClick with the chosen action + * as argument + * + * visually: it is a primary button with a select handler on the right + * + * @returns + */ +export function MultiActionButton({ + label, + actions, + onClick: doClick, +}: Props): VNode { + const defaultAction = actions.length > 0 ? actions[0] : ""; + + const [opened, setOpened] = useState(false); + const [selected, setSelected] = useState(defaultAction); + + const canChange = actions.length > 1; + const options = canChange ? actions.filter((a) => a !== selected) : []; + function select(m: string): void { + setSelected(m); + setOpened(false); + } + + if (!canChange) { + return ( + doClick(selected)}> + {label(selected)} + + ); + } + + return ( +
+ {opened && ( +
+ {options.map((m) => ( + select(m)}> + {label(m)} + + ))} +
+ )} + doClick(selected)} + style={{ + borderTopRightRadius: 0, + borderBottomRightRadius: 0, + marginRight: 0, + }} + > + {label(selected)} + + + setOpened((s) => !s)} + style={{ + marginLeft: 0, + borderTopLeftRadius: 0, + borderBottomLeftRadius: 0, + }} + > + + +
+ ); +} diff --git a/packages/taler-wallet-webextension/src/components/styled/index.tsx b/packages/taler-wallet-webextension/src/components/styled/index.tsx index 216a1fabc..2d16b496c 100644 --- a/packages/taler-wallet-webextension/src/components/styled/index.tsx +++ b/packages/taler-wallet-webextension/src/components/styled/index.tsx @@ -43,7 +43,7 @@ export const WalletAction = styled.div` } section { margin-bottom: 2em; - & button { + button { margin-right: 8px; margin-left: 8px; } @@ -92,6 +92,10 @@ export const WalletBox = styled.div<{ noPadding?: boolean }>` border-bottom: 1px solid black; border-top: 1px solid black; } + button { + margin-right: 8px; + margin-left: 8px; + } } & > header { @@ -123,7 +127,7 @@ export const WalletBox = styled.div<{ noPadding?: boolean }>` justify-content: space-between; display: flex; background-color: #f7f7f7; - & button { + button { margin-right: 8px; margin-left: 8px; } @@ -136,9 +140,9 @@ export const Middle = styled.div` height: 100%; `; -export const PopupBox = styled.div<{ noPadding?: boolean }>` +export const PopupBox = styled.div<{ noPadding?: boolean; devMode: boolean }>` height: 290px; - width: 400px; + width: ${({ devMode }) => (!devMode ? "400px" : "500px")}; display: flex; flex-direction: column; justify-content: space-between; @@ -156,6 +160,10 @@ export const PopupBox = styled.div<{ noPadding?: boolean }>` border-bottom: 1px solid black; border-top: 1px solid black; } + button { + margin-right: 8px; + margin-left: 8px; + } } & > section[data-expanded] { @@ -196,7 +204,7 @@ export const PopupBox = styled.div<{ noPadding?: boolean }>` flex-direction: row; justify-content: space-between; display: flex; - & button { + button { margin-right: 8px; margin-left: 8px; } @@ -363,11 +371,11 @@ export const CenteredDialog = styled.div` export const Button = styled.button<{ upperCased?: boolean }>` display: inline-block; - zoom: 1; + /* zoom: 1; */ line-height: normal; white-space: nowrap; - vertical-align: middle; - text-align: center; + vertical-align: middle; //check this + /* text-align: center; */ cursor: pointer; user-select: none; box-sizing: border-box; @@ -379,7 +387,7 @@ export const Button = styled.button<{ upperCased?: boolean }>` /* color: #444; rgba not supported (IE 8) */ color: rgba(0, 0, 0, 0.8); /* rgba supported */ border: 1px solid #999; /*IE 6/7/8*/ - border: none rgba(0, 0, 0, 0); /*IE9 + everything else*/ + /* border: none rgba(0, 0, 0, 0); IE9 + everything else */ background-color: "#e6e6e6"; text-decoration: none; border-radius: 2px; @@ -401,11 +409,11 @@ export const Button = styled.button<{ upperCased?: boolean }>` } :hover { - filter: alpha(opacity=90); + filter: alpha(opacity=80); background-image: linear-gradient( transparent, - rgba(0, 0, 0, 0.05) 40%, - rgba(0, 0, 0, 0.1) + rgba(0, 0, 0, 0.1) 40%, + rgba(0, 0, 0, 0.2) ); } `; @@ -415,7 +423,7 @@ export const Link = styled.a<{ upperCased?: boolean }>` zoom: 1; line-height: normal; white-space: nowrap; - vertical-align: middle; + /* vertical-align: middle; */ text-align: center; cursor: pointer; user-select: none; @@ -463,8 +471,8 @@ export const FontIcon = styled.div` /* vertical-align: text-top; */ `; export const ButtonBox = styled(Button)` - padding: 0.5em; - font-size: x-small; + padding: 8px; + /* font-size: small; */ & > ${FontIcon} { width: 1em; @@ -472,12 +480,13 @@ export const ButtonBox = styled(Button)` display: inline; line-height: 0px; } - background-color: transparent; + background-color: #f7f7f7; border: 1px solid; border-radius: 4px; border-color: black; color: black; + /* text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); */ /* -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; */ `; @@ -499,6 +508,7 @@ export const LinkPrimary = styled(Link)` export const ButtonPrimary = styled(ButtonVariant)<{ small?: boolean }>` font-size: ${({ small }) => (small ? "small" : "inherit")}; background-color: rgb(66, 184, 221); + border-color: rgb(66, 184, 221); `; export const ButtonBoxPrimary = styled(ButtonBox)` color: rgb(66, 184, 221); @@ -714,6 +724,7 @@ export const InputWithLabel = styled.div<{ invalid?: boolean }>` border-top-right-radius: 0.25em; border-color: ${({ invalid }) => (!invalid ? "lightgray" : "red")}; } + margin-bottom: 16px; `; export const ErrorText = styled.div` @@ -772,13 +783,13 @@ export const PopupNavigation = styled.div<{ devMode?: boolean }>` display: flex; & > div { - width: 400px; + width: ${({ devMode }) => (!devMode ? "400px" : "500px")}; } & > div > a { color: #f8faf7; display: inline-block; - width: calc(400px / ${({ devMode }) => (!devMode ? 4 : 5)}); + width: 100px; text-align: center; text-decoration: none; vertical-align: middle; @@ -804,10 +815,9 @@ export const NiceSelect = styled.div` box-shadow: none; background-image: ${image}; - background-position: right 0.5rem center; + background-position: right 8px center; background-repeat: no-repeat; background-size: 1.5em 1.5em; - padding-right: 2.5rem; background-color: white; @@ -967,3 +977,17 @@ export const StyledCheckboxLabel = styled.div` box-shadow: 0 0 0 0.05em #fff, 0 0 0.15em 0.1em currentColor; } `; + +export const ParagraphClickable = styled.p` + cursor: pointer; + margin: 0px; + padding: 8px 16px; + :hover { + filter: alpha(opacity=80); + background-image: linear-gradient( + transparent, + rgba(0, 0, 0, 0.1) 40%, + rgba(0, 0, 0, 0.2) + ); + } +`; diff --git a/packages/taler-wallet-webextension/src/context/devContext.ts b/packages/taler-wallet-webextension/src/context/devContext.ts index 7ed6858a7..4b8ba2bcd 100644 --- a/packages/taler-wallet-webextension/src/context/devContext.ts +++ b/packages/taler-wallet-webextension/src/context/devContext.ts @@ -42,5 +42,6 @@ export const DevContextProvider = ({ children }: { children: any }): VNode => { const [value, setter] = useLocalStorage("devMode"); const devMode = value === "true"; const toggleDevMode = () => setter((v) => (!v ? "true" : undefined)); + children = children.length === 1 && typeof children === "function" ? children({ devMode }) : children; return h(Context.Provider, { value: { devMode, toggleDevMode }, children }); }; diff --git a/packages/taler-wallet-webextension/src/cta/Deposit.stories.tsx b/packages/taler-wallet-webextension/src/cta/Deposit.stories.tsx new file mode 100644 index 000000000..df5947bb1 --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/Deposit.stories.tsx @@ -0,0 +1,168 @@ +/* + 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 { ContractTerms, PreparePayResultType } from "@gnu-taler/taler-util"; +import { createExample } from "../test-utils"; +import { PaymentRequestView as TestedComponent } from "./Deposit"; + +export default { + title: "cta/deposit", + component: TestedComponent, + argTypes: {}, +}; + +export const NoBalance = createExample(TestedComponent, { + payStatus: { + status: PreparePayResultType.InsufficientBalance, + noncePriv: "", + proposalId: "proposal1234", + contractTerms: { + merchant: { + name: "someone", + }, + summary: "some beers", + amount: "USD:10", + } as Partial as any, + amountRaw: "USD:10", + }, +}); + +export const NoEnoughBalance = createExample(TestedComponent, { + payStatus: { + status: PreparePayResultType.InsufficientBalance, + noncePriv: "", + proposalId: "proposal1234", + contractTerms: { + merchant: { + name: "someone", + }, + summary: "some beers", + amount: "USD:10", + } as Partial as any, + amountRaw: "USD:10", + }, + balance: { + currency: "USD", + fraction: 40000000, + value: 9, + }, +}); + +export const PaymentPossible = createExample(TestedComponent, { + uri: "taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0", + payStatus: { + status: PreparePayResultType.PaymentPossible, + amountEffective: "USD:10", + amountRaw: "USD:10", + noncePriv: "", + contractTerms: { + nonce: "123213123", + merchant: { + name: "someone", + }, + amount: "USD:10", + summary: "some beers", + } as Partial as any, + contractTermsHash: "123456", + proposalId: "proposal1234", + }, +}); + +export const PaymentPossibleWithFee = createExample(TestedComponent, { + uri: "taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0", + payStatus: { + status: PreparePayResultType.PaymentPossible, + amountEffective: "USD:10.20", + amountRaw: "USD:10", + noncePriv: "", + contractTerms: { + nonce: "123213123", + merchant: { + name: "someone", + }, + amount: "USD:10", + summary: "some beers", + } as Partial as any, + contractTermsHash: "123456", + proposalId: "proposal1234", + }, +}); + +export const AlreadyConfirmedWithFullfilment = createExample(TestedComponent, { + payStatus: { + status: PreparePayResultType.AlreadyConfirmed, + amountEffective: "USD:10", + amountRaw: "USD:10", + contractTerms: { + merchant: { + name: "someone", + }, + fulfillment_message: + "congratulations! you are looking at the fulfillment message! ", + summary: "some beers", + amount: "USD:10", + } as Partial as any, + contractTermsHash: "123456", + proposalId: "proposal1234", + paid: false, + }, +}); + +export const AlreadyConfirmedWithoutFullfilment = createExample( + TestedComponent, + { + payStatus: { + status: PreparePayResultType.AlreadyConfirmed, + amountEffective: "USD:10", + amountRaw: "USD:10", + contractTerms: { + merchant: { + name: "someone", + }, + summary: "some beers", + amount: "USD:10", + } as Partial as any, + contractTermsHash: "123456", + proposalId: "proposal1234", + paid: false, + }, + }, +); + +export const AlreadyPaid = createExample(TestedComponent, { + payStatus: { + status: PreparePayResultType.AlreadyConfirmed, + amountEffective: "USD:10", + amountRaw: "USD:10", + contractTerms: { + merchant: { + name: "someone", + }, + fulfillment_message: + "congratulations! you are looking at the fulfillment message! ", + summary: "some beers", + amount: "USD:10", + } as Partial as any, + contractTermsHash: "123456", + proposalId: "proposal1234", + paid: true, + }, +}); diff --git a/packages/taler-wallet-webextension/src/cta/Deposit.tsx b/packages/taler-wallet-webextension/src/cta/Deposit.tsx new file mode 100644 index 000000000..3696b0c2d --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/Deposit.tsx @@ -0,0 +1,251 @@ +/* + This file is part of TALER + (C) 2015 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 + */ + +/** + * Page shown to the user to confirm entering + * a contract. + */ + +/** + * Imports. + */ +// import * as i18n from "../i18n"; + +import { + AmountJson, + Amounts, + amountToPretty, + ConfirmPayResult, + ConfirmPayResultDone, + ConfirmPayResultType, + ContractTerms, + i18n, + NotificationType, + PreparePayResult, + PreparePayResultType, +} from "@gnu-taler/taler-util"; +import { OperationFailedError } from "@gnu-taler/taler-wallet-core"; +import { Fragment, h, VNode } from "preact"; +import { useEffect, useState } from "preact/hooks"; +import { ErrorTalerOperation } from "../components/ErrorTalerOperation"; +import { LogoHeader } from "../components/LogoHeader"; +import { Part } from "../components/Part"; +import { + ErrorBox, + SuccessBox, + WalletAction, + WarningBox, +} from "../components/styled"; +import { useAsyncAsHook } from "../hooks/useAsyncAsHook"; +import * as wxApi from "../wxApi"; + +interface Props { + talerPayUri?: string; + goBack: () => void; +} + +export function DepositPage({ talerPayUri, goBack }: Props): VNode { + const [payStatus, setPayStatus] = useState( + undefined, + ); + const [payResult, setPayResult] = useState( + undefined, + ); + const [payErrMsg, setPayErrMsg] = useState< + OperationFailedError | string | undefined + >(undefined); + + const balance = useAsyncAsHook(wxApi.getBalance, [ + NotificationType.CoinWithdrawn, + ]); + const balanceWithoutError = balance?.hasError + ? [] + : balance?.response.balances || []; + + const foundBalance = balanceWithoutError.find( + (b) => + payStatus && + Amounts.parseOrThrow(b.available).currency === + Amounts.parseOrThrow(payStatus?.amountRaw).currency, + ); + const foundAmount = foundBalance + ? Amounts.parseOrThrow(foundBalance.available) + : undefined; + // We use a string here so that dependency tracking for useEffect works properly + const foundAmountStr = foundAmount + ? Amounts.stringify(foundAmount) + : undefined; + + useEffect(() => { + if (!talerPayUri) return; + const doFetch = async (): Promise => { + try { + const p = await wxApi.preparePay(talerPayUri); + setPayStatus(p); + } catch (e) { + console.log("Got error while trying to pay", e); + if (e instanceof OperationFailedError) { + setPayErrMsg(e); + } + if (e instanceof Error) { + setPayErrMsg(e.message); + } + } + }; + doFetch(); + }, [talerPayUri, foundAmountStr]); + + if (!talerPayUri) { + return missing pay uri; + } + + if (!payStatus) { + if (payErrMsg instanceof OperationFailedError) { + return ( + + +

{i18n.str`Digital cash payment`}

+
+ +
+
+ ); + } + if (payErrMsg) { + return ( + + +

{i18n.str`Digital cash payment`}

+
+

Could not get the payment information for this order

+ {payErrMsg} +
+
+ ); + } + return Loading payment information ...; + } + + const onClick = async (): Promise => { + // try { + // const res = await doPayment(payStatus); + // setPayResult(res); + // } catch (e) { + // console.error(e); + // if (e instanceof Error) { + // setPayErrMsg(e.message); + // } + // } + }; + + return ( + + ); +} + +export interface PaymentRequestViewProps { + payStatus: PreparePayResult; + payResult?: ConfirmPayResult; + onClick: () => void; + payErrMsg?: string; + uri: string; + balance: AmountJson | undefined; +} +export function PaymentRequestView({ + uri, + payStatus, + payResult, + onClick, + balance, +}: PaymentRequestViewProps): VNode { + let totalFees: AmountJson = Amounts.getZero(payStatus.amountRaw); + const contractTerms: ContractTerms = payStatus.contractTerms; + + return ( + + + +

{i18n.str`Digital cash deposit`}

+ {payStatus.status === PreparePayResultType.AlreadyConfirmed && + (payStatus.paid ? ( + Already paid + ) : ( + Already claimed + ))} + {payResult && payResult.type === ConfirmPayResultType.Done && ( + +

Payment complete

+

+ {!payResult.contractTerms.fulfillment_message + ? "You will now be sent back to the merchant you came from." + : payResult.contractTerms.fulfillment_message} +

+
+ )} +
+ {payStatus.status !== PreparePayResultType.InsufficientBalance && + Amounts.isNonZero(totalFees) && ( + + )} + + {Amounts.isNonZero(totalFees) && ( + + + + )} + + + {contractTerms.order_id && ( + + )} +
+
+ ); +} diff --git a/packages/taler-wallet-webextension/src/cta/Pay.tsx b/packages/taler-wallet-webextension/src/cta/Pay.tsx index d7419d410..e61d3a9d6 100644 --- a/packages/taler-wallet-webextension/src/cta/Pay.tsx +++ b/packages/taler-wallet-webextension/src/cta/Pay.tsx @@ -57,35 +57,10 @@ import * as wxApi from "../wxApi"; interface Props { talerPayUri?: string; - goToWalletManualWithdraw: () => void; + goToWalletManualWithdraw: (currency?: string) => void; + goBack: () => void; } -// export function AlreadyPaid({ payStatus }: { payStatus: PreparePayResult }) { -// const fulfillmentUrl = payStatus.contractTerms.fulfillment_url; -// let message; -// if (fulfillmentUrl) { -// message = ( -// -// You have already paid for this article. Click{" "} -// here to view it again. -// -// ); -// } else { -// message = -// You have already paid for this article:{" "} -// -// {payStatus.contractTerms.fulfillment_message ?? "no message given"} -// -// ; -// } -// return
-//

GNU Taler Wallet

-//
-// {message} -//
-//
-// } - const doPayment = async ( payStatus: PreparePayResult, ): Promise => { diff --git a/packages/taler-wallet-webextension/src/popup/AddNewActionView.stories.tsx b/packages/taler-wallet-webextension/src/popup/AddNewActionView.stories.tsx deleted file mode 100644 index 6ee56ef77..000000000 --- a/packages/taler-wallet-webextension/src/popup/AddNewActionView.stories.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/* - 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: "popup/add new action", - component: TestedComponent, - argTypes: { - setDeviceName: () => Promise.resolve(), - }, -}; - -export const Initial = createExample(TestedComponent, {}); diff --git a/packages/taler-wallet-webextension/src/popup/AddNewActionView.tsx b/packages/taler-wallet-webextension/src/popup/AddNewActionView.tsx deleted file mode 100644 index d4158973e..000000000 --- a/packages/taler-wallet-webextension/src/popup/AddNewActionView.tsx +++ /dev/null @@ -1,64 +0,0 @@ -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/popup/Balance.stories.tsx b/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx index a4988cf2d..1af3b5858 100644 --- a/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx +++ b/packages/taler-wallet-webextension/src/popup/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,211 +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, { - balance: { - hasError: false, - response: { - balances: [], - }, - }, - Linker: NullLink, + balances: [], }); export const SomeCoins = createExample(TestedComponent, { - balance: { - hasError: false, - response: { - balances: [ - { - available: "USD:10.5", - hasPendingTransactions: false, - pendingIncoming: "USD:0", - pendingOutgoing: "USD:0", - requiresUserInput: false, - }, - ], + balances: [ + { + available: "USD:10.5", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, }, - }, - Linker: NullLink, + ], }); -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 SomeCoinsInTreeCurrencies = createExample(TestedComponent, { + balances: [ + { + available: "EUR:1", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, }, - }, - Linker: NullLink, -}); - -export const SomeCoinsAndOutgoingMoney = createExample(TestedComponent, { - balance: { - hasError: false, - response: { - balances: [ - { - available: "USD:2.23", - hasPendingTransactions: false, - pendingIncoming: "USD:0", - pendingOutgoing: "USD:5.11", - requiresUserInput: false, - }, - ], + { + available: "TESTKUDOS:2000", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, }, - }, - Linker: NullLink, -}); - -export const SomeCoinsAndMovingMoney = createExample(TestedComponent, { - balance: { - hasError: false, - response: { - balances: [ - { - available: "USD:2.23", - hasPendingTransactions: false, - pendingIncoming: "USD:2", - pendingOutgoing: "USD:5.11", - requiresUserInput: false, - }, - ], + { + available: "JPY:4", + hasPendingTransactions: false, + pendingIncoming: "EUR:15", + pendingOutgoing: "EUR:0", + requiresUserInput: false, }, - }, - Linker: NullLink, + ], }); -export const SomeCoinsInTwoCurrencies = createExample(TestedComponent, { - balance: { - hasError: false, - response: { - balances: [ - { - available: "USD:2", - hasPendingTransactions: false, - pendingIncoming: "USD:5.1", - pendingOutgoing: "USD:0", - requiresUserInput: false, - }, - { - available: "EUR:4", - hasPendingTransactions: false, - pendingIncoming: "EUR:0", - pendingOutgoing: "EUR:3.01", - requiresUserInput: false, - }, - ], +export const NoCoinsInTreeCurrencies = createExample(TestedComponent, { + balances: [ + { + available: "EUR:3", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, }, - }, - Linker: NullLink, -}); - -export const SomeCoinsInTreeCurrencies = createExample(TestedComponent, { - balance: { - hasError: false, - response: { - balances: [ - { - available: "USD:1", - hasPendingTransactions: false, - pendingIncoming: "USD:0", - pendingOutgoing: "USD:0", - requiresUserInput: false, - }, - { - available: "TESTKUDOS:2000", - hasPendingTransactions: false, - pendingIncoming: "USD:0", - pendingOutgoing: "USD:0", - requiresUserInput: false, - }, - { - available: "EUR:4", - hasPendingTransactions: false, - pendingIncoming: "EUR:15", - pendingOutgoing: "EUR:0", - requiresUserInput: false, - }, - ], + { + available: "USD:2", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, + }, + { + available: "ARS:1", + hasPendingTransactions: false, + pendingIncoming: "EUR:15", + pendingOutgoing: "EUR:0", + requiresUserInput: false, }, - }, - Linker: NullLink, + ], }); export const SomeCoinsInFiveCurrencies = createExample(TestedComponent, { - balance: { - hasError: false, - response: { - balances: [ - { - available: "USD: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: "ARS:30", - hasPendingTransactions: false, - pendingIncoming: "USD:0", - pendingOutgoing: "USD:0", - requiresUserInput: false, - }, - { - available: "JPY:51223233", - 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, - }, - ], + 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/popup/BalancePage.tsx b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx index 3eb5f4270..014d3b18e 100644 --- a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx +++ b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx @@ -14,70 +14,81 @@ 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, ErrorBox } from "../components/styled"; -import { HookResponse, useAsyncAsHook } from "../hooks/useAsyncAsHook"; +import { useAsyncAsHook } from "../hooks/useAsyncAsHook"; import { PageLink } from "../renderHtml"; import * as wxApi from "../wxApi"; +import { MultiActionButton } from "../components/MultiActionButton"; +import { Loading } from "../components/Loading"; + interface Props { goToWalletDeposit: (currency: string) => void; + goToWalletHistory: (currency: string) => void; goToWalletManualWithdraw: () => void; } export function BalancePage({ goToWalletManualWithdraw, goToWalletDeposit, + 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?