fix(web): make the 'connection lost' notice self-resolving
The notice promised 'this will clear once it's back', but nothing ever removed it: the toast auto-hid on a 15s timer (not on recovery) and the v0.9.0 unified inbox kept every notify() in persistent history, so the notice lingered there forever. Model it as a single live status instead: one sticky, transient (never persisted) notice while the server is unreachable, removed the moment any request reaches the server again. Adds a 'transient' notification flag (sticky toast + excluded from history so a reload can't orphan it) and replaces the throttled-toast helper with a connectivity lost/restored pair. i18n the strings (errors.offline.*) in EN/HU/DE — they were hardcoded English.
This commit is contained in:
parent
70b9be3fce
commit
37878068ea
5 changed files with 50 additions and 20 deletions
|
|
@ -38,6 +38,10 @@ export interface Notification {
|
|||
ts: number;
|
||||
read: boolean;
|
||||
dismissed: boolean; // transient toast surface closed (still kept in history)
|
||||
// Live session-only status (e.g. "connection lost"): sticky toast (no auto-dismiss
|
||||
// timer) that the producer removes itself when the condition resolves. Never persisted
|
||||
// to history, so a reload can't orphan it with no live handle to clear it.
|
||||
transient: boolean;
|
||||
}
|
||||
|
||||
export interface NotifyInput {
|
||||
|
|
@ -48,6 +52,7 @@ export interface NotifyInput {
|
|||
meta?: NotifMeta;
|
||||
requiresInteraction?: boolean;
|
||||
sound?: boolean; // force the alert tone (when sound is enabled) even for an info toast
|
||||
transient?: boolean; // live status: sticky toast, not persisted (see Notification.transient)
|
||||
}
|
||||
|
||||
const HISTORY_KEY = "subfeed.notifications";
|
||||
|
|
@ -122,7 +127,7 @@ function load(): Notification[] {
|
|||
// resurrect as active toasts on reload — their auto-dismiss timers aren't
|
||||
// re-armed across a reload, so otherwise they'd stick forever. Also drop the
|
||||
// live `action` callback, which can't survive serialization.
|
||||
return raw.map((n) => ({ ...n, action: undefined, dismissed: true }) as Notification);
|
||||
return raw.map((n) => ({ ...n, action: undefined, dismissed: true, transient: false }) as Notification);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
|
|
@ -130,7 +135,9 @@ function load(): Notification[] {
|
|||
|
||||
function persist() {
|
||||
try {
|
||||
const slim = items.map(({ action: _action, ...n }) => n);
|
||||
// Transient status notices are live-session only — never write them to history,
|
||||
// so a reload can't leave one stranded with no producer left to clear it.
|
||||
const slim = items.filter((n) => !n.transient).map(({ action: _action, ...n }) => n);
|
||||
localStorage.setItem(HISTORY_KEY, JSON.stringify(slim));
|
||||
} catch {
|
||||
/* ignore quota / serialization errors */
|
||||
|
|
@ -164,7 +171,9 @@ export function notify(input: NotifyInput): number {
|
|||
const requiresInteraction = input.requiresInteraction ?? false;
|
||||
const level = input.level ?? "info";
|
||||
// config.durationMs === 0 means "don't auto-dismiss" (toast stays until dismissed).
|
||||
const duration = requiresInteraction
|
||||
// A transient status stays put until its producer clears it (no auto-dismiss timer),
|
||||
// same as an interaction-awaiting notice.
|
||||
const duration = requiresInteraction || input.transient
|
||||
? undefined
|
||||
: level === "error" || level === "fatal"
|
||||
? ERROR_TTL
|
||||
|
|
@ -185,6 +194,7 @@ export function notify(input: NotifyInput): number {
|
|||
ts: Date.now(),
|
||||
read: false,
|
||||
dismissed: false,
|
||||
transient: input.transient ?? false,
|
||||
},
|
||||
];
|
||||
emit();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue