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
4f7aac7b92
commit
1278d079e2
5 changed files with 50 additions and 20 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { notify } from "./notifications";
|
||||
import { notify, remove } from "./notifications";
|
||||
import i18n from "../i18n";
|
||||
import { reportError } from "./errorDialog";
|
||||
|
||||
|
|
@ -163,14 +163,24 @@ class HttpError extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
// Collapse bursts of connection failures (e.g. while the server restarts) into a
|
||||
// single notification rather than one per failed request.
|
||||
let lastErrorNotifiedAt = 0;
|
||||
function notifyErrorThrottled(title: string, message: string): void {
|
||||
const now = Date.now();
|
||||
if (now - lastErrorNotifiedAt < 30_000) return;
|
||||
lastErrorNotifiedAt = now;
|
||||
notify({ level: "error", title, message });
|
||||
// A single, self-resolving "connection lost" status: while the server is unreachable we
|
||||
// show one sticky, transient (non-persisted) notice; the moment any request reaches the
|
||||
// server again we remove it. This makes good on its "clears once it's back" copy and keeps
|
||||
// bursts of concurrent failures from stacking up — there's only ever one live handle.
|
||||
let connectivityNotifId: number | null = null;
|
||||
function markConnectivityLost(): void {
|
||||
if (connectivityNotifId !== null) return;
|
||||
connectivityNotifId = notify({
|
||||
level: "error",
|
||||
title: i18n.t("errors.offline.title"),
|
||||
message: i18n.t("errors.offline.body"),
|
||||
transient: true,
|
||||
});
|
||||
}
|
||||
function markConnectivityRestored(): void {
|
||||
if (connectivityNotifId === null) return;
|
||||
remove(connectivityNotifId);
|
||||
connectivityNotifId = null;
|
||||
}
|
||||
|
||||
// Gateway statuses that mean "the upstream connection failed", not "the app rejected the
|
||||
|
|
@ -206,10 +216,7 @@ async function req(url: string, opts: RequestInit = {}, cfg: ReqConfig = {}): Pr
|
|||
await delay(250);
|
||||
continue;
|
||||
}
|
||||
notifyErrorThrottled(
|
||||
"Connection lost",
|
||||
"Couldn't reach the server — it may be restarting. This will clear once it's back."
|
||||
);
|
||||
markConnectivityLost();
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
|
@ -221,6 +228,10 @@ async function req(url: string, opts: RequestInit = {}, cfg: ReqConfig = {}): Pr
|
|||
continue;
|
||||
}
|
||||
|
||||
// The server answered (anything that isn't a gateway error means it's reachable) —
|
||||
// clear a standing "connection lost" status if one is showing.
|
||||
if (!RETRIABLE_GATEWAY.has(r.status)) markConnectivityRestored();
|
||||
|
||||
if (!r.ok) {
|
||||
// Capture the server's reason (FastAPI returns `{ detail }`) so callers can react.
|
||||
let detail: string | undefined;
|
||||
|
|
@ -236,10 +247,7 @@ async function req(url: string, opts: RequestInit = {}, cfg: ReqConfig = {}): Pr
|
|||
// A genuine 500 (real fault, carries a JSON detail) still gets the modal, as do
|
||||
// 400/409/422 (validation/conflict). 401/403/404 are caller-handled control flow.
|
||||
if (RETRIABLE_GATEWAY.has(r.status)) {
|
||||
notifyErrorThrottled(
|
||||
"Connection lost",
|
||||
"Couldn't reach the server — it may be restarting. This will clear once it's back."
|
||||
);
|
||||
markConnectivityLost();
|
||||
} else if (r.status >= 500) {
|
||||
reportError(detail || `${i18n.t("errors.server")} (${r.status})`);
|
||||
} else if (r.status === 400 || r.status === 409 || r.status === 422) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue