aboutsummaryrefslogtreecommitdiff
path: root/packages/demobank-ui/src
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-03-04 16:38:15 -0300
committerSebastian <sebasjm@gmail.com>2024-03-04 16:38:15 -0300
commite528c70159bbc147f8a947c16b60246fce65cca8 (patch)
tree09df591f62a84063591e9216f70a783c73d84426 /packages/demobank-ui/src
parentc9dd92b08dc65c131903c288dafc613456c206e8 (diff)
use async button
Diffstat (limited to 'packages/demobank-ui/src')
-rw-r--r--packages/demobank-ui/src/pages/LoginForm.tsx81
-rw-r--r--packages/demobank-ui/src/pages/QrCodeSection.tsx72
-rw-r--r--packages/demobank-ui/src/pages/RegistrationPage.tsx95
3 files changed, 71 insertions, 177 deletions
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx
index b351785d9..09c0a8785 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -20,9 +20,11 @@ import {
assertUnreachable,
} from "@gnu-taler/taler-util";
import {
+ Button,
LocalNotificationBanner,
ShowInputErrorLabel,
useLocalNotification,
+ useLocalNotificationHandler,
useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { VNode, h } from "preact";
@@ -55,7 +57,7 @@ export function LoginForm({
const [password, setPassword] = useState<string | undefined>();
const { i18n } = useTranslationContext();
const { api } = useBankCoreApiContext();
- const [notification, notify, handleError] = useLocalNotification();
+ const [notification, withErrorHandler] = useLocalNotificationHandler();
const { config } = useBankCoreApiContext();
const ref = useRef<HTMLInputElement>(null);
@@ -63,8 +65,6 @@ export function LoginForm({
ref.current?.focus();
}, []);
- const [busy, setBusy] = useState<Record<string, undefined>>();
-
const errors =
undefinedIfEmpty({
username: !username
@@ -73,50 +73,31 @@ export function LoginForm({
// ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
undefined,
password: !password ? i18n.str`Missing password` : undefined,
- }) ?? busy;
+ });
async function doLogout() {
backend.logOut();
}
- async function doLogin() {
- if (!username || !password) return;
- setBusy({});
- await handleError(async () => {
- const resp = await api
- .getAuthenticationAPI(username)
- .createAccessToken(password, {
- // scope: "readwrite" as "write", // FIX: different than merchant
- scope: "readwrite",
- duration: { d_us: "forever" },
- refreshable: true,
- });
- if (resp.type === "ok") {
- backend.logIn({ username, token: resp.body.access_token });
- } else {
- switch (resp.case) {
- case HttpStatusCode.Unauthorized:
- return notify({
- type: "error",
- title: i18n.str`Wrong credentials for "${username}"`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case HttpStatusCode.NotFound:
- return notify({
- type: "error",
- title: i18n.str`Account not found`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- default:
- assertUnreachable(resp);
- }
+ const loginHandler = !username || !password ? undefined : withErrorHandler(
+ async () => api
+ .getAuthenticationAPI(username)
+ .createAccessToken(password, {
+ // scope: "readwrite" as "write", // FIX: different than merchant
+ scope: "readwrite",
+ duration: { d_us: "forever" },
+ refreshable: true,
+ }),
+ (result) => {
+ backend.logIn({ username, token: result.body.access_token })
+ },
+ (fail) => {
+ switch (fail.case) {
+ case HttpStatusCode.Unauthorized: return i18n.str`Wrong credentials for "${username}"`;
+ case HttpStatusCode.NotFound: return i18n.str`Account not found`;
}
- });
- setPassword(undefined);
- setBusy(undefined);
- }
+ }
+ )
return (
<div class="flex min-h-full flex-col justify-center ">
@@ -209,33 +190,27 @@ export function LoginForm({
<i18n.Translate>Cancel</i18n.Translate>
</button>
- <button
+ <Button
type="submit"
name="check"
class="rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 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"
disabled={!!errors}
- onClick={async (e) => {
- e.preventDefault();
- doLogin();
- }}
+ handler={loginHandler}
>
<i18n.Translate>Check</i18n.Translate>
- </button>
+ </Button>
</div>
) : (
<div>
- <button
+ <Button
type="submit"
name="login"
class="flex w-full justify-center rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 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"
disabled={!!errors}
- onClick={(e) => {
- e.preventDefault();
- doLogin();
- }}
+ handler={loginHandler}
>
<i18n.Translate>Log in</i18n.Translate>
- </button>
+ </Button>
</div>
)}
</form>
diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx b/packages/demobank-ui/src/pages/QrCodeSection.tsx
index d20b269a8..7091214e0 100644
--- a/packages/demobank-ui/src/pages/QrCodeSection.tsx
+++ b/packages/demobank-ui/src/pages/QrCodeSection.tsx
@@ -15,16 +15,15 @@
*/
import {
- assertUnreachable,
HttpStatusCode,
stringifyWithdrawUri,
- TranslatedString,
- WithdrawUriResult,
+ WithdrawUriResult
} from "@gnu-taler/taler-util";
import {
+ Button,
LocalNotificationBanner,
- useLocalNotification,
- useTranslationContext,
+ useLocalNotificationHandler,
+ useTranslationContext
} from "@gnu-taler/web-util/browser";
import { Fragment, h, VNode } from "preact";
import { useEffect } from "preact/hooks";
@@ -50,47 +49,27 @@ export function QrCodeSection({
walletInegrationApi.publishTalerAction(withdrawUri);
}, []);
- const [notification, notify, handleError] = useLocalNotification();
+ const [notification, handleError] = useLocalNotificationHandler();
const { api } = useBankCoreApiContext();
- async function doAbort() {
- await handleError(async () => {
- if (!creds) return;
- const resp = await api.abortWithdrawalById(
+ const onAbortHandler = handleError(
+ async () => {
+ if (!creds) return undefined;
+ return api.abortWithdrawalById(
creds,
withdrawUri.withdrawalOperationId,
- );
- if (resp.type === "ok") {
- onAborted();
- } else {
- switch (resp.case) {
- case HttpStatusCode.Conflict:
- return notify({
- type: "error",
- title: i18n.str`The reserve operation has been confirmed previously and can't be aborted`,
- });
- case HttpStatusCode.BadRequest:
- return notify({
- type: "error",
- title: i18n.str`The operation id is invalid.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case HttpStatusCode.NotFound:
- return notify({
- type: "error",
- title: i18n.str`The operation was not found.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- default: {
- assertUnreachable(resp);
- }
- }
+ )
+ },
+ onAborted,
+ (fail) => {
+ switch (fail.case) {
+ case HttpStatusCode.BadRequest: return i18n.str`The operation id is invalid.`;
+ case HttpStatusCode.NotFound: return i18n.str`The operation was not found.`;
+ case HttpStatusCode.Conflict: return i18n.str`The reserve operation has been confirmed previously and can't be aborted`;
}
- });
- }
+ }
+ )
return (
<Fragment>
@@ -120,15 +99,14 @@ export function QrCodeSection({
</p>
</div>
<div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 pt-2 mt-2 ">
- <button
+ <Button
type="button"
name="cancel"
- // class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md px-3 py-2 text-sm font-semibold text-black shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600"
class="text-sm font-semibold leading-6 text-gray-900"
- onClick={doAbort}
+ handler={onAbortHandler}
>
<i18n.Translate>Cancel</i18n.Translate>
- </button>
+ </Button>
<a
href={talerWithdrawUri}
name="withdraw"
@@ -157,14 +135,14 @@ export function QrCodeSection({
</div>
</div>
<div class="flex items-center justify-center gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
- <button
+ <Button
type="button"
// class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md px-3 py-2 text-sm font-semibold text-black shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600"
class="text-sm font-semibold leading-6 text-gray-900"
- onClick={doAbort}
+ handler={onAbortHandler}
>
<i18n.Translate>Cancel</i18n.Translate>
- </button>
+ </Button>
</div>
</div>
</Fragment>
diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx b/packages/demobank-ui/src/pages/RegistrationPage.tsx
index 87e284411..18b4c470b 100644
--- a/packages/demobank-ui/src/pages/RegistrationPage.tsx
+++ b/packages/demobank-ui/src/pages/RegistrationPage.tsx
@@ -16,6 +16,7 @@
import {
AccessToken,
HttpStatusCode,
+ OperationFail,
TalerErrorCode,
TranslatedString,
assertUnreachable,
@@ -30,7 +31,7 @@ import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
import { useBankCoreApiContext } from "../context/config.js";
import { useSettingsContext } from "../context/settings.js";
-import { EmptyObject, RouteDefinition } from "../route.js";
+import { RouteDefinition } from "../route.js";
import { undefinedIfEmpty } from "../utils.js";
import { getRandomPassword, getRandomUsername } from "./rnd.js";
@@ -76,7 +77,7 @@ function RegistrationForm({
// const [phone, setPhone] = useState<string | undefined>();
// const [email, setEmail] = useState<string | undefined>();
const [repeatPassword, setRepeatPassword] = useState<string | undefined>();
- const [notification, notify, handleError] = useLocalNotification();
+ const [notification, _, handleError] = useLocalNotification();
const settings = useSettingsContext();
const { api } = useBankCoreApiContext();
@@ -114,7 +115,7 @@ function RegistrationForm({
password: string,
onComplete: () => void,
) {
- await handleError(async () => {
+ await handleError(async (onError) => {
const resp = await api.createAccount("" as AccessToken, {
name,
username,
@@ -123,80 +124,20 @@ function RegistrationForm({
if (resp.type === "ok") {
onComplete();
} else {
- switch (resp.case) {
- case HttpStatusCode.BadRequest:
- return notify({
- type: "error",
- title: i18n.str`Server replied with invalid phone or email.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case TalerErrorCode.BANK_UNALLOWED_DEBIT:
- return notify({
- type: "error",
- title: i18n.str`Registration is disabled because the bank ran out of bonus credit.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case HttpStatusCode.Unauthorized:
- return notify({
- type: "error",
- title: i18n.str`No enough permission to create that account.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case TalerErrorCode.BANK_REGISTER_PAYTO_URI_REUSE:
- return notify({
- type: "error",
- title: i18n.str`That account id is already taken.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case TalerErrorCode.BANK_REGISTER_USERNAME_REUSE:
- return notify({
- type: "error",
- title: i18n.str`That username is already taken.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT:
- return notify({
- type: "error",
- title: i18n.str`That username can't be used because is reserved.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT:
- return notify({
- type: "error",
- title: i18n.str`Only admin is allow to set debt limit.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case TalerErrorCode.BANK_MISSING_TAN_INFO:
- return notify({
- type: "error",
- title: i18n.str`No information for the selected authentication channel.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case TalerErrorCode.BANK_TAN_CHANNEL_NOT_SUPPORTED:
- return notify({
- type: "error",
- title: i18n.str`Authentication channel is not supported.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- case TalerErrorCode.BANK_NON_ADMIN_SET_TAN_CHANNEL:
- return notify({
- type: "error",
- title: i18n.str`Only admin can create accounts with second factor authentication.`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- });
- default:
- assertUnreachable(resp);
- }
+ onError(resp, (_case) => {
+ switch(_case) {
+ case HttpStatusCode.BadRequest: return i18n.str`Server replied with invalid phone or email.`;
+ case HttpStatusCode.Unauthorized: return i18n.str`No enough permission to create that account.`;
+ case TalerErrorCode.BANK_UNALLOWED_DEBIT: return i18n.str`Registration is disabled because the bank ran out of bonus credit.`;
+ case TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT: return i18n.str`That username can't be used because is reserved.`;
+ case TalerErrorCode.BANK_REGISTER_USERNAME_REUSE: return i18n.str`That username is already taken.`;
+ case TalerErrorCode.BANK_REGISTER_PAYTO_URI_REUSE: return i18n.str`That account id is already taken.`;
+ case TalerErrorCode.BANK_MISSING_TAN_INFO: return i18n.str`No information for the selected authentication channel.`;
+ case TalerErrorCode.BANK_TAN_CHANNEL_NOT_SUPPORTED: return i18n.str`Authentication channel is not supported.`;
+ case TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT: return i18n.str`Only admin is allow to set debt limit.`;
+ case TalerErrorCode.BANK_NON_ADMIN_SET_TAN_CHANNEL: return i18n.str`Only admin can create accounts with second factor authentication.`;
+ }
+ })
}
});
}