feat(fe): optimize error handling, state consistency, and WS feedback

- Add global SWR config with exponential backoff retry (swr-config.ts)
- Add ErrorBoundary component for React crash recovery (error-boundary.tsx)
- Standardize ErrorState onRetry on all 6 dashboard views (dashboard, media,
  messages, moderation, recordings, voice)
- Fix useAction: expose resetError + onError callback
- Refactor useSpeakers to SWR-backed state (was local useState) for
  consistent cache/revalidate semantics with other hooks
- Remove polling in useReview (15s refreshInterval); replace with
  useReviewWsSync subscribing to WS moderation_action events
- Extract hashUserId to lib/hash.ts (de-dup with ambient-canvas)
- WS context: add reconnect/error toast feedback via onStatusChange
- WS connection: expose reconnectAttemptCount getter

All typecheck + lint clean, Next 16 build passes.
This commit is contained in:
asepharyana
2026-08-25 13:46:15 +07:00
parent 796c6390ac
commit 31e303c187
17 changed files with 402 additions and 79 deletions
@@ -0,0 +1,56 @@
"use client";
import { Component, type ReactNode } from "react";
import { ErrorState } from "./states";
/**
* React error boundary — catches render/exception crashes in child trees
* (e.g. third-party lib throwing on unexpected payload shape) and surfaces
* a consistent ErrorState instead of unmounting the whole app shell.
*
* Usage: wrap leaf views in <ErrorBoundary><SomeView /></ErrorBoundary>.
*/
interface ErrorBoundaryProps {
children: ReactNode;
fallback?: (error: Error, reset: () => void) => ReactNode;
onReset?: () => void;
}
interface ErrorBoundaryState {
error: Error | null;
}
export class ErrorBoundary extends Component<
ErrorBoundaryProps,
ErrorBoundaryState
> {
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = { error: null };
}
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
return { error };
}
reset = () => {
this.setState({ error: null });
this.props.onReset?.();
};
override render() {
if (!this.state.error) return this.props.children;
if (this.props.fallback) {
return this.props.fallback(this.state.error, this.reset);
}
return (
<ErrorState
title="Something went wrong"
error={this.state.error}
onRetry={this.reset}
/>
);
}
}
@@ -2,6 +2,7 @@ export { GuildChannelPicker } from "./guild-picker";
export { MarkdownLite } from "./markdown";
export { PageTransition } from "./page-transition";
export { MetricTile, SectionHeader } from "./section";
export { ErrorBoundary } from "./error-boundary";
export {
EmptyState,
ErrorState,