diff options
Diffstat (limited to 'packages/bank-ui')
-rw-r--r-- | packages/bank-ui/package.json | 2 | ||||
-rw-r--r-- | packages/bank-ui/src/hooks/preferences.ts | 11 | ||||
-rw-r--r-- | packages/bank-ui/src/pages/OperationState/state.ts | 24 | ||||
-rw-r--r-- | packages/bank-ui/src/pages/PaytoWireTransferForm.tsx | 57 | ||||
-rw-r--r-- | packages/bank-ui/src/pages/QrCodeSection.tsx | 8 | ||||
-rw-r--r-- | packages/bank-ui/src/pages/WalletWithdrawForm.tsx | 31 | ||||
-rw-r--r-- | packages/bank-ui/src/pages/WithdrawalConfirmationQuestion.tsx | 23 | ||||
-rw-r--r-- | packages/bank-ui/src/pages/account/ShowAccountDetails.tsx | 41 | ||||
-rw-r--r-- | packages/bank-ui/src/settings.json | 2 | ||||
-rw-r--r-- | packages/bank-ui/src/settings.ts | 16 |
10 files changed, 152 insertions, 63 deletions
diff --git a/packages/bank-ui/package.json b/packages/bank-ui/package.json index f06905a93..db89e58be 100644 --- a/packages/bank-ui/package.json +++ b/packages/bank-ui/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@gnu-taler/bank-ui", - "version": "0.10.7", + "version": "0.11.4", "license": "AGPL-3.0-OR-LATER", "type": "module", "scripts": { diff --git a/packages/bank-ui/src/hooks/preferences.ts b/packages/bank-ui/src/hooks/preferences.ts index bb3dcb153..4cb5e6a95 100644 --- a/packages/bank-ui/src/hooks/preferences.ts +++ b/packages/bank-ui/src/hooks/preferences.ts @@ -31,8 +31,6 @@ interface Preferences { showWithdrawalSuccess: boolean; showDemoDescription: boolean; showInstallWallet: boolean; - maxWithdrawalAmount: number; - fastWithdrawal: boolean; showDebugInfo: boolean; } @@ -41,17 +39,13 @@ export const codecForPreferences = (): Codec<Preferences> => .property("showWithdrawalSuccess", codecForBoolean()) .property("showDemoDescription", codecForBoolean()) .property("showInstallWallet", codecForBoolean()) - .property("fastWithdrawal", codecForBoolean()) .property("showDebugInfo", codecForBoolean()) - .property("maxWithdrawalAmount", codecForNumber()) .build("Settings"); const defaultPreferences: Preferences = { showWithdrawalSuccess: true, showDemoDescription: true, showInstallWallet: true, - maxWithdrawalAmount: 25, - fastWithdrawal: false, showDebugInfo: false, }; @@ -82,7 +76,6 @@ export function usePreferences(): [ export function getAllBooleanPreferences(): Array<keyof Preferences> { return [ - "fastWithdrawal", "showDebugInfo", "showDemoDescription", "showInstallWallet", @@ -95,16 +88,12 @@ export function getLabelForPreferences( i18n: ReturnType<typeof useTranslationContext>["i18n"], ): TranslatedString { switch (k) { - case "maxWithdrawalAmount": - return i18n.str`Max withdrawal amount`; case "showWithdrawalSuccess": return i18n.str`Show withdrawal confirmation`; case "showDemoDescription": return i18n.str`Show demo description`; case "showInstallWallet": return i18n.str`Show install wallet first`; - case "fastWithdrawal": - return i18n.str`Use fast withdrawal form`; case "showDebugInfo": return i18n.str`Show debug info`; } diff --git a/packages/bank-ui/src/pages/OperationState/state.ts b/packages/bank-ui/src/pages/OperationState/state.ts index 19c097d18..5544c4e23 100644 --- a/packages/bank-ui/src/pages/OperationState/state.ts +++ b/packages/bank-ui/src/pages/OperationState/state.ts @@ -18,6 +18,7 @@ import { Amounts, HttpStatusCode, TalerCoreBankErrorsByMethod, + TalerCorebankApi, TalerError, assertUnreachable, parsePaytoUri, @@ -33,6 +34,7 @@ import { useSessionState } from "../../hooks/session.js"; import { useBankState } from "../../hooks/bank-state.js"; import { usePreferences } from "../../hooks/preferences.js"; import { Props, State } from "./index.js"; +import { useSettingsContext } from "../../context/settings.js"; export function useComponentState({ currency, @@ -41,7 +43,8 @@ export function useComponentState({ routeHere, onAuthorizationRequired, }: Props): utils.RecursiveState<State> { - const [settings] = usePreferences(); + const [preference] = usePreferences(); + const settings = useSettingsContext(); const [bankState, updateBankState] = useBankState(); const { state: credentials } = useSessionState(); const creds = credentials.status !== "loggedIn" ? undefined : credentials; @@ -52,15 +55,22 @@ export function useComponentState({ const [failure, setFailure] = useState< TalerCoreBankErrorsByMethod<"createWithdrawal"> | undefined >(); - const amount = settings.maxWithdrawalAmount; + const amount = settings.defaultSuggestedAmount; async function doSilentStart() { // FIXME: if amount is not enough use balance const parsedAmount = Amounts.parseOrThrow(`${currency}:${amount}`); if (!creds) return; - const resp = await bank.createWithdrawal(creds, { - amount: Amounts.stringify(parsedAmount), - }); + const params: TalerCorebankApi.BankAccountCreateWithdrawalRequest = + settings.fastWithdrawalForm + ? { + suggested_amount: Amounts.stringify(parsedAmount), + } + : { + amount: Amounts.stringify(parsedAmount), + }; + + const resp = await bank.createWithdrawal(creds, params); if (resp.type === "fail") { setFailure(resp); return; @@ -73,7 +83,7 @@ export function useComponentState({ if (withdrawalOperationId === undefined) { doSilentStart(); } - }, [settings.fastWithdrawal, amount]); + }, [settings.fastWithdrawalForm, amount]); if (failure) { return { @@ -174,7 +184,7 @@ export function useComponentState({ } if (data.status === "confirmed") { - if (!settings.showWithdrawalSuccess) { + if (!preference.showWithdrawalSuccess) { updateBankState("currentWithdrawalOperationId", undefined); // onClose() } diff --git a/packages/bank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/bank-ui/src/pages/PaytoWireTransferForm.tsx index 3bf891504..0fb8c0ac1 100644 --- a/packages/bank-ui/src/pages/PaytoWireTransferForm.tsx +++ b/packages/bank-ui/src/pages/PaytoWireTransferForm.tsx @@ -79,6 +79,7 @@ export function PaytoWireTransferForm({ routeHere, onAuthorizationRequired, limit, + balance, }: Props): VNode { const [inputType, setInputType] = useState<"form" | "payto" | "qr">("form"); const isRawPayto = inputType !== "form"; @@ -111,6 +112,16 @@ export function PaytoWireTransferForm({ ? ("x-taler-bank" as const) : ("iban" as const); + const wireFee = + config.wire_transfer_fees === undefined + ? Amounts.zeroOfCurrency(config.currency) + : Amounts.parseOrThrow(config.wire_transfer_fees); + + const limitWithFee = + Amounts.cmp(limit, wireFee) === 1 + ? Amounts.sub(limit, wireFee).amount + : Amounts.zeroOfAmount(limit); + const errorsWire = undefinedIfEmpty({ account: !account ? i18n.str`Required` @@ -124,7 +135,7 @@ export function PaytoWireTransferForm({ ? i18n.str`Required` : !parsedAmount ? i18n.str`Not valid` - : validateAmount(parsedAmount, limit, i18n), + : validateAmount(parsedAmount, limitWithFee, i18n), }); const parsed = !rawPaytoInput ? undefined : parsePaytoUri(rawPaytoInput); @@ -134,7 +145,7 @@ export function PaytoWireTransferForm({ ? i18n.str`Required` : !parsed ? i18n.str`Does not follow the pattern` - : validateRawPayto(parsed, limit, url.host, i18n, paytoType), + : validateRawPayto(parsed, limitWithFee, url.host, i18n, paytoType), }); async function doSend() { @@ -479,9 +490,9 @@ export function PaytoWireTransferForm({ e.preventDefault(); }} > - <div class="p-4 sm:p-8"> + <div class="m-4"> {!isRawPayto ? ( - <div class="grid max-w-xs grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> + <div class="grid max-w-xs grid-cols-1 gap-x-6 gap-y-8 "> {(() => { switch (paytoType) { case "x-taler-bank": { @@ -622,7 +633,45 @@ export function PaytoWireTransferForm({ </div> </div> )} + {Amounts.cmp(limitWithFee, balance) > 0 ? ( + <p class="mt-2 text-sm text-gray-900"> + <i18n.Translate> + You can transfer{" "} + <RenderAmount + value={limitWithFee} + spec={config.currency_specification} + /> + </i18n.Translate> + </p> + ) : undefined} </div> + {Amounts.isZero(wireFee) ? undefined : ( + <div class="px-4 my-4"> + <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> + <div class="sm:col-span-6"> + <dl class="mt-4 space-y-4"> + <Fragment> + <div class="flex items-center justify-between "> + <dt class="flex items-center text-sm text-gray-600"> + <span> + <i18n.Translate>Cost</i18n.Translate> + </span> + </dt> + <dd class="text-sm text-gray-900"> + <RenderAmount + value={wireFee} + negative + withColor + spec={config.currency_specification} + /> + </dd> + </div> + </Fragment> + </dl> + </div> + </div> + </div> + )} <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8"> {routeCancel ? ( <a diff --git a/packages/bank-ui/src/pages/QrCodeSection.tsx b/packages/bank-ui/src/pages/QrCodeSection.tsx index 359d4c18f..2a21295c7 100644 --- a/packages/bank-ui/src/pages/QrCodeSection.tsx +++ b/packages/bank-ui/src/pages/QrCodeSection.tsx @@ -86,10 +86,10 @@ export function QrCodeSection({ <div class="mt-4 mb-4 text-sm text-gray-500"> <p> <i18n.Translate> - You will see the details of the operation in your wallet - including the fees (if applies). If you still don't have one you - can install it following instructions in - </i18n.Translate>{" "} + Your wallet will display the details of the transaction + including the fees (if applicable). If you do not yet have a + wallet, please follow the instructions on + </i18n.Translate> <a class="font-semibold text-gray-500 hover:text-gray-400" name="wallet page" diff --git a/packages/bank-ui/src/pages/WalletWithdrawForm.tsx b/packages/bank-ui/src/pages/WalletWithdrawForm.tsx index a9c652643..7cf2c7881 100644 --- a/packages/bank-ui/src/pages/WalletWithdrawForm.tsx +++ b/packages/bank-ui/src/pages/WalletWithdrawForm.tsx @@ -19,6 +19,7 @@ import { AmountJson, Amounts, HttpStatusCode, + TalerCorebankApi, TranslatedString, assertUnreachable, parseWithdrawUri, @@ -45,6 +46,7 @@ import { RenderAmount, doAutoFocus, } from "./PaytoWireTransferForm.js"; +import { useSettingsContext } from "../context/settings.js"; const RefAmount = forwardRef(InputAmount); @@ -64,7 +66,7 @@ function OldWithdrawalForm({ routeCancel: RouteDefinition; }): VNode { const { i18n } = useTranslationContext(); - const [settings] = usePreferences(); + const settings = useSettingsContext(); // const walletInegrationApi = useTalerWalletIntegrationAPI() // const { navigateTo } = useNavigationContext(); @@ -79,7 +81,7 @@ function OldWithdrawalForm({ const creds = credentials.status !== "loggedIn" ? undefined : credentials; const [amountStr, setAmountStr] = useState<string | undefined>( - `${settings.maxWithdrawalAmount}`, + `${settings.defaultSuggestedAmount ?? 1}`, ); const [notification, notify, handleError] = useLocalNotification(); @@ -141,9 +143,15 @@ function OldWithdrawalForm({ async function doStart() { if (!parsedAmount || !creds) return; await handleError(async () => { - const resp = await api.createWithdrawal(creds, { - amount: Amounts.stringify(parsedAmount), - }); + const params: TalerCorebankApi.BankAccountCreateWithdrawalRequest = + settings.fastWithdrawalForm + ? { + suggested_amount: Amounts.stringify(parsedAmount), + } + : { + amount: Amounts.stringify(parsedAmount), + }; + const resp = await api.createWithdrawal(creds, params); if (resp.type === "ok") { const uri = parseWithdrawUri(resp.body.taler_withdraw_uri); if (!uri) { @@ -234,9 +242,9 @@ function OldWithdrawalForm({ </i18n.Translate> </p> {Amounts.cmp(limit, balance) > 0 ? ( - <p class="mt-2 text-sm text-gray-500"> + <p class="mt-2 text-sm text-gray-900"> <i18n.Translate> - Your account allows you to withdraw{" "} + You can withdraw{" "} <RenderAmount value={limit} spec={config.currency_specification} @@ -340,7 +348,8 @@ export function WalletWithdrawForm({ routeCancel: RouteDefinition; }): VNode { const { i18n } = useTranslationContext(); - const [settings, updateSettings] = usePreferences(); + const [pref, updatePref] = usePreferences(); + const settings = useSettingsContext(); return ( <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-6 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg"> @@ -357,11 +366,11 @@ export function WalletWithdrawForm({ </div> <div class="col-span-2"> - {settings.showInstallWallet && ( + {pref.showInstallWallet && ( <Attention title={i18n.str`You need a Taler wallet`} onClose={() => { - updateSettings("showInstallWallet", false); + updatePref("showInstallWallet", false); }} > <i18n.Translate> @@ -379,7 +388,7 @@ export function WalletWithdrawForm({ </Attention> )} - {!settings.fastWithdrawal ? ( + {!settings.fastWithdrawalForm ? ( <OldWithdrawalForm focus={focus} routeOperationDetails={routeOperationDetails} diff --git a/packages/bank-ui/src/pages/WithdrawalConfirmationQuestion.tsx b/packages/bank-ui/src/pages/WithdrawalConfirmationQuestion.tsx index 853dd7bae..b270c447a 100644 --- a/packages/bank-ui/src/pages/WithdrawalConfirmationQuestion.tsx +++ b/packages/bank-ui/src/pages/WithdrawalConfirmationQuestion.tsx @@ -17,6 +17,7 @@ import { AbsoluteTime, AmountJson, + Amounts, HttpStatusCode, PaytoUri, PaytoUriIBAN, @@ -79,6 +80,11 @@ export function WithdrawalConfirmationQuestion({ lib: { bank: api }, } = useBankCoreApiContext(); + const wireFee = + config.wire_transfer_fees === undefined + ? Amounts.zeroOfCurrency(config.currency) + : Amounts.parseOrThrow(config.wire_transfer_fees); + async function doTransfer() { await handleError(async () => { if (!creds) return; @@ -357,6 +363,23 @@ export function WithdrawalConfirmationQuestion({ /> </dd> </div> + {Amounts.isZero(wireFee) ? undefined : ( + <Fragment> + <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0"> + <dt class="text-sm font-medium leading-6 text-gray-900"> + <i18n.Translate>Cost</i18n.Translate> + </dt> + <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"> + <RenderAmount + value={wireFee} + negative + withColor + spec={config.currency_specification} + /> + </dd> + </div> + </Fragment> + )} </dl> </div> </div> diff --git a/packages/bank-ui/src/pages/account/ShowAccountDetails.tsx b/packages/bank-ui/src/pages/account/ShowAccountDetails.tsx index 6db0e5512..0e2144d77 100644 --- a/packages/bank-ui/src/pages/account/ShowAccountDetails.tsx +++ b/packages/bank-ui/src/pages/account/ShowAccountDetails.tsx @@ -15,6 +15,7 @@ */ import { AbsoluteTime, + AccountLetter, HttpStatusCode, TalerCorebankApi, TalerError, @@ -200,28 +201,17 @@ export function ShowAccountDetails({ } const url = bank.getRevenueAPI(account); - url.username = account; const baseURL = url.href; - + const revenueURL = new URL(baseURL) + revenueURL.username = account; + revenueURL.password = creds?.token ?? "" const ac = parsePaytoUri(result.body.payto_uri); const payto = !ac?.isKnown ? undefined : ac; - let accountLetter: string | undefined = undefined; - if (payto) { - switch (payto.targetType) { - case "iban": { - accountLetter = `account-info-url=${url.href}\naccount-type=${payto.targetType}\niban=${payto.iban}\nreceiver-name=${result.body.name}\n`; - break; - } - case "x-taler-bank": { - accountLetter = `account-info-url=${url.href}\naccount-type=${payto.targetType}\naccount=${payto.account}\nhost=${payto.host}\nreceiver-name=${result.body.name}\n`; - break; - } - case "bitcoin": { - accountLetter = `account-info-url=${url.href}\naccount-type=${payto.targetType}\naddress=${payto.address}\nreceiver-name=${result.body.name}\n`; - break; - } + const accountLetter : AccountLetter | undefined = !payto + ? undefined + : { + accountURI: result.body.payto_uri, infoURL: revenueURL.href } - } return ( <Fragment> @@ -327,7 +317,7 @@ export function ShowAccountDetails({ name="account-type" id="account-type" disabled={true} - value={account} + value={payto.targetType} autocomplete="off" /> </div> @@ -372,16 +362,16 @@ export function ShowAccountDetails({ <div class="sm:col-span-5"> <label class="block text-sm font-medium leading-6 text-gray-900" - for="iban" + for="account-name" > - {i18n.str`IBAN`} + {i18n.str`Account name`} </label> <div class="mt-2"> <input type="text" class="block w-full disabled:bg-gray-100 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" - name="iban" - id="iban" + name="account-name" + id="account-name" disabled={true} value={payto.account} autocomplete="off" @@ -389,7 +379,7 @@ export function ShowAccountDetails({ </div> <p class="mt-2 text-sm text-gray-500"> <i18n.Translate> - International Bank Account Number. + Bank account identifier for wire transfers. </i18n.Translate> </p> </div> @@ -486,7 +476,7 @@ export function ShowAccountDetails({ <i18n.Translate>Cancel</i18n.Translate> </a> <CopyButton - getContent={() => accountLetter ?? ""} + getContent={() => !accountLetter ? "" : JSON.stringify(accountLetter)} class="flex text-center disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" > <i18n.Translate>Copy</i18n.Translate> @@ -498,3 +488,4 @@ export function ShowAccountDetails({ </Fragment> ); } + diff --git a/packages/bank-ui/src/settings.json b/packages/bank-ui/src/settings.json index df5fe75ce..f14168e77 100644 --- a/packages/bank-ui/src/settings.json +++ b/packages/bank-ui/src/settings.json @@ -2,6 +2,8 @@ "backendBaseURL": "http://bank.taler.test:1180/", "simplePasswordForRandomAccounts": true, "allowRandomAccountCreation": true, + "fastWithdrawalForm": true, + "defaultSuggestedAmount": 11, "bankName": "Taler DEVELOPMENT Bank", "topNavSites": { "Exchange": "http://Exchnage.taler.test:1180/", diff --git a/packages/bank-ui/src/settings.ts b/packages/bank-ui/src/settings.ts index c085c7cd8..6d8f7b850 100644 --- a/packages/bank-ui/src/settings.ts +++ b/packages/bank-ui/src/settings.ts @@ -20,6 +20,7 @@ import { canonicalizeBaseUrl, codecForBoolean, codecForMap, + codecForNumber, codecForString, codecOptional, } from "@gnu-taler/taler-util"; @@ -45,6 +46,17 @@ export interface UiSettings { // - value: link target, where the user is going to be redirected // default: empty list topNavSites?: Record<string, string>; + // Use the withdrawal form which redirect the user to the wallet + // without asking the amount to the user. + // - true: on withdrawal creation the spa will use suggested_amount instead + // of fixed amount + // - false: on withdrawal creation the spa will use fixed amount + // default: false + fastWithdrawalForm?: boolean; + // When the withdrawal form use the suggested amount the bank + // will send a default value that the user can change. + // default: 10 + defaultSuggestedAmount?: number; } /** @@ -56,12 +68,16 @@ const defaultSettings: UiSettings = { simplePasswordForRandomAccounts: false, allowRandomAccountCreation: false, topNavSites: {}, + fastWithdrawalForm: false, + defaultSuggestedAmount: 10, }; const codecForUISettings = (): Codec<UiSettings> => buildCodecForObject<UiSettings>() .property("backendBaseURL", codecOptional(codecForString())) .property("allowRandomAccountCreation", codecOptional(codecForBoolean())) + .property("fastWithdrawalForm", codecOptional(codecForBoolean())) + .property("defaultSuggestedAmount", codecOptional(codecForNumber())) .property( "simplePasswordForRandomAccounts", codecOptional(codecForBoolean()), |