taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 97a05ff659af274dcfcd9c76bf19100bbd51ce0e
parent b015f76e7268cb5caff14a0ed88cb5e8fa53dc2e
Author: Sebastian <sebasjm@gmail.com>
Date:   Thu, 19 Aug 2021 00:34:47 -0300

new wallet history and view refactoring

Diffstat:
Mpackages/taler-wallet-webextension/.storybook/preview.js | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Mpackages/taler-wallet-webextension/package.json | 2+-
Mpackages/taler-wallet-webextension/rollup.config.js | 11+++++------
Apackages/taler-wallet-webextension/src/NavigationBar.tsx | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apackages/taler-wallet-webextension/src/components/LogoHeader.tsx | 14++++++++++++++
Mpackages/taler-wallet-webextension/src/components/styled/index.tsx | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Apackages/taler-wallet-webextension/src/cta/Pay.stories.tsx | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rpackages/taler-wallet-webextension/src/wallet/Pay.tsx -> packages/taler-wallet-webextension/src/cta/Pay.tsx | 0
Apackages/taler-wallet-webextension/src/cta/Refund.stories.tsx | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rpackages/taler-wallet-webextension/src/wallet/Refund.tsx -> packages/taler-wallet-webextension/src/cta/Refund.tsx | 0
Apackages/taler-wallet-webextension/src/cta/Tip.stories.tsx | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rpackages/taler-wallet-webextension/src/wallet/Tip.tsx -> packages/taler-wallet-webextension/src/cta/Tip.tsx | 0
Apackages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx | 45+++++++++++++++++++++++++++++++++++++++++++++
Apackages/taler-wallet-webextension/src/cta/Withdraw.tsx | 172+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rpackages/taler-wallet-webextension/src/wallet/payback.tsx -> packages/taler-wallet-webextension/src/cta/payback.tsx | 0
Rpackages/taler-wallet-webextension/src/wallet/reset-required.tsx -> packages/taler-wallet-webextension/src/cta/reset-required.tsx | 0
Rpackages/taler-wallet-webextension/src/wallet/return-coins.tsx -> packages/taler-wallet-webextension/src/cta/return-coins.tsx | 0
Mpackages/taler-wallet-webextension/src/popup/BackupPage.tsx | 2+-
Mpackages/taler-wallet-webextension/src/popup/History.tsx | 34+++++++++++++++++-----------------
Mpackages/taler-wallet-webextension/src/popup/Popup.stories.tsx | 2+-
Mpackages/taler-wallet-webextension/src/popup/Transaction.tsx | 2+-
Dpackages/taler-wallet-webextension/src/popup/popup.tsx | 76----------------------------------------------------------------------------
Mpackages/taler-wallet-webextension/src/popupEntryPoint.tsx | 5++---
Apackages/taler-wallet-webextension/src/test-utils.ts | 8++++++++
Apackages/taler-wallet-webextension/src/wallet/History.stories.tsx | 294+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apackages/taler-wallet-webextension/src/wallet/History.tsx | 248+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dpackages/taler-wallet-webextension/src/wallet/Pay.stories.tsx | 103-------------------------------------------------------------------------------
Dpackages/taler-wallet-webextension/src/wallet/Refund.stories.tsx | 83-------------------------------------------------------------------------------
Dpackages/taler-wallet-webextension/src/wallet/Tip.stories.tsx | 66------------------------------------------------------------------
Mpackages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx | 8+-------
Mpackages/taler-wallet-webextension/src/wallet/Welcome.tsx | 11+++--------
Dpackages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx | 50--------------------------------------------------
Dpackages/taler-wallet-webextension/src/wallet/Withdraw.tsx | 178-------------------------------------------------------------------------------
Mpackages/taler-wallet-webextension/src/walletEntryPoint.tsx | 70++++++++++++++++++++++++++++++++++++++++++++--------------------------
Apackages/taler-wallet-webextension/static/img/logo-2021.svg | 2++
Mpackages/taler-wallet-webextension/static/style/wallet.css | 1+
36 files changed, 1357 insertions(+), 647 deletions(-)

diff --git 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 <div style={{ width: 400, height: 320 }}> <Story /> </div> - } else { - const path = !isTestingHeader ? /popup(\/.*).*/.exec(kind)[1] : '' - // add a fake header so it looks similar - return <Fragment> - <NavBar path={path} devMode={path === '/dev'} /> - <div style={{ width: 400, height: 290 }}> - <Story /> - </div> - </Fragment> } + + const path = /popup(\/.*).*/.exec(kind)[1]; + // add a fake header so it looks similar + return <Fragment> + <NavBar path={path} devMode={path === '/dev'} /> + <div style={{ width: 400, height: 290 }}> + <Story /> + </div> + </Fragment> } return <div class="popup-container"> @@ -95,11 +96,71 @@ export const decorators = [ </div> </div> } - if (kind.startsWith('wallet')) { - return <div class="wallet-container"> + if (kind.startsWith('cta')) { + return <div> + <style>{` + html { + font-family: sans-serif; /* 1 */ + } + body { + margin: 0; + }`} + </style> + <style>{` + html { + } + h1 { + font-size: 2em; + } + input { + font: inherit; + } + body { + margin: 0; + font-size: 100%; + padding: 0; + background-color: #f8faf7; + font-family: Arial, Helvetica, sans-serif; + }`} + </style> <link key="1" rel="stylesheet" type="text/css" href="/style/pure.css" /> <link key="2" rel="stylesheet" type="text/css" href="/style/wallet.css" /> <Story /> + </div> + } + if (kind.startsWith('wallet')) { + const path = /wallet(\/.*).*/.exec(kind)[1]; + return <div class="wallet-container"> + <style>{` + html { + font-family: sans-serif; /* 1 */ + } + body { + margin: 0; + }`} + </style> + <style>{` + html { + } + h1 { + font-size: 2em; + } + input { + font: inherit; + } + body { + margin: 0; + font-size: 100%; + padding: 0; + background-color: #f8faf7; + font-family: Arial, Helvetica, sans-serif; + }`} + </style> + <LogoHeader /> + <NavBar path={path} devMode={path === '/dev'} /> + {/* <link key="1" rel="stylesheet" type="text/css" href="/style/pure.css" /> + <link key="2" rel="stylesheet" type="text/css" href="/style/wallet.css" /> */} + <Story /> </div> } return <div> diff --git 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 @@ -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 @@ -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 <http://www.gnu.org/licenses/> + */ + +/** + * 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 ( + <a href={props.target} class={cssClass}> + {props.children} + </a> + ); +} + +export function NavBar({ devMode, path }: { path: string, devMode: boolean }) { + return <PopupNavigation devMode={devMode}> + <div> + <Tab target="/balance" current={path}>{i18n.str`Balance`}</Tab> + <Tab target="/history" current={path}>{i18n.str`History`}</Tab> + <Tab target="/backup" current={path}>{i18n.str`Backup`}</Tab> + <Tab target="/settings" current={path}>{i18n.str`Settings`}</Tab> + {devMode && <Tab target="/dev" current={path}>{i18n.str`Dev`}</Tab>} + </div> + </PopupNavigation> +} + +export function WalletNavBar() { + const { devMode } = useDevContext() + return <Match>{({ path }: any) => { + console.log("path", path) + return <NavBar devMode={devMode} path={path} /> + }}</Match> +} + diff --git a/packages/taler-wallet-webextension/src/components/LogoHeader.tsx b/packages/taler-wallet-webextension/src/components/LogoHeader.tsx @@ -0,0 +1,13 @@ +export function LogoHeader() { + return <div style={{ + display: 'flex', + justifyContent: 'space-around', + margin: '2em', + }}> + <img style={{ + width: 150, + height: 70, + }} src="/static/img/logo-2021.svg" width="150" /> + </div> + +} +\ 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 @@ -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 @@ -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 <http://www.gnu.org/licenses/> + */ + +/** +* +* @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<ContractTerms> 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<ContractTerms> 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<ContractTerms> 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<ContractTerms> 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/cta/Pay.tsx diff --git a/packages/taler-wallet-webextension/src/cta/Refund.stories.tsx 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 <http://www.gnu.org/licenses/> + */ + +/** +* +* @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<OrderShortInfo> 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<OrderShortInfo> 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<OrderShortInfo> as any, + pendingAtExchange: true, + proposalId: "proposal123", + } +}); diff --git a/packages/taler-wallet-webextension/src/wallet/Refund.tsx b/packages/taler-wallet-webextension/src/cta/Refund.tsx diff --git a/packages/taler-wallet-webextension/src/cta/Tip.stories.tsx 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 <http://www.gnu.org/licenses/> + */ + +/** +* +* @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/wallet/Tip.tsx b/packages/taler-wallet-webextension/src/cta/Tip.tsx diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx 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 <http://www.gnu.org/licenses/> + */ + +/** +* +* @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 @@ -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 <http://www.gnu.org/licenses/> + */ + +/** + * 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<void>; + setCancelled: (b: boolean) => void; + setSelecting: (b: boolean) => void; +}; + +export function View({ details, selectedExchange, accept, setCancelled, setSelecting }: ViewProps) { + + return ( + <WalletAction> + <div style="border-bottom: 3px dashed #aa3939; margin-bottom: 2em;"> + <h1 style="font-family: monospace; font-size: 250%;"> + <span style="color: #aa3939;">❰</span>Taler Wallet<span style="color: #aa3939;">❱</span> + </h1> + </div> + <div class="fade"> + <div> + <h1><i18n.Translate>Digital Cash Withdrawal</i18n.Translate></h1> + <p><i18n.Translate> + You are about to withdraw{" "} + <strong>{renderAmount(details.amount)}</strong> from your bank account + into your wallet. + </i18n.Translate></p> + {selectedExchange ? ( + <p><i18n.Translate> + The exchange <strong>{selectedExchange}</strong> will be used as the + Taler payment service provider. + </i18n.Translate></p> + ) : null} + + <div> + <button + class="pure-button button-success" + disabled={!selectedExchange} + onClick={() => accept()} + > + {i18n.str`Accept fees and withdraw`} + </button> + <p> + <span + role="button" + tabIndex={0} + style={{ textDecoration: "underline", cursor: "pointer" }} + onClick={() => setSelecting(true)} + > + {i18n.str`Chose different exchange provider`} + </span> + <br /> + <span + role="button" + tabIndex={0} + style={{ textDecoration: "underline", cursor: "pointer" }} + onClick={() => setCancelled(true)} + > + {i18n.str`Cancel withdraw operation`} + </span> + </p> + </div> + </div> + </div> + </WalletAction> + ) +} + +export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element { + const [details, setDetails] = useState<WithdrawUriInfoResponse | undefined>(undefined); + const [selectedExchange, setSelectedExchange] = useState<string | undefined>(undefined); + const [cancelled, setCancelled] = useState(false); + const [selecting, setSelecting] = useState(false); + const [error, setError] = useState<boolean>(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<void> => { + 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 <span><i18n.Translate>missing withdraw uri</i18n.Translate></span>; + } + + const accept = async (): Promise<void> => { + 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 <span><i18n.Translate>Loading...</i18n.Translate></span>; + } + if (cancelled) { + return <span><i18n.Translate>Withdraw operation has been cancelled.</i18n.Translate></span>; + } + if (error) { + return <span><i18n.Translate>This URI is not valid anymore.</i18n.Translate></span>; + } + + return <View accept={accept} + setCancelled={setCancelled} setSelecting={setSelecting} + details={details} selectedExchange={selectedExchange} + /> +} + diff --git a/packages/taler-wallet-webextension/src/wallet/payback.tsx b/packages/taler-wallet-webextension/src/cta/payback.tsx diff --git a/packages/taler-wallet-webextension/src/wallet/reset-required.tsx b/packages/taler-wallet-webextension/src/cta/reset-required.tsx diff --git a/packages/taler-wallet-webextension/src/wallet/return-coins.tsx b/packages/taler-wallet-webextension/src/cta/return-coins.tsx diff --git 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 @@ -14,11 +14,19 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -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: ))} </section> <footer style={{ justifyContent: 'space-around' }}> - <a style={{ color: 'darkgreen', textDecoration:'none' }} href={Pages.transaction.replace(':tid', 'asd')}>VIEW MORE TRANSACTIONS</a> + <a target="_blank" + rel="noopener noreferrer" + style={{ color: 'darkgreen', textDecoration: 'none' }} + href={chrome.extension ? chrome.extension.getURL(`/static/wallet.html#/history`) : '#'}>VIEW MORE TRANSACTIONS</a> </footer> </PopupBox> } -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 ( - <HistoryRow> + <HistoryRow href={Pages.transaction.replace(':tid', props.id)}> <img src={props.iconPath} /> <Column> <ExtraLargeText> - <a href={Pages.transaction.replace(':tid', props.id)}><span>{props.title}</span></a> + <span>{props.title}</span> {props.pending ? ( <span style={{ color: "darkblue" }}> (Pending)</span> ) : null} </ExtraLargeText> <SmallTextLight>{dateStr}</SmallTextLight> - - {/* <div>{props.subtitle}</div> */} </Column> <TransactionAmount pending={props.pending} diff --git a/packages/taler-wallet-webextension/src/popup/Popup.stories.tsx b/packages/taler-wallet-webextension/src/popup/Popup.stories.tsx @@ -20,7 +20,7 @@ */ import { Fragment, FunctionalComponent } from 'preact'; -import { NavBar as TestedComponent } from './popup'; +import { NavBar as TestedComponent } from '../NavigationBar'; export default { title: 'popup/header', diff --git a/packages/taler-wallet-webextension/src/popup/Transaction.tsx b/packages/taler-wallet-webextension/src/popup/Transaction.tsx @@ -20,7 +20,7 @@ import { Fragment, JSX, VNode } from "preact"; import { route } from 'preact-router'; import { useEffect, useState } from "preact/hooks"; import * as wxApi from "../wxApi"; -import { Pages } from "./popup"; +import { Pages } from "../NavigationBar"; import emptyImg from "../../static/img/empty.png" import { Button, ButtonDestructive, ButtonPrimary, ListOfProducts, PopupBox, Row, RowBorderGray, SmallTextLight } from "../components/styled"; import { ErrorMessage } from "../components/ErrorMessage"; diff --git a/packages/taler-wallet-webextension/src/popup/popup.tsx b/packages/taler-wallet-webextension/src/popup/popup.tsx @@ -1,76 +0,0 @@ -/* - 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 <http://www.gnu.org/licenses/> - */ - -/** - * 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 ( - <a href={props.target} class={cssClass}> - {props.children} - </a> - ); -} - -export function NavBar({devMode, path}:{path:string, devMode:boolean}) { - return <PopupNavigation devMode={devMode}> - <Tab target="/balance" current={path}>{i18n.str`Balance`}</Tab> - <Tab target="/history" current={path}>{i18n.str`History`}</Tab> - <Tab target="/backup" current={path}>{i18n.str`Backup`}</Tab> - <Tab target="/settings" current={path}>{i18n.str`Settings`}</Tab> - {devMode && <Tab target="/dev" current={path}>{i18n.str`Dev`}</Tab>} - </PopupNavigation> -} - -export function WalletNavBar() { - const { devMode } = useDevContext() - return <Match>{({ path }: any) => <NavBar devMode={devMode} path={path} />}</Match> -} - diff --git 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 @@ -0,0 +1,8 @@ +import { FunctionalComponent, h as render } from 'preact'; + +export function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + 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 @@ -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 <http://www.gnu.org/licenses/> + */ + +/** +* +* @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<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + const r = (args: any) => <Component {...args} /> + 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 @@ -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 <http://www.gnu.org/licenses/> + */ + +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<void> => { + const res = await wxApi.getTransactions(); + setTransactions(res); + }; + fetchData(); + }, []); + + if (!transactions) { + return <div>Loading ...</div>; + } + + return <HistoryView balances={balanceWithoutError} list={[...transactions.transactions].reverse()} />; +} + +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 <WalletBox noPadding> + {balances.length > 0 && <header> + {balances.length === 1 && <div class="title"> + Balance: <span>{amountToString(balances[0].available)}</span> + </div>} + {balances.length > 1 && <div class="title"> + Balance: <ul style={{ margin: 0 }}> + {balances.map(b => <li>{b.available}</li>)} + </ul> + </div>} + </header>} + <section> + {Object.keys(byDate).map(d => { + return <Fragment> + <DateSeparator>{d}</DateSeparator> + {byDate[d].map((tx, i) => ( + <TransactionItem key={i} tx={tx} /> + ))} + </Fragment> + })} + </section> + </WalletBox> +} + +function TransactionItem(props: { tx: Transaction }): JSX.Element { + const tx = props.tx; + switch (tx.type) { + case TransactionType.Withdrawal: + return ( + <TransactionLayout + id={tx.transactionId} + amount={tx.amountEffective} + debitCreditIndicator={"credit"} + title="Withdrawal" + subtitle={`via ${tx.exchangeBaseUrl}`} + timestamp={tx.timestamp} + iconPath={imageBank} + pending={tx.pending} + ></TransactionLayout> + ); + case TransactionType.Payment: + return ( + <TransactionLayout + id={tx.transactionId} + amount={tx.amountEffective} + debitCreditIndicator={"debit"} + title="Payment" + subtitle={tx.info.summary} + timestamp={tx.timestamp} + iconPath={imageShoppingCart} + pending={tx.pending} + ></TransactionLayout> + ); + case TransactionType.Refund: + return ( + <TransactionLayout + id={tx.transactionId} + amount={tx.amountEffective} + debitCreditIndicator={"credit"} + title="Refund" + subtitle={tx.info.summary} + timestamp={tx.timestamp} + iconPath={imageRefund} + pending={tx.pending} + ></TransactionLayout> + ); + case TransactionType.Tip: + return ( + <TransactionLayout + id={tx.transactionId} + amount={tx.amountEffective} + debitCreditIndicator={"credit"} + title="Tip" + subtitle={`from ${new URL(tx.merchantBaseUrl).hostname}`} + timestamp={tx.timestamp} + iconPath={imageHandHeart} + pending={tx.pending} + ></TransactionLayout> + ); + case TransactionType.Refresh: + return ( + <TransactionLayout + id={tx.transactionId} + amount={tx.amountEffective} + debitCreditIndicator={"credit"} + title="Refresh" + subtitle={`via exchange ${tx.exchangeBaseUrl}`} + timestamp={tx.timestamp} + iconPath={imageRefresh} + pending={tx.pending} + ></TransactionLayout> + ); + case TransactionType.Deposit: + return ( + <TransactionLayout + id={tx.transactionId} + amount={tx.amountEffective} + debitCreditIndicator={"debit"} + title="Refresh" + subtitle={`to ${tx.targetPaytoUri}`} + timestamp={tx.timestamp} + iconPath={imageRefresh} + pending={tx.pending} + ></TransactionLayout> + ); + } +} + +function TransactionLayout(props: TransactionLayoutProps): JSX.Element { + const date = new Date(props.timestamp.t_ms); + const dateStr = format(date, 'HH:mm:ss') + return ( + // <a href={Pages.transaction.replace(':tid', props.id)}> + <HistoryRow href={Pages.transaction.replace(':tid', props.id)}> + <img src={props.iconPath} /> + <Column> + <ExtraLargeText> + <span>{props.title}</span> + {props.pending ? ( + <span style={{ color: "darkblue" }}> (Pending)</span> + ) : null} + </ExtraLargeText> + <SmallTextLight>{dateStr}</SmallTextLight> + </Column> + <TransactionAmount + pending={props.pending} + amount={props.amount} + debitCreditIndicator={props.debitCreditIndicator} + /> + </HistoryRow> + // </a> + ); +} + +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 ( + <Column style={{ + color: + props.pending ? "gray" : + (sign === '+' ? 'darkgreen' : + (sign === '-' ? 'darkred' : + undefined)) + }}> + <ExtraLargeText> + {sign} + {amount} + </ExtraLargeText> + <div>{currency}</div> + </Column> + ); +} + diff --git a/packages/taler-wallet-webextension/src/wallet/Pay.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Pay.stories.tsx @@ -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 <http://www.gnu.org/licenses/> - */ - -/** -* -* @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<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { - const r = (args: any) => <Component {...args} /> - 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<ContractTerms> 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<ContractTerms> 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<ContractTerms> 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<ContractTerms> as any, - contractTermsHash: '123456', - proposalId: 'proposal1234', - paid: false, - } -}); diff --git a/packages/taler-wallet-webextension/src/wallet/Refund.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Refund.stories.tsx @@ -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 <http://www.gnu.org/licenses/> - */ - -/** -* -* @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<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { - const r = (args: any) => <Component {...args} /> - 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<OrderShortInfo> 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<OrderShortInfo> 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<OrderShortInfo> as any, - pendingAtExchange: true, - proposalId: "proposal123", - } -}); diff --git a/packages/taler-wallet-webextension/src/wallet/Tip.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Tip.stories.tsx @@ -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 <http://www.gnu.org/licenses/> - */ - -/** -* -* @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<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { - const r = (args: any) => <Component {...args} /> - 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/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<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { - const r = (args: any) => <Component {...args} /> - 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 @@ -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 (<WalletPage> - <div style="border-bottom: 3px dashed #aa3939; margin-bottom: 2em;"> - <h1 style="font-family: monospace; font-size: 250%;"> - <span style="color: #aa3939;">❰</span>Taler Wallet<span style="color: #aa3939;">❱</span> - </h1> - </div> + return (<WalletBox> <h1>Browser Extension Installed!</h1> <div> <p>Thank you for installing the wallet.</p> @@ -68,6 +63,6 @@ export function View({ permissionsEnabled, togglePermissions, diagnostics, timed Learn how to top up your wallet balance » </a> </div> - </WalletPage> + </WalletBox> ); } diff --git a/packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx @@ -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 <http://www.gnu.org/licenses/> - */ - -/** -* -* @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) => <View {...a} />; -WithoutDetails.args = { -} as ViewProps - -export const CompleteWithExchange = (a: any) => <View {...a} />; -CompleteWithExchange.args = { - details: { - amount: 'USD:2', - }, - selectedExchange: 'Some exchange' -} as ViewProps - -export const CompleteWithoutExchange = (a: any) => <View {...a} />; -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 @@ -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 <http://www.gnu.org/licenses/> - */ - -/** - * 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<void>; - setCancelled: (b: boolean) => void; - setSelecting: (b: boolean) => void; -}; - -export function View({ details, selectedExchange, accept, setCancelled, setSelecting }: ViewProps) { - - return ( - <WalletPage> - <div style="border-bottom: 3px dashed #aa3939; margin-bottom: 2em;"> - <h1 style="font-family: monospace; font-size: 250%;"> - <span style="color: #aa3939;">❰</span>Taler Wallet<span style="color: #aa3939;">❱</span> - </h1> - </div> - <div class="fade"> - <div> - <h1><i18n.Translate>Digital Cash Withdrawal</i18n.Translate></h1> - <p><i18n.Translate> - You are about to withdraw{" "} - <strong>{renderAmount(details.amount)}</strong> from your bank account - into your wallet. - </i18n.Translate></p> - {selectedExchange ? ( - <p><i18n.Translate> - The exchange <strong>{selectedExchange}</strong> will be used as the - Taler payment service provider. - </i18n.Translate></p> - ) : null} - - <div> - <button - class="pure-button button-success" - disabled={!selectedExchange} - onClick={() => accept()} - > - {i18n.str`Accept fees and withdraw`} - </button> - <p> - <span - role="button" - tabIndex={0} - style={{ textDecoration: "underline", cursor: "pointer" }} - onClick={() => setSelecting(true)} - > - {i18n.str`Chose different exchange provider`} - </span> - <br /> - <span - role="button" - tabIndex={0} - style={{ textDecoration: "underline", cursor: "pointer" }} - onClick={() => setCancelled(true)} - > - {i18n.str`Cancel withdraw operation`} - </span> - </p> - </div> - </div> - </div> - </WalletPage> - ) -} - -export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element { - const [details, setDetails] = useState<WithdrawUriInfoResponse | undefined>(undefined); - const [selectedExchange, setSelectedExchange] = useState< - string | undefined - >(undefined); - const [cancelled, setCancelled] = useState(false); - const [selecting, setSelecting] = useState(false); - const [errMsg, setErrMsg] = useState<string | undefined>(""); - 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<void> => { - 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 <span><i18n.Translate>missing withdraw uri</i18n.Translate></span>; - } - - const accept = async (): Promise<void> => { - 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 <span><i18n.Translate>Loading...</i18n.Translate></span>; - } - if (cancelled) { - return <span><i18n.Translate>Withdraw operation has been cancelled.</i18n.Translate></span>; - } - - return <View accept={accept} - setCancelled={setCancelled} setSelecting={setSelecting} - details={details} selectedExchange={selectedExchange} - /> -} - diff --git a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx @@ -20,17 +20,24 @@ * @author Florian Dold <dold@taler.net> */ -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 () => <Fragment> + <LogoHeader /> + <WalletNavBar /> + <Component /> + </Fragment> } function Application() { - const h = createHashHistory(); - return <Router history={h} > + return <div> + <DevContextProvider> + <Router history={createHashHistory()} > + + <Route path={Pages.welcome} component={withLogoAndNavBar(WelcomePage)} /> - <Route path={Pages.welcome} component={WelcomePage} /> - <Route path={Pages.pay} component={PayPage} /> - <Route path={Pages.refund} component={RefundPage} /> + <Route path={Pages.history} component={withLogoAndNavBar(HistoryPage)} /> + <Route path={Pages.transaction} component={withLogoAndNavBar(HistoryPage)} /> - <Route path={Pages.tips} component={TipPage} /> - <Route path={Pages.withdraw} component={WithdrawPage} /> + <Route path={Pages.reset_required} component={() => <div>no yet implemented</div>} /> + <Route path={Pages.payback} component={() => <div>no yet implemented</div>} /> + <Route path={Pages.return_coins} component={() => <div>no yet implemented</div>} /> - <Route path={Pages.reset_required} component={() => <div>no yet implemented</div>} /> - <Route path={Pages.payback} component={() => <div>no yet implemented</div>} /> - <Route path={Pages.return_coins} component={() => <div>no yet implemented</div>} /> + {/** call to action */} + <Route path={Pages.pay} component={PayPage} /> + <Route path={Pages.refund} component={RefundPage} /> + <Route path={Pages.tips} component={TipPage} /> + <Route path={Pages.withdraw} component={WithdrawPage} /> + + <Route default component={Redirect} to={Pages.history} /> + </Router> + </DevContextProvider> + </div> +} - </Router> +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 @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" width="670" height="300" viewBox="0 0 201 90"><g fill="#0042b3" fill-rule="evenodd" stroke-width=".3"><path d="M86.7 1.1c15.6 0 29 9.4 36 23.2h-5.9A35.1 35.1 0 0086.7 6.5C67 6.5 51 23.6 51 44.7c0 10.4 3.8 19.7 10 26.6a31.4 31.4 0 01-4.2 3A45.2 45.2 0 0146 44.7c0-24 18.2-43.6 40.7-43.6zm35.8 64.3a40.4 40.4 0 01-39 22.8c3-1.5 6-3.5 8.6-5.7a35.6 35.6 0 0024.6-17.1z"/><path d="M64.2 1.1l3.1.1c-3 1.6-5.9 3.5-8.5 5.8a37.5 37.5 0 00-30.2 37.7c0 14.3 7.3 26.7 18 33.3a29.6 29.6 0 01-8.5.2c-9-8-14.6-20-14.6-33.5 0-24 18.2-43.6 40.7-43.6zm5.4 81.4a35.6 35.6 0 0024.6-17.1h5.9a40.4 40.4 0 01-39 22.8c3-1.5 5.9-3.5 8.5-5.7zm24.8-58.2a37 37 0 00-12.6-12.8 29.6 29.6 0 018.5-.2c4 3.6 7.4 8 9.9 13z"/><path d="M41.8 1.1c1 0 2 0 3.1.2-3 1.5-5.9 3.4-8.5 5.6A37.5 37.5 0 006.1 44.7c0 21.1 16 38.3 35.7 38.3 12.6 0 23.6-7 30-17.6h5.8a40.4 40.4 0 01-35.8 23C19.3 88.4 1 68.8 1 44.7c0-24 18.2-43.6 40.7-43.6zm30.1 23.2a38.1 38.1 0 00-4.5-6.1c1.3-1.2 2.7-2.2 4.3-3 2.3 2.7 4.4 5.8 6 9.1z"/></g><path d="M76.1 34.4h9.2v-5H61.9v5H71v26h5.1zM92.6 52.9h13.7l3 7.4h5.3l-12.7-31.2h-4.7L84.5 60.3h5.2zm11.8-4.9h-9.9l5-12.4zM123.8 29.4h-4.6v31h20.6v-5h-16zM166.5 29.4H145v31h21.6v-5H150v-8.3h14.5v-4.9h-14.5v-8h16.4zM191.2 39.5c0 1.6-.5 2.8-1.6 3.8s-2.6 1.4-4.4 1.4h-7.4V34.3h7.4c1.9 0 3.4.4 4.4 1.3 1 .9 1.6 2.2 1.6 3.9zm6 20.8l-7.7-11.7c1-.3 1.9-.7 2.7-1.3a8.8 8.8 0 003.6-4.6c.4-1 .5-2.2.5-3.5 0-1.5-.2-2.9-.7-4.1a8.4 8.4 0 00-2.1-3.1c-1-.8-2-1.5-3.4-2-1.3-.4-2.8-.6-4.5-.6h-12.9v31h5V49.4h6.5l7 10.8z"/></svg> +\ 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 @@ -2,6 +2,7 @@ body { font-size: 100%; overflow-y: scroll; margin-top: 2em; + font-family: Arial, Helvetica, sans-serif; } .wallet-container {