diff --git a/packages/taler-wallet-webextension/src/components/styled/index.tsx b/packages/taler-wallet-webextension/src/components/styled/index.tsx index 7d8118392..ebca5893e 100644 --- a/packages/taler-wallet-webextension/src/components/styled/index.tsx +++ b/packages/taler-wallet-webextension/src/components/styled/index.tsx @@ -238,3 +238,4 @@ export const ErrorBox = styled.div` } } ` + diff --git a/packages/taler-wallet-webextension/src/hooks/useBalances.tsx b/packages/taler-wallet-webextension/src/hooks/useBalances.tsx new file mode 100644 index 000000000..f12fca21c --- /dev/null +++ b/packages/taler-wallet-webextension/src/hooks/useBalances.tsx @@ -0,0 +1,52 @@ +/* + 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 { BalancesResponse } from "@gnu-taler/taler-util"; +import { useEffect, useState } from "preact/hooks"; +import * as wxApi from "../wxApi"; + + +interface BalancesHookOk { + error: false; + response: BalancesResponse; +} + +interface BalancesHookError { + error: true; +} + +export type BalancesHook = BalancesHookOk | BalancesHookError | undefined; + +export function useBalances(): BalancesHook { + const [balance, setBalance] = useState(undefined); + console.log('render balance') + useEffect(() => { + async function checkBalance() { + try { + const response = await wxApi.getBalance(); + console.log("got balance", balance); + setBalance({ error: false, response }); + } catch (e) { + console.error("could not retrieve balances", e); + setBalance({ error: true }); + } + } + checkBalance() + return wxApi.onUpdateNotification(checkBalance); + }, []); + + return balance; +} diff --git a/packages/taler-wallet-webextension/src/popup/BackupPage.tsx b/packages/taler-wallet-webextension/src/popup/BackupPage.tsx index 9428922d5..768a64a14 100644 --- a/packages/taler-wallet-webextension/src/popup/BackupPage.tsx +++ b/packages/taler-wallet-webextension/src/popup/BackupPage.tsx @@ -18,7 +18,7 @@ import { i18n, Timestamp } from "@gnu-taler/taler-util"; import { ProviderInfo, ProviderPaymentStatus } from "@gnu-taler/taler-wallet-core"; import { differenceInMonths, formatDuration, intervalToDuration } from "date-fns"; -import { FunctionalComponent, Fragment, JSX, VNode, AnyComponent } from "preact"; +import { Fragment, JSX, VNode } from "preact"; import { BoldLight, ButtonPrimary, ButtonSuccess, Centered, CenteredText, CenteredTextBold, PopupBox, RowBorderGray, diff --git a/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx b/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx new file mode 100644 index 000000000..b661ac679 --- /dev/null +++ b/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx @@ -0,0 +1,114 @@ +/* + 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 { ProviderPaymentType } from '@gnu-taler/taler-wallet-core'; +import { addDays } from 'date-fns'; +import { ComponentChild, ComponentChildren, FunctionalComponent, h } from 'preact'; +import { BalanceView as TestedComponent } from './BalancePage'; + +export default { + title: 'popup/balance/detail', + component: TestedComponent, + argTypes: { + } +}; + + +function createExample(Component: FunctionalComponent, props: Partial) { + const r = (args: any) => + r.args = props + return r +} + +export const NotYetLoaded = createExample(TestedComponent, { +}); + +const NullLink = ({ children }: { children?: ComponentChildren }) => h('a', { children, href: 'javascript:void(0);' }) +export const GotError = createExample(TestedComponent, { + balance: { + error: true + }, + Linker: NullLink, +}); + +export const EmptyBalance = createExample(TestedComponent, { + balance: { + error: false, + response: { + balances: [] + }, + }, + Linker: NullLink, +}); + +export const SomeCoins = createExample(TestedComponent, { + balance: { + error: false, + response: { + balances: [{ + available: 'USD:10.5', + hasPendingTransactions: false, + pendingIncoming: 'USD:0', + pendingOutgoing: 'USD:0', + requiresUserInput: false + }] + }, + }, + Linker: NullLink, +}); + +export const SomeCoinsAndIncomingMoney = createExample(TestedComponent, { + balance: { + error: false, + response: { + balances: [{ + available: 'USD:2.23', + hasPendingTransactions: false, + pendingIncoming: 'USD:5.11', + pendingOutgoing: 'USD:0', + requiresUserInput: false + }] + }, + }, + Linker: NullLink, +}); + +export const SomeCoinsInTwoCurrencies = createExample(TestedComponent, { + balance: { + error: false, + response: { + balances: [{ + available: 'USD:2', + hasPendingTransactions: false, + pendingIncoming: 'USD:5', + pendingOutgoing: 'USD:0', + requiresUserInput: false + },{ + available: 'EUR:4', + hasPendingTransactions: false, + pendingIncoming: 'EUR:5', + pendingOutgoing: 'EUR:0', + requiresUserInput: false + }] + }, + }, + Linker: NullLink, +}); diff --git a/packages/taler-wallet-webextension/src/popup/Balance.tsx b/packages/taler-wallet-webextension/src/popup/Balance.tsx deleted file mode 100644 index ae0eb07ff..000000000 --- a/packages/taler-wallet-webextension/src/popup/Balance.tsx +++ /dev/null @@ -1,173 +0,0 @@ -/* - This file is part of TALER - (C) 2016 GNUnet e.V. - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see - */ - -import { - Amounts, - BalancesResponse, - Balance, i18n, AmountJson, amountFractionalBase -} from "@gnu-taler/taler-util"; -import { Component, JSX } from "preact"; -import { PageLink, renderAmount } from "../renderHtml"; -import * as wxApi from "../wxApi"; - - -/** - * Render an amount as a large number with a small currency symbol. - */ -function bigAmount(amount: AmountJson): JSX.Element { - const v = amount.value + amount.fraction / amountFractionalBase; - return ( - - {v}{" "} - {amount.currency} - - ); -} - -function EmptyBalanceView(): JSX.Element { - return ( -

- You have no balance to show. Need some{" "} - help getting started? -

- ); -} - - -export class BalancePage extends Component { - private balance?: BalancesResponse; - private gotError = false; - private canceler: (() => void) | undefined = undefined; - private unmount = false; - private updateBalanceRunning = false; - - componentWillMount(): void { - this.canceler = wxApi.onUpdateNotification(() => this.updateBalance()); - this.updateBalance(); - } - - componentWillUnmount(): void { - console.log("component WalletBalanceView will unmount"); - if (this.canceler) { - this.canceler(); - } - this.unmount = true; - } - - async updateBalance(): Promise { - if (this.updateBalanceRunning) { - return; - } - this.updateBalanceRunning = true; - let balance: BalancesResponse; - try { - balance = await wxApi.getBalance(); - } catch (e) { - if (this.unmount) { - return; - } - this.gotError = true; - console.error("could not retrieve balances", e); - this.setState({}); - return; - } finally { - this.updateBalanceRunning = false; - } - if (this.unmount) { - return; - } - this.gotError = false; - console.log("got balance", balance); - this.balance = balance; - this.setState({}); - } - - formatPending(entry: Balance): JSX.Element { - let incoming: JSX.Element | undefined; - let payment: JSX.Element | undefined; - - const available = Amounts.parseOrThrow(entry.available); - const pendingIncoming = Amounts.parseOrThrow(entry.pendingIncoming); - const pendingOutgoing = Amounts.parseOrThrow(entry.pendingOutgoing); - - console.log( - "available: ", - entry.pendingIncoming ? renderAmount(entry.available) : null - ); - console.log( - "incoming: ", - entry.pendingIncoming ? renderAmount(entry.pendingIncoming) : null - ); - - if (!Amounts.isZero(pendingIncoming)) { - incoming = ( - - - {"+"} - {renderAmount(entry.pendingIncoming)} - {" "} - incoming - - ); - } - - const l = [incoming, payment].filter((x) => x !== undefined); - if (l.length === 0) { - return ; - } - - if (l.length === 1) { - return ({l}); - } - return ( - - ({l[0]}, {l[1]}) - - ); - } - - render(): JSX.Element { - const wallet = this.balance; - if (this.gotError) { - return ( -
-

{i18n.str`Error: could not retrieve balance information.`}

-

- Click here for help and - diagnostics. -

-
- ); - } - if (!wallet) { - return ; - } - - const listing = wallet.balances.map((entry) => { - const av = Amounts.parseOrThrow(entry.available); - return ( -

- {bigAmount(av)} {this.formatPending(entry)} -

- ); - }); - return listing.length > 0 ? ( -
{listing}
- ) : ( - - ); - } -} diff --git a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx new file mode 100644 index 000000000..24744d3f2 --- /dev/null +++ b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx @@ -0,0 +1,118 @@ +/* + 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 { + amountFractionalBase, Amounts, + + Balance, BalancesResponse, + i18n +} from "@gnu-taler/taler-util"; +import { JSX } from "preact"; +import { PopupBox, Centered } from "../components/styled/index"; +import { BalancesHook, useBalances } from "../hooks/useBalances"; +import { PageLink, renderAmount } from "../renderHtml"; + + +export function BalancePage() { + const balance = useBalances() + return +} +export interface BalanceViewProps { + balance: BalancesHook, + Linker: typeof PageLink, +} +export function BalanceView({ balance, Linker }: BalanceViewProps) { + if (!balance) { + return + } + + if (balance.error) { + return ( +
+

{i18n.str`Error: could not retrieve balance information.`}

+

+ Click here for help and + diagnostics. +

+
+ ) + } + if (balance.response.balances.length === 0) { + return ( +

+ You have no balance to show. Need some{" "} + help getting started? +

+ ) + } + return +} + +function formatPending(entry: Balance): JSX.Element { + let incoming: JSX.Element | undefined; + let payment: JSX.Element | undefined; + + const available = Amounts.parseOrThrow(entry.available); + const pendingIncoming = Amounts.parseOrThrow(entry.pendingIncoming); + const pendingOutgoing = Amounts.parseOrThrow(entry.pendingOutgoing); + + if (!Amounts.isZero(pendingIncoming)) { + incoming = ( + + + {"+"} + {renderAmount(entry.pendingIncoming)} + {" "} + incoming + + ); + } + + const l = [incoming, payment].filter((x) => x !== undefined); + if (l.length === 0) { + return ; + } + + if (l.length === 1) { + return ({l}); + } + return ( + + ({l[0]}, {l[1]}) + + ); +} + + +function ShowBalances({ wallet }: { wallet: BalancesResponse }) { + return +
+ {wallet.balances.map((entry) => { + const av = Amounts.parseOrThrow(entry.available); + const v = av.value + av.fraction / amountFractionalBase; + return ( +

+ + {v}{" "} + {av.currency} + + {formatPending(entry)} +

+ ); + })}
+
+
+} diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx index 42e9ab90e..613218b80 100644 --- a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx +++ b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx @@ -29,7 +29,7 @@ import { DevContextProvider } from "./context/useDevContext"; import { useTalerActionURL } from "./hooks/useTalerActionURL"; import { strings } from "./i18n/strings"; import { BackupPage } from "./popup/BackupPage"; -import { BalancePage } from "./popup/Balance"; +import { BalancePage } from "./popup/BalancePage"; import { DeveloperPage as DeveloperPage } from "./popup/Debug"; import { HistoryPage } from "./popup/History"; import {