From a6b6c6abf3f2c5cc9b20a6204078416ea5fba510 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 5 Mar 2024 10:02:32 -0300 Subject: fix #8573 --- .../demobank-ui/src/components/Cashouts/views.tsx | 19 ++++--- .../src/components/Transactions/views.tsx | 32 +++++------ packages/demobank-ui/src/context/config.ts | 1 + packages/demobank-ui/src/pages/BankFrame.tsx | 6 ++- packages/demobank-ui/src/pages/LoginForm.tsx | 9 ++-- packages/web-util/src/components/Attention.tsx | 62 ++++++++++++++-------- packages/web-util/src/components/Button.tsx | 3 +- .../src/components/GlobalNotificationBanner.tsx | 11 ++-- packages/web-util/src/hooks/useNotifications.ts | 47 ++++++++++------ 9 files changed, 113 insertions(+), 77 deletions(-) (limited to 'packages') diff --git a/packages/demobank-ui/src/components/Cashouts/views.tsx b/packages/demobank-ui/src/components/Cashouts/views.tsx index db1fdbfc5..90ee6bc2f 100644 --- a/packages/demobank-ui/src/components/Cashouts/views.tsx +++ b/packages/demobank-ui/src/components/Cashouts/views.tsx @@ -148,16 +148,15 @@ export function ReadyView({ locale: dateLocale, }); return ( - -
{creationTime} @@ -201,10 +200,10 @@ export function ReadyView({ + {item.subject} - - + ); })} diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx b/packages/demobank-ui/src/components/Transactions/views.tsx index ba400b37a..cdf134b2f 100644 --- a/packages/demobank-ui/src/components/Transactions/views.tsx +++ b/packages/demobank-ui/src/components/Transactions/views.tsx @@ -16,11 +16,10 @@ import { Attention, useTranslationContext } from "@gnu-taler/web-util/browser"; import { format } from "date-fns"; -import { Fragment, h, VNode } from "preact"; +import { Fragment, VNode, h } from "preact"; import { useBankCoreApiContext } from "../../context/config.js"; import { RenderAmount } from "../../pages/PaytoWireTransferForm.js"; import { State } from "./index.js"; -import { useAccountDetails } from "../../hooks/access.js"; export function ReadyView({ transactions, @@ -31,21 +30,24 @@ export function ReadyView({ const { i18n, dateLocale } = useTranslationContext(); const { config } = useBankCoreApiContext() - if (!transactions.length) return
-
-
-

- Transactions history -

+ if (!transactions.length) { + return
+
+
+

+ Transactions history +

+
-
- - - You can start sending a wire transfer or withdrawing to your wallet. - - -
; + + + You can start sending a wire transfer or withdrawing to your wallet. + + +
; + } + const txByDate = transactions.reduce( (prev, cur) => { const d = diff --git a/packages/demobank-ui/src/context/config.ts b/packages/demobank-ui/src/context/config.ts index 529108275..e968b7ff4 100644 --- a/packages/demobank-ui/src/context/config.ts +++ b/packages/demobank-ui/src/context/config.ts @@ -239,6 +239,7 @@ class CacheAwareTalerCoreBankHttpClient extends TalerCoreBankHttpClient { if (resp.type === "ok") { await revalidateAccountDetails(); await revalidateCashouts(); + await revalidateTransactions(); } return resp; } diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx index b914aa360..b6bfe1cfb 100644 --- a/packages/demobank-ui/src/pages/BankFrame.tsx +++ b/packages/demobank-ui/src/pages/BankFrame.tsx @@ -144,7 +144,11 @@ export function BankFrame({
- +
+
+ +
+
{account && routeAccountDetails && ( diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx index 09c0a8785..f0ca447e1 100644 --- a/packages/demobank-ui/src/pages/LoginForm.tsx +++ b/packages/demobank-ui/src/pages/LoginForm.tsx @@ -15,25 +15,22 @@ */ import { - HttpStatusCode, - TranslatedString, - assertUnreachable, + HttpStatusCode } from "@gnu-taler/taler-util"; import { Button, LocalNotificationBanner, ShowInputErrorLabel, - useLocalNotification, useLocalNotificationHandler, - useTranslationContext, + useTranslationContext } from "@gnu-taler/web-util/browser"; import { VNode, h } from "preact"; import { useEffect, useRef, useState } from "preact/hooks"; import { useBankCoreApiContext } from "../context/config.js"; import { useBackendState } from "../hooks/backend.js"; +import { RouteDefinition } from "../route.js"; import { undefinedIfEmpty } from "../utils.js"; import { doAutoFocus } from "./PaytoWireTransferForm.js"; -import { EmptyObject, RouteDefinition } from "../route.js"; /** * Collect and submit login data. diff --git a/packages/web-util/src/components/Attention.tsx b/packages/web-util/src/components/Attention.tsx index b85230a1b..50378e85a 100644 --- a/packages/web-util/src/components/Attention.tsx +++ b/packages/web-util/src/components/Attention.tsx @@ -1,36 +1,51 @@ -import { TranslatedString, assertUnreachable } from "@gnu-taler/taler-util"; +import { Duration, TranslatedString, assertUnreachable } from "@gnu-taler/taler-util"; import { ComponentChildren, Fragment, VNode, h } from "preact"; interface Props { - type?: "info" | "success" | "warning" | "danger", + type?: "info" | "success" | "warning" | "danger" | "low", onClose?: () => void, title: TranslatedString, children?: ComponentChildren, + timeout?: Duration, } -export function Attention({ type = "info", title, children, onClose }: Props): VNode { +export function Attention({ type = "info", title, children, onClose, timeout = Duration.getForever() }: Props): VNode { return
-
+ + +
- - {(() => { - switch (type) { - case "info": - return - case "warning": - return - case "danger": - return - case "success": - return - default: - assertUnreachable(type) - } - })()} - + {type === "low" ? undefined : + + {(() => { + switch (type) { + case "info": + return + case "warning": + return + case "danger": + return + case "success": + return + default: + assertUnreachable(type) + } + })()} + + }
-

+

{title}

@@ -53,6 +68,11 @@ export function Attention({ type = "info", title, children, onClose }: Props): V }
+ {timeout.d_ms === "forever" ? undefined : +
+ +
+ }
} diff --git a/packages/web-util/src/components/Button.tsx b/packages/web-util/src/components/Button.tsx index 758efafcf..26b778eec 100644 --- a/packages/web-util/src/components/Button.tsx +++ b/packages/web-util/src/components/Button.tsx @@ -45,8 +45,7 @@ export function Button, A, B>({ handler.onClick().then((resp) => { if (resp) { if (resp.type === "ok") { - // @ts-expect-error this is an operationOk - const result: OperationOk = resp.body + const result: OperationOk = resp // @ts-expect-error this is an operationOk const msg = handler.onOperationSuccess(result) if (msg) { diff --git a/packages/web-util/src/components/GlobalNotificationBanner.tsx b/packages/web-util/src/components/GlobalNotificationBanner.tsx index c8049acc3..b0a06f7e1 100644 --- a/packages/web-util/src/components/GlobalNotificationBanner.tsx +++ b/packages/web-util/src/components/GlobalNotificationBanner.tsx @@ -1,28 +1,27 @@ import { Fragment, VNode, h } from "preact" -import { Attention, useNotifications } from "../index.browser.js" +import { Attention, GLOBAL_NOTIFICATION_TIMEOUT, useNotifications } from "../index.browser.js" export function GlobalNotificationsBanner(): VNode { const notifs = useNotifications() if (notifs.length === 0) return - return
{ + return { notifs.map(n => { switch (n.message.type) { case "error": return { n.remove() - }}> + }} timeout={GLOBAL_NOTIFICATION_TIMEOUT}> {n.message.description &&
{n.message.description}
} - {/* */}
case "info": return { n.remove(); - }} /> + }} timeout={GLOBAL_NOTIFICATION_TIMEOUT} /> } })} -
+
} diff --git a/packages/web-util/src/hooks/useNotifications.ts b/packages/web-util/src/hooks/useNotifications.ts index 33e0cdf53..9f955f92d 100644 --- a/packages/web-util/src/hooks/useNotifications.ts +++ b/packages/web-util/src/hooks/useNotifications.ts @@ -1,4 +1,4 @@ -import { OperationFail, OperationOk, OperationResult, TalerError, TalerErrorCode, TranslatedString } from "@gnu-taler/taler-util"; +import { Duration, OperationFail, OperationOk, OperationResult, TalerError, TalerErrorCode, TranslatedString } from "@gnu-taler/taler-util"; import { useEffect, useState } from "preact/hooks"; import { ButtonHandler } from "../components/Button.js"; import { InternationalizationAPI, memoryMap, useTranslationContext } from "../index.browser.js"; @@ -19,10 +19,28 @@ export interface InfoNotification { const storage = memoryMap>(); const NOTIFICATION_KEY = "notification"; +export const GLOBAL_NOTIFICATION_TIMEOUT: Duration = { d_ms: 3 * 1000 } + +function removeFromStorage(n: NotificationMessage) { + const h = hash(n) + const mem = storage.get(NOTIFICATION_KEY) ?? new Map(); + const newState = new Map(mem); + newState.delete(h); + storage.set(NOTIFICATION_KEY, newState); +} + + export function notify(notif: NotificationMessage): void { const currentState: Map = storage.get(NOTIFICATION_KEY) ?? new Map(); const newState = currentState.set(hash(notif), notif); + + if (GLOBAL_NOTIFICATION_TIMEOUT.d_ms !== "forever") { + setTimeout(() => { + removeFromStorage(notif) + }, GLOBAL_NOTIFICATION_TIMEOUT.d_ms); + } + storage.set(NOTIFICATION_KEY, newState); } export function notifyError( @@ -73,10 +91,7 @@ export function useNotifications(): Notification[] { return { message, remove: () => { - const mem = storage.get(NOTIFICATION_KEY) ?? new Map(); - const newState = new Map(mem); - newState.delete(hash(message)); - storage.set(NOTIFICATION_KEY, newState); + removeFromStorage(message) }, }; }); @@ -124,7 +139,7 @@ export type ErrorNotificationHandler = (cb: (notify: typeof errorMap) => Promise * @returns */ export function useLocalNotification(): [Notification | undefined, (n: NotificationMessage) => void, ErrorNotificationHandler] { - const {i18n} = useTranslationContext(); + const { i18n } = useTranslationContext(); const [value, setter] = useState(); const notif = !value ? undefined : { @@ -154,12 +169,12 @@ export function useLocalNotification(): [Notification | undefined, (n: Notificat return [notif, setter, errorHandling] } -type HandlerMaker = ,A,B>( +type HandlerMaker = , A, B>( onClick: () => Promise, - onOperationSuccess: ((result:T extends OperationOk ? T :never) => void) | ((result:T extends OperationOk ? T :never) => TranslatedString | undefined), + onOperationSuccess: ((result: T extends OperationOk ? T : never) => void) | ((result: T extends OperationOk ? T : never) => TranslatedString | undefined), onOperationFail: (d: T extends OperationFail ? T : never) => TranslatedString, onOperationComplete?: () => void, -) => ButtonHandler; +) => ButtonHandler; export function useLocalNotificationHandler(): [Notification | undefined, HandlerMaker, (n: NotificationMessage) => void] { const [value, setter] = useState(); @@ -169,20 +184,20 @@ export function useLocalNotificationHandler(): [Notification | undefined, Handle setter(undefined); }, } - - function makeHandler,A,B>( + + function makeHandler, A, B>( onClick: () => Promise, - onOperationSuccess: ((result:T extends OperationOk ? T :never) => void) | ((result:T extends OperationOk ? T :never) => TranslatedString | undefined), + onOperationSuccess: ((result: T extends OperationOk ? T : never) => void) | ((result: T extends OperationOk ? T : never) => TranslatedString | undefined), onOperationFail: (d: T extends OperationFail ? T : never) => TranslatedString, - onOperationComplete?: () => void, - ): ButtonHandler { + onOperationComplete?: () => void, + ): ButtonHandler { return { onClick, onNotification: setter, onOperationFail, onOperationSuccess, onOperationComplete } } - + return [notif, makeHandler, setter] } -export function buildRequestErrorMessage( i18n: InternationalizationAPI, cause: TalerError): ErrorNotification { +export function buildRequestErrorMessage(i18n: InternationalizationAPI, cause: TalerError): ErrorNotification { let result: ErrorNotification; switch (cause.errorDetail.code) { case TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT: { -- cgit v1.2.3