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 commonjs from "@rollup/plugin-commonjs"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import json from "@rollup/plugin-json"; -import builtins from "builtin-modules"; -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 commonjs from "@rollup/plugin-commonjs"; +import image from '@rollup/plugin-image'; +import json from "@rollup/plugin-json"; +import nodeResolve from "@rollup/plugin-node-resolve"; +import replace from "@rollup/plugin-replace"; +import css from 'rollup-plugin-css-only'; +import ignore from "rollup-plugin-ignore"; const makePlugins = () => [ alias({ diff --git a/packages/taler-wallet-webextension/src/popup/popup.tsx b/packages/taler-wallet-webextension/src/NavigationBar.tsx similarity index 65% rename from packages/taler-wallet-webextension/src/popup/popup.tsx rename to packages/taler-wallet-webextension/src/NavigationBar.tsx index 4aee48fb7..e07032d0a 100644 --- a/packages/taler-wallet-webextension/src/popup/popup.tsx +++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx @@ -27,10 +27,11 @@ 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' +import { useDevContext } from "./context/devContext"; +import { PopupNavigation } from './components/styled' export enum Pages { + welcome = '/welcome', balance = '/balance', settings = '/settings', dev = '/dev', @@ -39,6 +40,15 @@ export enum Pages { 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 { @@ -59,18 +69,23 @@ function Tab(props: TabProps): JSX.Element { ); } -export function NavBar({devMode, path}:{path:string, devMode:boolean}) { +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`}} +
+ {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) => } + 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/wallet/Pay.stories.tsx b/packages/taler-wallet-webextension/src/cta/Pay.stories.tsx similarity index 91% rename from packages/taler-wallet-webextension/src/wallet/Pay.stories.tsx rename to packages/taler-wallet-webextension/src/cta/Pay.stories.tsx index 0297d6264..38e3d0f35 100644 --- a/packages/taler-wallet-webextension/src/wallet/Pay.stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Pay.stories.tsx @@ -20,23 +20,16 @@ */ import { ContractTerms, PreparePayResultType } from '@gnu-taler/taler-util'; -import { FunctionalComponent, h } from 'preact'; +import { createExample } from '../test-utils'; import { PaymentRequestView as TestedComponent } from './Pay'; - export default { - title: 'wallet/pay', + title: 'cta/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, diff --git a/packages/taler-wallet-webextension/src/wallet/Pay.tsx b/packages/taler-wallet-webextension/src/cta/Pay.tsx similarity index 100% rename from packages/taler-wallet-webextension/src/wallet/Pay.tsx rename to packages/taler-wallet-webextension/src/cta/Pay.tsx diff --git a/packages/taler-wallet-webextension/src/wallet/Refund.stories.tsx b/packages/taler-wallet-webextension/src/cta/Refund.stories.tsx similarity index 85% rename from packages/taler-wallet-webextension/src/wallet/Refund.stories.tsx rename to packages/taler-wallet-webextension/src/cta/Refund.stories.tsx index 044141f0c..88e714cb7 100644 --- a/packages/taler-wallet-webextension/src/wallet/Refund.stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Refund.stories.tsx @@ -19,24 +19,18 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { ContractTerms, OrderShortInfo, PreparePayResultType } from '@gnu-taler/taler-util'; -import { FunctionalComponent, h } from 'preact'; +import { OrderShortInfo } from '@gnu-taler/taler-util'; +import { createExample } from '../test-utils'; import { View as TestedComponent } from './Refund'; export default { - title: 'wallet/refund', + title: 'cta/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', diff --git a/packages/taler-wallet-webextension/src/wallet/Refund.tsx b/packages/taler-wallet-webextension/src/cta/Refund.tsx similarity index 100% rename from packages/taler-wallet-webextension/src/wallet/Refund.tsx rename to packages/taler-wallet-webextension/src/cta/Refund.tsx diff --git a/packages/taler-wallet-webextension/src/wallet/Tip.stories.tsx b/packages/taler-wallet-webextension/src/cta/Tip.stories.tsx similarity index 82% rename from packages/taler-wallet-webextension/src/wallet/Tip.stories.tsx rename to packages/taler-wallet-webextension/src/cta/Tip.stories.tsx index ffd976144..389b183f0 100644 --- a/packages/taler-wallet-webextension/src/wallet/Tip.stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Tip.stories.tsx @@ -19,24 +19,17 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { ContractTerms, PreparePayResultType } from '@gnu-taler/taler-util'; -import { FunctionalComponent, h } from 'preact'; +import { createExample } from '../test-utils'; import { View as TestedComponent } from './Tip'; export default { - title: 'wallet/tip', + title: 'cta/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, diff --git a/packages/taler-wallet-webextension/src/wallet/Tip.tsx b/packages/taler-wallet-webextension/src/cta/Tip.tsx similarity index 100% rename from packages/taler-wallet-webextension/src/wallet/Tip.tsx rename to packages/taler-wallet-webextension/src/cta/Tip.tsx diff --git a/packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx similarity index 66% rename from packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx rename to packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx index fef36b820..747f855fa 100644 --- a/packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx @@ -19,32 +19,27 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { h } from 'preact'; -import { View, ViewProps } from './Withdraw'; +import { createExample } from '../test-utils'; +import { View as TestedComponent } from './Withdraw'; export default { - title: 'wallet/withdraw', - component: View, + title: 'cta/withdraw', + component: TestedComponent, argTypes: { }, }; -export const WithoutDetails = (a: any) => ; -WithoutDetails.args = { -} as ViewProps - -export const CompleteWithExchange = (a: any) => ; -CompleteWithExchange.args = { +export const CompleteWithExchange = createExample(TestedComponent, { details: { amount: 'USD:2', + possibleExchanges: [], }, selectedExchange: 'Some exchange' -} as ViewProps - -export const CompleteWithoutExchange = (a: any) => ; -CompleteWithoutExchange.args = { +}) +export const CompleteWithoutExchange = createExample(TestedComponent, { details: { amount: 'USD:2', + possibleExchanges: [], }, -} as ViewProps +}) diff --git a/packages/taler-wallet-webextension/src/wallet/Withdraw.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx similarity index 90% rename from packages/taler-wallet-webextension/src/wallet/Withdraw.tsx rename to packages/taler-wallet-webextension/src/cta/Withdraw.tsx index 442ee7dae..b5182b070 100644 --- a/packages/taler-wallet-webextension/src/wallet/Withdraw.tsx +++ b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx @@ -32,14 +32,13 @@ import { } from "../wxApi"; import { WithdrawUriInfoResponse } from "@gnu-taler/taler-util"; import { JSX } from "preact/jsx-runtime"; -import { WalletPage } from '../components/styled'; +import { WalletAction } from '../components/styled'; interface Props { talerWithdrawUri?: string; } export interface ViewProps { - talerWithdrawUri?: string; details: WithdrawUriInfoResponse; selectedExchange?: string; accept: () => Promise; @@ -50,7 +49,7 @@ export interface ViewProps { export function View({ details, selectedExchange, accept, setCancelled, setSelecting }: ViewProps) { return ( - +

Taler Wallet @@ -101,26 +100,19 @@ export function View({ details, selectedExchange, accept, setCancelled, setSelec

- + ) } export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element { const [details, setDetails] = useState(undefined); - const [selectedExchange, setSelectedExchange] = useState< - string | undefined - >(undefined); + const [selectedExchange, setSelectedExchange] = useState(undefined); const [cancelled, setCancelled] = useState(false); const [selecting, setSelecting] = useState(false); - const [errMsg, setErrMsg] = useState(""); + const [error, setError] = useState(false); 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...') @@ -132,20 +124,19 @@ export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element 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) + console.error('error',JSON.stringify(e,undefined,2)) + setError(true) } }; fetchData(); - }, [selectedExchange, errMsg, selecting, talerWithdrawUri, updateCounter, state]); + }, [selectedExchange, selecting, talerWithdrawUri, updateCounter, state]); if (!talerWithdrawUri) { return missing withdraw uri; @@ -169,6 +160,9 @@ export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element if (cancelled) { return Withdraw operation has been cancelled.; } + if (error) { + return This URI is not valid anymore.; + } return 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}
*/}
(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/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/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 {