From 97a05ff659af274dcfcd9c76bf19100bbd51ce0e Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 19 Aug 2021 00:34:47 -0300 Subject: new wallet history and view refactoring --- .../.storybook/preview.js | 87 +++++- packages/taler-wallet-webextension/package.json | 2 +- .../taler-wallet-webextension/rollup.config.js | 11 +- .../src/NavigationBar.tsx | 91 +++++++ .../src/components/LogoHeader.tsx | 13 + .../src/components/styled/index.tsx | 107 +++++++- .../src/cta/Pay.stories.tsx | 96 +++++++ packages/taler-wallet-webextension/src/cta/Pay.tsx | 253 ++++++++++++++++++ .../src/cta/Refund.stories.tsx | 77 ++++++ .../taler-wallet-webextension/src/cta/Refund.tsx | 95 +++++++ .../src/cta/Tip.stories.tsx | 59 +++++ packages/taler-wallet-webextension/src/cta/Tip.tsx | 110 ++++++++ .../src/cta/Withdraw.stories.tsx | 45 ++++ .../taler-wallet-webextension/src/cta/Withdraw.tsx | 172 ++++++++++++ .../taler-wallet-webextension/src/cta/payback.tsx | 31 +++ .../src/cta/reset-required.tsx | 97 +++++++ .../src/cta/return-coins.tsx | 30 +++ .../src/popup/BackupPage.tsx | 2 +- .../src/popup/History.tsx | 34 +-- .../src/popup/Popup.stories.tsx | 2 +- .../src/popup/Transaction.tsx | 2 +- .../taler-wallet-webextension/src/popup/popup.tsx | 76 ------ .../src/popupEntryPoint.tsx | 4 +- .../taler-wallet-webextension/src/test-utils.ts | 8 + .../src/wallet/History.stories.tsx | 294 +++++++++++++++++++++ .../src/wallet/History.tsx | 248 +++++++++++++++++ .../src/wallet/Pay.stories.tsx | 103 -------- .../taler-wallet-webextension/src/wallet/Pay.tsx | 253 ------------------ .../src/wallet/Refund.stories.tsx | 83 ------ .../src/wallet/Refund.tsx | 95 ------- .../src/wallet/Tip.stories.tsx | 66 ----- .../taler-wallet-webextension/src/wallet/Tip.tsx | 110 -------- .../src/wallet/Welcome.stories.tsx | 8 +- .../src/wallet/Welcome.tsx | 11 +- .../src/wallet/Withdraw.stories.tsx | 50 ---- .../src/wallet/Withdraw.tsx | 178 ------------- .../src/wallet/payback.tsx | 31 --- .../src/wallet/reset-required.tsx | 97 ------- .../src/wallet/return-coins.tsx | 30 --- .../src/walletEntryPoint.tsx | 70 +++-- .../static/img/logo-2021.svg | 1 + .../static/style/wallet.css | 1 + 42 files changed, 1971 insertions(+), 1262 deletions(-) create mode 100644 packages/taler-wallet-webextension/src/NavigationBar.tsx create mode 100644 packages/taler-wallet-webextension/src/components/LogoHeader.tsx create mode 100644 packages/taler-wallet-webextension/src/cta/Pay.stories.tsx create mode 100644 packages/taler-wallet-webextension/src/cta/Pay.tsx create mode 100644 packages/taler-wallet-webextension/src/cta/Refund.stories.tsx create mode 100644 packages/taler-wallet-webextension/src/cta/Refund.tsx create mode 100644 packages/taler-wallet-webextension/src/cta/Tip.stories.tsx create mode 100644 packages/taler-wallet-webextension/src/cta/Tip.tsx create mode 100644 packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx create mode 100644 packages/taler-wallet-webextension/src/cta/Withdraw.tsx create mode 100644 packages/taler-wallet-webextension/src/cta/payback.tsx create mode 100644 packages/taler-wallet-webextension/src/cta/reset-required.tsx create mode 100644 packages/taler-wallet-webextension/src/cta/return-coins.tsx delete mode 100644 packages/taler-wallet-webextension/src/popup/popup.tsx create mode 100644 packages/taler-wallet-webextension/src/test-utils.ts create mode 100644 packages/taler-wallet-webextension/src/wallet/History.stories.tsx create mode 100644 packages/taler-wallet-webextension/src/wallet/History.tsx delete mode 100644 packages/taler-wallet-webextension/src/wallet/Pay.stories.tsx delete mode 100644 packages/taler-wallet-webextension/src/wallet/Pay.tsx delete mode 100644 packages/taler-wallet-webextension/src/wallet/Refund.stories.tsx delete mode 100644 packages/taler-wallet-webextension/src/wallet/Refund.tsx delete mode 100644 packages/taler-wallet-webextension/src/wallet/Tip.stories.tsx delete mode 100644 packages/taler-wallet-webextension/src/wallet/Tip.tsx delete mode 100644 packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx delete mode 100644 packages/taler-wallet-webextension/src/wallet/Withdraw.tsx delete mode 100644 packages/taler-wallet-webextension/src/wallet/payback.tsx delete mode 100644 packages/taler-wallet-webextension/src/wallet/reset-required.tsx delete mode 100644 packages/taler-wallet-webextension/src/wallet/return-coins.tsx create mode 100644 packages/taler-wallet-webextension/static/img/logo-2021.svg (limited to 'packages') diff --git a/packages/taler-wallet-webextension/.storybook/preview.js b/packages/taler-wallet-webextension/.storybook/preview.js index 02a4e43d4..1b6f62400 100644 --- a/packages/taler-wallet-webextension/.storybook/preview.js +++ b/packages/taler-wallet-webextension/.storybook/preview.js @@ -15,7 +15,8 @@ */ import { Fragment } from "preact" -import { NavBar } from '../src/popup/popup' +import { NavBar } from '../src/NavigationBar' +import { LogoHeader } from '../src/components/LogoHeader' import { TranslationProvider } from '../src/context/translation' export const parameters = { @@ -43,7 +44,7 @@ export const globalTypes = { export const decorators = [ (Story, { kind }) => { if (kind.startsWith('popup')) { - + function Body() { const isTestingHeader = (/.*\/header\/?.*/.test(kind)); if (isTestingHeader) { @@ -51,16 +52,16 @@ export const decorators = [ return
- } else { - const path = !isTestingHeader ? /popup(\/.*).*/.exec(kind)[1] : '' - // add a fake header so it looks similar - return - -
- -
-
} + + const path = /popup(\/.*).*/.exec(kind)[1]; + // add a fake header so it looks similar + return + +
+ +
+
} return } - if (kind.startsWith('wallet')) { - return
+ if (kind.startsWith('cta')) { + return
+ + +
+ } + if (kind.startsWith('wallet')) { + const path = /wallet(\/.*).*/.exec(kind)[1]; + return
+ + + + + {/* + */} +
} return
diff --git a/packages/taler-wallet-webextension/package.json b/packages/taler-wallet-webextension/package.json index a5908af25..e41c6cb86 100644 --- a/packages/taler-wallet-webextension/package.json +++ b/packages/taler-wallet-webextension/package.json @@ -12,7 +12,7 @@ "test": "jest ./tests", "compile": "tsc && rollup -c", "build-storybook": "build-storybook", - "storybook": "start-storybook -s static -p 6006", + "storybook": "start-storybook -s . -p 6006", "watch": "tsc --watch & rollup -w -c" }, "dependencies": { diff --git a/packages/taler-wallet-webextension/rollup.config.js b/packages/taler-wallet-webextension/rollup.config.js index 7e7ec0032..5a3f0db5f 100644 --- a/packages/taler-wallet-webextension/rollup.config.js +++ b/packages/taler-wallet-webextension/rollup.config.js @@ -1,14 +1,13 @@ // rollup.config.js +import linaria from '@linaria/rollup'; +import alias from '@rollup/plugin-alias'; import commonjs from "@rollup/plugin-commonjs"; -import nodeResolve from "@rollup/plugin-node-resolve"; +import image from '@rollup/plugin-image'; import json from "@rollup/plugin-json"; -import builtins from "builtin-modules"; +import nodeResolve from "@rollup/plugin-node-resolve"; import replace from "@rollup/plugin-replace"; -import ignore from "rollup-plugin-ignore" -import image from '@rollup/plugin-image'; -import linaria from '@linaria/rollup'; import css from 'rollup-plugin-css-only'; -import alias from '@rollup/plugin-alias'; +import ignore from "rollup-plugin-ignore"; const makePlugins = () => [ alias({ diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx b/packages/taler-wallet-webextension/src/NavigationBar.tsx new file mode 100644 index 000000000..e07032d0a --- /dev/null +++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx @@ -0,0 +1,91 @@ +/* + 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 + */ + +/** + * Popup shown to the user when they click + * the Taler browser action button. + * + * @author Florian Dold + */ + +/** + * Imports. + */ +import { i18n } from "@gnu-taler/taler-util"; +import { ComponentChildren, JSX } from "preact"; +import Match from "preact-router/match"; +import { useDevContext } from "./context/devContext"; +import { PopupNavigation } from './components/styled' + +export enum Pages { + welcome = '/welcome', + balance = '/balance', + settings = '/settings', + dev = '/dev', + backup = '/backup', + history = '/history', + transaction = '/transaction/:tid', + provider_detail = '/provider/:pid', + provider_add = '/provider/add', + + reset_required = '/reset-required', + payback = '/payback', + return_coins = '/return-coins', + + pay = '/pay', + refund = '/refund', + tips = '/tips', + withdraw = '/withdraw', +} + +interface TabProps { + target: string; + current?: string; + children?: ComponentChildren; +} + +function Tab(props: TabProps): JSX.Element { + let cssClass = ""; + if (props.current?.startsWith(props.target)) { + cssClass = "active"; + } + return ( + + {props.children} + + ); +} + +export function NavBar({ devMode, path }: { path: string, devMode: boolean }) { + return +
+ {i18n.str`Balance`} + {i18n.str`History`} + {i18n.str`Backup`} + {i18n.str`Settings`} + {devMode && {i18n.str`Dev`}} +
+
+} + +export function WalletNavBar() { + const { devMode } = useDevContext() + return {({ path }: any) => { + console.log("path", path) + return + }} +} + diff --git a/packages/taler-wallet-webextension/src/components/LogoHeader.tsx b/packages/taler-wallet-webextension/src/components/LogoHeader.tsx new file mode 100644 index 000000000..0217289eb --- /dev/null +++ b/packages/taler-wallet-webextension/src/components/LogoHeader.tsx @@ -0,0 +1,13 @@ +export function LogoHeader() { + return
+ +
+ +} \ No newline at end of file diff --git a/packages/taler-wallet-webextension/src/components/styled/index.tsx b/packages/taler-wallet-webextension/src/components/styled/index.tsx index 7f709db46..6067fa446 100644 --- a/packages/taler-wallet-webextension/src/components/styled/index.tsx +++ b/packages/taler-wallet-webextension/src/components/styled/index.tsx @@ -11,7 +11,7 @@ export const PaymentStatus = styled.div<{ color: string }>` background-color: ${p => p.color}; ` -export const WalletPage = styled.section` +export const WalletAction = styled.section` border: solid 5px black; border-radius: 10px; margin-left: auto; @@ -28,8 +28,73 @@ export const WalletPage = styled.section` } ` +export const DateSeparator = styled.div` + color: gray; + margin: .2em; + margin-top: 1em; +` +export const WalletBox = styled.div<{ noPadding?: boolean }>` + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + & > * { + width: 400px; + } + & > section { + padding-left: ${({ noPadding }) => noPadding ? '0px' : '8px'}; + padding-right: ${({ noPadding }) => noPadding ? '0px' : '8px'}; + // this margin will send the section up when used with a header + margin-bottom: auto; + overflow: auto; + + table td { + padding: 5px 10px; + } + table tr { + border-bottom: 1px solid black; + border-top: 1px solid black; + } + } + + & > header { + flex-direction: row; + justify-content: space-between; + display: flex; + padding: 8px; + margin-bottom: 5px; + + & > div { + align-self: center; + } + + & > h3 { + margin: 0px; + } + + & > .title { + /* margin: 1em; */ + font-size: large; + color: #3c4e92; + } + } + + & > footer { + padding-top: 8px; + padding-bottom: 8px; + flex-direction: row; + justify-content: space-between; + display: flex; + & button { + margin-right: 8px; + margin-left: 8px; + } + } +` + export const PopupBox = styled.div<{ noPadding?: boolean }>` height: 290px; + width: 400px; display: flex; flex-direction: column; justify-content: space-between; @@ -194,10 +259,32 @@ export const RowBorderGray = styled(Row)` export const RowLightBorderGray = styled(Row2)` border: 1px solid lightgray; - /* border-radius: 0.5em; */ + border-top: 0px; + + ${DateSeparator} + & { + border: 1px solid lightgray; + background-color: red; + } ` -export const HistoryRow = styled(RowLightBorderGray)` +export const HistoryRow = styled.a` + text-decoration: none; + + display: flex; + justify-content: space-between; + padding: 0.5em; + + border: 1px solid lightgray; + border-top: 0px; + + ${DateSeparator} + & { + border: 1px solid lightgray; + } + + :hover { + background-color: lightgray; + } + & > ${Column}:last-of-type { margin-left: auto; align-self: center; @@ -284,11 +371,17 @@ export const ErrorBox = styled.div` } } ` -export const PopupNavigation = styled.div<{devMode?:boolean}>` +export const PopupNavigation = styled.div<{ devMode?: boolean }>` background-color:#0042b2; height: 35px; - - & > a { + justify-content: space-around; + display: flex; + + & > div { + width: 400px; + } + + & > div > a { color: #f8faf7; display: inline-block; width: calc(400px / ${({ devMode }) => !devMode ? 4 : 5}); @@ -298,7 +391,7 @@ export const PopupNavigation = styled.div<{devMode?:boolean}>` line-height: 35px; } - & > a.active { + & > div > a.active { background-color: #f8faf7; color: #0042b2; font-weight: bold; diff --git a/packages/taler-wallet-webextension/src/cta/Pay.stories.tsx b/packages/taler-wallet-webextension/src/cta/Pay.stories.tsx new file mode 100644 index 000000000..38e3d0f35 --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/Pay.stories.tsx @@ -0,0 +1,96 @@ +/* + 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 './Pay'; + +export default { + title: 'cta/pay', + component: TestedComponent, + argTypes: { + }, +}; + +export const InsufficientBalance = createExample(TestedComponent, { + payStatus: { + status: PreparePayResultType.InsufficientBalance, + proposalId: "proposal1234", + contractTerms: { + merchant: { + name: 'someone' + }, + amount: 'USD:10', + } as Partial as any, + amountRaw: 'USD:10', + } +}); + +export const PaymentPossible = createExample(TestedComponent, { + payStatus: { + status: PreparePayResultType.PaymentPossible, + amountEffective: 'USD:10', + amountRaw: 'USD:10', + contractTerms: { + merchant: { + name: 'someone' + }, + amount: 'USD:10', + } 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! ', + 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' + }, + amount: 'USD:10', + } as Partial as any, + contractTermsHash: '123456', + proposalId: 'proposal1234', + paid: false, + } +}); diff --git a/packages/taler-wallet-webextension/src/cta/Pay.tsx b/packages/taler-wallet-webextension/src/cta/Pay.tsx new file mode 100644 index 000000000..a5849bb28 --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/Pay.tsx @@ -0,0 +1,253 @@ +/* + 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 { renderAmount, ProgressButton } from "../renderHtml"; +import * as wxApi from "../wxApi"; + +import { useState, useEffect } from "preact/hooks"; + +import { ConfirmPayResultDone, getJsonI18n, i18n } from "@gnu-taler/taler-util"; +import { + PreparePayResult, + ConfirmPayResult, + AmountJson, + PreparePayResultType, + Amounts, + ContractTerms, + ConfirmPayResultType, +} from "@gnu-taler/taler-util"; +import { JSX, VNode } from "preact"; + +interface Props { + talerPayUri?: string +} + +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 => { + if (payStatus.status !== "payment-possible") { + throw Error(`invalid state: ${payStatus.status}`); + } + const proposalId = payStatus.proposalId; + const res = await wxApi.confirmPay(proposalId, undefined); + if (res.type !== ConfirmPayResultType.Done) { + throw Error("payment pending"); + } + const fu = res.contractTerms.fulfillment_url; + if (fu) { + document.location.href = fu; + } + return res; +}; + + + +export function PayPage({ talerPayUri }: Props): JSX.Element { + const [payStatus, setPayStatus] = useState(undefined); + const [payResult, setPayResult] = useState(undefined); + const [payErrMsg, setPayErrMsg] = useState(""); + + useEffect(() => { + if (!talerPayUri) return; + const doFetch = async (): Promise => { + const p = await wxApi.preparePay(talerPayUri); + setPayStatus(p); + }; + doFetch(); + }, [talerPayUri]); + + if (!talerPayUri) { + return missing pay uri + } + + if (!payStatus) { + return Loading payment information ...; + } + + if (payResult && payResult.type === ConfirmPayResultType.Done) { + if (payResult.contractTerms.fulfillment_message) { + const obj = { + fulfillment_message: payResult.contractTerms.fulfillment_message, + fulfillment_message_i18n: + payResult.contractTerms.fulfillment_message_i18n, + }; + const msg = getJsonI18n(obj, "fulfillment_message"); + return ( +
+

Payment succeeded.

+

{msg}

+
+ ); + } else { + return Redirecting ...; + } + } + + const onClick = async () => { + try { + const res = await doPayment(payStatus) + setPayResult(res); + } catch (e) { + console.error(e); + setPayErrMsg(e.message); + } + + } + + return ; +} + +export interface PaymentRequestViewProps { + payStatus: PreparePayResult; + onClick: () => void; + payErrMsg?: string; + +} +export function PaymentRequestView({ payStatus, onClick, payErrMsg }: PaymentRequestViewProps) { + let totalFees: AmountJson | undefined = undefined; + let insufficientBalance = false; + const [loading, setLoading] = useState(false); + const contractTerms: ContractTerms = payStatus.contractTerms; + + if ( + payStatus.status === PreparePayResultType.AlreadyConfirmed + ) { + return + } + + if (!contractTerms) { + return ( + + Error: did not get contract terms from merchant or wallet backend. + + ); + } + + if (payStatus.status == PreparePayResultType.InsufficientBalance) { + insufficientBalance = true; + } + + if (payStatus.status === PreparePayResultType.PaymentPossible) { + const amountRaw = Amounts.parseOrThrow(payStatus.amountRaw); + const amountEffective: AmountJson = Amounts.parseOrThrow( + payStatus.amountEffective, + ); + totalFees = Amounts.sub(amountEffective, amountRaw).amount; + } + + let merchantName: VNode; + if (contractTerms.merchant && contractTerms.merchant.name) { + merchantName = {contractTerms.merchant.name}; + } else { + merchantName = (pub: {contractTerms.merchant_pub}); + } + + const amount = ( + {renderAmount(Amounts.parseOrThrow(contractTerms.amount))} + ); + + return
+

GNU Taler Wallet

+
+
+

+ + The merchant {merchantName} offers you to purchase: + +

+ {contractTerms.summary} +
+ {totalFees ? ( + + The total price is {amount} + (plus {renderAmount(totalFees)} fees). + + ) : ( + + The total price is {amount}. + + )} +

+ + {insufficientBalance ? ( +
+

+ Unable to pay: Your balance is insufficient. +

+
+ ) : null} + + {payErrMsg ? ( +
+

Payment failed: {payErrMsg}

+ +
+ ) : ( +
+ + {i18n.str`Confirm payment`} + +
+ )} +
+
+
+ + +} \ No newline at end of file diff --git a/packages/taler-wallet-webextension/src/cta/Refund.stories.tsx b/packages/taler-wallet-webextension/src/cta/Refund.stories.tsx new file mode 100644 index 000000000..88e714cb7 --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/Refund.stories.tsx @@ -0,0 +1,77 @@ +/* + 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 { OrderShortInfo } from '@gnu-taler/taler-util'; +import { createExample } from '../test-utils'; +import { View as TestedComponent } from './Refund'; + + +export default { + title: 'cta/refund', + component: TestedComponent, + argTypes: { + }, +}; + +export const Complete = createExample(TestedComponent, { + applyResult: { + amountEffectivePaid: 'USD:10', + amountRefundGone: 'USD:0', + amountRefundGranted: 'USD:2', + contractTermsHash: 'QWEASDZXC', + info: { + summary: 'tasty cold beer', + contractTermsHash: 'QWEASDZXC', + } as Partial as any, + pendingAtExchange: false, + proposalId: "proposal123", + } +}); + +export const Partial = createExample(TestedComponent, { + applyResult: { + amountEffectivePaid: 'USD:10', + amountRefundGone: 'USD:1', + amountRefundGranted: 'USD:2', + contractTermsHash: 'QWEASDZXC', + info: { + summary: 'tasty cold beer', + contractTermsHash: 'QWEASDZXC', + } as Partial as any, + pendingAtExchange: false, + proposalId: "proposal123", + } +}); + +export const InProgress = createExample(TestedComponent, { + applyResult: { + amountEffectivePaid: 'USD:10', + amountRefundGone: 'USD:1', + amountRefundGranted: 'USD:2', + contractTermsHash: 'QWEASDZXC', + info: { + summary: 'tasty cold beer', + contractTermsHash: 'QWEASDZXC', + } as Partial as any, + pendingAtExchange: true, + proposalId: "proposal123", + } +}); diff --git a/packages/taler-wallet-webextension/src/cta/Refund.tsx b/packages/taler-wallet-webextension/src/cta/Refund.tsx new file mode 100644 index 000000000..bb26d933b --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/Refund.tsx @@ -0,0 +1,95 @@ +/* + This file is part of TALER + (C) 2015-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 + */ + +/** + * Page that shows refund status for purchases. + * + * @author Florian Dold + */ + +import * as wxApi from "../wxApi"; +import { AmountView } from "../renderHtml"; +import { + ApplyRefundResponse, + Amounts, +} from "@gnu-taler/taler-util"; +import { useEffect, useState } from "preact/hooks"; +import { JSX } from "preact/jsx-runtime"; + +interface Props { + talerRefundUri?: string +} +export interface ViewProps { + applyResult: ApplyRefundResponse; +} +export function View({ applyResult }: ViewProps) { + return
+

GNU Taler Wallet

+
+

Refund Status

+

+ The product {applyResult.info.summary} has received a total + effective refund of{" "} + . +

+ {applyResult.pendingAtExchange ? ( +

Refund processing is still in progress.

+ ) : null} + {!Amounts.isZero(applyResult.amountRefundGone) ? ( +

+ The refund amount of{" "} + {" "} + could not be applied. +

+ ) : null} +
+
+} +export function RefundPage({ talerRefundUri }: Props): JSX.Element { + const [applyResult, setApplyResult] = useState(undefined); + const [errMsg, setErrMsg] = useState(undefined); + + useEffect(() => { + if (!talerRefundUri) return; + const doFetch = async (): Promise => { + try { + const result = await wxApi.applyRefund(talerRefundUri); + setApplyResult(result); + } catch (e) { + console.error(e); + setErrMsg(e.message); + console.log("err message", e.message); + } + }; + doFetch(); + }, [talerRefundUri]); + + console.log("rendering"); + + if (!talerRefundUri) { + return missing taler refund uri; + } + + if (errMsg) { + return Error: {errMsg}; + } + + if (!applyResult) { + return Updating refund status; + } + + return ; +} diff --git a/packages/taler-wallet-webextension/src/cta/Tip.stories.tsx b/packages/taler-wallet-webextension/src/cta/Tip.stories.tsx new file mode 100644 index 000000000..389b183f0 --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/Tip.stories.tsx @@ -0,0 +1,59 @@ +/* + 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 { View as TestedComponent } from './Tip'; + + +export default { + title: 'cta/tip', + component: TestedComponent, + argTypes: { + }, +}; + +export const Accepted = createExample(TestedComponent, { + prepareTipResult: { + accepted: true, + merchantBaseUrl: '', + exchangeBaseUrl: '', + expirationTimestamp : { + t_ms: 0 + }, + tipAmountEffective: 'USD:10', + tipAmountRaw: 'USD:5', + walletTipId: 'id' + } +}); + +export const NotYetAccepted = createExample(TestedComponent, { + prepareTipResult: { + accepted: false, + merchantBaseUrl: 'http://merchant.url/', + exchangeBaseUrl: 'http://exchange.url/', + expirationTimestamp : { + t_ms: 0 + }, + tipAmountEffective: 'USD:10', + tipAmountRaw: 'USD:5', + walletTipId: 'id' + } +}); diff --git a/packages/taler-wallet-webextension/src/cta/Tip.tsx b/packages/taler-wallet-webextension/src/cta/Tip.tsx new file mode 100644 index 000000000..69886668b --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/Tip.tsx @@ -0,0 +1,110 @@ +/* + This file is part of TALER + (C) 2017 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 accept or ignore a tip from a merchant. + * + * @author Florian Dold + */ + +import { useEffect, useState } from "preact/hooks"; +import { PrepareTipResult } from "@gnu-taler/taler-util"; +import { AmountView } from "../renderHtml"; +import * as wxApi from "../wxApi"; +import { JSX } from "preact/jsx-runtime"; + +interface Props { + talerTipUri?: string +} +export interface ViewProps { + prepareTipResult: PrepareTipResult; + onAccept: () => void; + onIgnore: () => void; + +} +export function View({ prepareTipResult, onAccept, onIgnore }: ViewProps) { + return
+

GNU Taler Wallet

+
+ {prepareTipResult.accepted ? ( + + Tip from {prepareTipResult.merchantBaseUrl} accepted. Check + your transactions list for more details. + + ) : ( +
+

+ The merchant {prepareTipResult.merchantBaseUrl} is + offering you a tip of{" "} + + + {" "} + via the exchange {prepareTipResult.exchangeBaseUrl} +

+ + +
+ )} +
+
+ +} + +export function TipPage({ talerTipUri }: Props): JSX.Element { + const [updateCounter, setUpdateCounter] = useState(0); + const [prepareTipResult, setPrepareTipResult] = useState< + PrepareTipResult | undefined + >(undefined); + + const [tipIgnored, setTipIgnored] = useState(false); + + useEffect(() => { + if (!talerTipUri) return; + const doFetch = async (): Promise => { + const p = await wxApi.prepareTip({ talerTipUri }); + setPrepareTipResult(p); + }; + doFetch(); + }, [talerTipUri, updateCounter]); + + const doAccept = async () => { + if (!prepareTipResult) { + return; + } + await wxApi.acceptTip({ walletTipId: prepareTipResult?.walletTipId }); + setUpdateCounter(updateCounter + 1); + }; + + const doIgnore = () => { + setTipIgnored(true); + }; + + if (!talerTipUri) { + return missing tip uri; + } + + if (tipIgnored) { + return You've ignored the tip.; + } + + if (!prepareTipResult) { + return Loading ...; + } + + return +} diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx new file mode 100644 index 000000000..747f855fa --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx @@ -0,0 +1,45 @@ +/* + 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 { View as TestedComponent } from './Withdraw'; + + +export default { + title: 'cta/withdraw', + component: TestedComponent, + argTypes: { + }, +}; + +export const CompleteWithExchange = createExample(TestedComponent, { + details: { + amount: 'USD:2', + possibleExchanges: [], + }, + selectedExchange: 'Some exchange' +}) +export const CompleteWithoutExchange = createExample(TestedComponent, { + details: { + amount: 'USD:2', + possibleExchanges: [], + }, +}) diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx new file mode 100644 index 000000000..b5182b070 --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx @@ -0,0 +1,172 @@ +/* + This file is part of TALER + (C) 2015-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 + */ + +/** + * Page shown to the user to confirm creation + * of a reserve, usually requested by the bank. + * + * @author Florian Dold + */ + +import { i18n } from '@gnu-taler/taler-util' +import { renderAmount } from "../renderHtml"; + +import { useState, useEffect } from "preact/hooks"; +import { + acceptWithdrawal, + onUpdateNotification, + getWithdrawalDetailsForUri, +} from "../wxApi"; +import { WithdrawUriInfoResponse } from "@gnu-taler/taler-util"; +import { JSX } from "preact/jsx-runtime"; +import { WalletAction } from '../components/styled'; + +interface Props { + talerWithdrawUri?: string; +} + +export interface ViewProps { + details: WithdrawUriInfoResponse; + selectedExchange?: string; + accept: () => Promise; + setCancelled: (b: boolean) => void; + setSelecting: (b: boolean) => void; +}; + +export function View({ details, selectedExchange, accept, setCancelled, setSelecting }: ViewProps) { + + return ( + +
+

+ Taler Wallet +

+
+
+
+

Digital Cash Withdrawal

+

+ You are about to withdraw{" "} + {renderAmount(details.amount)} from your bank account + into your wallet. +

+ {selectedExchange ? ( +

+ The exchange {selectedExchange} will be used as the + Taler payment service provider. +

+ ) : null} + +
+ +

+ setSelecting(true)} + > + {i18n.str`Chose different exchange provider`} + +
+ setCancelled(true)} + > + {i18n.str`Cancel withdraw operation`} + +

+
+
+
+
+ ) +} + +export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element { + const [details, setDetails] = useState(undefined); + const [selectedExchange, setSelectedExchange] = useState(undefined); + const [cancelled, setCancelled] = useState(false); + const [selecting, setSelecting] = useState(false); + const [error, setError] = useState(false); + const [updateCounter, setUpdateCounter] = useState(1); + const [state, setState] = useState(1) + + useEffect(() => { + return onUpdateNotification(() => { + console.log('updating...') + setUpdateCounter(updateCounter + 1); + }); + }, []); + + useEffect(() => { + console.log('on effect yes', talerWithdrawUri) + if (!talerWithdrawUri) return + const fetchData = async (): Promise => { + try { + const res = await getWithdrawalDetailsForUri({ talerWithdrawUri }); + setDetails(res); + if (res.defaultExchangeBaseUrl) { + setSelectedExchange(res.defaultExchangeBaseUrl); + } + } catch (e) { + console.error('error',JSON.stringify(e,undefined,2)) + setError(true) + } + }; + fetchData(); + }, [selectedExchange, selecting, talerWithdrawUri, updateCounter, state]); + + if (!talerWithdrawUri) { + return missing withdraw uri; + } + + const accept = async (): Promise => { + if (!selectedExchange) { + throw Error("can't accept, no exchange selected"); + } + console.log("accepting exchange", selectedExchange); + const res = await acceptWithdrawal(talerWithdrawUri, selectedExchange); + console.log("accept withdrawal response", res); + if (res.confirmTransferUrl) { + document.location.href = res.confirmTransferUrl; + } + }; + + if (!details) { + return Loading...; + } + if (cancelled) { + return Withdraw operation has been cancelled.; + } + if (error) { + return This URI is not valid anymore.; + } + + return +} + diff --git a/packages/taler-wallet-webextension/src/cta/payback.tsx b/packages/taler-wallet-webextension/src/cta/payback.tsx new file mode 100644 index 000000000..4233b1f96 --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/payback.tsx @@ -0,0 +1,31 @@ +/* + This file is part of TALER + (C) 2017 Inria + + 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 { JSX } from "preact/jsx-runtime"; + +/** + * View and edit auditors. + * + * @author Florian Dold + */ + +/** + * Imports. + */ + +export function makePaybackPage(): JSX.Element { + return
not implemented
; +} diff --git a/packages/taler-wallet-webextension/src/cta/reset-required.tsx b/packages/taler-wallet-webextension/src/cta/reset-required.tsx new file mode 100644 index 000000000..87751561c --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/reset-required.tsx @@ -0,0 +1,97 @@ +/* + This file is part of TALER + (C) 2017 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 to inform the user when a database reset is required. + * + * @author Florian Dold + */ + +import { Component, JSX } from "preact"; +import * as wxApi from "../wxApi"; + +interface State { + /** + * Did the user check the confirmation check box? + */ + checked: boolean; + + /** + * Do we actually need to reset the db? + */ + resetRequired: boolean; +} + +class ResetNotification extends Component { + constructor(props: any) { + super(props); + this.state = { checked: false, resetRequired: true }; + setInterval(() => this.update(), 500); + } + async update(): Promise { + const res = await wxApi.checkUpgrade(); + this.setState({ resetRequired: res.dbResetRequired }); + } + render(): JSX.Element { + if (this.state.resetRequired) { + return ( +
+

Manual Reset Required

+

+ The wallet's database in your browser is incompatible with the{" "} + currently installed wallet. Please reset manually. +

+

+ Once the database format has stabilized, we will provide automatic + upgrades. +

+ { + this.setState(prev => ({ checked: prev.checked })) + }} + />{" "} + +
+ +
+ ); + } + return ( +
+

Everything is fine!

A reset is not required anymore, you can + close this page. +
+ ); + } +} + +/** + * @deprecated to be removed + */ +export function createResetRequiredPage(): JSX.Element { + return ; +} diff --git a/packages/taler-wallet-webextension/src/cta/return-coins.tsx b/packages/taler-wallet-webextension/src/cta/return-coins.tsx new file mode 100644 index 000000000..2273d1454 --- /dev/null +++ b/packages/taler-wallet-webextension/src/cta/return-coins.tsx @@ -0,0 +1,30 @@ +/* + This file is part of TALER + (C) 2017 Inria + + 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 { JSX } from "preact/jsx-runtime"; + +/** + * Return coins to own bank account. + * + * @author Florian Dold + */ + +/** + * Imports. + */ +export function createReturnCoinsPage(): JSX.Element { + return Not implemented yet.; +} diff --git a/packages/taler-wallet-webextension/src/popup/BackupPage.tsx b/packages/taler-wallet-webextension/src/popup/BackupPage.tsx index 940d1f2a4..72139e1f8 100644 --- a/packages/taler-wallet-webextension/src/popup/BackupPage.tsx +++ b/packages/taler-wallet-webextension/src/popup/BackupPage.tsx @@ -25,7 +25,7 @@ import { SmallText, SmallTextLight } from "../components/styled"; import { useBackupStatus } from "../hooks/useBackupStatus"; -import { Pages } from "./popup"; +import { Pages } from "../NavigationBar"; interface Props { onAddProvider: () => void; diff --git a/packages/taler-wallet-webextension/src/popup/History.tsx b/packages/taler-wallet-webextension/src/popup/History.tsx index b6b65314e..7c9eae54b 100644 --- a/packages/taler-wallet-webextension/src/popup/History.tsx +++ b/packages/taler-wallet-webextension/src/popup/History.tsx @@ -14,11 +14,19 @@ TALER; see the file COPYING. If not, see */ -import { AmountJson, Amounts, AmountString, Balance, Timestamp, Transaction, TransactionsResponse, TransactionType } from "@gnu-taler/taler-util"; +import { AmountString, Balance, Timestamp, Transaction, TransactionsResponse, TransactionType } from "@gnu-taler/taler-util"; +import { formatDistance } from "date-fns"; import { JSX } from "preact"; import { useEffect, useState } from "preact/hooks"; +import imageBank from '../../static/img/ri-bank-line.svg'; +import imageHandHeart from '../../static/img/ri-hand-heart-line.svg'; +import imageRefresh from '../../static/img/ri-refresh-line.svg'; +import imageRefund from '../../static/img/ri-refund-2-line.svg'; +import imageShoppingCart from '../../static/img/ri-shopping-cart-line.svg'; +import { Column, ExtraLargeText, HistoryRow, PopupBox, SmallTextLight } from "../components/styled"; +import { useBalances } from "../hooks/useBalances"; import * as wxApi from "../wxApi"; -import { Pages } from "./popup"; +import { Pages } from "../NavigationBar"; export function HistoryPage(props: any): JSX.Element { @@ -45,7 +53,7 @@ export function HistoryPage(props: any): JSX.Element { function amountToString(c: AmountString) { const idx = c.indexOf(':') - return `${c.substring(idx+1)} ${c.substring(0,idx)}` + return `${c.substring(idx + 1)} ${c.substring(0, idx)}` } @@ -68,20 +76,14 @@ export function HistoryView({ list, balances }: { list: Transaction[], balances: ))} } -import imageBank from '../../static/img/ri-bank-line.svg'; -import imageShoppingCart from '../../static/img/ri-shopping-cart-line.svg'; -import imageRefund from '../../static/img/ri-refund-2-line.svg'; -import imageHandHeart from '../../static/img/ri-hand-heart-line.svg'; -import imageRefresh from '../../static/img/ri-refresh-line.svg'; -import { Column, ExtraLargeText, HistoryRow, PopupBox, Row, RowBorderGray, SmallTextLight } from "../components/styled"; -import { useBalances } from "../hooks/useBalances"; -import { formatDistance } from "date-fns"; - function TransactionItem(props: { tx: Transaction }): JSX.Element { const tx = props.tx; switch (tx.type) { @@ -171,18 +173,16 @@ function TransactionLayout(props: TransactionLayoutProps): JSX.Element { const now = new Date(); const dateStr = formatDistance(date, now, { addSuffix: true }) return ( - + - {props.title} + {props.title} {props.pending ? ( (Pending) ) : null} {dateStr} - - {/*
{props.subtitle}
*/}
- */ - -/** - * Popup shown to the user when they click - * the Taler browser action button. - * - * @author Florian Dold - */ - -/** - * Imports. - */ -import { i18n } from "@gnu-taler/taler-util"; -import { ComponentChildren, JSX } from "preact"; -import Match from "preact-router/match"; -import { useDevContext } from "../context/devContext"; -import { PopupNavigation } from '../components/styled' - -export enum Pages { - balance = '/balance', - settings = '/settings', - dev = '/dev', - backup = '/backup', - history = '/history', - transaction = '/transaction/:tid', - provider_detail = '/provider/:pid', - provider_add = '/provider/add', -} - -interface TabProps { - target: string; - current?: string; - children?: ComponentChildren; -} - -function Tab(props: TabProps): JSX.Element { - let cssClass = ""; - if (props.current?.startsWith(props.target)) { - cssClass = "active"; - } - return ( - - {props.children} - - ); -} - -export function NavBar({devMode, path}:{path:string, devMode:boolean}) { - return - {i18n.str`Balance`} - {i18n.str`History`} - {i18n.str`Backup`} - {i18n.str`Settings`} - {devMode && {i18n.str`Dev`}} - -} - -export function WalletNavBar() { - const { devMode } = useDevContext() - return {({ path }: any) => } -} - diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx index faa5149ac..4a9fe9abc 100644 --- a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx +++ b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx @@ -34,7 +34,7 @@ import { DeveloperPage as DeveloperPage } from "./popup/Debug"; import { HistoryPage } from "./popup/History"; import { Pages, WalletNavBar -} from "./popup/popup"; +} from "./NavigationBar"; import { ProviderAddPage } from "./popup/ProviderAddPage"; import { ProviderDetailPage } from "./popup/ProviderDetailPage"; import { SettingsPage } from "./popup/Settings"; @@ -129,4 +129,4 @@ function Redirect({ to }: { to: string }): null { route(to, true) }) return null -} \ No newline at end of file +} diff --git a/packages/taler-wallet-webextension/src/test-utils.ts b/packages/taler-wallet-webextension/src/test-utils.ts new file mode 100644 index 000000000..16262b3c2 --- /dev/null +++ b/packages/taler-wallet-webextension/src/test-utils.ts @@ -0,0 +1,8 @@ +import { FunctionalComponent, h as render } from 'preact'; + +export function createExample(Component: FunctionalComponent, props: Partial) { + const r = (args: any) => render(Component, args) + r.args = props + return r +} + diff --git a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx new file mode 100644 index 000000000..f50fd3b68 --- /dev/null +++ b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx @@ -0,0 +1,294 @@ +/* + 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 { + PaymentStatus, + TransactionCommon, TransactionDeposit, TransactionPayment, + TransactionRefresh, TransactionRefund, TransactionTip, TransactionType, + TransactionWithdrawal, + WithdrawalType +} from '@gnu-taler/taler-util'; +import { FunctionalComponent } from 'preact'; +import { HistoryView as TestedComponent } from './History'; + +export default { + title: 'wallet/history/list', + component: TestedComponent, +}; + +let count = 0 +const commonTransaction = () => ({ + amountRaw: 'USD:10', + amountEffective: 'USD:9', + pending: false, + timestamp: { + t_ms: new Date().getTime() - (count++ * 1000*60*60*7) + }, + transactionId: '12', +} as TransactionCommon) + +const exampleData = { + withdraw: { + ...commonTransaction(), + type: TransactionType.Withdrawal, + exchangeBaseUrl: 'http://exchange.taler', + withdrawalDetails: { + confirmed: false, + exchangePaytoUris: ['payto://x-taler-bank/bank/account'], + type: WithdrawalType.ManualTransfer, + } + } as TransactionWithdrawal, + payment: { + ...commonTransaction(), + amountEffective: 'USD:11', + type: TransactionType.Payment, + info: { + contractTermsHash: 'ASDZXCASD', + merchant: { + name: 'the merchant', + }, + orderId: '2021.167-03NPY6MCYMVGT', + products: [], + summary: 'the summary', + fulfillmentMessage: '', + }, + proposalId: '1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0', + status: PaymentStatus.Accepted, + } as TransactionPayment, + deposit: { + ...commonTransaction(), + type: TransactionType.Deposit, + depositGroupId: '#groupId', + targetPaytoUri: 'payto://x-taler-bank/bank/account', + } as TransactionDeposit, + refresh: { + ...commonTransaction(), + type: TransactionType.Refresh, + exchangeBaseUrl: 'http://exchange.taler', + } as TransactionRefresh, + tip: { + ...commonTransaction(), + type: TransactionType.Tip, + merchantBaseUrl: 'http://merchant.taler', + } as TransactionTip, + refund: { + ...commonTransaction(), + type: TransactionType.Refund, + refundedTransactionId: 'payment:1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0', + info: { + contractTermsHash: 'ASDZXCASD', + merchant: { + name: 'the merchant', + }, + orderId: '2021.167-03NPY6MCYMVGT', + products: [], + summary: 'the summary', + fulfillmentMessage: '', + }, + } as TransactionRefund, +} + +function createExample(Component: FunctionalComponent, props: Partial) { + const r = (args: any) => + r.args = props + return r +} + +export const Empty = createExample(TestedComponent, { + list: [], + balances: [{ + available: 'TESTKUDOS:10', + pendingIncoming: 'TESTKUDOS:0', + pendingOutgoing: 'TESTKUDOS:0', + hasPendingTransactions: false, + requiresUserInput: false, + }] +}); + + +export const One = createExample(TestedComponent, { + list: [exampleData.withdraw], + balances: [{ + available: 'USD:10', + pendingIncoming: 'USD:0', + pendingOutgoing: 'USD:0', + hasPendingTransactions: false, + requiresUserInput: false, + }] +}); + +export const Several = createExample(TestedComponent, { + list: [ + exampleData.withdraw, + exampleData.payment, + exampleData.withdraw, + exampleData.payment, + exampleData.refresh, + exampleData.refund, + exampleData.tip, + exampleData.deposit, + ], + balances: [{ + available: 'TESTKUDOS:10', + pendingIncoming: 'TESTKUDOS:0', + pendingOutgoing: 'TESTKUDOS: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, + ], + balances: [{ + available: 'TESTKUDOS:10', + pendingIncoming: 'TESTKUDOS:0', + pendingOutgoing: 'TESTKUDOS:0', + hasPendingTransactions: false, + requiresUserInput: false, + },{ + available: 'USD:10', + pendingIncoming: 'USD:0', + pendingOutgoing: 'USD:0', + hasPendingTransactions: false, + requiresUserInput: false, + }] +}); + +// export const WithdrawPending = createExample(TestedComponent, { +// transaction: { ...exampleData.withdraw, pending: true }, +// }); + + +// export const Payment = createExample(TestedComponent, { +// transaction: exampleData.payment +// }); + +// export const PaymentWithoutFee = createExample(TestedComponent, { +// transaction: { +// ...exampleData.payment, +// amountRaw: 'USD:11', + +// } +// }); + +// export const PaymentPending = createExample(TestedComponent, { +// transaction: { ...exampleData.payment, pending: true }, +// }); + +// export const PaymentWithProducts = createExample(TestedComponent, { +// transaction: { +// ...exampleData.payment, +// info: { +// ...exampleData.payment.info, +// summary: 'this order has 5 products', +// products: [{ +// description: 't-shirt', +// unit: 'shirts', +// quantity: 1, +// }, { +// description: 't-shirt', +// unit: 'shirts', +// quantity: 1, +// }, { +// description: 'e-book', +// }, { +// description: 'beer', +// unit: 'pint', +// quantity: 15, +// }, { +// description: 'beer', +// unit: 'pint', +// quantity: 15, +// }] +// } +// } as TransactionPayment, +// }); + +// export const PaymentWithLongSummary = createExample(TestedComponent, { +// transaction: { +// ...exampleData.payment, +// info: { +// ...exampleData.payment.info, +// summary: 'this is a very long summary that will occupy severals lines, this is a very long summary that will occupy severals lines, this is a very long summary that will occupy severals lines, this is a very long summary that will occupy severals lines, ', +// products: [{ +// description: 'an xl sized t-shirt with some drawings on it, color pink', +// unit: 'shirts', +// quantity: 1, +// }, { +// description: 'beer', +// unit: 'pint', +// quantity: 15, +// }] +// } +// } as TransactionPayment, +// }); + + +// export const Deposit = createExample(TestedComponent, { +// transaction: exampleData.deposit +// }); + +// export const DepositPending = createExample(TestedComponent, { +// transaction: { ...exampleData.deposit, pending: true } +// }); + +// export const Refresh = createExample(TestedComponent, { +// transaction: exampleData.refresh +// }); + +// export const Tip = createExample(TestedComponent, { +// transaction: exampleData.tip +// }); + +// export const TipPending = createExample(TestedComponent, { +// transaction: { ...exampleData.tip, pending: true } +// }); + +// export const Refund = createExample(TestedComponent, { +// transaction: exampleData.refund +// }); + +// export const RefundPending = createExample(TestedComponent, { +// transaction: { ...exampleData.refund, pending: true } +// }); + +// export const RefundWithProducts = createExample(TestedComponent, { +// transaction: { +// ...exampleData.refund, +// info: { +// ...exampleData.refund.info, +// products: [{ +// description: 't-shirt', +// }, { +// description: 'beer', +// }] +// } +// } as TransactionRefund, +// }); diff --git a/packages/taler-wallet-webextension/src/wallet/History.tsx b/packages/taler-wallet-webextension/src/wallet/History.tsx new file mode 100644 index 000000000..6ef5047ae --- /dev/null +++ b/packages/taler-wallet-webextension/src/wallet/History.tsx @@ -0,0 +1,248 @@ +/* + 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 { AmountString, Balance, Timestamp, Transaction, TransactionsResponse, TransactionType } from "@gnu-taler/taler-util"; +import { format } from "date-fns"; +import { Fragment, JSX } from "preact"; +import { useEffect, useState } from "preact/hooks"; +import imageBank from '../../static/img/ri-bank-line.svg'; +import imageHandHeart from '../../static/img/ri-hand-heart-line.svg'; +import imageRefresh from '../../static/img/ri-refresh-line.svg'; +import imageRefund from '../../static/img/ri-refund-2-line.svg'; +import imageShoppingCart from '../../static/img/ri-shopping-cart-line.svg'; +import { Column, ExtraLargeText, HistoryRow, WalletBox, DateSeparator, SmallTextLight } from "../components/styled"; +import { useBalances } from "../hooks/useBalances"; +import * as wxApi from "../wxApi"; +import { Pages } from "../NavigationBar"; + + +export function HistoryPage(props: any): JSX.Element { + const [transactions, setTransactions] = useState< + TransactionsResponse | undefined + >(undefined); + const balance = useBalances() + const balanceWithoutError = balance?.error ? [] : (balance?.response.balances || []) + + useEffect(() => { + const fetchData = async (): Promise => { + const res = await wxApi.getTransactions(); + setTransactions(res); + }; + fetchData(); + }, []); + + if (!transactions) { + return
Loading ...
; + } + + return ; +} + +function amountToString(c: AmountString) { + const idx = c.indexOf(':') + return `${c.substring(idx + 1)} ${c.substring(0, idx)}` +} + + + +export function HistoryView({ list, balances }: { list: Transaction[], balances: Balance[] }) { + const byDate = list.reduce(function (rv, x) { + const theDate = x.timestamp.t_ms === "never" ? "never" : format(x.timestamp.t_ms, 'dd MMMM yyyy'); + (rv[theDate] = rv[theDate] || []).push(x); + return rv; + }, {} as { [x: string]: Transaction[] }); + + return + {balances.length > 0 &&
+ {balances.length === 1 &&
+ Balance: {amountToString(balances[0].available)} +
} + {balances.length > 1 &&
+ Balance:
    + {balances.map(b =>
  • {b.available}
  • )} +
+
} +
} +
+ {Object.keys(byDate).map(d => { + return + {d} + {byDate[d].map((tx, i) => ( + + ))} + + })} +
+
+} + +function TransactionItem(props: { tx: Transaction }): JSX.Element { + const tx = props.tx; + switch (tx.type) { + case TransactionType.Withdrawal: + return ( + + ); + case TransactionType.Payment: + return ( + + ); + case TransactionType.Refund: + return ( + + ); + case TransactionType.Tip: + return ( + + ); + case TransactionType.Refresh: + return ( + + ); + case TransactionType.Deposit: + return ( + + ); + } +} + +function TransactionLayout(props: TransactionLayoutProps): JSX.Element { + const date = new Date(props.timestamp.t_ms); + const dateStr = format(date, 'HH:mm:ss') + return ( + // + + + + + {props.title} + {props.pending ? ( + (Pending) + ) : null} + + {dateStr} + + + + // + ); +} + +interface TransactionLayoutProps { + debitCreditIndicator: "debit" | "credit" | "unknown"; + amount: AmountString | "unknown"; + timestamp: Timestamp; + title: string; + id: string; + subtitle: string; + iconPath: string; + pending: boolean; +} + +interface TransactionAmountProps { + debitCreditIndicator: "debit" | "credit" | "unknown"; + amount: AmountString | "unknown"; + pending: boolean; +} + +function TransactionAmount(props: TransactionAmountProps): JSX.Element { + const [currency, amount] = props.amount.split(":"); + let sign: string; + switch (props.debitCreditIndicator) { + case "credit": + sign = "+"; + break; + case "debit": + sign = "-"; + break; + case "unknown": + sign = ""; + } + return ( + + + {sign} + {amount} + +
{currency}
+
+ ); +} + diff --git a/packages/taler-wallet-webextension/src/wallet/Pay.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Pay.stories.tsx deleted file mode 100644 index 0297d6264..000000000 --- a/packages/taler-wallet-webextension/src/wallet/Pay.stories.tsx +++ /dev/null @@ -1,103 +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 { ContractTerms, PreparePayResultType } from '@gnu-taler/taler-util'; -import { FunctionalComponent, h } from 'preact'; -import { PaymentRequestView as TestedComponent } from './Pay'; - - -export default { - title: 'wallet/pay', - component: TestedComponent, - argTypes: { - }, -}; - -function createExample(Component: FunctionalComponent, props: Partial) { - const r = (args: any) => - r.args = props - return r -} - -export const InsufficientBalance = createExample(TestedComponent, { - payStatus: { - status: PreparePayResultType.InsufficientBalance, - proposalId: "proposal1234", - contractTerms: { - merchant: { - name: 'someone' - }, - amount: 'USD:10', - } as Partial as any, - amountRaw: 'USD:10', - } -}); - -export const PaymentPossible = createExample(TestedComponent, { - payStatus: { - status: PreparePayResultType.PaymentPossible, - amountEffective: 'USD:10', - amountRaw: 'USD:10', - contractTerms: { - merchant: { - name: 'someone' - }, - amount: 'USD:10', - } 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! ', - 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' - }, - amount: 'USD:10', - } as Partial as any, - contractTermsHash: '123456', - proposalId: 'proposal1234', - paid: false, - } -}); diff --git a/packages/taler-wallet-webextension/src/wallet/Pay.tsx b/packages/taler-wallet-webextension/src/wallet/Pay.tsx deleted file mode 100644 index a5849bb28..000000000 --- a/packages/taler-wallet-webextension/src/wallet/Pay.tsx +++ /dev/null @@ -1,253 +0,0 @@ -/* - 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 { renderAmount, ProgressButton } from "../renderHtml"; -import * as wxApi from "../wxApi"; - -import { useState, useEffect } from "preact/hooks"; - -import { ConfirmPayResultDone, getJsonI18n, i18n } from "@gnu-taler/taler-util"; -import { - PreparePayResult, - ConfirmPayResult, - AmountJson, - PreparePayResultType, - Amounts, - ContractTerms, - ConfirmPayResultType, -} from "@gnu-taler/taler-util"; -import { JSX, VNode } from "preact"; - -interface Props { - talerPayUri?: string -} - -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 => { - if (payStatus.status !== "payment-possible") { - throw Error(`invalid state: ${payStatus.status}`); - } - const proposalId = payStatus.proposalId; - const res = await wxApi.confirmPay(proposalId, undefined); - if (res.type !== ConfirmPayResultType.Done) { - throw Error("payment pending"); - } - const fu = res.contractTerms.fulfillment_url; - if (fu) { - document.location.href = fu; - } - return res; -}; - - - -export function PayPage({ talerPayUri }: Props): JSX.Element { - const [payStatus, setPayStatus] = useState(undefined); - const [payResult, setPayResult] = useState(undefined); - const [payErrMsg, setPayErrMsg] = useState(""); - - useEffect(() => { - if (!talerPayUri) return; - const doFetch = async (): Promise => { - const p = await wxApi.preparePay(talerPayUri); - setPayStatus(p); - }; - doFetch(); - }, [talerPayUri]); - - if (!talerPayUri) { - return missing pay uri - } - - if (!payStatus) { - return Loading payment information ...; - } - - if (payResult && payResult.type === ConfirmPayResultType.Done) { - if (payResult.contractTerms.fulfillment_message) { - const obj = { - fulfillment_message: payResult.contractTerms.fulfillment_message, - fulfillment_message_i18n: - payResult.contractTerms.fulfillment_message_i18n, - }; - const msg = getJsonI18n(obj, "fulfillment_message"); - return ( -
-

Payment succeeded.

-

{msg}

-
- ); - } else { - return Redirecting ...; - } - } - - const onClick = async () => { - try { - const res = await doPayment(payStatus) - setPayResult(res); - } catch (e) { - console.error(e); - setPayErrMsg(e.message); - } - - } - - return ; -} - -export interface PaymentRequestViewProps { - payStatus: PreparePayResult; - onClick: () => void; - payErrMsg?: string; - -} -export function PaymentRequestView({ payStatus, onClick, payErrMsg }: PaymentRequestViewProps) { - let totalFees: AmountJson | undefined = undefined; - let insufficientBalance = false; - const [loading, setLoading] = useState(false); - const contractTerms: ContractTerms = payStatus.contractTerms; - - if ( - payStatus.status === PreparePayResultType.AlreadyConfirmed - ) { - return - } - - if (!contractTerms) { - return ( - - Error: did not get contract terms from merchant or wallet backend. - - ); - } - - if (payStatus.status == PreparePayResultType.InsufficientBalance) { - insufficientBalance = true; - } - - if (payStatus.status === PreparePayResultType.PaymentPossible) { - const amountRaw = Amounts.parseOrThrow(payStatus.amountRaw); - const amountEffective: AmountJson = Amounts.parseOrThrow( - payStatus.amountEffective, - ); - totalFees = Amounts.sub(amountEffective, amountRaw).amount; - } - - let merchantName: VNode; - if (contractTerms.merchant && contractTerms.merchant.name) { - merchantName = {contractTerms.merchant.name}; - } else { - merchantName = (pub: {contractTerms.merchant_pub}); - } - - const amount = ( - {renderAmount(Amounts.parseOrThrow(contractTerms.amount))} - ); - - return
-

GNU Taler Wallet

-
-
-

- - The merchant {merchantName} offers you to purchase: - -

- {contractTerms.summary} -
- {totalFees ? ( - - The total price is {amount} - (plus {renderAmount(totalFees)} fees). - - ) : ( - - The total price is {amount}. - - )} -

- - {insufficientBalance ? ( -
-

- Unable to pay: Your balance is insufficient. -

-
- ) : null} - - {payErrMsg ? ( -
-

Payment failed: {payErrMsg}

- -
- ) : ( -
- - {i18n.str`Confirm payment`} - -
- )} -
-
-
- - -} \ No newline at end of file diff --git a/packages/taler-wallet-webextension/src/wallet/Refund.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Refund.stories.tsx deleted file mode 100644 index 044141f0c..000000000 --- a/packages/taler-wallet-webextension/src/wallet/Refund.stories.tsx +++ /dev/null @@ -1,83 +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 { ContractTerms, OrderShortInfo, PreparePayResultType } from '@gnu-taler/taler-util'; -import { FunctionalComponent, h } from 'preact'; -import { View as TestedComponent } from './Refund'; - - -export default { - title: 'wallet/refund', - component: TestedComponent, - argTypes: { - }, -}; - -function createExample(Component: FunctionalComponent, props: Partial) { - const r = (args: any) => - r.args = props - return r -} - -export const Complete = createExample(TestedComponent, { - applyResult: { - amountEffectivePaid: 'USD:10', - amountRefundGone: 'USD:0', - amountRefundGranted: 'USD:2', - contractTermsHash: 'QWEASDZXC', - info: { - summary: 'tasty cold beer', - contractTermsHash: 'QWEASDZXC', - } as Partial as any, - pendingAtExchange: false, - proposalId: "proposal123", - } -}); - -export const Partial = createExample(TestedComponent, { - applyResult: { - amountEffectivePaid: 'USD:10', - amountRefundGone: 'USD:1', - amountRefundGranted: 'USD:2', - contractTermsHash: 'QWEASDZXC', - info: { - summary: 'tasty cold beer', - contractTermsHash: 'QWEASDZXC', - } as Partial as any, - pendingAtExchange: false, - proposalId: "proposal123", - } -}); - -export const InProgress = createExample(TestedComponent, { - applyResult: { - amountEffectivePaid: 'USD:10', - amountRefundGone: 'USD:1', - amountRefundGranted: 'USD:2', - contractTermsHash: 'QWEASDZXC', - info: { - summary: 'tasty cold beer', - contractTermsHash: 'QWEASDZXC', - } as Partial as any, - pendingAtExchange: true, - proposalId: "proposal123", - } -}); diff --git a/packages/taler-wallet-webextension/src/wallet/Refund.tsx b/packages/taler-wallet-webextension/src/wallet/Refund.tsx deleted file mode 100644 index bb26d933b..000000000 --- a/packages/taler-wallet-webextension/src/wallet/Refund.tsx +++ /dev/null @@ -1,95 +0,0 @@ -/* - This file is part of TALER - (C) 2015-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 - */ - -/** - * Page that shows refund status for purchases. - * - * @author Florian Dold - */ - -import * as wxApi from "../wxApi"; -import { AmountView } from "../renderHtml"; -import { - ApplyRefundResponse, - Amounts, -} from "@gnu-taler/taler-util"; -import { useEffect, useState } from "preact/hooks"; -import { JSX } from "preact/jsx-runtime"; - -interface Props { - talerRefundUri?: string -} -export interface ViewProps { - applyResult: ApplyRefundResponse; -} -export function View({ applyResult }: ViewProps) { - return
-

GNU Taler Wallet

-
-

Refund Status

-

- The product {applyResult.info.summary} has received a total - effective refund of{" "} - . -

- {applyResult.pendingAtExchange ? ( -

Refund processing is still in progress.

- ) : null} - {!Amounts.isZero(applyResult.amountRefundGone) ? ( -

- The refund amount of{" "} - {" "} - could not be applied. -

- ) : null} -
-
-} -export function RefundPage({ talerRefundUri }: Props): JSX.Element { - const [applyResult, setApplyResult] = useState(undefined); - const [errMsg, setErrMsg] = useState(undefined); - - useEffect(() => { - if (!talerRefundUri) return; - const doFetch = async (): Promise => { - try { - const result = await wxApi.applyRefund(talerRefundUri); - setApplyResult(result); - } catch (e) { - console.error(e); - setErrMsg(e.message); - console.log("err message", e.message); - } - }; - doFetch(); - }, [talerRefundUri]); - - console.log("rendering"); - - if (!talerRefundUri) { - return missing taler refund uri; - } - - if (errMsg) { - return Error: {errMsg}; - } - - if (!applyResult) { - return Updating refund status; - } - - return ; -} diff --git a/packages/taler-wallet-webextension/src/wallet/Tip.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Tip.stories.tsx deleted file mode 100644 index ffd976144..000000000 --- a/packages/taler-wallet-webextension/src/wallet/Tip.stories.tsx +++ /dev/null @@ -1,66 +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 { ContractTerms, PreparePayResultType } from '@gnu-taler/taler-util'; -import { FunctionalComponent, h } from 'preact'; -import { View as TestedComponent } from './Tip'; - - -export default { - title: 'wallet/tip', - component: TestedComponent, - argTypes: { - }, -}; - -function createExample(Component: FunctionalComponent, props: Partial) { - const r = (args: any) => - r.args = props - return r -} - -export const Accepted = createExample(TestedComponent, { - prepareTipResult: { - accepted: true, - merchantBaseUrl: '', - exchangeBaseUrl: '', - expirationTimestamp : { - t_ms: 0 - }, - tipAmountEffective: 'USD:10', - tipAmountRaw: 'USD:5', - walletTipId: 'id' - } -}); - -export const NotYetAccepted = createExample(TestedComponent, { - prepareTipResult: { - accepted: false, - merchantBaseUrl: 'http://merchant.url/', - exchangeBaseUrl: 'http://exchange.url/', - expirationTimestamp : { - t_ms: 0 - }, - tipAmountEffective: 'USD:10', - tipAmountRaw: 'USD:5', - walletTipId: 'id' - } -}); diff --git a/packages/taler-wallet-webextension/src/wallet/Tip.tsx b/packages/taler-wallet-webextension/src/wallet/Tip.tsx deleted file mode 100644 index 69886668b..000000000 --- a/packages/taler-wallet-webextension/src/wallet/Tip.tsx +++ /dev/null @@ -1,110 +0,0 @@ -/* - This file is part of TALER - (C) 2017 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 accept or ignore a tip from a merchant. - * - * @author Florian Dold - */ - -import { useEffect, useState } from "preact/hooks"; -import { PrepareTipResult } from "@gnu-taler/taler-util"; -import { AmountView } from "../renderHtml"; -import * as wxApi from "../wxApi"; -import { JSX } from "preact/jsx-runtime"; - -interface Props { - talerTipUri?: string -} -export interface ViewProps { - prepareTipResult: PrepareTipResult; - onAccept: () => void; - onIgnore: () => void; - -} -export function View({ prepareTipResult, onAccept, onIgnore }: ViewProps) { - return
-

GNU Taler Wallet

-
- {prepareTipResult.accepted ? ( - - Tip from {prepareTipResult.merchantBaseUrl} accepted. Check - your transactions list for more details. - - ) : ( -
-

- The merchant {prepareTipResult.merchantBaseUrl} is - offering you a tip of{" "} - - - {" "} - via the exchange {prepareTipResult.exchangeBaseUrl} -

- - -
- )} -
-
- -} - -export function TipPage({ talerTipUri }: Props): JSX.Element { - const [updateCounter, setUpdateCounter] = useState(0); - const [prepareTipResult, setPrepareTipResult] = useState< - PrepareTipResult | undefined - >(undefined); - - const [tipIgnored, setTipIgnored] = useState(false); - - useEffect(() => { - if (!talerTipUri) return; - const doFetch = async (): Promise => { - const p = await wxApi.prepareTip({ talerTipUri }); - setPrepareTipResult(p); - }; - doFetch(); - }, [talerTipUri, updateCounter]); - - const doAccept = async () => { - if (!prepareTipResult) { - return; - } - await wxApi.acceptTip({ walletTipId: prepareTipResult?.walletTipId }); - setUpdateCounter(updateCounter + 1); - }; - - const doIgnore = () => { - setTipIgnored(true); - }; - - if (!talerTipUri) { - return missing tip uri; - } - - if (tipIgnored) { - return You've ignored the tip.; - } - - if (!prepareTipResult) { - return Loading ...; - } - - return -} diff --git a/packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx index 4fa87a137..6579450b3 100644 --- a/packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { FunctionalComponent, h } from 'preact'; +import { createExample } from '../test-utils'; import { View as TestedComponent } from './Welcome'; @@ -28,12 +28,6 @@ export default { component: TestedComponent, }; -function createExample(Component: FunctionalComponent, props: Partial) { - const r = (args: any) => - r.args = props - return r -} - export const Normal = createExample(TestedComponent, { permissionsEnabled: true, diagnostics: { diff --git a/packages/taler-wallet-webextension/src/wallet/Welcome.tsx b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx index 4c33e1c72..0738e14b6 100644 --- a/packages/taler-wallet-webextension/src/wallet/Welcome.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx @@ -24,7 +24,7 @@ import { JSX } from "preact/jsx-runtime"; import { Checkbox } from "../components/Checkbox"; import { useExtendedPermissions } from "../hooks/useExtendedPermissions"; import { Diagnostics } from "../components/Diagnostics"; -import { WalletPage } from "../components/styled"; +import { WalletBox } from "../components/styled"; import { useDiagnostics } from "../hooks/useDiagnostics"; import { WalletDiagnostics } from "@gnu-taler/taler-util"; @@ -44,12 +44,7 @@ export interface ViewProps { timedOut: boolean, } export function View({ permissionsEnabled, togglePermissions, diagnostics, timedOut }: ViewProps): JSX.Element { - return ( -
-

- Taler Wallet -

-
+ return (

Browser Extension Installed!

Thank you for installing the wallet.

@@ -68,6 +63,6 @@ export function View({ permissionsEnabled, togglePermissions, diagnostics, timed Learn how to top up your wallet balance »
-
+ ); } diff --git a/packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx deleted file mode 100644 index fef36b820..000000000 --- a/packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx +++ /dev/null @@ -1,50 +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 { h } from 'preact'; -import { View, ViewProps } from './Withdraw'; - - -export default { - title: 'wallet/withdraw', - component: View, - argTypes: { - }, -}; - -export const WithoutDetails = (a: any) => ; -WithoutDetails.args = { -} as ViewProps - -export const CompleteWithExchange = (a: any) => ; -CompleteWithExchange.args = { - details: { - amount: 'USD:2', - }, - selectedExchange: 'Some exchange' -} as ViewProps - -export const CompleteWithoutExchange = (a: any) => ; -CompleteWithoutExchange.args = { - details: { - amount: 'USD:2', - }, -} as ViewProps diff --git a/packages/taler-wallet-webextension/src/wallet/Withdraw.tsx b/packages/taler-wallet-webextension/src/wallet/Withdraw.tsx deleted file mode 100644 index 442ee7dae..000000000 --- a/packages/taler-wallet-webextension/src/wallet/Withdraw.tsx +++ /dev/null @@ -1,178 +0,0 @@ -/* - This file is part of TALER - (C) 2015-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 - */ - -/** - * Page shown to the user to confirm creation - * of a reserve, usually requested by the bank. - * - * @author Florian Dold - */ - -import { i18n } from '@gnu-taler/taler-util' -import { renderAmount } from "../renderHtml"; - -import { useState, useEffect } from "preact/hooks"; -import { - acceptWithdrawal, - onUpdateNotification, - getWithdrawalDetailsForUri, -} from "../wxApi"; -import { WithdrawUriInfoResponse } from "@gnu-taler/taler-util"; -import { JSX } from "preact/jsx-runtime"; -import { WalletPage } from '../components/styled'; - -interface Props { - talerWithdrawUri?: string; -} - -export interface ViewProps { - talerWithdrawUri?: string; - details: WithdrawUriInfoResponse; - selectedExchange?: string; - accept: () => Promise; - setCancelled: (b: boolean) => void; - setSelecting: (b: boolean) => void; -}; - -export function View({ details, selectedExchange, accept, setCancelled, setSelecting }: ViewProps) { - - return ( - -
-

- Taler Wallet -

-
-
-
-

Digital Cash Withdrawal

-

- You are about to withdraw{" "} - {renderAmount(details.amount)} from your bank account - into your wallet. -

- {selectedExchange ? ( -

- The exchange {selectedExchange} will be used as the - Taler payment service provider. -

- ) : null} - -
- -

- setSelecting(true)} - > - {i18n.str`Chose different exchange provider`} - -
- setCancelled(true)} - > - {i18n.str`Cancel withdraw operation`} - -

-
-
-
-
- ) -} - -export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element { - const [details, setDetails] = useState(undefined); - const [selectedExchange, setSelectedExchange] = useState< - string | undefined - >(undefined); - const [cancelled, setCancelled] = useState(false); - const [selecting, setSelecting] = useState(false); - const [errMsg, setErrMsg] = useState(""); - const [updateCounter, setUpdateCounter] = useState(1); - const [state, setState] = useState(1) - - // setTimeout(() => { - // console.log('tick...') - // setState(s => s + 1) - // }, 1000); - - useEffect(() => { - return onUpdateNotification(() => { - console.log('updating...') - setUpdateCounter(updateCounter + 1); - }); - }, []); - - useEffect(() => { - console.log('on effect yes', talerWithdrawUri) - if (!talerWithdrawUri) return - const fetchData = async (): Promise => { - console.log('que pasa') - try { - const res = await getWithdrawalDetailsForUri({ talerWithdrawUri }); - console.log('res', res) - setDetails(res); - if (res.defaultExchangeBaseUrl) { - setSelectedExchange(res.defaultExchangeBaseUrl); - } - } catch (e) { - console.error(e) - } - }; - fetchData(); - }, [selectedExchange, errMsg, selecting, talerWithdrawUri, updateCounter, state]); - - if (!talerWithdrawUri) { - return missing withdraw uri; - } - - const accept = async (): Promise => { - if (!selectedExchange) { - throw Error("can't accept, no exchange selected"); - } - console.log("accepting exchange", selectedExchange); - const res = await acceptWithdrawal(talerWithdrawUri, selectedExchange); - console.log("accept withdrawal response", res); - if (res.confirmTransferUrl) { - document.location.href = res.confirmTransferUrl; - } - }; - - if (!details) { - return Loading...; - } - if (cancelled) { - return Withdraw operation has been cancelled.; - } - - return -} - diff --git a/packages/taler-wallet-webextension/src/wallet/payback.tsx b/packages/taler-wallet-webextension/src/wallet/payback.tsx deleted file mode 100644 index 4233b1f96..000000000 --- a/packages/taler-wallet-webextension/src/wallet/payback.tsx +++ /dev/null @@ -1,31 +0,0 @@ -/* - This file is part of TALER - (C) 2017 Inria - - 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 { JSX } from "preact/jsx-runtime"; - -/** - * View and edit auditors. - * - * @author Florian Dold - */ - -/** - * Imports. - */ - -export function makePaybackPage(): JSX.Element { - return
not implemented
; -} diff --git a/packages/taler-wallet-webextension/src/wallet/reset-required.tsx b/packages/taler-wallet-webextension/src/wallet/reset-required.tsx deleted file mode 100644 index 87751561c..000000000 --- a/packages/taler-wallet-webextension/src/wallet/reset-required.tsx +++ /dev/null @@ -1,97 +0,0 @@ -/* - This file is part of TALER - (C) 2017 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 to inform the user when a database reset is required. - * - * @author Florian Dold - */ - -import { Component, JSX } from "preact"; -import * as wxApi from "../wxApi"; - -interface State { - /** - * Did the user check the confirmation check box? - */ - checked: boolean; - - /** - * Do we actually need to reset the db? - */ - resetRequired: boolean; -} - -class ResetNotification extends Component { - constructor(props: any) { - super(props); - this.state = { checked: false, resetRequired: true }; - setInterval(() => this.update(), 500); - } - async update(): Promise { - const res = await wxApi.checkUpgrade(); - this.setState({ resetRequired: res.dbResetRequired }); - } - render(): JSX.Element { - if (this.state.resetRequired) { - return ( -
-

Manual Reset Required

-

- The wallet's database in your browser is incompatible with the{" "} - currently installed wallet. Please reset manually. -

-

- Once the database format has stabilized, we will provide automatic - upgrades. -

- { - this.setState(prev => ({ checked: prev.checked })) - }} - />{" "} - -
- -
- ); - } - return ( -
-

Everything is fine!

A reset is not required anymore, you can - close this page. -
- ); - } -} - -/** - * @deprecated to be removed - */ -export function createResetRequiredPage(): JSX.Element { - return ; -} diff --git a/packages/taler-wallet-webextension/src/wallet/return-coins.tsx b/packages/taler-wallet-webextension/src/wallet/return-coins.tsx deleted file mode 100644 index 2273d1454..000000000 --- a/packages/taler-wallet-webextension/src/wallet/return-coins.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/* - This file is part of TALER - (C) 2017 Inria - - 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 { JSX } from "preact/jsx-runtime"; - -/** - * Return coins to own bank account. - * - * @author Florian Dold - */ - -/** - * Imports. - */ -export function createReturnCoinsPage(): JSX.Element { - return Not implemented yet.; -} diff --git a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx index f487e54fc..f8191a0fb 100644 --- a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx +++ b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx @@ -20,17 +20,24 @@ * @author Florian Dold */ -import { render } from "preact"; +import { Fragment, render } from "preact"; import { setupI18n } from "@gnu-taler/taler-util"; import { strings } from "./i18n/strings"; import { createHashHistory } from 'history'; -import { WithdrawPage } from "./wallet/Withdraw"; import { WelcomePage } from "./wallet/Welcome"; -import { PayPage } from "./wallet/Pay"; -import { RefundPage } from "./wallet/Refund"; -import { TipPage } from './wallet/Tip'; +import { HistoryPage } from "./wallet/History"; +import { WithdrawPage } from "./cta/Withdraw"; +import { PayPage } from "./cta/Pay"; +import { RefundPage } from "./cta/Refund"; +import { TipPage } from './cta/Tip'; import Router, { route, Route } from "preact-router"; +import { DevContextProvider } from "./context/devContext"; +import { LogoHeader } from "./components/LogoHeader"; +import { useEffect } from "preact/hooks"; +import { + Pages, WalletNavBar +} from "./NavigationBar"; function main(): void { try { @@ -53,32 +60,43 @@ if (document.readyState === "loading") { main(); } - -enum Pages { - welcome = '/welcome', - pay = '/pay', - payback = '/payback', - refund = '/refund', - reset_required = '/reset-required', - return_coins = '/return-coins', - tips = '/tips', - withdraw = '/withdraw', +function withLogoAndNavBar(Component: any) { + return () => + + + + } function Application() { - const h = createHashHistory(); - return + return
+ + + + - - - + + - - +
no yet implemented
} /> +
no yet implemented
} /> +
no yet implemented
} /> -
no yet implemented
} /> -
no yet implemented
} /> -
no yet implemented
} /> + {/** call to action */} + + + + + + +
+
+
+} -
+function Redirect({ to }: { to: string }): null { + useEffect(() => { + route(to, true) + }) + return null } diff --git a/packages/taler-wallet-webextension/static/img/logo-2021.svg b/packages/taler-wallet-webextension/static/img/logo-2021.svg new file mode 100644 index 000000000..e72611eba --- /dev/null +++ b/packages/taler-wallet-webextension/static/img/logo-2021.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/taler-wallet-webextension/static/style/wallet.css b/packages/taler-wallet-webextension/static/style/wallet.css index 32a96dbc4..859789149 100644 --- a/packages/taler-wallet-webextension/static/style/wallet.css +++ b/packages/taler-wallet-webextension/static/style/wallet.css @@ -2,6 +2,7 @@ body { font-size: 100%; overflow-y: scroll; margin-top: 2em; + font-family: Arial, Helvetica, sans-serif; } .wallet-container { -- cgit v1.2.3