From 8cde98947ba1a6d8c7928578b053786c4e5db17f Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 20 Sep 2021 14:05:40 -0300 Subject: manual withdrawal process --- .../src/NavigationBar.tsx | 1 + .../src/components/styled/index.tsx | 27 ++++++++ .../taler-wallet-webextension/src/cta/Withdraw.tsx | 2 +- .../src/popup/BalancePage.tsx | 23 ++++--- .../src/popupEntryPoint.tsx | 20 +++++- .../src/wallet/BackupPage.tsx | 6 +- .../src/wallet/BalancePage.tsx | 27 +++++--- .../src/wallet/CreateManualWithdraw.stories.tsx | 56 +++++++++++++++++ .../src/wallet/CreateManualWithdraw.tsx | 57 +++++++++++++++++ .../src/wallet/ManualWithdrawPage.tsx | 71 ++++++++++++++++++++++ .../src/wallet/ReserveCreated.stories.tsx | 40 ++++++++++++ .../src/wallet/ReserveCreated.tsx | 41 +++++++++++++ .../src/wallet/Transaction.tsx | 2 +- .../src/walletEntryPoint.tsx | 18 ++++-- packages/taler-wallet-webextension/src/wxApi.ts | 18 ++++++ 15 files changed, 380 insertions(+), 29 deletions(-) create mode 100644 packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx create mode 100644 packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx create mode 100644 packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx create mode 100644 packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx create mode 100644 packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx b/packages/taler-wallet-webextension/src/NavigationBar.tsx index 9ddf610cd..5345e3bae 100644 --- a/packages/taler-wallet-webextension/src/NavigationBar.tsx +++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx @@ -33,6 +33,7 @@ import { PopupNavigation } from './components/styled' export enum Pages { welcome = '/welcome', balance = '/balance', + manual_withdraw = '/manual-withdraw', settings = '/settings', dev = '/dev', backup = '/backup', diff --git a/packages/taler-wallet-webextension/src/components/styled/index.tsx b/packages/taler-wallet-webextension/src/components/styled/index.tsx index 23b0b1b57..0dbf34b5c 100644 --- a/packages/taler-wallet-webextension/src/components/styled/index.tsx +++ b/packages/taler-wallet-webextension/src/components/styled/index.tsx @@ -486,6 +486,33 @@ export const Input = styled.div<{ invalid?: boolean }>` } ` +export const InputWithLabel = styled.div<{ invalid?: boolean }>` + & label { + display: block; + padding: 5px; + color: ${({ invalid }) => !invalid ? 'inherit' : 'red'} + } + & > div { + position: relative; + display: flex; + top: 0px; + bottom: 0px; + + & > div { + position: absolute; + background-color: lightgray; + padding: 5px; + margin: 2px; + } + + & > input { + flex: 1; + padding: 5px; + border-color: ${({ invalid }) => !invalid ? 'inherit' : 'red'} + } + } +` + export const ErrorBox = styled.div` border: 2px solid #f5c6cb; border-radius: 0.25em; diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx index b8b8159f3..46451e72c 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx +++ b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx @@ -141,7 +141,7 @@ export function View({ details, amount, onWithdraw, terms, reviewing, onReview, }
- {terms.status === 'new' && !accepted && + {terms.status === 'new' && !accepted && !reviewing && void }) { const balance = useBalances() - return + return } 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 } @@ -57,7 +58,9 @@ export function BalanceView({ balance, Linker }: BalanceViewProps) {

) } - return + return } function formatPending(entry: Balance): JSX.Element { @@ -96,7 +99,7 @@ function formatPending(entry: Balance): JSX.Element { } -function ShowBalances({ wallet }: { wallet: BalancesResponse }) { +function ShowBalances({ wallet, onWithdraw }: { wallet: BalancesResponse, onWithdraw: () => void }) { return
{wallet.balances.map((entry) => { @@ -113,5 +116,9 @@ function ShowBalances({ wallet }: { wallet: BalancesResponse }) { ); })}
+
+
+ Withdraw +
} diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx index 15e27486f..4bdc2d888 100644 --- a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx +++ b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx @@ -22,8 +22,8 @@ import { setupI18n } from "@gnu-taler/taler-util"; import { createHashHistory } from "history"; -import { render, h } from "preact"; -import Router, { route, Route } from "preact-router"; +import { render, h, VNode } from "preact"; +import Router, { route, Route, getCurrentUrl } from "preact-router"; import { useEffect, useState } from "preact/hooks"; import { DevContextProvider } from "./context/devContext"; import { useTalerActionURL } from "./hooks/useTalerActionURL"; @@ -96,9 +96,16 @@ function Application() {
- + goToWalletPage(Pages.manual_withdraw)} + /> + + goToWalletPage(Pages.transaction.replace(':tid', tid))} + /> + { @@ -123,6 +130,13 @@ function Application() { ); } +function goToWalletPage(page: Pages | string): null { + chrome.tabs.create({ + active: true, + url: chrome.extension.getURL(`/static/wallet.html#${page}`), + }) + return null +} function Redirect({ to }: { to: string }): null { useEffect(() => { 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 && + {!providers.length && No backup providers configured Add provider } @@ -98,8 +98,8 @@ function BackupLayout(props: TransactionLayoutProps): JSX.Element {
{props.title} - {dateStr && Last synced: {dateStr}} - {!dateStr && Not synced} + {dateStr && Last synced: {dateStr}} + {!dateStr && Not synced}
{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 + return } + 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 } @@ -57,7 +60,9 @@ export function BalanceView({ balance, Linker }: BalanceViewProps) {

) } - return + return } 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
{wallet.balances.map((entry) => { @@ -113,5 +118,9 @@ function ShowBalances({ wallet }: { wallet: BalancesResponse }) { ); })}
+
+
+ Withdraw +
} 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 + */ + +/** +* +* @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; +} + +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(undefined); + useEffect(() => { + if (timeout) window.clearTimeout(timeout.current) + timeout.current = window.setTimeout(async () => { + onExchangeChange(exchange) + }, 1000); + }, [exchange]) + + + return ( + +
+ +

Manual Withdrawal

+ Choose a exchange to create a reserve and then fill the reserve to withdraw the coins +

+ + + setExchange(e.currentTarget.value)} /> + http://exchange.taler:8081 + + {currency && + +

+
{currency}
+ setAmount(e.currentTarget.value)} /> +
+ } +

+
+
+
+ onCreate(exchange, parsedAmount!)}>Create +
+
+ ); +} 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 +*/ + + +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(undefined) + const [currency, setCurrency] = useState(undefined) + const [error, setError] = useState(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 {}}/> + } + + return ; +} + + + 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 + */ + +/** +* +* @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 ( + +
+

Reserve created!

+

Now you need to send money to the exchange to one of the following accounts

+

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.

+
+
+ +
+
+ +
+
+
+ ); +} 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 { diff --git a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx index aa007786c..023ee94c5 100644 --- a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx +++ b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx @@ -41,6 +41,8 @@ import { SettingsPage } from "./wallet/Settings"; import { TransactionPage } from './wallet/Transaction'; import { WelcomePage } from "./wallet/Welcome"; import { BackupPage } from './wallet/BackupPage'; +import { DeveloperPage } from "./popup/Debug.js"; +import { ManualWithdrawPage } from "./wallet/ManualWithdrawPage.js"; function main(): void { @@ -52,7 +54,9 @@ function main(): void { render(, container); } catch (e) { console.error("got error", e); - document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`; + if (e instanceof Error) { + document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`; + } } } @@ -65,10 +69,10 @@ if (document.readyState === "loading") { } function withLogoAndNavBar(Component: any) { - return () => + return (props: any) => - + } @@ -81,14 +85,20 @@ function Application() { - + route(Pages.manual_withdraw)} + /> + +
no yet implemented
} />
no yet implemented
} />
no yet implemented
} /> + + {/** call to action */} diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts index 63774b00b..8a0881a6c 100644 --- a/packages/taler-wallet-webextension/src/wxApi.ts +++ b/packages/taler-wallet-webextension/src/wxApi.ts @@ -40,6 +40,9 @@ import { SetWalletDeviceIdRequest, GetExchangeWithdrawalInfo, AcceptExchangeTosRequest, + AcceptManualWithdrawalResult, + AcceptManualWithdrawalRequest, + AmountJson, } from "@gnu-taler/taler-util"; import { AddBackupProviderRequest, BackupProviderState, OperationFailedError, RemoveBackupProviderRequest } from "@gnu-taler/taler-wallet-core"; import { BackupInfo } from "@gnu-taler/taler-wallet-core"; @@ -252,6 +255,21 @@ export function acceptWithdrawal( }); } +/** + * Create a reserve into the exchange that expect the amount indicated + * @param exchangeBaseUrl + * @param amount + * @returns + */ +export function acceptManualWithdrawal( + exchangeBaseUrl: string, + amount: string, +): Promise { + return callBackend("acceptManualWithdrawal", { + amount, exchangeBaseUrl + }); +} + export function setExchangeTosAccepted( exchangeBaseUrl: string, etag: string | undefined -- cgit v1.2.3