aboutsummaryrefslogtreecommitdiff
path: root/packages/demobank-ui/src/pages
diff options
context:
space:
mode:
Diffstat (limited to 'packages/demobank-ui/src/pages')
-rw-r--r--packages/demobank-ui/src/pages/AccountPage.tsx4
-rw-r--r--packages/demobank-ui/src/pages/AdminPage.tsx45
-rw-r--r--packages/demobank-ui/src/pages/BusinessAccount.tsx257
-rw-r--r--packages/demobank-ui/src/pages/HomePage.tsx56
-rw-r--r--packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx14
-rw-r--r--packages/demobank-ui/src/pages/PublicHistoriesPage.tsx4
-rw-r--r--packages/demobank-ui/src/pages/RegistrationPage.tsx69
-rw-r--r--packages/demobank-ui/src/pages/Routing.tsx56
-rw-r--r--packages/demobank-ui/src/pages/WithdrawalQRCode.tsx4
9 files changed, 374 insertions, 135 deletions
diff --git a/packages/demobank-ui/src/pages/AccountPage.tsx b/packages/demobank-ui/src/pages/AccountPage.tsx
index ae0c2b1f8..bd9a5acd7 100644
--- a/packages/demobank-ui/src/pages/AccountPage.tsx
+++ b/packages/demobank-ui/src/pages/AccountPage.tsx
@@ -28,7 +28,9 @@ import { PaymentOptions } from "./PaymentOptions.js";
interface Props {
account: string;
- onLoadNotOk: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => VNode;
}
/**
* Query account information and show QR code if there is pending withdrawal
diff --git a/packages/demobank-ui/src/pages/AdminPage.tsx b/packages/demobank-ui/src/pages/AdminPage.tsx
index b4ce58588..0a1dc26ec 100644
--- a/packages/demobank-ui/src/pages/AdminPage.tsx
+++ b/packages/demobank-ui/src/pages/AdminPage.tsx
@@ -59,7 +59,9 @@ function randomPassword(): string {
}
interface Props {
- onLoadNotOk: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => VNode;
}
/**
* Query account information and show QR code if there is pending withdrawal
@@ -109,6 +111,11 @@ export function AdminPage({ onLoadNotOk }: Props): VNode {
if (showCashouts) {
return (
<div>
+ <div>
+ <h1 class="nav welcome-text">
+ <i18n.Translate>Cashout for account {showCashouts}</i18n.Translate>
+ </h1>
+ </div>
<Cashouts
account={showCashouts}
onSelected={(id) => {
@@ -116,15 +123,17 @@ export function AdminPage({ onLoadNotOk }: Props): VNode {
setShowCashouts(undefined);
}}
/>
- <input
- class="pure-button"
- type="submit"
- value={i18n.str`Close`}
- onClick={async (e) => {
- e.preventDefault();
- setShowCashouts(undefined);
- }}
- />
+ <p>
+ <input
+ class="pure-button"
+ type="submit"
+ value={i18n.str`Close`}
+ onClick={async (e) => {
+ e.preventDefault();
+ setShowCashouts(undefined);
+ }}
+ />
+ </p>
</div>
);
}
@@ -184,7 +193,7 @@ export function AdminPage({ onLoadNotOk }: Props): VNode {
onClose={() => setCreateAccount(false)}
onCreateSuccess={(password) => {
showInfoMessage(
- i18n.str`Account created with password "${password}"`,
+ i18n.str`Account created with password "${password}". The user must change the password on the next login.`,
);
setCreateAccount(false);
}}
@@ -326,7 +335,9 @@ export function UpdateAccountPassword({
onUpdateSuccess,
onLoadNotOk,
}: {
- onLoadNotOk: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => VNode;
onClear: () => void;
onUpdateSuccess: () => void;
account: string;
@@ -521,7 +532,9 @@ export function ShowAccountDetails({
onLoadNotOk,
onChangePassword,
}: {
- onLoadNotOk: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => VNode;
onClear?: () => void;
onChangePassword: () => void;
onUpdateSuccess: () => void;
@@ -628,7 +641,9 @@ function RemoveAccount({
onUpdateSuccess,
onLoadNotOk,
}: {
- onLoadNotOk: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => VNode;
onClear: () => void;
onUpdateSuccess: () => void;
account: string;
@@ -806,7 +821,7 @@ function AccountForm({
/>
</fieldset>
<fieldset>
- <label>{i18n.str`IBAN`}</label>
+ <label>{i18n.str`Internal IBAN`}</label>
<input
disabled={purpose !== "create"}
value={form.iban ?? ""}
diff --git a/packages/demobank-ui/src/pages/BusinessAccount.tsx b/packages/demobank-ui/src/pages/BusinessAccount.tsx
index 23a03cc9b..6278fe08b 100644
--- a/packages/demobank-ui/src/pages/BusinessAccount.tsx
+++ b/packages/demobank-ui/src/pages/BusinessAccount.tsx
@@ -20,6 +20,7 @@ import {
TranslatedString,
} from "@gnu-taler/taler-util";
import {
+ ErrorType,
HttpResponsePaginated,
RequestError,
useTranslationContext,
@@ -44,7 +45,9 @@ import { ShowInputErrorLabel } from "./ShowInputErrorLabel.js";
interface Props {
onClose: () => void;
onRegister: () => void;
- onLoadNotOk: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => VNode;
}
export function BusinessAccount({
onClose,
@@ -79,6 +82,9 @@ export function BusinessAccount({
setNewcashout(false);
}}
onComplete={(id) => {
+ showInfoMessage(
+ i18n.str`Cashout created. You need to confirm the operation to complete the transaction.`,
+ );
setNewcashout(false);
setShowCashoutDetails(id);
}}
@@ -156,7 +162,9 @@ interface PropsCashout {
account: string;
onComplete: (id: string) => void;
onCancel: () => void;
- onLoadNotOk: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => VNode;
}
type FormType = {
@@ -180,7 +188,7 @@ function CreateCashout({
const result = useAccountDetails(account);
const [error, saveError] = useState<ErrorMessage | undefined>();
- const [form, setForm] = useState<Partial<FormType>>({});
+ const [form, setForm] = useState<Partial<FormType>>({ isDebit: true });
const { createCashout } = useCircuitAccountAPI();
if (!result.ok) return onLoadNotOk(result);
@@ -277,10 +285,14 @@ function CreateCashout({
type="text"
readonly
class="currency-indicator"
- size={balance.currency.length}
- maxLength={balance.currency.length}
+ size={
+ !form.isDebit ? fiatCurrency.length : balance.currency.length
+ }
+ maxLength={
+ !form.isDebit ? fiatCurrency.length : balance.currency.length
+ }
tabIndex={-1}
- value={balance.currency}
+ value={!form.isDebit ? fiatCurrency : balance.currency}
/>
&nbsp;
<input
@@ -389,16 +401,16 @@ function CreateCashout({
{Amounts.isZero(sellFee) ? undefined : (
<Fragment>
<fieldset>
- <label>{i18n.str`Transfer before fee`}</label>
+ <label>{i18n.str`Amount after conversion`}</label>
<div style={{ width: "max-content" }}>
<input
type="text"
readonly
class="currency-indicator"
- size={balance.currency.length}
- maxLength={balance.currency.length}
+ size={fiatCurrency.length}
+ maxLength={fiatCurrency.length}
tabIndex={-1}
- value={balance.currency}
+ value={fiatCurrency}
/>
&nbsp;
<input
@@ -417,10 +429,10 @@ function CreateCashout({
type="text"
readonly
class="currency-indicator"
- size={balance.currency.length}
- maxLength={balance.currency.length}
+ size={fiatCurrency.length}
+ maxLength={fiatCurrency.length}
tabIndex={-1}
- value={balance.currency}
+ value={fiatCurrency}
/>
&nbsp;
<input
@@ -442,10 +454,10 @@ function CreateCashout({
type="text"
readonly
class="currency-indicator"
- size={balance.currency.length}
- maxLength={balance.currency.length}
+ size={fiatCurrency.length}
+ maxLength={fiatCurrency.length}
tabIndex={-1}
- value={balance.currency}
+ value={fiatCurrency}
/>
&nbsp;
<input
@@ -543,34 +555,69 @@ function CreateCashout({
onComplete(res.data.uuid);
} catch (error) {
if (error instanceof RequestError) {
- const errorData: SandboxBackend.SandboxError =
- error.info.error;
- if (error.info.status === HttpStatusCode.PreconditionFailed) {
- saveError({
- title: i18n.str`The account does not have sufficient funds`,
- description: errorData.error.description,
- debug: JSON.stringify(error.info),
- });
- } else if (
- error.info.status === HttpStatusCode.ServiceUnavailable
- ) {
- saveError({
- title: i18n.str`The bank does not support the TAN channel for this operation`,
- description: errorData.error.description,
- debug: JSON.stringify(error.info),
- });
- } else if (error.info.status === HttpStatusCode.Conflict) {
- saveError({
- title: i18n.str`No contact information for this channel`,
- description: errorData.error.description,
- debug: JSON.stringify(error.info),
- });
- } else {
- saveError({
- title: i18n.str`New cashout gave response error`,
- description: errorData.error.description,
- debug: JSON.stringify(error.info),
- });
+ const e = error as RequestError<SandboxBackend.SandboxError>;
+ switch (e.cause.type) {
+ case ErrorType.TIMEOUT: {
+ saveError({
+ title: i18n.str`Request timeout, try again later.`,
+ });
+ break;
+ }
+ case ErrorType.CLIENT: {
+ const errorData = e.cause.error;
+
+ if (
+ e.cause.status === HttpStatusCode.PreconditionFailed
+ ) {
+ saveError({
+ title: i18n.str`The account does not have sufficient funds`,
+ description: errorData.error.description,
+ debug: JSON.stringify(error.info),
+ });
+ } else if (e.cause.status === HttpStatusCode.Conflict) {
+ saveError({
+ title: i18n.str`No contact information for this channel`,
+ description: errorData.error.description,
+ debug: JSON.stringify(error.info),
+ });
+ } else {
+ saveError({
+ title: i18n.str`New cashout gave response error`,
+ description: errorData.error.description,
+ debug: JSON.stringify(error.info),
+ });
+ }
+ break;
+ }
+ case ErrorType.SERVER: {
+ const errorData = e.cause.error;
+ if (
+ e.cause.status === HttpStatusCode.ServiceUnavailable
+ ) {
+ saveError({
+ title: i18n.str`The bank does not support the TAN channel for this operation`,
+ description: errorData.error.description,
+ debug: JSON.stringify(error.info),
+ });
+ } else {
+ saveError({
+ title: i18n.str`Creating cashout returned with a server error`,
+ description: errorData.error.description,
+ debug: JSON.stringify(error.cause),
+ });
+ }
+ break;
+ }
+ case ErrorType.UNEXPECTED: {
+ saveError({
+ title: i18n.str`Unexpected error trying to create cashout.`,
+ debug: JSON.stringify(error.cause),
+ });
+ break;
+ }
+ default: {
+ assertUnreachable(e.cause);
+ }
}
} else if (error instanceof Error) {
saveError({
@@ -592,7 +639,9 @@ function CreateCashout({
interface ShowCashoutProps {
id: string;
onCancel: () => void;
- onLoadNotOk: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => VNode;
}
export function ShowCashoutDetails({
id,
@@ -699,22 +748,59 @@ export function ShowCashoutDetails({
onCancel();
} catch (error) {
if (error instanceof RequestError) {
- const errorData: SandboxBackend.SandboxError =
- error.info.error;
- if (
- error.info.status === HttpStatusCode.PreconditionFailed
- ) {
- saveError({
- title: i18n.str`Cashout was already aborted`,
- description: errorData.error.description,
- debug: JSON.stringify(error.info),
- });
- } else {
- saveError({
- title: i18n.str`Aborting cashout gave response error`,
- description: errorData.error.description,
- debug: JSON.stringify(error.info),
- });
+ const e =
+ error as RequestError<SandboxBackend.SandboxError>;
+ switch (e.cause.type) {
+ case ErrorType.TIMEOUT: {
+ saveError({
+ title: i18n.str`Request timeout, try again later.`,
+ });
+ break;
+ }
+ case ErrorType.CLIENT: {
+ const errorData = e.cause.error;
+ if (
+ e.cause.status === HttpStatusCode.PreconditionFailed
+ ) {
+ saveError({
+ title: i18n.str`Cashout was already aborted`,
+ description: errorData.error.description,
+ debug: JSON.stringify(error.info),
+ });
+ } else {
+ saveError({
+ title: i18n.str`Aborting cashout gave response error`,
+ description: errorData.error.description,
+ debug: JSON.stringify(error.info),
+ });
+ }
+
+ saveError({
+ title: i18n.str`Aborting cashout gave response error`,
+ description: errorData.error.description,
+ debug: JSON.stringify(error.cause),
+ });
+ break;
+ }
+ case ErrorType.SERVER: {
+ const errorData = e.cause.error;
+ saveError({
+ title: i18n.str`Aborting cashout returned with a server error`,
+ description: errorData.error.description,
+ debug: JSON.stringify(error.cause),
+ });
+ break;
+ }
+ case ErrorType.UNEXPECTED: {
+ saveError({
+ title: i18n.str`Unexpected error trying to abort cashout.`,
+ debug: JSON.stringify(error.cause),
+ });
+ break;
+ }
+ default: {
+ assertUnreachable(e.cause);
+ }
}
} else if (error instanceof Error) {
saveError({
@@ -741,13 +827,44 @@ export function ShowCashoutDetails({
});
} catch (error) {
if (error instanceof RequestError) {
- const errorData: SandboxBackend.SandboxError =
- error.info.error;
- saveError({
- title: i18n.str`Confirmation of cashout gave response error`,
- description: errorData.error.description,
- debug: JSON.stringify(error.info),
- });
+ const e =
+ error as RequestError<SandboxBackend.SandboxError>;
+ switch (e.cause.type) {
+ case ErrorType.TIMEOUT: {
+ saveError({
+ title: i18n.str`Request timeout, try again later.`,
+ });
+ break;
+ }
+ case ErrorType.CLIENT: {
+ const errorData = e.cause.error;
+ saveError({
+ title: i18n.str`Confirmation of cashout gave response error`,
+ description: errorData.error.description,
+ debug: JSON.stringify(error.cause),
+ });
+ break;
+ }
+ case ErrorType.SERVER: {
+ const errorData = e.cause.error;
+ saveError({
+ title: i18n.str`Confirmation of cashout gave response error`,
+ description: errorData.error.description,
+ debug: JSON.stringify(error.cause),
+ });
+ break;
+ }
+ case ErrorType.UNEXPECTED: {
+ saveError({
+ title: i18n.str`Unexpected error trying to cashout.`,
+ debug: JSON.stringify(error.cause),
+ });
+ break;
+ }
+ default: {
+ assertUnreachable(e.cause);
+ }
+ }
} else if (error instanceof Error) {
saveError({
title: i18n.str`Confirmation failed, please report`,
@@ -767,3 +884,7 @@ export function ShowCashoutDetails({
</div>
);
}
+
+export function assertUnreachable(x: never): never {
+ throw new Error("Didn't expect to get here");
+}
diff --git a/packages/demobank-ui/src/pages/HomePage.tsx b/packages/demobank-ui/src/pages/HomePage.tsx
index 5af195f48..a360bd64c 100644
--- a/packages/demobank-ui/src/pages/HomePage.tsx
+++ b/packages/demobank-ui/src/pages/HomePage.tsx
@@ -14,9 +14,11 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { Logger } from "@gnu-taler/taler-util";
+import { HttpStatusCode, Logger } from "@gnu-taler/taler-util";
import {
+ ErrorType,
HttpResponsePaginated,
+ RequestError,
useTranslationContext,
} from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
@@ -119,9 +121,9 @@ function handleNotOkResult(
onErrorHandler: (state: PageStateType["error"]) => void,
i18n: ReturnType<typeof useTranslationContext>["i18n"],
onRegister: () => void,
-): <T, E>(result: HttpResponsePaginated<T, E>) => VNode {
- return function handleNotOkResult2<T, E>(
- result: HttpResponsePaginated<T, E>,
+): <T>(result: HttpResponsePaginated<T, SandboxBackend.SandboxError>) => VNode {
+ return function handleNotOkResult2<T>(
+ result: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
): VNode {
if (result.clientError && result.isUnauthorized) {
onErrorHandler({
@@ -137,13 +139,49 @@ function handleNotOkResult(
}
if (result.loading) return <Loading />;
if (!result.ok) {
- onErrorHandler({
- title: i18n.str`The backend reported a problem: HTTP status #${result.status}`,
- description: `Diagnostic from ${result.info?.url} is "${result.message}"`,
- debug: JSON.stringify(result.error),
- });
+ switch (result.type) {
+ case ErrorType.TIMEOUT: {
+ onErrorHandler({
+ title: i18n.str`Request timeout, try again later.`,
+ });
+ break;
+ }
+ case ErrorType.CLIENT: {
+ const errorData = result.error;
+ onErrorHandler({
+ title: i18n.str`Could not load due to a client error`,
+ description: errorData.error.description,
+ debug: JSON.stringify(result),
+ });
+ break;
+ }
+ case ErrorType.SERVER: {
+ const errorData = result.error;
+ onErrorHandler({
+ title: i18n.str`Server returned with error`,
+ description: errorData.error.description,
+ debug: JSON.stringify(result),
+ });
+ break;
+ }
+ case ErrorType.UNEXPECTED: {
+ onErrorHandler({
+ title: i18n.str`Unexpected error.`,
+ description: `Diagnostic from ${result.info?.url} is "${result.message}"`,
+ debug: JSON.stringify(result.error),
+ });
+ break;
+ }
+ default: {
+ assertUnreachable(result);
+ }
+ }
+
return <LoginForm onRegister={onRegister} />;
}
return <div />;
};
}
+export function assertUnreachable(x: never): never {
+ throw new Error("Didn't expect to get here");
+}
diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
index d859b1cc7..07b011a00 100644
--- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -48,7 +48,7 @@ export function PaytoWireTransferForm({
onSuccess: () => void;
currency: string;
}): VNode {
- const backend = useBackendContext();
+ // const backend = useBackendContext();
// const { pageState, pageStateSetter } = usePageContext(); // NOTE: used for go-back button?
const [isRawPayto, setIsRawPayto] = useState(false);
@@ -188,17 +188,7 @@ export function PaytoWireTransferForm({
paytoUri,
amount: `${currency}:${amount}`,
});
- // return await createTransactionCall(
- // transactionData,
- // backend.state,
- // pageStateSetter,
- // () => {
- // setAmount(undefined);
- // setIban(undefined);
- // setSubject(undefined);
- // },
- // i18n,
- // );
+ onSuccess();
}}
/>
<input
diff --git a/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx b/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
index 54a77b42a..2b5f7e26c 100644
--- a/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
+++ b/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
@@ -36,7 +36,9 @@ const logger = new Logger("PublicHistoriesPage");
// }
interface Props {
- onLoadNotOk: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => VNode;
}
/**
diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx b/packages/demobank-ui/src/pages/RegistrationPage.tsx
index 247ef8d80..c6bc3c327 100644
--- a/packages/demobank-ui/src/pages/RegistrationPage.tsx
+++ b/packages/demobank-ui/src/pages/RegistrationPage.tsx
@@ -15,6 +15,7 @@
*/
import { HttpStatusCode, Logger } from "@gnu-taler/taler-util";
import {
+ ErrorType,
RequestError,
useTranslationContext,
} from "@gnu-taler/web-util/lib/index.browser";
@@ -176,26 +177,52 @@ function RegistrationForm({
onComplete();
} catch (error) {
if (error instanceof RequestError) {
- const errorData: SandboxBackend.SandboxError =
- error.info.error;
- if (error.info.status === HttpStatusCode.Conflict) {
- onError({
- title: i18n.str`That username is already taken`,
- description: errorData.error.description,
- debug: JSON.stringify(error.info),
- });
- } else {
- onError({
- title: i18n.str`New registration gave response error`,
- description: errorData.error.description,
- debug: JSON.stringify(error.info),
- });
+ const e =
+ error as RequestError<SandboxBackend.SandboxError>;
+ switch (e.cause.type) {
+ case ErrorType.TIMEOUT: {
+ onError({
+ title: i18n.str`Request timeout, try again later.`,
+ });
+ break;
+ }
+ case ErrorType.CLIENT: {
+ const errorData = e.cause.error;
+ if (e.cause.status === HttpStatusCode.Conflict) {
+ onError({
+ title: i18n.str`That username is already taken`,
+ description: errorData.error.description,
+ debug: JSON.stringify(error.cause),
+ });
+ } else {
+ onError({
+ title: i18n.str`New registration gave response error`,
+ description: errorData.error.description,
+ debug: JSON.stringify(error.cause),
+ });
+ }
+ break;
+ }
+ case ErrorType.SERVER: {
+ const errorData = e.cause.error;
+ onError({
+ title: i18n.str`New registration gave response error`,
+ description: errorData?.error?.description,
+ debug: JSON.stringify(error.cause),
+ });
+ break;
+ }
+ case ErrorType.UNEXPECTED: {
+ onError({
+ title: i18n.str`Unexpected error doing the registration.`,
+ debug: JSON.stringify(error.cause),
+ });
+ break;
+ }
+ default: {
+ assertUnreachable(e.cause);
+ }
}
- } else if (error instanceof Error) {
- onError({
- title: i18n.str`Registration failed, please report`,
- description: error.message,
- });
}
}
}}
@@ -222,3 +249,7 @@ function RegistrationForm({
</Fragment>
);
}
+
+export function assertUnreachable(x: never): never {
+ throw new Error("Didn't expect to get here");
+}
diff --git a/packages/demobank-ui/src/pages/Routing.tsx b/packages/demobank-ui/src/pages/Routing.tsx
index 48f226574..8234d8988 100644
--- a/packages/demobank-ui/src/pages/Routing.tsx
+++ b/packages/demobank-ui/src/pages/Routing.tsx
@@ -15,6 +15,7 @@
*/
import {
+ ErrorType,
HttpResponsePaginated,
useTranslationContext,
} from "@gnu-taler/web-util/lib/index.browser";
@@ -34,9 +35,9 @@ function handleNotOkResult(
safe: string,
saveError: (state: PageStateType["error"]) => void,
i18n: ReturnType<typeof useTranslationContext>["i18n"],
-): <T, E>(result: HttpResponsePaginated<T, E>) => VNode {
- return function handleNotOkResult2<T, E>(
- result: HttpResponsePaginated<T, E>,
+): <T>(result: HttpResponsePaginated<T, SandboxBackend.SandboxError>) => VNode {
+ return function handleNotOkResult2<T>(
+ result: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
): VNode {
if (result.clientError && result.isUnauthorized) {
route(safe);
@@ -50,12 +51,45 @@ function handleNotOkResult(
}
if (result.loading) return <Loading />;
if (!result.ok) {
- saveError({
- title: i18n.str`The backend reported a problem: HTTP status #${result.status}`,
- description: i18n.str`Diagnostic from ${result.info?.url} is "${result.message}"`,
- debug: JSON.stringify(result.error),
- });
- route(safe);
+ switch (result.type) {
+ case ErrorType.TIMEOUT: {
+ saveError({
+ title: i18n.str`Request timeout, try again later.`,
+ });
+ break;
+ }
+ case ErrorType.CLIENT: {
+ const errorData = result.error;
+ saveError({
+ title: i18n.str`Could not load due to a client error`,
+ description: errorData.error.description,
+ debug: JSON.stringify(result),
+ });
+ break;
+ }
+ case ErrorType.SERVER: {
+ const errorData = result.error;
+ saveError({
+ title: i18n.str`Server returned with error`,
+ description: errorData.error.description,
+ debug: JSON.stringify(result),
+ });
+ break;
+ }
+ case ErrorType.UNEXPECTED: {
+ saveError({
+ title: i18n.str`Unexpected error.`,
+ description: `Diagnostic from ${result.info?.url} is "${result.message}"`,
+ debug: JSON.stringify(result.error),
+ });
+ break;
+ }
+ default:
+ {
+ assertUnreachable(result);
+ }
+ route(safe);
+ }
}
return <div />;
};
@@ -137,3 +171,7 @@ function Redirect({ to }: { to: string }): VNode {
}, []);
return <div>being redirected to {to}</div>;
}
+
+export function assertUnreachable(x: never): never {
+ throw new Error("Didn't expect to get here");
+}
diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
index fd91c0e1a..d4c95d830 100644
--- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
@@ -33,7 +33,9 @@ interface Props {
withdrawalId: string;
talerWithdrawUri: string;
onAbort: () => void;
- onLoadNotOk: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onLoadNotOk: <T>(
+ error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+ ) => VNode;
}
/**
* Offer the QR code (and a clickable taler://-link) to