mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: harden against three production Sentry crashes (#328)
Three production crashes from the snapotter/node Sentry project.
feature-status (NODE-12): a valid-JSON-but-wrong-shape installed.json
crashed boot via Object.keys(data.bundles). readInstalled() now
normalizes any unusable shape to { bundles: {} }, and the boot recovery
call is wrapped so cleanup can never fatal startup.
image-viewer (NODE-15/17/18): drag-to-pan read .x off an undefined
use-gesture memo on pointerUp or a pinch-into-pan. A guarded pure helper
(resolvePanStart) now falls back to the live pan offset.
Fastify (NODE-14): raised pluginTimeout to 60s so slow self-hosted boots
do not fatal at @fastify/static.
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
// Pure, framework-free helpers for ImageViewer drag-to-pan. No React, no DOM,
|
||||
// no @use-gesture, so the offset math stays unit-testable in isolation.
|
||||
|
||||
export interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the pan offset a drag started from. @use-gesture only populates
|
||||
* `memo` on the first drag frame, but the handler can still fire on a later
|
||||
* frame without that first frame having run with panning active: on pointerUp,
|
||||
* or when a concurrent pinch flips the viewer into actual-size (pan) mode
|
||||
* mid-gesture. Reading `memo.x` directly then threw in production
|
||||
* (Sentry NODE-15 / NODE-17 / NODE-18: "Cannot read properties of undefined
|
||||
* (reading 'x')", across Chrome/Safari/Firefox). Fall back to the current pan
|
||||
* offset whenever memo is missing; the caller persists the return value as the
|
||||
* next frame's memo.
|
||||
*/
|
||||
export function resolvePanStart(first: boolean, memo: Point | undefined, panOffset: Point): Point {
|
||||
if (first || !memo) return { ...panOffset };
|
||||
return memo;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useGesture } from "@use-gesture/react";
|
||||
import { FileImage, Maximize, Minimize2, ZoomIn, ZoomOut } from "lucide-react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { type Point, resolvePanStart } from "@/components/common/image-viewer-drag";
|
||||
import { useTranslation } from "@/contexts/i18n-context";
|
||||
import { formatFileSize } from "@/lib/download";
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -148,12 +149,9 @@ export function ImageViewer({
|
||||
},
|
||||
onDrag: ({ movement: [mx, my], first, memo }) => {
|
||||
if (fitModeRef.current !== "actual") return;
|
||||
if (first) {
|
||||
memo = { ...panOffset };
|
||||
}
|
||||
const start = memo as { x: number; y: number };
|
||||
const start = resolvePanStart(first, memo as Point | undefined, panOffset);
|
||||
setPanOffset({ x: start.x + mx, y: start.y + my });
|
||||
return memo;
|
||||
return start;
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user