diff options
| author | Sebastian <sebasjm@gmail.com> | 2021-08-19 00:34:47 -0300 | 
|---|---|---|
| committer | Sebastian <sebasjm@gmail.com> | 2021-08-19 00:35:21 -0300 | 
| commit | 97a05ff659af274dcfcd9c76bf19100bbd51ce0e (patch) | |
| tree | 9cce837ec9a5ec06279dc48eac75e1993ede983f /packages/taler-wallet-webextension | |
| parent | b015f76e7268cb5caff14a0ed88cb5e8fa53dc2e (diff) | |
new wallet history and view refactoring
Diffstat (limited to 'packages/taler-wallet-webextension')
30 files changed, 867 insertions, 158 deletions
| 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 <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 index a5908af25..e41c6cb86 100644 --- a/packages/taler-wallet-webextension/package.json +++ b/packages/taler-wallet-webextension/package.json @@ -12,7 +12,7 @@      "test": "jest ./tests",      "compile": "tsc && rollup -c",      "build-storybook": "build-storybook", -    "storybook": "start-storybook -s static -p 6006", +    "storybook": "start-storybook -s . -p 6006",      "watch": "tsc --watch & rollup -w -c"    },    "dependencies": { diff --git a/packages/taler-wallet-webextension/rollup.config.js b/packages/taler-wallet-webextension/rollup.config.js index 7e7ec0032..5a3f0db5f 100644 --- a/packages/taler-wallet-webextension/rollup.config.js +++ b/packages/taler-wallet-webextension/rollup.config.js @@ -1,14 +1,13 @@  // rollup.config.js +import linaria from '@linaria/rollup'; +import alias from '@rollup/plugin-alias';  import commonjs from "@rollup/plugin-commonjs"; -import nodeResolve from "@rollup/plugin-node-resolve"; +import image from '@rollup/plugin-image';  import json from "@rollup/plugin-json"; -import builtins from "builtin-modules"; +import nodeResolve from "@rollup/plugin-node-resolve";  import replace from "@rollup/plugin-replace"; -import ignore from "rollup-plugin-ignore" -import image from '@rollup/plugin-image'; -import linaria from '@linaria/rollup';  import css from 'rollup-plugin-css-only'; -import alias from '@rollup/plugin-alias'; +import ignore from "rollup-plugin-ignore";  const makePlugins = () => [      alias({ diff --git a/packages/taler-wallet-webextension/src/popup/popup.tsx b/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 <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>} +    <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) => <NavBar devMode={devMode} path={path} />}</Match> +  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 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 <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 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 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<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, diff --git a/packages/taler-wallet-webextension/src/wallet/Pay.tsx b/packages/taler-wallet-webextension/src/cta/Pay.tsx index a5849bb28..a5849bb28 100644 --- 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/wallet/Refund.stories.tsx b/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<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', diff --git a/packages/taler-wallet-webextension/src/wallet/Refund.tsx b/packages/taler-wallet-webextension/src/cta/Refund.tsx index bb26d933b..bb26d933b 100644 --- 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/wallet/Tip.stories.tsx b/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<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, diff --git a/packages/taler-wallet-webextension/src/wallet/Tip.tsx b/packages/taler-wallet-webextension/src/cta/Tip.tsx index 69886668b..69886668b 100644 --- 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/wallet/Withdraw.stories.tsx b/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) => <View {...a} />; -WithoutDetails.args = { -} as ViewProps - -export const CompleteWithExchange = (a: any) => <View {...a} />; -CompleteWithExchange.args = { +export const CompleteWithExchange = createExample(TestedComponent, {    details: {      amount: 'USD:2', +    possibleExchanges: [],    },    selectedExchange: 'Some exchange' -} as ViewProps - -export const CompleteWithoutExchange = (a: any) => <View {...a} />; -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 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<void>; @@ -50,7 +49,7 @@ export interface ViewProps {  export function View({ details, selectedExchange, accept, setCancelled, setSelecting }: ViewProps) {    return ( -    <WalletPage> +    <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> @@ -101,26 +100,19 @@ export function View({ details, selectedExchange, accept, setCancelled, setSelec            </div>          </div>        </div> -    </WalletPage> +    </WalletAction>    )  }  export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element {    const [details, setDetails] = useState<WithdrawUriInfoResponse | undefined>(undefined); -  const [selectedExchange, setSelectedExchange] = useState< -    string | 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 [error, setError] = useState<boolean>(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<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) +        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 <span><i18n.Translate>missing withdraw uri</i18n.Translate></span>; @@ -169,6 +160,9 @@ export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element    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} diff --git a/packages/taler-wallet-webextension/src/wallet/payback.tsx b/packages/taler-wallet-webextension/src/cta/payback.tsx index 4233b1f96..4233b1f96 100644 --- 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 index 87751561c..87751561c 100644 --- 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 index 2273d1454..2273d1454 100644 --- 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 index 940d1f2a4..72139e1f8 100644 --- a/packages/taler-wallet-webextension/src/popup/BackupPage.tsx +++ b/packages/taler-wallet-webextension/src/popup/BackupPage.tsx @@ -25,7 +25,7 @@ import {    SmallText, SmallTextLight  } from "../components/styled";  import { useBackupStatus } from "../hooks/useBackupStatus"; -import { Pages } from "./popup"; +import { Pages } from "../NavigationBar";  interface Props {    onAddProvider: () => void; diff --git a/packages/taler-wallet-webextension/src/popup/History.tsx b/packages/taler-wallet-webextension/src/popup/History.tsx index b6b65314e..7c9eae54b 100644 --- a/packages/taler-wallet-webextension/src/popup/History.tsx +++ b/packages/taler-wallet-webextension/src/popup/History.tsx @@ -14,11 +14,19 @@   TALER; see the file COPYING.  If not, see <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 index ec3634d9b..ce5b11c32 100644 --- 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 index fd7389c04..8177b74ad 100644 --- 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/popupEntryPoint.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx index faa5149ac..4a9fe9abc 100644 --- a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx +++ b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx @@ -34,7 +34,7 @@ import { DeveloperPage as DeveloperPage } from "./popup/Debug";  import { HistoryPage } from "./popup/History";  import {    Pages, WalletNavBar -} from "./popup/popup"; +} from "./NavigationBar";  import { ProviderAddPage } from "./popup/ProviderAddPage";  import { ProviderDetailPage } from "./popup/ProviderDetailPage";  import { SettingsPage } from "./popup/Settings"; @@ -129,4 +129,4 @@ function Redirect({ to }: { to: string }): null {      route(to, true)    })    return null -}
\ No newline at end of file +} diff --git a/packages/taler-wallet-webextension/src/test-utils.ts b/packages/taler-wallet-webextension/src/test-utils.ts new file mode 100644 index 000000000..16262b3c2 --- /dev/null +++ b/packages/taler-wallet-webextension/src/test-utils.ts @@ -0,0 +1,8 @@ +import { FunctionalComponent, h as render } from 'preact'; + +export function createExample<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 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 <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 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 <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/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<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 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 (<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/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 <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 new file mode 100644 index 000000000..e72611eba --- /dev/null +++ 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 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 { | 
