This commit is contained in:
Sebastian 2023-04-05 12:06:24 -03:00
parent d5c5c7463e
commit 8eee38d559
No known key found for this signature in database
GPG Key ID: 173909D1A5F66069
11 changed files with 80 additions and 73 deletions

View File

@ -122,7 +122,7 @@ export function AlertView({
<Alert title={alert.message} severity={alert.type} onClose={onClose}> <Alert title={alert.message} severity={alert.type} onClose={onClose}>
<div style={{ display: "flex", flexDirection: "column" }}> <div style={{ display: "flex", flexDirection: "column" }}>
<div>{alert.description}</div> <div>{alert.description}</div>
{alert.type === "error" ? ( {alert.type === "error" && alert.cause !== undefined ? (
<AlertContext context={alert.context} cause={alert.cause} /> <AlertContext context={alert.context} cause={alert.cause} />
) : undefined} ) : undefined}
</div> </div>

View File

@ -40,8 +40,8 @@ export interface ErrorAlert {
message: TranslatedString; message: TranslatedString;
description: TranslatedString | VNode; description: TranslatedString | VNode;
type: "error"; type: "error";
context: object; context: object | undefined;
cause: any; cause: any | undefined;
} }
type Type = { type Type = {

View File

@ -37,7 +37,7 @@ export type State =
| State.LoadingUriError | State.LoadingUriError
| State.Ready | State.Ready
| SelectExchangeState.Selecting | SelectExchangeState.Selecting
| SelectExchangeState.NoExchange; | SelectExchangeState.NoExchangeFound;
export namespace State { export namespace State {
export interface Loading { export interface Loading {
@ -70,7 +70,7 @@ export namespace State {
const viewMapping: StateViewMap<State> = { const viewMapping: StateViewMap<State> = {
loading: Loading, loading: Loading,
error: ErrorAlertView, error: ErrorAlertView,
"no-exchange": NoExchangesView, "no-exchange-found": NoExchangesView,
"selecting-exchange": ExchangeSelectionPage, "selecting-exchange": ExchangeSelectionPage,
ready: ReadyView, ready: ReadyView,
}; };

View File

@ -46,7 +46,7 @@ export interface PropsFromParams {
export type State = export type State =
| State.Loading | State.Loading
| State.LoadingUriError | State.LoadingUriError
| SelectExchangeState.NoExchange | SelectExchangeState.NoExchangeFound
| SelectExchangeState.Selecting | SelectExchangeState.Selecting
| State.Success; | State.Success;
@ -84,7 +84,7 @@ export namespace State {
const viewMapping: StateViewMap<State> = { const viewMapping: StateViewMap<State> = {
loading: Loading, loading: Loading,
error: ErrorAlertView, error: ErrorAlertView,
"no-exchange": NoExchangesView, "no-exchange-found": NoExchangesView,
"selecting-exchange": ExchangeSelectionPage, "selecting-exchange": ExchangeSelectionPage,
success: SuccessView, success: SuccessView,
}; };

View File

@ -118,12 +118,12 @@ export function useComponentStateFromURI({
talerWithdrawUri, talerWithdrawUri,
}, },
); );
const { amount, defaultExchangeBaseUrl } = uriInfo; const { amount, defaultExchangeBaseUrl, possibleExchanges } = uriInfo;
return { return {
talerWithdrawUri, talerWithdrawUri,
amount: Amounts.parseOrThrow(amount), amount: Amounts.parseOrThrow(amount),
thisExchange: defaultExchangeBaseUrl, thisExchange: defaultExchangeBaseUrl,
exchanges: uriInfo.possibleExchanges, exchanges: possibleExchanges,
}; };
}); });
@ -191,12 +191,12 @@ function exchangeSelectionState(
talerWithdrawUri: string | undefined, talerWithdrawUri: string | undefined,
chosenAmount: AmountJson, chosenAmount: AmountJson,
exchangeList: ExchangeListItem[], exchangeList: ExchangeListItem[],
defaultExchange: string | undefined, exchangeSuggestedByTheBank: string | undefined,
): RecursiveState<State> { ): RecursiveState<State> {
const api = useBackendContext(); const api = useBackendContext();
const selectedExchange = useSelectedExchange({ const selectedExchange = useSelectedExchange({
currency: chosenAmount.currency, currency: chosenAmount.currency,
defaultExchange, defaultExchange: exchangeSuggestedByTheBank,
list: exchangeList, list: exchangeList,
}); });

View File

@ -122,7 +122,7 @@ describe("Withdraw CTA states", () => {
expect(status).equals("loading"); expect(status).equals("loading");
}, },
({ status, error }) => { ({ status, error }) => {
expect(status).equals("no-exchange"); expect(status).equals("no-exchange-found");
expect(error).undefined; expect(error).undefined;
}, },
], ],

View File

@ -19,13 +19,14 @@ import { useState } from "preact/hooks";
import { useAlertContext } from "../context/alert.js"; import { useAlertContext } from "../context/alert.js";
import { ButtonHandler } from "../mui/handlers.js"; import { ButtonHandler } from "../mui/handlers.js";
type State = State.Ready | State.NoExchange | State.Selecting; type State = State.Ready | State.NoExchangeFound | State.Selecting;
export namespace State { export namespace State {
export interface NoExchange { export interface NoExchangeFound {
status: "no-exchange"; status: "no-exchange-found";
error: undefined; error: undefined;
currency: string | undefined; currency: string;
defaultExchange: string | undefined;
} }
export interface Ready { export interface Ready {
status: "ready"; status: "ready";
@ -39,7 +40,7 @@ export namespace State {
onCancel: () => Promise<void>; onCancel: () => Promise<void>;
list: ExchangeListItem[]; list: ExchangeListItem[];
currency: string; currency: string;
currentExchange: string; initialValue: string;
} }
} }
@ -64,31 +65,35 @@ export function useSelectedExchange({
if (!list.length) { if (!list.length) {
return { return {
status: "no-exchange", status: "no-exchange-found",
error: undefined, error: undefined,
currency: undefined, currency,
defaultExchange,
}; };
} }
const listCurrency = list.filter((e) => e.currency === currency); const exchangesWithThisCurrency = list.filter((e) => e.currency === currency);
if (!listCurrency.length) { if (!exchangesWithThisCurrency.length) {
// there should be at least one exchange for this currency // there should be at least one exchange for this currency
return { return {
status: "no-exchange", status: "no-exchange-found",
error: undefined, error: undefined,
currency, currency,
defaultExchange,
}; };
} }
if (isSelecting) { if (isSelecting) {
const currentExchange = const currentExchange =
selectedExchange ?? defaultExchange ?? listCurrency[0].exchangeBaseUrl; selectedExchange ??
defaultExchange ??
exchangesWithThisCurrency[0].exchangeBaseUrl;
return { return {
status: "selecting-exchange", status: "selecting-exchange",
error: undefined, error: undefined,
list: listCurrency, list: exchangesWithThisCurrency,
currency, currency,
currentExchange: currentExchange, initialValue: currentExchange,
onSelection: async (exchangeBaseUrl: string) => { onSelection: async (exchangeBaseUrl: string) => {
setIsSelecting(false); setIsSelecting(false);
setSelectedExchange(exchangeBaseUrl); setSelectedExchange(exchangeBaseUrl);
@ -131,6 +136,6 @@ export function useSelectedExchange({
doSelect: { doSelect: {
onClick: pushAlertOnError(async () => setIsSelecting(true)), onClick: pushAlertOnError(async () => setIsSelecting(true)),
}, },
selected: listCurrency[0], selected: exchangesWithThisCurrency[0],
}; };
} }

View File

@ -38,7 +38,7 @@ import {
export interface Props { export interface Props {
list: ExchangeListItem[]; list: ExchangeListItem[];
currentExchange: string; initialValue: string;
onCancel: () => Promise<void>; onCancel: () => Promise<void>;
onSelection: (exchange: string) => Promise<void>; onSelection: (exchange: string) => Promise<void>;
} }
@ -50,7 +50,7 @@ export type State =
| State.Comparing | State.Comparing
| State.ShowingTos | State.ShowingTos
| State.ShowingPrivacy | State.ShowingPrivacy
| SelectExchangeState.NoExchange; | SelectExchangeState.NoExchangeFound;
export namespace State { export namespace State {
export interface Loading { export interface Loading {
@ -102,7 +102,7 @@ const viewMapping: StateViewMap<State> = {
loading: Loading, loading: Loading,
error: ErrorAlertView, error: ErrorAlertView,
comparing: ComparingView, comparing: ComparingView,
"no-exchange": NoExchangesView, "no-exchange-found": NoExchangesView,
"showing-tos": TosContentView, "showing-tos": TosContentView,
"showing-privacy": PrivacyContentView, "showing-privacy": PrivacyContentView,
ready: ReadyView, ready: ReadyView,

View File

@ -30,37 +30,37 @@ export function useComponentState({
onCancel, onCancel,
onSelection, onSelection,
list: exchanges, list: exchanges,
currentExchange, initialValue,
}: Props): State { }: Props): State {
const api = useBackendContext(); const api = useBackendContext();
const { pushAlertOnError } = useAlertContext(); const { pushAlertOnError } = useAlertContext();
const { i18n } = useTranslationContext(); const { i18n } = useTranslationContext();
const initialValue = exchanges.findIndex( const initialValueIdx = exchanges.findIndex(
(e) => e.exchangeBaseUrl === currentExchange, (e) => e.exchangeBaseUrl === initialValue,
); );
if (initialValue === -1) { if (initialValueIdx === -1) {
throw Error( throw Error(
`wrong usage of ExchangeSelection component, currentExchange '${currentExchange}' is not in the list of exchanges`, `wrong usage of ExchangeSelection component, currentExchange '${initialValue}' is not in the list of exchanges`,
); );
} }
const [value, setValue] = useState(String(initialValue)); const [value, setValue] = useState(String(initialValueIdx));
const selectedIdx = parseInt(value, 10); const selectedIdx = parseInt(value, 10);
const selectedExchange = const selectedExchange = exchanges[selectedIdx];
exchanges.length == 0 ? undefined : exchanges[selectedIdx];
const comparingExchanges = selectedIdx !== initialValue; const comparingExchanges = selectedIdx !== initialValueIdx;
const initialExchange = comparingExchanges const initialExchange = comparingExchanges
? exchanges[initialValue] ? exchanges[initialValueIdx]
: undefined; : undefined;
const hook = useAsyncAsHook(async () => { const hook = useAsyncAsHook(async () => {
const selected = !selectedExchange const selected = await api.wallet.call(
? undefined WalletApiOperation.GetExchangeDetailedInfo,
: await api.wallet.call(WalletApiOperation.GetExchangeDetailedInfo, { {
exchangeBaseUrl: selectedExchange.exchangeBaseUrl, exchangeBaseUrl: selectedExchange.exchangeBaseUrl,
}); },
);
const original = !initialExchange const original = !initialExchange
? undefined ? undefined
@ -70,7 +70,7 @@ export function useComponentState({
return { return {
exchanges, exchanges,
selected: selected?.exchange, selected: selected.exchange,
original: original?.exchange, original: original?.exchange,
}; };
}, [selectedExchange, initialExchange]); }, [selectedExchange, initialExchange]);
@ -98,14 +98,6 @@ export function useComponentState({
const { selected, original } = hook.response; const { selected, original } = hook.response;
if (selectedExchange === undefined || !selected) {
return {
status: "no-exchange",
error: undefined,
currency: undefined,
};
}
const exchangeMap = exchanges.reduce( const exchangeMap = exchanges.reduce(
(prev, cur, idx) => ({ ...prev, [String(idx)]: cur.exchangeBaseUrl }), (prev, cur, idx) => ({ ...prev, [String(idx)]: cur.exchangeBaseUrl }),
{} as Record<string, string>, {} as Record<string, string>,

View File

@ -20,12 +20,17 @@
*/ */
import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { tests } from "@gnu-taler/web-util/lib/index.browser";
import { ComparingView, ReadyView } from "./views.js"; import { ComparingView, ReadyView, NoExchangesView } from "./views.js";
export default { export default {
title: "select exchange", title: "select exchange",
}; };
export const NoExchangeFound = tests.createExample(NoExchangesView, {
currency: "USD",
defaultExchange: "https://exchange.taler.ar",
});
export const Bitcoin1 = tests.createExample(ReadyView, { export const Bitcoin1 = tests.createExample(ReadyView, {
exchanges: { exchanges: {
list: { "0": "https://exchange.taler.ar" }, list: { "0": "https://exchange.taler.ar" },

View File

@ -141,28 +141,33 @@ export function TosContentView({
} }
export function NoExchangesView({ export function NoExchangesView({
defaultExchange,
currency, currency,
}: SelectExchangeState.NoExchange): VNode { }: SelectExchangeState.NoExchangeFound): VNode {
const { i18n } = useTranslationContext(); const { i18n } = useTranslationContext();
if (!currency) {
return ( return (
<Fragment>
<p>
<AlertView
alert={{
type: "error",
message: i18n.str`There is no exchange available for currency ${currency}`,
description: i18n.str`You can add more exchanges from the settings.`,
cause: undefined,
context: undefined,
}}
/>
</p>
{defaultExchange && (
<AlertView <AlertView
alert={{ alert={{
type: "warning", type: "warning",
message: i18n.str`Could not find any exchange `, message: i18n.str`Exchange ${defaultExchange} is not available`,
description: i18n.str`You are trying to withdraw coins but there is no exchange and the bank didn't suggested one.`, description: i18n.str`Exchange status can view accessed from the settings.`,
}}
/>
);
}
return (
<AlertView
alert={{
type: "warning",
message: i18n.str`Could not find any exchange `,
description: i18n.str`You are trying to withdraw coins for the currency ${currency} but there is no exchange registered in this wallet and the bank didn't suggested one.`,
}} }}
/> />
)}
</Fragment>
); );
} }