balance refactor

This commit is contained in:
Sebastian 2021-07-14 15:21:40 -03:00
parent c34c7d37cb
commit 18c5371d65
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
7 changed files with 287 additions and 175 deletions

View File

@ -238,3 +238,4 @@ export const ErrorBox = styled.div`
} }
} }
` `

View File

@ -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;
}

View File

@ -18,7 +18,7 @@
import { i18n, Timestamp } from "@gnu-taler/taler-util"; import { i18n, Timestamp } from "@gnu-taler/taler-util";
import { ProviderInfo, ProviderPaymentStatus } from "@gnu-taler/taler-wallet-core"; import { ProviderInfo, ProviderPaymentStatus } from "@gnu-taler/taler-wallet-core";
import { differenceInMonths, formatDuration, intervalToDuration } from "date-fns"; import { differenceInMonths, formatDuration, intervalToDuration } from "date-fns";
import { FunctionalComponent, Fragment, JSX, VNode, AnyComponent } from "preact"; import { Fragment, JSX, VNode } from "preact";
import { import {
BoldLight, ButtonPrimary, ButtonSuccess, Centered, BoldLight, ButtonPrimary, ButtonSuccess, Centered,
CenteredText, CenteredTextBold, PopupBox, RowBorderGray, CenteredText, CenteredTextBold, PopupBox, RowBorderGray,

View File

@ -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,
});

View File

@ -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 />
);
}
}

View File

@ -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>
}

View File

@ -29,7 +29,7 @@ import { DevContextProvider } from "./context/useDevContext";
import { useTalerActionURL } from "./hooks/useTalerActionURL"; import { useTalerActionURL } from "./hooks/useTalerActionURL";
import { strings } from "./i18n/strings"; import { strings } from "./i18n/strings";
import { BackupPage } from "./popup/BackupPage"; import { BackupPage } from "./popup/BackupPage";
import { BalancePage } from "./popup/Balance"; import { BalancePage } from "./popup/BalancePage";
import { DeveloperPage as DeveloperPage } from "./popup/Debug"; import { DeveloperPage as DeveloperPage } from "./popup/Debug";
import { HistoryPage } from "./popup/History"; import { HistoryPage } from "./popup/History";
import { import {