some ui fixing from belen comments

This commit is contained in:
Sebastian 2021-09-27 13:06:50 -03:00
parent 8cde98947b
commit b1bf3538e6
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
6 changed files with 212 additions and 118 deletions

View File

@ -407,7 +407,7 @@ export class Amounts {
return `${a.currency}:${s}`;
}
static stringifyValue(a: AmountJson): string {
static stringifyValue(a: AmountJson, minFractional: number = 0): string {
const av = a.value + Math.floor(a.fraction / amountFractionalBase);
const af = a.fraction % amountFractionalBase;
let s = av.toString();
@ -416,7 +416,7 @@ export class Amounts {
s = s + ".";
let n = af;
for (let i = 0; i < amountFractionalLength; i++) {
if (!n) {
if (!n && i >= minFractional) {
break;
}
s = s + Math.floor((n / amountFractionalBase) * 10).toString();

View File

@ -22,7 +22,7 @@ export function ErrorMessage({ title, description }: { title?: string|VNode; des
const [showErrorDetail, setShowErrorDetail] = useState(false);
if (!title)
return null;
return <ErrorBox>
return <ErrorBox style={{paddingTop: 0, paddingBottom: 0}}>
<div>
<p>{title}</p>
{ description && <button onClick={() => { setShowErrorDetail(v => !v); }}>

View File

@ -520,8 +520,7 @@ export const ErrorBox = styled.div`
justify-content: space-between;
flex-direction: column;
/* margin: 0.5em; */
padding-left: 1em;
padding-right: 1em;
padding: 1em;
/* width: 100%; */
color: #721c24;
background: #f8d7da;
@ -539,6 +538,19 @@ export const ErrorBox = styled.div`
}
}
`
export const SuccessBox = styled(ErrorBox)`
color: #0f5132;
background-color: #d1e7dd;
border-color: #badbcc;
`
export const WarningBox = styled(ErrorBox)`
color: #664d03;
background-color: #fff3cd;
border-color: #ffecb5;
`
export const PopupNavigation = styled.div<{ devMode?: boolean }>`
background-color:#0042b2;
height: 35px;

View File

@ -30,7 +30,7 @@ export default {
},
};
export const InsufficientBalance = createExample(TestedComponent, {
export const NoBalance = createExample(TestedComponent, {
payStatus: {
status: PreparePayResultType.InsufficientBalance,
noncePriv: '',
@ -46,6 +46,27 @@ export const InsufficientBalance = createExample(TestedComponent, {
}
});
export const NoEnoughBalance = createExample(TestedComponent, {
payStatus: {
status: PreparePayResultType.InsufficientBalance,
noncePriv: '',
proposalId: "proposal1234",
contractTerms: {
merchant: {
name: 'someone'
},
summary: 'some beers',
amount: 'USD:10',
} as Partial<ContractTerms> as any,
amountRaw: 'USD:10',
},
balance: {
currency: 'USD',
fraction: 40000000,
value: 9
}
});
export const PaymentPossible = createExample(TestedComponent, {
uri: 'taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0',
payStatus: {
@ -66,6 +87,26 @@ export const PaymentPossible = createExample(TestedComponent, {
}
});
export const PaymentPossibleWithFee = createExample(TestedComponent, {
uri: 'taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0',
payStatus: {
status: PreparePayResultType.PaymentPossible,
amountEffective: 'USD:10.20',
amountRaw: 'USD:10',
noncePriv: '',
contractTerms: {
nonce: '123213123',
merchant: {
name: 'someone'
},
amount: 'USD:10',
summary: 'some beers',
} as Partial<ContractTerms> as any,
contractTermsHash: '123456',
proposalId: 'proposal1234'
}
});
export const AlreadyConfirmedWithFullfilment = createExample(TestedComponent, {
payStatus: {
status: PreparePayResultType.AlreadyConfirmed,
@ -102,3 +143,22 @@ export const AlreadyConfirmedWithoutFullfilment = createExample(TestedComponent,
paid: false,
}
});
export const AlreadyPaid = createExample(TestedComponent, {
payStatus: {
status: PreparePayResultType.AlreadyConfirmed,
amountEffective: 'USD:10',
amountRaw: 'USD:10',
contractTerms: {
merchant: {
name: 'someone'
},
fulfillment_message: 'congratulations! you are looking at the fulfillment message! ',
summary: 'some beers',
amount: 'USD:10',
} as Partial<ContractTerms> as any,
contractTermsHash: '123456',
proposalId: 'proposal1234',
paid: true,
}
});

View File

@ -24,56 +24,45 @@
*/
// import * as i18n from "../i18n";
import { renderAmount, ProgressButton } from "../renderHtml";
import * as wxApi from "../wxApi";
import { useState, useEffect } from "preact/hooks";
import { AmountLike, ConfirmPayResultDone, getJsonI18n, i18n } from "@gnu-taler/taler-util";
import {
PreparePayResult,
ConfirmPayResult,
AmountJson,
PreparePayResultType,
Amounts,
ContractTerms,
ConfirmPayResultType,
} from "@gnu-taler/taler-util";
import { JSX, VNode, h, Fragment } from "preact";
import { ButtonDestructive, ButtonSuccess, ButtonWarning, LinkSuccess, LinkWarning, WalletAction } from "../components/styled";
import { AmountJson, AmountLike, Amounts, ConfirmPayResult, ConfirmPayResultDone, ConfirmPayResultType, ContractTerms, getJsonI18n, i18n, PreparePayResult, PreparePayResultType } from "@gnu-taler/taler-util";
import { Fragment, JSX, VNode } from "preact";
import { useEffect, useState } from "preact/hooks";
import { LogoHeader } from "../components/LogoHeader";
import { Part } from "../components/Part";
import { QR } from "../components/QR";
import { ButtonSuccess, LinkSuccess, SuccessBox, WalletAction, WarningBox } from "../components/styled";
import { useBalances } from "../hooks/useBalances";
import * as wxApi from "../wxApi";
interface Props {
talerPayUri?: string
}
export function AlreadyPaid({ payStatus }: { payStatus: PreparePayResult }) {
const fulfillmentUrl = payStatus.contractTerms.fulfillment_url;
let message;
if (fulfillmentUrl) {
message = (
<span>
You have already paid for this article. Click{" "}
<a href={fulfillmentUrl} target="_bank" rel="external">here</a> to view it again.
</span>
);
} else {
message = <span>
You have already paid for this article:{" "}
<em>
{payStatus.contractTerms.fulfillment_message ?? "no message given"}
</em>
</span>;
}
return <section class="main">
<h1>GNU Taler Wallet</h1>
<article class="fade">
{message}
</article>
</section>
}
// export function AlreadyPaid({ payStatus }: { payStatus: PreparePayResult }) {
// const fulfillmentUrl = payStatus.contractTerms.fulfillment_url;
// let message;
// if (fulfillmentUrl) {
// message = (
// <span>
// You have already paid for this article. Click{" "}
// <a href={fulfillmentUrl} target="_bank" rel="external">here</a> to view it again.
// </span>
// );
// } else {
// message = <span>
// You have already paid for this article:{" "}
// <em>
// {payStatus.contractTerms.fulfillment_message ?? "no message given"}
// </em>
// </span>;
// }
// return <section class="main">
// <h1>GNU Taler Wallet</h1>
// <article class="fade">
// {message}
// </article>
// </section>
// }
const doPayment = async (payStatus: PreparePayResult): Promise<ConfirmPayResultDone> => {
if (payStatus.status !== "payment-possible") {
@ -98,6 +87,12 @@ export function PayPage({ talerPayUri }: Props): JSX.Element {
const [payResult, setPayResult] = useState<ConfirmPayResult | undefined>(undefined);
const [payErrMsg, setPayErrMsg] = useState<string | undefined>("");
const balance = useBalances()
const balanceWithoutError = balance?.error ? [] : (balance?.response.balances || [])
const foundBalance = balanceWithoutError.find(b => payStatus && Amounts.parseOrThrow(b.available).currency === Amounts.parseOrThrow(payStatus?.amountRaw).currency)
const foundAmount = foundBalance ? Amounts.parseOrThrow(foundBalance.available) : undefined
useEffect(() => {
if (!talerPayUri) return;
const doFetch = async (): Promise<void> => {
@ -115,24 +110,24 @@ export function PayPage({ talerPayUri }: Props): JSX.Element {
return <span>Loading payment information ...</span>;
}
if (payResult && payResult.type === ConfirmPayResultType.Done) {
if (payResult.contractTerms.fulfillment_message) {
const obj = {
fulfillment_message: payResult.contractTerms.fulfillment_message,
fulfillment_message_i18n:
payResult.contractTerms.fulfillment_message_i18n,
};
const msg = getJsonI18n(obj, "fulfillment_message");
return (
<div>
<p>Payment succeeded.</p>
<p>{msg}</p>
</div>
);
} else {
return <span>Redirecting ...</span>;
}
}
// if (payResult && payResult.type === ConfirmPayResultType.Done) {
// if (payResult.contractTerms.fulfillment_message) {
// const obj = {
// fulfillment_message: payResult.contractTerms.fulfillment_message,
// fulfillment_message_i18n:
// payResult.contractTerms.fulfillment_message_i18n,
// };
// const msg = getJsonI18n(obj, "fulfillment_message");
// return (
// <div>
// <p>Payment succeeded.</p>
// <p>{msg}</p>
// </div>
// );
// } else {
// return <span>Redirecting ...</span>;
// }
// }
const onClick = async () => {
try {
@ -147,7 +142,7 @@ export function PayPage({ talerPayUri }: Props): JSX.Element {
}
return <PaymentRequestView uri={talerPayUri} payStatus={payStatus} onClick={onClick} payErrMsg={payErrMsg} />;
return <PaymentRequestView uri={talerPayUri} payStatus={payStatus} onClick={onClick} payErrMsg={payErrMsg} balance={foundAmount} />;
}
export interface PaymentRequestViewProps {
@ -155,8 +150,9 @@ export interface PaymentRequestViewProps {
onClick: () => void;
payErrMsg?: string;
uri: string;
balance: AmountJson | undefined;
}
export function PaymentRequestView({ uri, payStatus, onClick, payErrMsg }: PaymentRequestViewProps) {
export function PaymentRequestView({ uri, payStatus, onClick, payErrMsg, balance }: PaymentRequestViewProps) {
let totalFees: AmountJson = Amounts.getZero(payStatus.amountRaw);
const contractTerms: ContractTerms = payStatus.contractTerms;
@ -183,71 +179,98 @@ export function PaymentRequestView({ uri, payStatus, onClick, payErrMsg }: Payme
merchantName = <strong>(pub: {contractTerms.merchant_pub})</strong>;
}
const [showQR, setShowQR] = useState<boolean>(false)
const privateUri = payStatus.status !== PreparePayResultType.AlreadyConfirmed ? `${uri}&n=${payStatus.noncePriv}` : uri
return <WalletAction>
<LogoHeader />
<h2>
{i18n.str`Digital cash payment`}
</h2>
<section>
{payStatus.status === PreparePayResultType.InsufficientBalance ?
<Part title="Insufficient balance" text="No enough coins to pay" kind='negative' /> :
<Part big title="Total amount with fee" text={amountToString(payStatus.amountEffective)} kind='negative' />
}
<Part big title="Purchase amount" text={amountToString(payStatus.amountRaw)} kind='neutral' />
{Amounts.isNonZero(totalFees) && <Part big title="Fee" text={amountToString(totalFees)} kind='negative' />}
<Part title="Merchant" text={contractTerms.merchant.name} kind='neutral' />
<Part title="Purchase" text={contractTerms.summary} kind='neutral' />
{contractTerms.order_id && <Part title="Receipt" text={`#${contractTerms.order_id}`} kind='neutral' />}
function Alternative() {
const [showQR, setShowQR] = useState<boolean>(false)
const privateUri = payStatus.status !== PreparePayResultType.AlreadyConfirmed ? `${uri}&n=${payStatus.noncePriv}` : uri
return <section>
<LinkSuccess upperCased onClick={() => setShowQR(qr => !qr)}>
{!showQR ? i18n.str`Pay with a mobile phone` : i18n.str`Hide QR`}
</LinkSuccess>
{showQR && <div>
<QR text={privateUri} />
Scan the QR code or <a href={privateUri}>click here</a>
</div>}
</section>
{showQR && <section>
<QR text={privateUri} />
Scan the QR code or <a href={privateUri}>click here</a>
</section>}
<section>
{payErrMsg ? (
}
function ButtonsSection() {
if (payErrMsg) {
return <section>
<div>
<p>Payment failed: {payErrMsg}</p>
<button class="pure-button button-success" onClick={onClick} >
{i18n.str`Retry`}
</button>
</div>
) : (
payStatus.status === PreparePayResultType.PaymentPossible ? <Fragment>
<LinkSuccess upperCased onClick={() => setShowQR(qr => !qr)}>
{!showQR ? i18n.str`Complete with mobile wallet` : i18n.str`Hide QR`}
</LinkSuccess>
</section>
}
if (payStatus.status === PreparePayResultType.PaymentPossible) {
return <Fragment>
<section>
<ButtonSuccess upperCased>
{i18n.str`Confirm payment`}
{i18n.str`Pay`} {amountToString(payStatus.amountEffective)}
</ButtonSuccess>
</Fragment> : (
payStatus.status === PreparePayResultType.InsufficientBalance ? <Fragment>
<LinkSuccess upperCased onClick={() => setShowQR(qr => !qr)}>
{!showQR ? i18n.str`Pay with other device` : i18n.str`Hide QR`}
</LinkSuccess>
<ButtonDestructive upperCased disabled>
{i18n.str`No enough coins`}
</ButtonDestructive>
</Fragment> :
<Fragment>
{payStatus.contractTerms.fulfillment_message && <div>
{payStatus.contractTerms.fulfillment_message}
</div>}
<LinkWarning upperCased href={payStatus.contractTerms.fulfillment_url}>
{i18n.str`Already paid`}
</LinkWarning>
</Fragment>
</section>
<Alternative />
</Fragment>
}
if (payStatus.status === PreparePayResultType.InsufficientBalance) {
return <Fragment>
<section>
{balance ? <WarningBox>
Your balance of {amountToString(balance)} is not enough to pay for this purchase
</WarningBox> : <WarningBox>
Your balance is not enough to pay for this purchase.
</WarningBox>}
</section>
<section>
<ButtonSuccess upperCased>
{i18n.str`Withdraw digital cash`}
</ButtonSuccess>
</section>
<Alternative />
</Fragment>
}
if (payStatus.status === PreparePayResultType.AlreadyConfirmed) {
return <Fragment>
<section>
{payStatus.paid && contractTerms.fulfillment_message && <Part title="Merchant message" text={contractTerms.fulfillment_message} kind='neutral' />}
</section>
{!payStatus.paid && <Alternative />}
</Fragment>
}
return <span />
}
)
)}
return <WalletAction>
<LogoHeader />
<h2>
{i18n.str`Digital cash payment`}
</h2>
{payStatus.status === PreparePayResultType.AlreadyConfirmed &&
(payStatus.paid ? <SuccessBox> Already paid </SuccessBox> : <WarningBox> Already confirmed </WarningBox>)
}
<section>
{payStatus.status !== PreparePayResultType.InsufficientBalance && Amounts.isNonZero(totalFees) &&
<Part big title="Total to pay" text={amountToString(payStatus.amountEffective)} kind='negative' />
}
<Part big title="Purchase amount" text={amountToString(payStatus.amountRaw)} kind='neutral' />
{Amounts.isNonZero(totalFees) && <Fragment>
<Part big title="Fee" text={amountToString(totalFees)} kind='negative' />
</Fragment>
}
<Part title="Merchant" text={contractTerms.merchant.name} kind='neutral' />
<Part title="Purchase" text={contractTerms.summary} kind='neutral' />
{contractTerms.order_id && <Part title="Receipt" text={`#${contractTerms.order_id}`} kind='neutral' />}
</section>
<ButtonsSection />
</WalletAction>
}
function amountToString(text: AmountLike) {
const aj = Amounts.jsonifyAmount(text)
const amount = Amounts.stringifyValue(aj)
const amount = Amounts.stringifyValue(aj, 2)
return `${amount} ${aj.currency}`
}

View File

@ -32,7 +32,6 @@ 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 {