diff options
Diffstat (limited to 'packages/taler-wallet-webextension')
11 files changed, 167 insertions, 25 deletions
diff --git a/packages/taler-wallet-webextension/src/components/Checkbox.tsx b/packages/taler-wallet-webextension/src/components/Checkbox.tsx index 0eb087b07..2e14f3367 100644 --- a/packages/taler-wallet-webextension/src/components/Checkbox.tsx +++ b/packages/taler-wallet-webextension/src/components/Checkbox.tsx @@ -17,8 +17,8 @@ import { h, VNode } from "preact"; interface Props { - enabled: boolean; - onToggle: () => void; + enabled?: boolean; + onToggle?: () => void; label: VNode; name: string; description?: VNode; diff --git a/packages/taler-wallet-webextension/src/components/SelectList.tsx b/packages/taler-wallet-webextension/src/components/SelectList.tsx index aa17d82b8..9271240f0 100644 --- a/packages/taler-wallet-webextension/src/components/SelectList.tsx +++ b/packages/taler-wallet-webextension/src/components/SelectList.tsx @@ -20,7 +20,7 @@ import { NiceSelect } from "./styled/index.js"; interface Props { value?: string; - onChange: (s: string) => void; + onChange?: (s: string) => void; label: VNode; list: { [label: string]: string; @@ -28,6 +28,7 @@ interface Props { name: string; description?: string; canBeNull?: boolean; + maxWidth?: boolean; } export function SelectList({ @@ -36,6 +37,7 @@ export function SelectList({ list, onChange, label, + maxWidth, description, canBeNull, }: Props): VNode { @@ -53,8 +55,9 @@ export function SelectList({ <select name={name} value={value} + style={maxWidth ? { width: "100%" } : undefined} onChange={(e) => { - onChange(e.currentTarget.value); + if (onChange) onChange(e.currentTarget.value); }} > {value === undefined || diff --git a/packages/taler-wallet-webextension/src/cta/Pay.stories.tsx b/packages/taler-wallet-webextension/src/cta/Pay.stories.tsx index 3656bbbd4..76bfa3ab3 100644 --- a/packages/taler-wallet-webextension/src/cta/Pay.stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Pay.stories.tsx @@ -101,6 +101,42 @@ export const NoEnoughBalance = createExample(TestedComponent, { goToWalletManualWithdraw: () => null, }); +export const EnoughBalanceButRestricted = createExample(TestedComponent, { + state: { + status: "ready", + hook: undefined, + amount: Amounts.parseOrThrow("USD:10"), + balance: { + currency: "USD", + fraction: 40000000, + value: 19, + }, + payHandler: { + onClick: async () => { + null; + }, + }, + totalFees: Amounts.parseOrThrow("USD:0"), + payResult: undefined, + uri: "", + 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", + }, + }, + goBack: () => null, + goToWalletManualWithdraw: () => null, +}); + export const PaymentPossible = createExample(TestedComponent, { state: { status: "ready", diff --git a/packages/taler-wallet-webextension/src/cta/Pay.tsx b/packages/taler-wallet-webextension/src/cta/Pay.tsx index 0e2530149..4f44ebab2 100644 --- a/packages/taler-wallet-webextension/src/cta/Pay.tsx +++ b/packages/taler-wallet-webextension/src/cta/Pay.tsx @@ -542,23 +542,22 @@ function ButtonsSection({ ); } if (payStatus.status === PreparePayResultType.InsufficientBalance) { + let BalanceMessage = ""; + if (!state.balance) { + BalanceMessage = i18n.str`You have no balance for this currency. Withdraw digital cash first.`; + } else { + const balanceShouldBeEnough = + Amounts.cmp(state.balance, state.amount) !== -1; + if (balanceShouldBeEnough) { + BalanceMessage = i18n.str`Could not find enough coins to pay this order. Even if you have enough ${state.balance.currency} some restriction may apply.`; + } else { + BalanceMessage = i18n.str`Your current balance is not enough for this order.`; + } + } return ( <Fragment> <section> - {state.balance ? ( - <WarningBox> - <i18n.Translate> - Your balance of {<Amount value={state.balance} />} is not - enough to pay for this purchase - </i18n.Translate> - </WarningBox> - ) : ( - <WarningBox> - <i18n.Translate> - Your balance is not enough to pay for this purchase. - </i18n.Translate> - </WarningBox> - )} + <WarningBox>{BalanceMessage}</WarningBox> </section> <section> <ButtonSuccess diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx index b77e98a10..79f9c220d 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx @@ -50,12 +50,24 @@ const normalTosState = { reviewing: false, }; +const ageRestrictionOptions: Record<string, string> = "6:12:18" + .split(":") + .reduce((p, c) => ({ ...p, [c]: `under ${c}` }), {}); + +ageRestrictionOptions["0"] = "Not restricted"; + +const ageRestrictionSelectField = { + list: ageRestrictionOptions, + value: "0", +}; + export const TermsOfServiceNotYetLoaded = createExample(TestedComponent, { state: { hook: undefined, status: "success", cancelEditExchange: nullHandler, confirmEditExchange: nullHandler, + ageRestriction: ageRestrictionSelectField, chosenAmount: { currency: "USD", value: 2, @@ -91,6 +103,7 @@ export const WithSomeFee = createExample(TestedComponent, { status: "success", cancelEditExchange: nullHandler, confirmEditExchange: nullHandler, + ageRestriction: ageRestrictionSelectField, chosenAmount: { currency: "USD", value: 2, @@ -127,6 +140,7 @@ export const WithoutFee = createExample(TestedComponent, { status: "success", cancelEditExchange: nullHandler, confirmEditExchange: nullHandler, + ageRestriction: ageRestrictionSelectField, chosenAmount: { currency: "USD", value: 2, @@ -163,6 +177,7 @@ export const EditExchangeUntouched = createExample(TestedComponent, { status: "success", cancelEditExchange: nullHandler, confirmEditExchange: nullHandler, + ageRestriction: ageRestrictionSelectField, chosenAmount: { currency: "USD", value: 2, @@ -199,6 +214,7 @@ export const EditExchangeModified = createExample(TestedComponent, { status: "success", cancelEditExchange: nullHandler, confirmEditExchange: nullHandler, + ageRestriction: ageRestrictionSelectField, chosenAmount: { currency: "USD", value: 2, @@ -236,3 +252,40 @@ export const CompletedWithoutBankURL = createExample(TestedComponent, { hook: undefined, }, }); + +export const WithAgeRestrictionSelected = createExample(TestedComponent, { + state: { + hook: undefined, + status: "success", + cancelEditExchange: nullHandler, + confirmEditExchange: nullHandler, + ageRestriction: ageRestrictionSelectField, + chosenAmount: { + currency: "USD", + value: 2, + fraction: 10000000, + }, + doWithdrawal: nullHandler, + editExchange: nullHandler, + exchange: { + list: exchangeList, + value: "exchange.demo.taler.net", + onChange: async () => { + null; + }, + }, + showExchangeSelection: false, + mustAcceptFirst: false, + withdrawalFee: { + currency: "USD", + fraction: 0, + value: 0, + }, + toBeReceived: { + currency: "USD", + fraction: 0, + value: 2, + }, + tosProps: normalTosState, + }, +}); diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx index cd0ba2cc3..c4bc3457a 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx +++ b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx @@ -35,6 +35,7 @@ import { SelectList } from "../components/SelectList.js"; import { ButtonSuccess, ButtonWarning, + Input, LinkSuccess, SubTitle, SuccessBox, @@ -43,12 +44,18 @@ import { import { useTranslationContext } from "../context/translation.js"; import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; import { buildTermsOfServiceState } from "../utils/index.js"; -import { ButtonHandler, SelectFieldHandler } from "../mui/handlers.js"; +import { + ButtonHandler, + SelectFieldHandler, + ToggleHandler, +} from "../mui/handlers.js"; import * as wxApi from "../wxApi.js"; import { Props as TermsOfServiceSectionProps, TermsOfServiceSection, } from "./TermsOfServiceSection.js"; +import { startOfWeekYear } from "date-fns/esm"; +import { Checkbox } from "../components/Checkbox.js"; interface Props { talerWithdrawUri?: string; @@ -97,6 +104,8 @@ type Success = { doWithdrawal: ButtonHandler; tosProps?: TermsOfServiceSectionProps; mustAcceptFirst: boolean; + + ageRestriction: SelectFieldHandler; }; export function useComponentState( @@ -106,6 +115,7 @@ export function useComponentState( const [customExchange, setCustomExchange] = useState<string | undefined>( undefined, ); + const [ageRestricted, setAgeRestricted] = useState(0); /** * Ask the wallet about the withdraw URI @@ -228,6 +238,7 @@ export function useComponentState( const res = await api.acceptWithdrawal( talerWithdrawUri, selectedExchange, + !ageRestricted ? undefined : ageRestricted, ); if (res.confirmTransferUrl) { document.location.href = res.confirmTransferUrl; @@ -320,6 +331,14 @@ export function useComponentState( termsState !== undefined && (termsState.status === "changed" || termsState.status === "new"); + const ageRestrictionOptions: Record<string, string> | undefined = "6:12:18" + .split(":") + .reduce((p, c) => ({ ...p, [c]: `under ${c}` }), {}); + + if (ageRestrictionOptions) { + ageRestrictionOptions["0"] = "Not restricted"; + } + return { status: "success", hook: undefined, @@ -331,6 +350,11 @@ export function useComponentState( toBeReceived, withdrawalFee, chosenAmount: amount, + ageRestriction: { + list: ageRestrictionOptions, + value: String(ageRestricted), + onChange: async (v) => setAgeRestricted(parseInt(v, 10)), + }, doWithdrawal: { onClick: doingWithdraw || (mustAcceptFirst && !reviewed) @@ -486,6 +510,18 @@ export function View({ state }: { state: State }): VNode { </LinkSuccess> )} </section> + <section> + <Input> + <SelectList + label={<i18n.Translate>Age restriction</i18n.Translate>} + list={state.ageRestriction.list} + name="age" + maxWidth + value={state.ageRestriction.value} + onChange={state.ageRestriction.onChange} + /> + </Input> + </section> {state.tosProps && <TermsOfServiceSection {...state.tosProps} />} {state.tosProps ? ( <section> diff --git a/packages/taler-wallet-webextension/src/mui/handlers.ts b/packages/taler-wallet-webextension/src/mui/handlers.ts index 60cfee421..646bdcf17 100644 --- a/packages/taler-wallet-webextension/src/mui/handlers.ts +++ b/packages/taler-wallet-webextension/src/mui/handlers.ts @@ -17,7 +17,7 @@ export interface ToggleHandler { } export interface SelectFieldHandler { - onChange: (value: string) => Promise<void>; + onChange?: (value: string) => Promise<void>; error?: string; value: string; isDirty?: boolean; diff --git a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.test.ts b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.test.ts index a4b333f02..7a9a5314b 100644 --- a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.test.ts +++ b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.test.ts @@ -87,7 +87,7 @@ describe("CreateManualWithdraw states", () => { const { exchange, currency } = getLastResultOrThrow() expect(exchange.value).equal("url2") - + if (currency.onChange === undefined) expect.fail(); currency.onChange("USD") } @@ -111,6 +111,7 @@ describe("CreateManualWithdraw states", () => { expect(exchange.value).equal("url2") expect(currency.value).equal("ARS") + if (exchange.onChange === undefined) expect.fail(); exchange.onChange("url1") } @@ -205,6 +206,7 @@ async function defaultTestForInputSelect(awaiter: () => Promise<void>, getField: throw new Error('no enough values') } nextValue = keys[nextIdx] + if (field.onChange === undefined) expect.fail(); field.onChange(nextValue) } diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage.test.ts b/packages/taler-wallet-webextension/src/wallet/DepositPage.test.ts index c863b27d5..5fc55934d 100644 --- a/packages/taler-wallet-webextension/src/wallet/DepositPage.test.ts +++ b/packages/taler-wallet-webextension/src/wallet/DepositPage.test.ts @@ -258,6 +258,7 @@ describe("DepositPage states", () => { expect(r.depositHandler.onClick).undefined; expect(r.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:0`)) + if (r.account.onChange === undefined) expect.fail(); r.account.onChange("1") } @@ -290,6 +291,7 @@ describe("DepositPage states", () => { expect(r.totalToDeposit).deep.eq(Amounts.parseOrThrow(`${currency}:7`)) expect(r.depositHandler.onClick).undefined; + if (r.account.onChange === undefined) expect.fail(); r.account.onChange("0") } diff --git a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx index 829e60b44..c4725a8d7 100644 --- a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx @@ -81,6 +81,7 @@ export function DeveloperPage(): VNode { type CoinsInfo = CoinDumpJson["coins"]; type CalculatedCoinfInfo = { + ageKeysCount: number | undefined; denom_value: number; remain_value: number; status: string; @@ -132,11 +133,13 @@ export function View({ const money_by_exchange = coins.reduce( (prev, cur) => { const denom = Amounts.parseOrThrow(cur.denom_value); + console.log(cur); if (!prev[cur.exchange_base_url]) { prev[cur.exchange_base_url] = []; currencies[cur.exchange_base_url] = denom.currency; } prev[cur.exchange_base_url].push({ + ageKeysCount: cur.ageCommitmentProof?.proof.privateKeys.length, denom_value: parseFloat(Amounts.stringifyValue(denom)), remain_value: parseFloat( Amounts.stringifyValue(Amounts.parseOrThrow(cur.remaining_value)), @@ -305,7 +308,7 @@ function ShowAllCoins({ <p> <b>{ex}</b>: {total} {currencies[ex]} </p> - <p> + <p onClick={() => setCollapsedUnspent(true)}> <b> <i18n.Translate>usable coins</i18n.Translate> </b> @@ -313,7 +316,7 @@ function ShowAllCoins({ {collapsedUnspent ? ( <div onClick={() => setCollapsedUnspent(false)}>click to show</div> ) : ( - <table onClick={() => setCollapsedUnspent(true)}> + <table> <tr> <td> <i18n.Translate>id</i18n.Translate> @@ -330,6 +333,9 @@ function ShowAllCoins({ <td> <i18n.Translate>from refresh?</i18n.Translate> </td> + <td> + <i18n.Translate>age key count</i18n.Translate> + </td> </tr> {coins.usable.map((c, idx) => { return ( @@ -339,12 +345,13 @@ function ShowAllCoins({ <td>{c.remain_value}</td> <td>{c.status}</td> <td>{c.from_refresh ? "true" : "false"}</td> + <td>{String(c.ageKeysCount)}</td> </tr> ); })} </table> )} - <p> + <p onClick={() => setCollapsedSpent(true)}> <i18n.Translate>spent coins</i18n.Translate> </p> {collapsedSpent ? ( @@ -352,7 +359,7 @@ function ShowAllCoins({ <i18n.Translate>click to show</i18n.Translate> </div> ) : ( - <table onClick={() => setCollapsedSpent(true)}> + <table> <tr> <td> <i18n.Translate>id</i18n.Translate> diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts index b48826645..dd4eb2cf4 100644 --- a/packages/taler-wallet-webextension/src/wxApi.ts +++ b/packages/taler-wallet-webextension/src/wxApi.ts @@ -324,10 +324,12 @@ export function preparePay(talerPayUri: string): Promise<PreparePayResult> { export function acceptWithdrawal( talerWithdrawUri: string, selectedExchange: string, + restrictAge?: number, ): Promise<AcceptWithdrawalResponse> { return callBackend("acceptBankIntegratedWithdrawal", { talerWithdrawUri, exchangeBaseUrl: selectedExchange, + restrictAge }); } @@ -340,10 +342,12 @@ export function acceptWithdrawal( export function acceptManualWithdrawal( exchangeBaseUrl: string, amount: string, + restrictAge?: number, ): Promise<AcceptManualWithdrawalResult> { return callBackend("acceptManualWithdrawal", { amount, exchangeBaseUrl, + restrictAge }); } |