Initial release: JMAP Webmail Client
A modern, privacy-focused webmail client built with Next.js and the JMAP protocol. Designed for Stalwart Mail Server. Features: - Full email operations (compose, reply, forward, threading) - Real-time push notifications - Dark/light theme support - Mobile responsive design - Keyboard shortcuts - Drag-and-drop organization - i18n (English/French) - Security-first (external content blocked, HTML sanitization)
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
"use client";
|
||||
|
||||
import React, { Component, ReactNode } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { debug } from "@/lib/debug";
|
||||
|
||||
export interface FallbackProps {
|
||||
error: Error;
|
||||
resetError: () => void;
|
||||
t: (key: string) => string;
|
||||
}
|
||||
|
||||
interface ErrorBoundaryProps {
|
||||
children: ReactNode;
|
||||
fallback: (props: FallbackProps) => ReactNode;
|
||||
onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
|
||||
onReset?: () => void;
|
||||
}
|
||||
|
||||
interface ErrorBoundaryState {
|
||||
hasError: boolean;
|
||||
error: Error | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Core error boundary class component (React requirement).
|
||||
* Receives translation function as prop from the functional wrapper.
|
||||
*/
|
||||
class ErrorBoundaryCore extends Component<
|
||||
ErrorBoundaryProps & { t: (key: string) => string },
|
||||
ErrorBoundaryState
|
||||
> {
|
||||
state: ErrorBoundaryState = { hasError: false, error: null };
|
||||
|
||||
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
||||
return { hasError: true, error };
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
|
||||
// Always log errors
|
||||
debug.error("[ErrorBoundary]", error.message, {
|
||||
stack: error.stack,
|
||||
componentStack: errorInfo.componentStack,
|
||||
});
|
||||
|
||||
// Call optional error handler
|
||||
this.props.onError?.(error, errorInfo);
|
||||
}
|
||||
|
||||
resetError = (): void => {
|
||||
this.props.onReset?.();
|
||||
this.setState({ hasError: false, error: null });
|
||||
};
|
||||
|
||||
render(): ReactNode {
|
||||
if (this.state.hasError && this.state.error) {
|
||||
return this.props.fallback({
|
||||
error: this.state.error,
|
||||
resetError: this.resetError,
|
||||
t: this.props.t,
|
||||
});
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Functional wrapper that injects translations into the error boundary.
|
||||
* Use this component to wrap any part of your UI that might throw errors.
|
||||
*
|
||||
* @example
|
||||
* <ErrorBoundary fallback={SidebarErrorFallback}>
|
||||
* <Sidebar />
|
||||
* </ErrorBoundary>
|
||||
*/
|
||||
export function ErrorBoundary({
|
||||
children,
|
||||
fallback,
|
||||
onError,
|
||||
onReset,
|
||||
}: ErrorBoundaryProps) {
|
||||
const t = useTranslations("errors");
|
||||
|
||||
return (
|
||||
<ErrorBoundaryCore
|
||||
fallback={fallback}
|
||||
onError={onError}
|
||||
onReset={onReset}
|
||||
t={t}
|
||||
>
|
||||
{children}
|
||||
</ErrorBoundaryCore>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user