balance refactor
This commit is contained in:
parent
c34c7d37cb
commit
18c5371d65
@ -238,3 +238,4 @@ export const ErrorBox = styled.div`
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
|
52
packages/taler-wallet-webextension/src/hooks/useBalances.tsx
Normal file
52
packages/taler-wallet-webextension/src/hooks/useBalances.tsx
Normal 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;
|
||||
}
|
@ -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,
|
||||
|
114
packages/taler-wallet-webextension/src/popup/Balance.stories.tsx
Normal file
114
packages/taler-wallet-webextension/src/popup/Balance.stories.tsx
Normal 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,
|
||||
});
|
@ -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 />
|
||||
);
|
||||
}
|
||||
}
|
118
packages/taler-wallet-webextension/src/popup/BalancePage.tsx
Normal file
118
packages/taler-wallet-webextension/src/popup/BalancePage.tsx
Normal 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>
|
||||
}
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user