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:
Siddharth Kumar Sah
2026-03-23 19:22:52 +08:00
parent c2c256a180
commit 70c73d85c9
3 changed files with 269 additions and 80 deletions
+54 -8
View File
@@ -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>
);
}