diff options
author | Sebastian <sebasjm@gmail.com> | 2021-08-13 18:04:05 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2021-08-13 18:04:30 -0300 |
commit | e9bb85a212dbd9b86875e89a0aca5d805e2ad61b (patch) | |
tree | 7fb2f9f28fc382d7348cd45cb730b302217099c7 /packages/taler-wallet-webextension/src/wallet | |
parent | eb553be84163946e4ffa5b2a4dfaa2029aebc534 (diff) |
new wallet UI and more tests
Diffstat (limited to 'packages/taler-wallet-webextension/src/wallet')
10 files changed, 646 insertions, 265 deletions
diff --git a/packages/taler-wallet-webextension/src/wallet/Pay.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Pay.stories.tsx new file mode 100644 index 000000000..0297d6264 --- /dev/null +++ b/packages/taler-wallet-webextension/src/wallet/Pay.stories.tsx @@ -0,0 +1,103 @@ +/* + 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 { ContractTerms, PreparePayResultType } from '@gnu-taler/taler-util'; +import { FunctionalComponent, h } from 'preact'; +import { PaymentRequestView as TestedComponent } from './Pay'; + + +export default { + title: 'wallet/pay', + 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 InsufficientBalance = createExample(TestedComponent, { + payStatus: { + status: PreparePayResultType.InsufficientBalance, + proposalId: "proposal1234", + contractTerms: { + merchant: { + name: 'someone' + }, + amount: 'USD:10', + } as Partial<ContractTerms> as any, + amountRaw: 'USD:10', + } +}); + +export const PaymentPossible = createExample(TestedComponent, { + payStatus: { + status: PreparePayResultType.PaymentPossible, + amountEffective: 'USD:10', + amountRaw: 'USD:10', + contractTerms: { + merchant: { + name: 'someone' + }, + amount: 'USD:10', + } as Partial<ContractTerms> as any, + contractTermsHash: '123456', + proposalId: 'proposal1234' + } +}); + +export const AlreadyConfirmedWithFullfilment = 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! ', + amount: 'USD:10', + } as Partial<ContractTerms> as any, + contractTermsHash: '123456', + proposalId: 'proposal1234', + paid: false, + } +}); + +export const AlreadyConfirmedWithoutFullfilment = createExample(TestedComponent, { + payStatus: { + status: PreparePayResultType.AlreadyConfirmed, + amountEffective: 'USD:10', + amountRaw: 'USD:10', + contractTerms: { + merchant: { + name: 'someone' + }, + amount: 'USD:10', + } as Partial<ContractTerms> as any, + contractTermsHash: '123456', + proposalId: 'proposal1234', + paid: false, + } +}); diff --git a/packages/taler-wallet-webextension/src/wallet/Pay.tsx b/packages/taler-wallet-webextension/src/wallet/Pay.tsx index bd06656c7..a5849bb28 100644 --- a/packages/taler-wallet-webextension/src/wallet/Pay.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Pay.tsx @@ -29,7 +29,7 @@ import * as wxApi from "../wxApi"; import { useState, useEffect } from "preact/hooks"; -import { getJsonI18n, i18n } from "@gnu-taler/taler-util"; +import { ConfirmPayResultDone, getJsonI18n, i18n } from "@gnu-taler/taler-util"; import { PreparePayResult, ConfirmPayResult, @@ -45,13 +45,54 @@ 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> +} + +const doPayment = async (payStatus: PreparePayResult): Promise<ConfirmPayResultDone> => { + if (payStatus.status !== "payment-possible") { + throw Error(`invalid state: ${payStatus.status}`); + } + const proposalId = payStatus.proposalId; + const res = await wxApi.confirmPay(proposalId, undefined); + if (res.type !== ConfirmPayResultType.Done) { + throw Error("payment pending"); + } + const fu = res.contractTerms.fulfillment_url; + if (fu) { + document.location.href = fu; + } + return res; +}; + + + export function PayPage({ talerPayUri }: Props): JSX.Element { const [payStatus, setPayStatus] = useState<PreparePayResult | undefined>(undefined); const [payResult, setPayResult] = useState<ConfirmPayResult | undefined>(undefined); const [payErrMsg, setPayErrMsg] = useState<string | undefined>(""); - const [numTries, setNumTries] = useState(0); - const [loading, setLoading] = useState(false); - let totalFees: AmountJson | undefined = undefined; useEffect(() => { if (!talerPayUri) return; @@ -60,53 +101,67 @@ export function PayPage({ talerPayUri }: Props): JSX.Element { setPayStatus(p); }; doFetch(); - }, [numTries, talerPayUri]); + }, [talerPayUri]); if (!talerPayUri) { return <span>missing pay uri</span> } - + if (!payStatus) { return <span>Loading payment information ...</span>; } - let insufficientBalance = false; - if (payStatus.status == PreparePayResultType.InsufficientBalance) { - insufficientBalance = true; - } - - if (payStatus.status === PreparePayResultType.PaymentPossible) { - const amountRaw = Amounts.parseOrThrow(payStatus.amountRaw); - const amountEffective: AmountJson = Amounts.parseOrThrow( - payStatus.amountEffective, - ); - totalFees = Amounts.sub(amountEffective, amountRaw).amount; - } - - if ( - payStatus.status === PreparePayResultType.AlreadyConfirmed && - numTries === 0 - ) { - const fulfillmentUrl = payStatus.contractTerms.fulfillment_url; - if (fulfillmentUrl) { + 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 ( - <span> - You have already paid for this article. Click{" "} - <a href={fulfillmentUrl} target="_bank" rel="external">here</a> to view it again. - </span> + <div> + <p>Payment succeeded.</p> + <p>{msg}</p> + </div> ); } else { - <span> - You have already paid for this article:{" "} - <em> - {payStatus.contractTerms.fulfillment_message ?? "no message given"} - </em> - </span>; + return <span>Redirecting ...</span>; } } + const onClick = async () => { + try { + const res = await doPayment(payStatus) + setPayResult(res); + } catch (e) { + console.error(e); + setPayErrMsg(e.message); + } + + } + + return <PaymentRequestView payStatus={payStatus} onClick={onClick} payErrMsg={payErrMsg} />; +} + +export interface PaymentRequestViewProps { + payStatus: PreparePayResult; + onClick: () => void; + payErrMsg?: string; + +} +export function PaymentRequestView({ payStatus, onClick, payErrMsg }: PaymentRequestViewProps) { + let totalFees: AmountJson | undefined = undefined; + let insufficientBalance = false; + const [loading, setLoading] = useState(false); const contractTerms: ContractTerms = payStatus.contractTerms; + if ( + payStatus.status === PreparePayResultType.AlreadyConfirmed + ) { + return <AlreadyPaid payStatus={payStatus} /> + } + if (!contractTerms) { return ( <span> @@ -115,6 +170,18 @@ export function PayPage({ talerPayUri }: Props): JSX.Element { ); } + if (payStatus.status == PreparePayResultType.InsufficientBalance) { + insufficientBalance = true; + } + + if (payStatus.status === PreparePayResultType.PaymentPossible) { + const amountRaw = Amounts.parseOrThrow(payStatus.amountRaw); + const amountEffective: AmountJson = Amounts.parseOrThrow( + payStatus.amountEffective, + ); + totalFees = Amounts.sub(amountEffective, amountRaw).amount; + } + let merchantName: VNode; if (contractTerms.merchant && contractTerms.merchant.name) { merchantName = <strong>{contractTerms.merchant.name}</strong>; @@ -126,99 +193,61 @@ export function PayPage({ talerPayUri }: Props): JSX.Element { <strong>{renderAmount(Amounts.parseOrThrow(contractTerms.amount))}</strong> ); - const doPayment = async (): Promise<void> => { - if (payStatus.status !== "payment-possible") { - throw Error(`invalid state: ${payStatus.status}`); - } - const proposalId = payStatus.proposalId; - setNumTries(numTries + 1); - try { - setLoading(true); - const res = await wxApi.confirmPay(proposalId, undefined); - if (res.type !== ConfirmPayResultType.Done) { - throw Error("payment pending"); - } - const fu = res.contractTerms.fulfillment_url; - if (fu) { - document.location.href = fu; - } - setPayResult(res); - } catch (e) { - console.error(e); - setPayErrMsg(e.message); - } - }; - - 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>; - } - } - - return ( - <div> - <p> - <i18n.Translate> - The merchant <span>{merchantName}</span> offers you to purchase: - </i18n.Translate> - <div style={{ textAlign: "center" }}> - <strong>{contractTerms.summary}</strong> - </div> - {totalFees ? ( + return <section class="main"> + <h1>GNU Taler Wallet</h1> + <article class="fade"> + <div> + <p> <i18n.Translate> - The total price is <span>{amount} </span> - (plus <span>{renderAmount(totalFees)}</span> fees). - </i18n.Translate> + The merchant <span>{merchantName}</span> offers you to purchase: + </i18n.Translate> + <div style={{ textAlign: "center" }}> + <strong>{contractTerms.summary}</strong> + </div> + {totalFees ? ( + <i18n.Translate> + The total price is <span>{amount} </span> + (plus <span>{renderAmount(totalFees)}</span> fees). + </i18n.Translate> + ) : ( + <i18n.Translate> + The total price is <span>{amount}</span>. + </i18n.Translate> + )} + </p> + + {insufficientBalance ? ( + <div> + <p style={{ color: "red", fontWeight: "bold" }}> + Unable to pay: Your balance is insufficient. + </p> + </div> + ) : null} + + {payErrMsg ? ( + <div> + <p>Payment failed: {payErrMsg}</p> + <button + class="pure-button button-success" + onClick={onClick} + > + {i18n.str`Retry`} + </button> + </div> ) : ( - <i18n.Translate> - The total price is <span>{amount}</span>. - </i18n.Translate> - )} - </p> - - {insufficientBalance ? ( - <div> - <p style={{ color: "red", fontWeight: "bold" }}> - Unable to pay: Your balance is insufficient. - </p> - </div> - ) : null} - - {payErrMsg ? ( - <div> - <p>Payment failed: {payErrMsg}</p> - <button - class="pure-button button-success" - onClick={() => doPayment()} - > - {i18n.str`Retry`} - </button> - </div> - ) : ( - <div> - <ProgressButton - isLoading={loading} - disabled={insufficientBalance} - onClick={() => doPayment()} - > - {i18n.str`Confirm payment`} - </ProgressButton> - </div> - )} - </div> - ); -} - + <div> + <ProgressButton + isLoading={loading} + disabled={insufficientBalance} + onClick={onClick} + > + {i18n.str`Confirm payment`} + </ProgressButton> + </div> + )} + </div> + </article> + </section> + + +}
\ No newline at end of file diff --git a/packages/taler-wallet-webextension/src/wallet/Refund.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Refund.stories.tsx new file mode 100644 index 000000000..044141f0c --- /dev/null +++ b/packages/taler-wallet-webextension/src/wallet/Refund.stories.tsx @@ -0,0 +1,83 @@ +/* + 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 { ContractTerms, OrderShortInfo, PreparePayResultType } from '@gnu-taler/taler-util'; +import { FunctionalComponent, h } from 'preact'; +import { View as TestedComponent } from './Refund'; + + +export default { + title: 'wallet/refund', + 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 Complete = createExample(TestedComponent, { + applyResult: { + amountEffectivePaid: 'USD:10', + amountRefundGone: 'USD:0', + amountRefundGranted: 'USD:2', + contractTermsHash: 'QWEASDZXC', + info: { + summary: 'tasty cold beer', + contractTermsHash: 'QWEASDZXC', + } as Partial<OrderShortInfo> as any, + pendingAtExchange: false, + proposalId: "proposal123", + } +}); + +export const Partial = createExample(TestedComponent, { + applyResult: { + amountEffectivePaid: 'USD:10', + amountRefundGone: 'USD:1', + amountRefundGranted: 'USD:2', + contractTermsHash: 'QWEASDZXC', + info: { + summary: 'tasty cold beer', + contractTermsHash: 'QWEASDZXC', + } as Partial<OrderShortInfo> as any, + pendingAtExchange: false, + proposalId: "proposal123", + } +}); + +export const InProgress = createExample(TestedComponent, { + applyResult: { + amountEffectivePaid: 'USD:10', + amountRefundGone: 'USD:1', + amountRefundGranted: 'USD:2', + contractTermsHash: 'QWEASDZXC', + info: { + summary: 'tasty cold beer', + contractTermsHash: 'QWEASDZXC', + } as Partial<OrderShortInfo> as any, + pendingAtExchange: true, + proposalId: "proposal123", + } +}); diff --git a/packages/taler-wallet-webextension/src/wallet/Refund.tsx b/packages/taler-wallet-webextension/src/wallet/Refund.tsx index 702217415..bb26d933b 100644 --- a/packages/taler-wallet-webextension/src/wallet/Refund.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Refund.tsx @@ -32,7 +32,32 @@ import { JSX } from "preact/jsx-runtime"; interface Props { talerRefundUri?: string } - +export interface ViewProps { + applyResult: ApplyRefundResponse; +} +export function View({ applyResult }: ViewProps) { + return <section class="main"> + <h1>GNU Taler Wallet</h1> + <article class="fade"> + <h2>Refund Status</h2> + <p> + The product <em>{applyResult.info.summary}</em> has received a total + effective refund of{" "} + <AmountView amount={applyResult.amountRefundGranted} />. + </p> + {applyResult.pendingAtExchange ? ( + <p>Refund processing is still in progress.</p> + ) : null} + {!Amounts.isZero(applyResult.amountRefundGone) ? ( + <p> + The refund amount of{" "} + <AmountView amount={applyResult.amountRefundGone} />{" "} + could not be applied. + </p> + ) : null} + </article> + </section> +} export function RefundPage({ talerRefundUri }: Props): JSX.Element { const [applyResult, setApplyResult] = useState<ApplyRefundResponse | undefined>(undefined); const [errMsg, setErrMsg] = useState<string | undefined>(undefined); @@ -66,24 +91,5 @@ export function RefundPage({ talerRefundUri }: Props): JSX.Element { return <span>Updating refund status</span>; } - return ( - <> - <h2>Refund Status</h2> - <p> - The product <em>{applyResult.info.summary}</em> has received a total - effective refund of{" "} - <AmountView amount={applyResult.amountRefundGranted} />. - </p> - {applyResult.pendingAtExchange ? ( - <p>Refund processing is still in progress.</p> - ) : null} - {!Amounts.isZero(applyResult.amountRefundGone) ? ( - <p> - The refund amount of{" "} - <AmountView amount={applyResult.amountRefundGone} /> - could not be applied. - </p> - ) : null} - </> - ); + return <View applyResult={applyResult} />; } diff --git a/packages/taler-wallet-webextension/src/wallet/Tip.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Tip.stories.tsx new file mode 100644 index 000000000..ffd976144 --- /dev/null +++ b/packages/taler-wallet-webextension/src/wallet/Tip.stories.tsx @@ -0,0 +1,66 @@ +/* + 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 { ContractTerms, PreparePayResultType } from '@gnu-taler/taler-util'; +import { FunctionalComponent, h } from 'preact'; +import { View as TestedComponent } from './Tip'; + + +export default { + title: 'wallet/tip', + 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 Accepted = createExample(TestedComponent, { + prepareTipResult: { + accepted: true, + merchantBaseUrl: '', + exchangeBaseUrl: '', + expirationTimestamp : { + t_ms: 0 + }, + tipAmountEffective: 'USD:10', + tipAmountRaw: 'USD:5', + walletTipId: 'id' + } +}); + +export const NotYetAccepted = createExample(TestedComponent, { + prepareTipResult: { + accepted: false, + merchantBaseUrl: 'http://merchant.url/', + exchangeBaseUrl: 'http://exchange.url/', + expirationTimestamp : { + t_ms: 0 + }, + tipAmountEffective: 'USD:10', + tipAmountRaw: 'USD:5', + walletTipId: 'id' + } +}); diff --git a/packages/taler-wallet-webextension/src/wallet/Tip.tsx b/packages/taler-wallet-webextension/src/wallet/Tip.tsx index 708e8940b..69886668b 100644 --- a/packages/taler-wallet-webextension/src/wallet/Tip.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Tip.tsx @@ -26,8 +26,41 @@ import { AmountView } from "../renderHtml"; import * as wxApi from "../wxApi"; import { JSX } from "preact/jsx-runtime"; -interface Props { - talerTipUri?: string +interface Props { + talerTipUri?: string +} +export interface ViewProps { + prepareTipResult: PrepareTipResult; + onAccept: () => void; + onIgnore: () => void; + +} +export function View({ prepareTipResult, onAccept, onIgnore }: ViewProps) { + return <section class="main"> + <h1>GNU Taler Wallet</h1> + <article class="fade"> + {prepareTipResult.accepted ? ( + <span> + Tip from <code>{prepareTipResult.merchantBaseUrl}</code> accepted. Check + your transactions list for more details. + </span> + ) : ( + <div> + <p> + The merchant <code>{prepareTipResult.merchantBaseUrl}</code> is + offering you a tip of{" "} + <strong> + <AmountView amount={prepareTipResult.tipAmountEffective} /> + </strong>{" "} + via the exchange <code>{prepareTipResult.exchangeBaseUrl}</code> + </p> + <button onClick={onAccept}>Accept tip</button> + <button onClick={onIgnore}>Ignore</button> + </div> + )} + </article> + </section> + } export function TipPage({ talerTipUri }: Props): JSX.Element { @@ -71,27 +104,7 @@ export function TipPage({ talerTipUri }: Props): JSX.Element { return <span>Loading ...</span>; } - if (prepareTipResult.accepted) { - return ( - <span> - Tip from <code>{prepareTipResult.merchantBaseUrl}</code> accepted. Check - your transactions list for more details. - </span> - ); - } else { - return ( - <div> - <p> - The merchant <code>{prepareTipResult.merchantBaseUrl}</code> is - offering you a tip of{" "} - <strong> - <AmountView amount={prepareTipResult.tipAmountEffective} /> - </strong>{" "} - via the exchange <code>{prepareTipResult.exchangeBaseUrl}</code> - </p> - <button onClick={doAccept}>Accept tip</button> - <button onClick={doIgnore}>Ignore</button> - </div> - ); - } + return <View prepareTipResult={prepareTipResult} + onAccept={doAccept} onIgnore={doIgnore} + /> } diff --git a/packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx new file mode 100644 index 000000000..4fa87a137 --- /dev/null +++ b/packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx @@ -0,0 +1,56 @@ +/* + 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 { FunctionalComponent, h } from 'preact'; +import { View as TestedComponent } from './Welcome'; + + +export default { + title: 'wallet/welcome', + component: TestedComponent, +}; + +function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + const r = (args: any) => <Component {...args} /> + r.args = props + return r +} + +export const Normal = createExample(TestedComponent, { + permissionsEnabled: true, + diagnostics: { + errors: [], + walletManifestVersion: '1.0', + walletManifestDisplayVersion: '1.0', + firefoxIdbProblem: false, + dbOutdated: false, + } +}); + +export const TimedoutDiagnostics = createExample(TestedComponent, { + timedOut: true, + permissionsEnabled: false, +}); + +export const RunningDiagnostics = createExample(TestedComponent, { + permissionsEnabled: false, +}); + diff --git a/packages/taler-wallet-webextension/src/wallet/Welcome.tsx b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx index c74384596..4c33e1c72 100644 --- a/packages/taler-wallet-webextension/src/wallet/Welcome.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx @@ -24,13 +24,36 @@ import { JSX } from "preact/jsx-runtime"; import { Checkbox } from "../components/Checkbox"; import { useExtendedPermissions } from "../hooks/useExtendedPermissions"; import { Diagnostics } from "../components/Diagnostics"; +import { WalletPage } from "../components/styled"; +import { useDiagnostics } from "../hooks/useDiagnostics"; +import { WalletDiagnostics } from "@gnu-taler/taler-util"; -export function WelcomePage(): JSX.Element { +export function WelcomePage() { const [permissionsEnabled, togglePermissions] = useExtendedPermissions() - return ( - <> + const [diagnostics, timedOut] = useDiagnostics() + return <View + permissionsEnabled={permissionsEnabled} togglePermissions={togglePermissions} + diagnostics={diagnostics} timedOut={timedOut} + /> +} + +export interface ViewProps { + permissionsEnabled: boolean, + togglePermissions: () => void, + diagnostics: WalletDiagnostics | undefined, + timedOut: boolean, +} +export function View({ permissionsEnabled, togglePermissions, diagnostics, timedOut }: ViewProps): JSX.Element { + return (<WalletPage> + <div style="border-bottom: 3px dashed #aa3939; margin-bottom: 2em;"> + <h1 style="font-family: monospace; font-size: 250%;"> + <span style="color: #aa3939;">❰</span>Taler Wallet<span style="color: #aa3939;">❱</span> + </h1> + </div> + <h1>Browser Extension Installed!</h1> + <div> <p>Thank you for installing the wallet.</p> - <Diagnostics /> + <Diagnostics diagnostics={diagnostics} timedOut={timedOut} /> <h2>Permissions</h2> <Checkbox label="Automatically open wallet based on page content" name="perm" @@ -44,6 +67,7 @@ export function WelcomePage(): JSX.Element { <a href="https://demo.taler.net/" style={{ display: "block" }}> Learn how to top up your wallet balance » </a> - </> + </div> + </WalletPage> ); } diff --git a/packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx index 24fb17dfa..fef36b820 100644 --- a/packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx @@ -30,27 +30,12 @@ export default { }, }; -export const WithoutURI = (a: any) => <View {...a} />; -WithoutURI.args = { -} as ViewProps - export const WithoutDetails = (a: any) => <View {...a} />; WithoutDetails.args = { - talerWithdrawUri: 'http://something' -} as ViewProps - -export const Cancelled = (a: any) => <View {...a} />; -Cancelled.args = { - talerWithdrawUri: 'http://something', - details: { - amount: 'USD:2', - }, - cancelled: true } as ViewProps export const CompleteWithExchange = (a: any) => <View {...a} />; CompleteWithExchange.args = { - talerWithdrawUri: 'http://something', details: { amount: 'USD:2', }, @@ -59,7 +44,6 @@ CompleteWithExchange.args = { export const CompleteWithoutExchange = (a: any) => <View {...a} />; CompleteWithoutExchange.args = { - talerWithdrawUri: 'http://something', details: { amount: 'USD:2', }, diff --git a/packages/taler-wallet-webextension/src/wallet/Withdraw.tsx b/packages/taler-wallet-webextension/src/wallet/Withdraw.tsx index 4cb8ebfa1..442ee7dae 100644 --- a/packages/taler-wallet-webextension/src/wallet/Withdraw.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Withdraw.tsx @@ -32,6 +32,7 @@ import { } from "../wxApi"; import { WithdrawUriInfoResponse } from "@gnu-taler/taler-util"; import { JSX } from "preact/jsx-runtime"; +import { WalletPage } from '../components/styled'; interface Props { talerWithdrawUri?: string; @@ -39,79 +40,72 @@ interface Props { export interface ViewProps { talerWithdrawUri?: string; - details?: WithdrawUriInfoResponse; - cancelled?: boolean; + details: WithdrawUriInfoResponse; selectedExchange?: string; accept: () => Promise<void>; setCancelled: (b: boolean) => void; setSelecting: (b: boolean) => void; }; -export function View({ talerWithdrawUri, details, cancelled, selectedExchange, accept, setCancelled, setSelecting }: ViewProps) { - const [state, setState] = useState(1) - setTimeout(() => { - setState(s => s + 1) - }, 1000); - if (!talerWithdrawUri) { - return <span><i18n.Translate>missing withdraw uri</i18n.Translate></span>; - } - - if (!details) { - return <span><i18n.Translate>Loading...</i18n.Translate></span>; - } - - if (cancelled) { - return <span><i18n.Translate>Withdraw operation has been cancelled.{state}</i18n.Translate></span>; - } +export function View({ details, selectedExchange, accept, setCancelled, setSelecting }: ViewProps) { return ( - <div> - <h1><i18n.Translate>Digital Cash Withdrawal</i18n.Translate></h1> - <p><i18n.Translate> - You are about to withdraw{" "} - <strong>{renderAmount(details.amount)}</strong> from your bank account - into your wallet. - </i18n.Translate></p> - {selectedExchange ? ( - <p><i18n.Translate> - The exchange <strong>{selectedExchange}</strong> will be used as the - Taler payment service provider. - </i18n.Translate></p> - ) : null} - - <div> - <button - class="pure-button button-success" - disabled={!selectedExchange} - onClick={() => accept()} - > - {i18n.str`Accept fees and withdraw`} - </button> - <p> - <span - role="button" - tabIndex={0} - style={{ textDecoration: "underline", cursor: "pointer" }} - onClick={() => setSelecting(true)} - > - {i18n.str`Chose different exchange provider`} - </span> - <br /> - <span - role="button" - tabIndex={0} - style={{ textDecoration: "underline", cursor: "pointer" }} - onClick={() => setCancelled(true)} - > - {i18n.str`Cancel withdraw operation`} - </span> - </p> + <WalletPage> + <div style="border-bottom: 3px dashed #aa3939; margin-bottom: 2em;"> + <h1 style="font-family: monospace; font-size: 250%;"> + <span style="color: #aa3939;">❰</span>Taler Wallet<span style="color: #aa3939;">❱</span> + </h1> + </div> + <div class="fade"> + <div> + <h1><i18n.Translate>Digital Cash Withdrawal</i18n.Translate></h1> + <p><i18n.Translate> + You are about to withdraw{" "} + <strong>{renderAmount(details.amount)}</strong> from your bank account + into your wallet. + </i18n.Translate></p> + {selectedExchange ? ( + <p><i18n.Translate> + The exchange <strong>{selectedExchange}</strong> will be used as the + Taler payment service provider. + </i18n.Translate></p> + ) : null} + + <div> + <button + class="pure-button button-success" + disabled={!selectedExchange} + onClick={() => accept()} + > + {i18n.str`Accept fees and withdraw`} + </button> + <p> + <span + role="button" + tabIndex={0} + style={{ textDecoration: "underline", cursor: "pointer" }} + onClick={() => setSelecting(true)} + > + {i18n.str`Chose different exchange provider`} + </span> + <br /> + <span + role="button" + tabIndex={0} + style={{ textDecoration: "underline", cursor: "pointer" }} + onClick={() => setCancelled(true)} + > + {i18n.str`Cancel withdraw operation`} + </span> + </p> + </div> + </div> </div> - </div> + </WalletPage> ) } -export function WithdrawPage({ talerWithdrawUri }: Props): JSX.Element { +export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element { const [details, setDetails] = useState<WithdrawUriInfoResponse | undefined>(undefined); const [selectedExchange, setSelectedExchange] = useState< string | undefined @@ -120,27 +114,44 @@ export function WithdrawPage({ talerWithdrawUri }: Props): JSX.Element { const [selecting, setSelecting] = useState(false); const [errMsg, setErrMsg] = useState<string | undefined>(""); const [updateCounter, setUpdateCounter] = useState(1); + const [state, setState] = useState(1) + + // setTimeout(() => { + // console.log('tick...') + // setState(s => s + 1) + // }, 1000); useEffect(() => { return onUpdateNotification(() => { + console.log('updating...') setUpdateCounter(updateCounter + 1); }); }, []); useEffect(() => { + console.log('on effect yes', talerWithdrawUri) if (!talerWithdrawUri) return const fetchData = async (): Promise<void> => { - const res = await getWithdrawalDetailsForUri({ talerWithdrawUri }); - setDetails(res); - if (res.defaultExchangeBaseUrl) { - setSelectedExchange(res.defaultExchangeBaseUrl); + console.log('que pasa') + try { + const res = await getWithdrawalDetailsForUri({ talerWithdrawUri }); + console.log('res', res) + setDetails(res); + if (res.defaultExchangeBaseUrl) { + setSelectedExchange(res.defaultExchangeBaseUrl); + } + } catch (e) { + console.error(e) } }; fetchData(); - }, [selectedExchange, errMsg, selecting, talerWithdrawUri, updateCounter]); + }, [selectedExchange, errMsg, selecting, talerWithdrawUri, updateCounter, state]); + + if (!talerWithdrawUri) { + return <span><i18n.Translate>missing withdraw uri</i18n.Translate></span>; + } const accept = async (): Promise<void> => { - if (!talerWithdrawUri) return if (!selectedExchange) { throw Error("can't accept, no exchange selected"); } @@ -152,10 +163,16 @@ export function WithdrawPage({ talerWithdrawUri }: Props): JSX.Element { } }; + if (!details) { + return <span><i18n.Translate>Loading...</i18n.Translate></span>; + } + if (cancelled) { + return <span><i18n.Translate>Withdraw operation has been cancelled.</i18n.Translate></span>; + } + return <View accept={accept} setCancelled={setCancelled} setSelecting={setSelecting} - cancelled={cancelled} details={details} selectedExchange={selectedExchange} - talerWithdrawUri={talerWithdrawUri} + details={details} selectedExchange={selectedExchange} /> } |