diff options
Diffstat (limited to 'packages/taler-wallet-webextension/src/wallet')
8 files changed, 287 insertions, 13 deletions
diff --git a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx index ffc4418e0..712329bf8 100644 --- a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx @@ -57,7 +57,7 @@ export function BackupView({ providers, onAddProvider, onSyncAll }: ViewProps): title={provider.name} /> )} - {!providers.length && <Centered style={{marginTop: 100}}> + {!providers.length && <Centered style={{ marginTop: 100 }}> <BoldLight>No backup providers configured</BoldLight> <ButtonSuccess onClick={onAddProvider}><i18n.Translate>Add provider</i18n.Translate></ButtonSuccess> </Centered>} @@ -98,8 +98,8 @@ function BackupLayout(props: TransactionLayoutProps): JSX.Element { <div style={{ color: !props.active ? "grey" : undefined }}> <a href={Pages.provider_detail.replace(':pid', encodeURIComponent(props.id))}><span>{props.title}</span></a> - {dateStr && <SmallText style={{marginTop: 5}}>Last synced: {dateStr}</SmallText>} - {!dateStr && <SmallLightText style={{marginTop: 5}}>Not synced</SmallLightText>} + {dateStr && <SmallText style={{ marginTop: 5 }}>Last synced: {dateStr}</SmallText>} + {!dateStr && <SmallLightText style={{ marginTop: 5 }}>Not synced</SmallLightText>} </div> <div> {props.status?.type === 'paid' ? diff --git a/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx b/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx index 4846d47f7..e06e884ce 100644 --- a/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx @@ -19,21 +19,24 @@ import { Balance, BalancesResponse, i18n } from "@gnu-taler/taler-util"; -import { JSX, h } from "preact"; -import { WalletBox, Centered } from "../components/styled/index"; +import { JSX } from "preact"; +import { ButtonPrimary, Centered, WalletBox } from "../components/styled/index"; import { BalancesHook, useBalances } from "../hooks/useBalances"; import { PageLink, renderAmount } from "../renderHtml"; -export function BalancePage() { +export function BalancePage({ goToWalletManualWithdraw }: { goToWalletManualWithdraw: () => void }) { const balance = useBalances() - return <BalanceView balance={balance} Linker={PageLink} /> + return <BalanceView balance={balance} Linker={PageLink} goToWalletManualWithdraw={goToWalletManualWithdraw} /> } + export interface BalanceViewProps { - balance: BalancesHook, - Linker: typeof PageLink, + balance: BalancesHook; + Linker: typeof PageLink; + goToWalletManualWithdraw: () => void; } -export function BalanceView({ balance, Linker }: BalanceViewProps) { + +export function BalanceView({ balance, Linker, goToWalletManualWithdraw }: BalanceViewProps) { if (!balance) { return <span /> } @@ -57,7 +60,9 @@ export function BalanceView({ balance, Linker }: BalanceViewProps) { </i18n.Translate></p> ) } - return <ShowBalances wallet={balance.response} /> + return <ShowBalances wallet={balance.response} + onWithdraw={goToWalletManualWithdraw} + /> } function formatPending(entry: Balance): JSX.Element { @@ -96,7 +101,7 @@ function formatPending(entry: Balance): JSX.Element { } -function ShowBalances({ wallet }: { wallet: BalancesResponse }) { +function ShowBalances({ wallet, onWithdraw }: { wallet: BalancesResponse, onWithdraw: () => void }) { return <WalletBox> <section> <Centered>{wallet.balances.map((entry) => { @@ -113,5 +118,9 @@ function ShowBalances({ wallet }: { wallet: BalancesResponse }) { ); })}</Centered> </section> + <footer> + <div /> + <ButtonPrimary onClick={onWithdraw} >Withdraw</ButtonPrimary> + </footer> </WalletBox> } diff --git a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx new file mode 100644 index 000000000..35da52392 --- /dev/null +++ b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx @@ -0,0 +1,56 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { createExample } from '../test-utils'; +import { CreateManualWithdraw as TestedComponent } from './CreateManualWithdraw'; + +export default { + title: 'wallet/manual withdraw/creation', + component: TestedComponent, + argTypes: { + } +}; + + +export const InitialState = createExample(TestedComponent, { +}); + +export const WithExchangeFilled = createExample(TestedComponent, { + currency: 'COL', + initialExchange: 'http://exchange.taler:8081', +}); + +export const WithExchangeAndAmountFilled = createExample(TestedComponent, { + currency: 'COL', + initialExchange: 'http://exchange.taler:8081', + initialAmount: '10' +}); + +export const WithExchangeError = createExample(TestedComponent, { + initialExchange: 'http://exchange.tal', + error: 'The exchange url seems invalid' +}); + +export const WithAmountError = createExample(TestedComponent, { + currency: 'COL', + initialExchange: 'http://exchange.taler:8081', + initialAmount: 'e' +}); diff --git a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx new file mode 100644 index 000000000..be2cbe41d --- /dev/null +++ b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx @@ -0,0 +1,57 @@ +import { AmountJson, Amounts } from "@gnu-taler/taler-util"; +import { VNode } from "preact"; +import { useEffect, useRef, useState } from "preact/hooks"; +import { ErrorMessage } from "../components/ErrorMessage"; +import { ButtonPrimary, Input, InputWithLabel, LightText, WalletBox } from "../components/styled"; + +export interface Props { + error: string | undefined; + currency: string | undefined; + initialExchange?: string; + initialAmount?: string; + onExchangeChange: (exchange: string) => void; + onCreate: (exchangeBaseUrl: string, amount: AmountJson) => Promise<void>; +} + +export function CreateManualWithdraw({ onExchangeChange, initialExchange, initialAmount, error, currency, onCreate }: Props): VNode { + const [exchange, setExchange] = useState(initialExchange || ""); + const [amount, setAmount] = useState(initialAmount || ""); + const parsedAmount = Amounts.parse(`${currency}:${amount}`) + + let timeout = useRef<number | undefined>(undefined); + useEffect(() => { + if (timeout) window.clearTimeout(timeout.current) + timeout.current = window.setTimeout(async () => { + onExchangeChange(exchange) + }, 1000); + }, [exchange]) + + + return ( + <WalletBox> + <section> + <ErrorMessage title={error && "Can't create the reserve"} description={error} /> + <h2>Manual Withdrawal</h2> + <LightText>Choose a exchange to create a reserve and then fill the reserve to withdraw the coins</LightText> + <p> + <Input invalid={!!exchange && !currency}> + <label>Exchange</label> + <input type="text" placeholder="https://" value={exchange} onChange={(e) => setExchange(e.currentTarget.value)} /> + <small>http://exchange.taler:8081</small> + </Input> + {currency && <InputWithLabel invalid={!!amount && !parsedAmount}> + <label>Amount</label> + <div> + <div>{currency}</div> + <input type="number" style={{ paddingLeft: `${currency.length}em` }} value={amount} onChange={e => setAmount(e.currentTarget.value)} /> + </div> + </InputWithLabel>} + </p> + </section> + <footer> + <div /> + <ButtonPrimary disabled={!parsedAmount || !exchange} onClick={() => onCreate(exchange, parsedAmount!)}>Create</ButtonPrimary> + </footer> + </WalletBox> + ); +} diff --git a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx new file mode 100644 index 000000000..d4daefc2e --- /dev/null +++ b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx @@ -0,0 +1,71 @@ +/* + 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 { VNode } from "preact"; +import { useEffect, useRef, useState } from "preact/hooks"; +import { CreateManualWithdraw } from "./CreateManualWithdraw"; +import * as wxApi from '../wxApi' +import { AcceptManualWithdrawalResult, AmountJson, Amounts } from "@gnu-taler/taler-util"; +import { ReserveCreated } from "./ReserveCreated.js"; + +interface Props { + +} + +export function ManualWithdrawPage({ }: Props): VNode { + const [success, setSuccess] = useState<AcceptManualWithdrawalResult | undefined>(undefined) + const [currency, setCurrency] = useState<string | undefined>(undefined) + const [error, setError] = useState<string | undefined>(undefined) + + async function onExchangeChange(exchange: string | undefined) { + if (!exchange) return + try { + const r = await fetch(`${exchange}/keys`) + const j = await r.json() + setCurrency(j.currency) + } catch (e) { + setError('The exchange url seems invalid') + setCurrency(undefined) + } + } + + async function doCreate(exchangeBaseUrl: string, amount: AmountJson) { + try { + const resp = await wxApi.acceptManualWithdrawal(exchangeBaseUrl, Amounts.stringify(amount)) + setSuccess(resp) + } catch (e) { + if (e instanceof Error) { + setError(e.message) + } else { + setError('unexpected error') + } + setSuccess(undefined) + } + } + + if (success) { + return <ReserveCreated reservePub={success.reservePub} paytos={success.exchangePaytoUris} onBack={() => {}}/> + } + + return <CreateManualWithdraw + error={error} currency={currency} + onCreate={doCreate} onExchangeChange={onExchangeChange} + />; +} + + + diff --git a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx new file mode 100644 index 000000000..ca524f4e2 --- /dev/null +++ b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx @@ -0,0 +1,40 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { createExample } from '../test-utils'; +import { ReserveCreated as TestedComponent } from './ReserveCreated'; + +export default { + title: 'wallet/manual withdraw/reserve created', + component: TestedComponent, + argTypes: { + } +}; + + +export const InitialState = createExample(TestedComponent, { + reservePub: 'ASLKDJQWLKEJASLKDJSADLKASJDLKSADJ', + paytos: [ + 'payto://x-taler-bank/bank.taler:5882/exchangeminator?amount=COL%3A1&message=Taler+Withdrawal+A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG', + 'payto://x-taler-bank/international-bank.com/myaccount?amount=COL%3A1&message=Taler+Withdrawal+TYQTE7VA4M9GZQ4TR06YBNGA05AJGMFNSK4Q62NXR2FKNDB1J4EX', + ] +}); + diff --git a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx new file mode 100644 index 000000000..e01336e02 --- /dev/null +++ b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx @@ -0,0 +1,41 @@ +import { Fragment, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { QR } from "../components/QR"; +import { ButtonBox, FontIcon, WalletBox } from "../components/styled"; + +export interface Props { + reservePub: string; + paytos: string[]; + onBack: () => void; +} + +export function ReserveCreated({ reservePub, paytos, onBack }: Props): VNode { + const [opened, setOpened] = useState(-1) + return ( + <WalletBox> + <section> + <h2>Reserve created!</h2> + <p>Now you need to send money to the exchange to one of the following accounts</p> + <p>To complete the setup of the reserve, you must now initiate a wire transfer using the given wire transfer subject and crediting the specified amount to the indicated account of the exchange.</p> + </section> + <section> + <ul> + {paytos.map((href, idx) => { + const url = new URL(href) + return <li key={idx}><p> + <a href="" onClick={(e) => { setOpened(o => o === idx ? -1 : idx); e.preventDefault() }}>{url.pathname}</a> + {opened === idx && <Fragment> + <p>If your system supports RFC 8905, you can do this by opening <a href={href}>this URI</a> or scan the QR with your wallet</p> + <QR text={href} /> + </Fragment>} + </p></li> + })} + </ul> + </section> + <footer> + <ButtonBox onClick={onBack}><FontIcon>←</FontIcon></ButtonBox> + <div /> + </footer> + </WalletBox> + ); +} diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx index 435753725..052b77dd0 100644 --- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx @@ -51,7 +51,7 @@ export function TransactionPage({ tid }: { tid: string; }): JSX.Element { transaction={transaction} onDelete={() => wxApi.deleteTransaction(tid).then(_ => history.go(-1))} onRetry={() => wxApi.retryTransaction(tid).then(_ => history.go(-1))} - onBack={() => { history.go(-1); }} />; + onBack={() => { route(Pages.history) }} />; } export interface WalletTransactionProps { |