8dcaf1c6c6
- lib/error/: classify any caught error into a stable ErrorKind from the transport signal (HTTP status / Connect Code / fetch TypeError / navigator.onLine); map to translated error.* messages via reportError (sticky Retry toast for retryable kinds) or errorMessageKey (inline). Mail compose now surfaces the translated 403/error inline. - lib/ui/view-state.svelte: shared loading/empty/error placeholder with the right live-region role + optional action; entity tables (races/sciences/ship-classes) migrated, rest adopt incrementally. - map/selection-ring.ts: accent ring around the selected planet, fed into the map buildExtras alongside the reach circles. - lib/ui/sheet-dismiss.ts: tap-outside + drag-handle swipe-down dismissal for the planet/ship-group bottom-sheets (hand-rolled pointer events). Tests: error, view-state, selection-ring, sheet-dismiss (761 total). Docs: ui/docs/error-state-ux.md (+ index); F4 marked done. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
58 lines
1.7 KiB
TypeScript
58 lines
1.7 KiB
TypeScript
/**
|
|
* Error reporting surface.
|
|
*
|
|
* Maps a classified {@link ErrorKind} to a translated, actionable message
|
|
* and either raises it as a toast (with a Retry action for retryable
|
|
* kinds) or hands the message key back for a view to render inline.
|
|
*/
|
|
|
|
import { toast } from "../toast.svelte";
|
|
import type { TranslationKey } from "../i18n/index.svelte";
|
|
import { classifyError, isRetryable, type ErrorKind } from "./classify";
|
|
|
|
const MESSAGE_KEY: Record<ErrorKind, TranslationKey> = {
|
|
offline: "error.offline",
|
|
network: "error.network",
|
|
auth: "error.auth",
|
|
forbidden: "error.forbidden",
|
|
conflict: "error.conflict",
|
|
notFound: "error.not_found",
|
|
rateLimit: "error.rate_limit",
|
|
server: "error.server",
|
|
unknown: "error.unknown",
|
|
};
|
|
|
|
/** The translated message key for an error, for inline rendering. */
|
|
export function errorMessageKey(err: unknown): TranslationKey {
|
|
return MESSAGE_KEY[classifyError(err)];
|
|
}
|
|
|
|
export interface ReportErrorOptions {
|
|
/**
|
|
* Retry callback. When supplied and the error kind is retryable, the
|
|
* toast gains a Retry action and stays until dismissed; otherwise the
|
|
* toast auto-dismisses.
|
|
*/
|
|
onRetry?: () => void;
|
|
}
|
|
|
|
/**
|
|
* Classify `err`, raise a translated toast, and return the kind. A
|
|
* retryable error with an `onRetry` handler shows a sticky toast with a
|
|
* Retry action; everything else auto-dismisses.
|
|
*/
|
|
export function reportError(
|
|
err: unknown,
|
|
options: ReportErrorOptions = {},
|
|
): ErrorKind {
|
|
const kind = classifyError(err);
|
|
const withRetry = options.onRetry !== undefined && isRetryable(kind);
|
|
toast.show({
|
|
messageKey: MESSAGE_KEY[kind],
|
|
actionLabelKey: withRetry ? "common.retry" : undefined,
|
|
onAction: withRetry ? options.onRetry : undefined,
|
|
durationMs: withRetry ? null : 8000,
|
|
});
|
|
return kind;
|
|
}
|