support for exchange-withdraw call to action, pending use case when the user need to specify the amount
This commit is contained in:
parent
87fc6ebf48
commit
2779086a32
@ -84,7 +84,7 @@ function pageDefinition<T extends object>(pattern: string): PageLocation<T> {
|
|||||||
return { ...prev, [name]: cur };
|
return { ...prev, [name]: cur };
|
||||||
}, {} as Record<string, string>);
|
}, {} as Record<string, string>);
|
||||||
|
|
||||||
const f = (values: T): string => replaceAll(pattern, vars, values);
|
const f = (values: T): string => replaceAll(pattern, vars, values ?? {});
|
||||||
f.pattern = pattern;
|
f.pattern = pattern;
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
@ -152,6 +152,7 @@ const talerUriActionToPageName: {
|
|||||||
[TalerUriAction.PayPush]: "ctaTransferPickup",
|
[TalerUriAction.PayPush]: "ctaTransferPickup",
|
||||||
[TalerUriAction.Restore]: "ctaRecovery",
|
[TalerUriAction.Restore]: "ctaRecovery",
|
||||||
[TalerUriAction.PayTemplate]: "ctaPayTemplate",
|
[TalerUriAction.PayTemplate]: "ctaPayTemplate",
|
||||||
|
[TalerUriAction.WithdrawExchange]: "ctaWithdrawManual",
|
||||||
[TalerUriAction.DevExperiment]: undefined,
|
[TalerUriAction.DevExperiment]: undefined,
|
||||||
[TalerUriAction.Exchange]: undefined,
|
[TalerUriAction.Exchange]: undefined,
|
||||||
[TalerUriAction.Auditor]: undefined,
|
[TalerUriAction.Auditor]: undefined,
|
||||||
@ -166,7 +167,11 @@ export function getPathnameForTalerURI(talerUri: string): string | undefined {
|
|||||||
if (!pageName) {
|
if (!pageName) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return `${Pages[pageName]}?talerUri=${encodeURIComponent(talerUri)}`;
|
const pageString: string =
|
||||||
|
typeof Pages[pageName] === "function"
|
||||||
|
? (Pages[pageName] as any)()
|
||||||
|
: Pages[pageName];
|
||||||
|
return `${pageString}?talerUri=${encodeURIComponent(talerUri)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PopupNavBarOptions = "balance" | "backup" | "dev";
|
export type PopupNavBarOptions = "balance" | "backup" | "dev";
|
||||||
|
@ -16,20 +16,19 @@
|
|||||||
|
|
||||||
import { AmountJson, ExchangeListItem } from "@gnu-taler/taler-util";
|
import { AmountJson, ExchangeListItem } from "@gnu-taler/taler-util";
|
||||||
import { Loading } from "../../components/Loading.js";
|
import { Loading } from "../../components/Loading.js";
|
||||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
|
||||||
import { State as SelectExchangeState } from "../../hooks/useSelectedExchange.js";
|
import { State as SelectExchangeState } from "../../hooks/useSelectedExchange.js";
|
||||||
import { ButtonHandler, SelectFieldHandler } from "../../mui/handlers.js";
|
import { ButtonHandler, SelectFieldHandler } from "../../mui/handlers.js";
|
||||||
import { compose, StateViewMap } from "../../utils/index.js";
|
import { StateViewMap, compose } from "../../utils/index.js";
|
||||||
import {
|
import {
|
||||||
useComponentStateFromParams,
|
useComponentStateFromParams,
|
||||||
useComponentStateFromURI,
|
useComponentStateFromURI,
|
||||||
} from "./state.js";
|
} from "./state.js";
|
||||||
|
|
||||||
|
import { ErrorAlertView } from "../../components/CurrentAlerts.js";
|
||||||
|
import { ErrorAlert } from "../../context/alert.js";
|
||||||
import { ExchangeSelectionPage } from "../../wallet/ExchangeSelection/index.js";
|
import { ExchangeSelectionPage } from "../../wallet/ExchangeSelection/index.js";
|
||||||
import { NoExchangesView } from "../../wallet/ExchangeSelection/views.js";
|
import { NoExchangesView } from "../../wallet/ExchangeSelection/views.js";
|
||||||
import { SuccessView } from "./views.js";
|
import { SelectAmountView, SuccessView } from "./views.js";
|
||||||
import { ErrorAlert } from "../../context/alert.js";
|
|
||||||
import { ErrorAlertView } from "../../components/CurrentAlerts.js";
|
|
||||||
|
|
||||||
export interface PropsFromURI {
|
export interface PropsFromURI {
|
||||||
talerWithdrawUri: string | undefined;
|
talerWithdrawUri: string | undefined;
|
||||||
@ -38,6 +37,7 @@ export interface PropsFromURI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PropsFromParams {
|
export interface PropsFromParams {
|
||||||
|
talerExchangeWithdrawUri: string;
|
||||||
amount: string;
|
amount: string;
|
||||||
cancel: () => Promise<void>;
|
cancel: () => Promise<void>;
|
||||||
onSuccess: (txid: string) => Promise<void>;
|
onSuccess: (txid: string) => Promise<void>;
|
||||||
@ -48,6 +48,7 @@ export type State =
|
|||||||
| State.LoadingUriError
|
| State.LoadingUriError
|
||||||
| SelectExchangeState.NoExchangeFound
|
| SelectExchangeState.NoExchangeFound
|
||||||
| SelectExchangeState.Selecting
|
| SelectExchangeState.Selecting
|
||||||
|
| State.SelectAmount
|
||||||
| State.Success;
|
| State.Success;
|
||||||
|
|
||||||
export namespace State {
|
export namespace State {
|
||||||
@ -60,6 +61,13 @@ export namespace State {
|
|||||||
error: ErrorAlert;
|
error: ErrorAlert;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SelectAmount {
|
||||||
|
status: "select-amount";
|
||||||
|
error: undefined;
|
||||||
|
currentExchange: ExchangeListItem;
|
||||||
|
currency: string;
|
||||||
|
}
|
||||||
|
|
||||||
export type Success = {
|
export type Success = {
|
||||||
status: "success";
|
status: "success";
|
||||||
error: undefined;
|
error: undefined;
|
||||||
@ -84,13 +92,14 @@ export namespace State {
|
|||||||
const viewMapping: StateViewMap<State> = {
|
const viewMapping: StateViewMap<State> = {
|
||||||
loading: Loading,
|
loading: Loading,
|
||||||
error: ErrorAlertView,
|
error: ErrorAlertView,
|
||||||
|
"select-amount": SelectAmountView,
|
||||||
"no-exchange-found": NoExchangesView,
|
"no-exchange-found": NoExchangesView,
|
||||||
"selecting-exchange": ExchangeSelectionPage,
|
"selecting-exchange": ExchangeSelectionPage,
|
||||||
success: SuccessView,
|
success: SuccessView,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WithdrawPageFromURI = compose(
|
export const WithdrawPageFromURI = compose(
|
||||||
"WithdrawPageFromURI",
|
"WithdrawPageFromURI_Withdraw",
|
||||||
(p: PropsFromURI) => useComponentStateFromURI(p),
|
(p: PropsFromURI) => useComponentStateFromURI(p),
|
||||||
viewMapping,
|
viewMapping,
|
||||||
);
|
);
|
||||||
|
@ -18,9 +18,12 @@
|
|||||||
import {
|
import {
|
||||||
AmountJson,
|
AmountJson,
|
||||||
Amounts,
|
Amounts,
|
||||||
|
ExchangeFullDetails,
|
||||||
ExchangeListItem,
|
ExchangeListItem,
|
||||||
ExchangeTosStatus,
|
ExchangeTosStatus,
|
||||||
TalerError,
|
TalerError,
|
||||||
|
parseWithdrawExchangeUri,
|
||||||
|
stringifyWithdrawUri,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
@ -33,6 +36,7 @@ import { RecursiveState } from "../../utils/index.js";
|
|||||||
import { PropsFromParams, PropsFromURI, State } from "./index.js";
|
import { PropsFromParams, PropsFromURI, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentStateFromParams({
|
export function useComponentStateFromParams({
|
||||||
|
talerExchangeWithdrawUri: maybeTalerUri,
|
||||||
amount,
|
amount,
|
||||||
cancel,
|
cancel,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
@ -44,7 +48,29 @@ export function useComponentStateFromParams({
|
|||||||
WalletApiOperation.ListExchanges,
|
WalletApiOperation.ListExchanges,
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
return { amount: Amounts.parseOrThrow(amount), exchanges };
|
const uri = parseWithdrawExchangeUri(maybeTalerUri);
|
||||||
|
const exchangeByTalerUri = uri?.exchangeBaseUrl;
|
||||||
|
let ex: ExchangeFullDetails | undefined;
|
||||||
|
if (exchangeByTalerUri && uri.exchangePub) {
|
||||||
|
await api.wallet.call(WalletApiOperation.AddExchange, {
|
||||||
|
exchangeBaseUrl: exchangeByTalerUri,
|
||||||
|
masterPub: uri.exchangePub,
|
||||||
|
});
|
||||||
|
const info = await api.wallet.call(
|
||||||
|
WalletApiOperation.GetExchangeDetailedInfo,
|
||||||
|
{
|
||||||
|
exchangeBaseUrl: exchangeByTalerUri,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
ex = info.exchange;
|
||||||
|
}
|
||||||
|
const chosenAmount = uri
|
||||||
|
? uri.amount
|
||||||
|
? Amounts.parseOrThrow(uri.amount)
|
||||||
|
: Amounts.parseOrThrow(`${ex ? ex.currency : "KUDOS"}:66`)
|
||||||
|
: Amounts.parseOrThrow(amount);
|
||||||
|
return { amount: chosenAmount, exchanges, exchange: ex };
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!uriInfoHook) return { status: "loading", error: undefined };
|
if (!uriInfoHook) return { status: "loading", error: undefined };
|
||||||
@ -60,6 +86,7 @@ export function useComponentStateFromParams({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const chosenAmount = uriInfoHook.response.amount;
|
const chosenAmount = uriInfoHook.response.amount;
|
||||||
|
const exchangeByTalerUri = uriInfoHook.response.exchange?.exchangeBaseUrl;
|
||||||
const exchangeList = uriInfoHook.response.exchanges.exchanges;
|
const exchangeList = uriInfoHook.response.exchanges.exchanges;
|
||||||
|
|
||||||
async function doManualWithdraw(
|
async function doManualWithdraw(
|
||||||
@ -92,7 +119,7 @@ export function useComponentStateFromParams({
|
|||||||
undefined,
|
undefined,
|
||||||
chosenAmount,
|
chosenAmount,
|
||||||
exchangeList,
|
exchangeList,
|
||||||
undefined,
|
exchangeByTalerUri,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,3 +142,12 @@ function WithdrawWithMobile({
|
|||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function SelectAmountView({ currency }: State.SelectAmount): VNode {
|
||||||
|
const { i18n } = useTranslationContext();
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<p>select the amount for ${currency}</p>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -259,6 +259,7 @@ function openWalletURIFromPopup(uri: TalerUri): void {
|
|||||||
encodeURIComponent;
|
encodeURIComponent;
|
||||||
let url: string | undefined = undefined;
|
let url: string | undefined = undefined;
|
||||||
switch (uri.type) {
|
switch (uri.type) {
|
||||||
|
case TalerUriAction.WithdrawExchange:
|
||||||
case TalerUriAction.Withdraw:
|
case TalerUriAction.Withdraw:
|
||||||
url = chrome.runtime.getURL(
|
url = chrome.runtime.getURL(
|
||||||
`static/wallet.html#/cta/withdraw?talerUri=${encodeURIComponent(
|
`static/wallet.html#/cta/withdraw?talerUri=${encodeURIComponent(
|
||||||
|
@ -40,6 +40,7 @@ function ContentByUriType({
|
|||||||
}) {
|
}) {
|
||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
switch (uri.type) {
|
switch (uri.type) {
|
||||||
|
case TalerUriAction.WithdrawExchange:
|
||||||
case TalerUriAction.Withdraw:
|
case TalerUriAction.Withdraw:
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -391,9 +391,16 @@ export function Application(): VNode {
|
|||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path={Pages.ctaWithdrawManual.pattern}
|
path={Pages.ctaWithdrawManual.pattern}
|
||||||
component={({ amount }: { amount: string }) => (
|
component={({
|
||||||
|
amount,
|
||||||
|
talerUri,
|
||||||
|
}: {
|
||||||
|
amount: string;
|
||||||
|
talerUri: string;
|
||||||
|
}) => (
|
||||||
<CallToActionTemplate title={i18n.str`Digital cash withdrawal`}>
|
<CallToActionTemplate title={i18n.str`Digital cash withdrawal`}>
|
||||||
<WithdrawPageFromParams
|
<WithdrawPageFromParams
|
||||||
|
talerExchangeWithdrawUri={talerUri}
|
||||||
amount={amount}
|
amount={amount}
|
||||||
cancel={() => redirectTo(Pages.balance)}
|
cancel={() => redirectTo(Pages.balance)}
|
||||||
onSuccess={(tid: string) =>
|
onSuccess={(tid: string) =>
|
||||||
|
@ -250,13 +250,6 @@ export function SettingsView({
|
|||||||
<EnabledBySettings name="advanceMode">
|
<EnabledBySettings name="advanceMode">
|
||||||
<AdvanceSettings />
|
<AdvanceSettings />
|
||||||
</EnabledBySettings>
|
</EnabledBySettings>
|
||||||
<Checkbox
|
|
||||||
label={i18n.str`Lang selector`}
|
|
||||||
name="langSelector"
|
|
||||||
description={i18n.str`Allows to manually change the language of the UI. Otherwise it will be automatically selected by your browser configuration.`}
|
|
||||||
enabled={langToggle.value!}
|
|
||||||
onToggle={langToggle.button.onClick!}
|
|
||||||
/>
|
|
||||||
<EnabledBySettings name="langSelector">
|
<EnabledBySettings name="langSelector">
|
||||||
<SubTitle>
|
<SubTitle>
|
||||||
<i18n.Translate>Display</i18n.Translate>
|
<i18n.Translate>Display</i18n.Translate>
|
||||||
@ -340,6 +333,10 @@ function AdvanceSettings(): VNode {
|
|||||||
label: i18n.str`Allow batch withdrawals`,
|
label: i18n.str`Allow batch withdrawals`,
|
||||||
description: i18n.str`Using the batch withdrawal API allows faster withdrawals (wallet restart required)`,
|
description: i18n.str`Using the batch withdrawal API allows faster withdrawals (wallet restart required)`,
|
||||||
},
|
},
|
||||||
|
langSelector: {
|
||||||
|
label: i18n.str`Lang selector`,
|
||||||
|
description: i18n.str`Allows to manually change the language of the UI. Otherwise it will be automatically selected by your browser configuration.`,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
Loading…
Reference in New Issue
Block a user