mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: white screen crash when uploading photos with null GPS EXIF data
Samsung Galaxy phones (and others) can have GPS EXIF tags with null coordinate values when location is disabled. The server returned _latitude: null, and the client guard used !== undefined which passed for null, causing null.toFixed() to crash React with no ErrorBoundary. - Server: validate GPS array values are actual numbers before computing - Client: use != null guard (catches both null and undefined) - App: add ErrorBoundary to prevent white screens from any future crash
This commit is contained in:
+54
-8
@@ -1,3 +1,4 @@
|
||||
import { Component, type ErrorInfo, type ReactNode } from "react";
|
||||
import { BrowserRouter, Routes, Route, Navigate, useLocation } from "react-router-dom";
|
||||
import { HomePage } from "./pages/home-page";
|
||||
import { LoginPage } from "./pages/login-page";
|
||||
@@ -7,6 +8,49 @@ import { FullscreenGridPage } from "./pages/fullscreen-grid-page";
|
||||
import { KeyboardShortcutProvider } from "./components/common/keyboard-shortcut-provider";
|
||||
import { useAuth } from "./hooks/use-auth";
|
||||
|
||||
class ErrorBoundary extends Component<
|
||||
{ children: ReactNode },
|
||||
{ hasError: boolean; error: Error | null }
|
||||
> {
|
||||
constructor(props: { children: ReactNode }) {
|
||||
super(props);
|
||||
this.state = { hasError: false, error: null };
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error: Error) {
|
||||
return { hasError: true, error };
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, info: ErrorInfo) {
|
||||
console.error("Uncaught render error:", error, info.componentStack);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return (
|
||||
<div className="flex h-screen items-center justify-center bg-background text-foreground">
|
||||
<div className="text-center space-y-4 max-w-md px-6">
|
||||
<h1 className="text-xl font-semibold">Something went wrong</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{this.state.error?.message || "An unexpected error occurred."}
|
||||
</p>
|
||||
<button
|
||||
onClick={() => {
|
||||
this.setState({ hasError: false, error: null });
|
||||
window.location.href = "/";
|
||||
}}
|
||||
className="px-4 py-2 rounded-lg bg-primary text-primary-foreground text-sm font-medium"
|
||||
>
|
||||
Go Home
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
function AuthGuard({ children }: { children: React.ReactNode }) {
|
||||
const { loading, authEnabled, isAuthenticated } = useAuth();
|
||||
const location = useLocation();
|
||||
@@ -36,18 +80,20 @@ function AuthGuard({ children }: { children: React.ReactNode }) {
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<KeyboardShortcutProvider>
|
||||
<AuthGuard>
|
||||
<Routes>
|
||||
<ErrorBoundary>
|
||||
<BrowserRouter>
|
||||
<KeyboardShortcutProvider>
|
||||
<AuthGuard>
|
||||
<Routes>
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/automate" element={<AutomatePage />} />
|
||||
<Route path="/fullscreen" element={<FullscreenGridPage />} />
|
||||
<Route path="/:toolId" element={<ToolPage />} />
|
||||
<Route path="/" element={<HomePage />} />
|
||||
</Routes>
|
||||
</AuthGuard>
|
||||
</KeyboardShortcutProvider>
|
||||
</BrowserRouter>
|
||||
</Routes>
|
||||
</AuthGuard>
|
||||
</KeyboardShortcutProvider>
|
||||
</BrowserRouter>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user