diff options
author | Sebastian <sebasjm@gmail.com> | 2021-07-14 15:21:40 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2021-07-14 15:21:40 -0300 |
commit | 18c5371d659222155b27883b2531576af0f86d54 (patch) | |
tree | 457f2ae32f5146256dd0b912cf054ead57694e29 | |
parent | c34c7d37cb0327ff7d8b5c3d25e2928f3023d30c (diff) |
balance refactor
7 files changed, 287 insertions, 175 deletions
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 <http://www.gnu.org/licenses/> + */ + +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<BalancesHook>(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 <http://www.gnu.org/licenses/> + */ + +/** +* +* @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<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + const r = (args: any) => <Component {...args} /> + 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 <http://www.gnu.org/licenses/> - */ - -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 ( - <span> - <span style={{ fontSize: "5em", display: "block" }}>{v}</span>{" "} - <span>{amount.currency}</span> - </span> - ); -} - -function EmptyBalanceView(): JSX.Element { - return ( - <p><i18n.Translate> - You have no balance to show. Need some{" "} - <PageLink pageName="/welcome">help</PageLink> getting started? - </i18n.Translate></p> - ); -} - - -export class BalancePage extends Component<any, any> { - 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<void> { - 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 = ( - <span><i18n.Translate> - <span style={{ color: "darkgreen" }}> - {"+"} - {renderAmount(entry.pendingIncoming)} - </span>{" "} - incoming - </i18n.Translate></span> - ); - } - - const l = [incoming, payment].filter((x) => x !== undefined); - if (l.length === 0) { - return <span />; - } - - if (l.length === 1) { - return <span>({l})</span>; - } - return ( - <span> - ({l[0]}, {l[1]}) - </span> - ); - } - - render(): JSX.Element { - const wallet = this.balance; - if (this.gotError) { - return ( - <div class="balance"> - <p>{i18n.str`Error: could not retrieve balance information.`}</p> - <p> - Click <PageLink pageName="welcome.html">here</PageLink> for help and - diagnostics. - </p> - </div> - ); - } - if (!wallet) { - return <span></span>; - } - - const listing = wallet.balances.map((entry) => { - const av = Amounts.parseOrThrow(entry.available); - return ( - <p key={av.currency}> - {bigAmount(av)} {this.formatPending(entry)} - </p> - ); - }); - return listing.length > 0 ? ( - <div class="balance">{listing}</div> - ) : ( - <EmptyBalanceView /> - ); - } -} 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 <http://www.gnu.org/licenses/> + */ + +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 <BalanceView balance={balance} Linker={PageLink} /> +} +export interface BalanceViewProps { + balance: BalancesHook, + Linker: typeof PageLink, +} +export function BalanceView({ balance, Linker }: BalanceViewProps) { + if (!balance) { + return <span /> + } + + if (balance.error) { + return ( + <div class="balance"> + <p>{i18n.str`Error: could not retrieve balance information.`}</p> + <p> + Click <Linker pageName="welcome.html">here</Linker> for help and + diagnostics. + </p> + </div> + ) + } + if (balance.response.balances.length === 0) { + return ( + <p><i18n.Translate> + You have no balance to show. Need some{" "} + <Linker pageName="/welcome">help</Linker> getting started? + </i18n.Translate></p> + ) + } + return <ShowBalances wallet={balance.response} /> +} + +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 = ( + <span><i18n.Translate> + <span style={{ color: "darkgreen" }}> + {"+"} + {renderAmount(entry.pendingIncoming)} + </span>{" "} + incoming + </i18n.Translate></span> + ); + } + + const l = [incoming, payment].filter((x) => x !== undefined); + if (l.length === 0) { + return <span />; + } + + if (l.length === 1) { + return <span>({l})</span>; + } + return ( + <span> + ({l[0]}, {l[1]}) + </span> + ); +} + + +function ShowBalances({ wallet }: { wallet: BalancesResponse }) { + return <PopupBox> + <section> + <Centered>{wallet.balances.map((entry) => { + const av = Amounts.parseOrThrow(entry.available); + const v = av.value + av.fraction / amountFractionalBase; + return ( + <p key={av.currency}> + <span> + <span style={{ fontSize: "5em", display: "block" }}>{v}</span>{" "} + <span>{av.currency}</span> + </span> + {formatPending(entry)} + </p> + ); + })}</Centered> + </section> + </PopupBox> +} 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 { |