diff --git a/packages/web-util/src/hooks/useNotifications.ts b/packages/web-util/src/hooks/useNotifications.ts index deaa7a7c1..733950592 100644 --- a/packages/web-util/src/hooks/useNotifications.ts +++ b/packages/web-util/src/hooks/useNotifications.ts @@ -1,5 +1,5 @@ import { TranslatedString } from "@gnu-taler/taler-util"; -import { StateUpdater, useEffect, useState } from "preact/hooks"; +import { useEffect, useState } from "preact/hooks"; import { memoryMap } from "../index.browser.js"; export type NotificationMessage = ErrorNotification | InfoNotification; @@ -15,7 +15,7 @@ interface InfoNotification { title: TranslatedString; } -const storage = memoryMap(); +const storage = memoryMap>(); const NOTIFICATION_KEY = "notification"; export function notifyError( @@ -23,20 +23,24 @@ export function notifyError( description: TranslatedString | undefined, debug?: any, ) { - const currentState: NotificationMessage[] = - storage.get(NOTIFICATION_KEY) ?? []; - const newState = currentState.concat({ - type: "error", + const currentState: Map = + storage.get(NOTIFICATION_KEY) ?? new Map(); + + const notif = { + type: "error" as const, title, description, debug, - }); + }; + const newState = currentState.set(hash(notif), notif); storage.set(NOTIFICATION_KEY, newState); } export function notifyInfo(title: TranslatedString) { - const currentState: NotificationMessage[] = - storage.get(NOTIFICATION_KEY) ?? []; - const newState = currentState.concat({ type: "info", title }); + const currentState: Map = + storage.get(NOTIFICATION_KEY) ?? new Map(); + + const notif = { type: "info" as const, title }; + const newState = currentState.set(hash(notif), notif); storage.set(NOTIFICATION_KEY, newState); } @@ -46,22 +50,48 @@ type Notification = { }; export function useNotifications(): Notification[] { - const [value, setter] = useState([]); + const [value, setter] = useState>(new Map()); useEffect(() => { return storage.onUpdate(NOTIFICATION_KEY, () => { - const mem = storage.get(NOTIFICATION_KEY) ?? []; + const mem = storage.get(NOTIFICATION_KEY) ?? new Map(); setter(mem); }); }); - return value.map((message, idx) => { + + return Array.from(value.values()).map((message, idx) => { return { message, remove: () => { - const mem = storage.get(NOTIFICATION_KEY) ?? []; - const newState = Array.from(mem); - newState.splice(idx, 1); + const mem = storage.get(NOTIFICATION_KEY) ?? new Map(); + const newState = new Map(mem); + newState.delete(hash(message)); storage.set(NOTIFICATION_KEY, newState); }, }; }); } + +function hashCode(str: string): string { + if (str.length === 0) return "0"; + let hash = 0; + let chr; + for (let i = 0; i < str.length; i++) { + chr = str.charCodeAt(i); + hash = (hash << 5) - hash + chr; + hash |= 0; // Convert to 32bit integer + } + return hash.toString(16); +} + +function hash(msg: NotificationMessage): string { + let str = (msg.type + ":" + msg.title) as string; + if (msg.type === "error") { + if (msg.description) { + str += ":" + msg.description; + } + if (msg.debug) { + str += ":" + msg.debug; + } + } + return hashCode(str); +}