diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index cc1c9e5..b8a661a 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -21,7 +21,7 @@ function useTheme() { return { dark, toggle: () => setDark((d) => !d) }; } -function Sidebar({ onToggleTheme, dark }: { onToggleTheme: () => void; dark: boolean }) { +function Sidebar({ onToggleTheme, dark, onClose }: { onToggleTheme: () => void; dark: boolean; onClose?: () => void }) { const { data: libraries = [] } = useQuery({ queryKey: ["libraries"], queryFn: api.libraries.list, @@ -52,7 +52,7 @@ function Sidebar({ onToggleTheme, dark }: { onToggleTheme: () => void; dark: boo MediaLore - Search + Search {libraries.length > 0 && ( <> @@ -60,7 +60,7 @@ function Sidebar({ onToggleTheme, dark }: { onToggleTheme: () => void; dark: boo Libraries {libraries.map((lib) => ( - + {lib.name} ))} @@ -68,7 +68,7 @@ function Sidebar({ onToggleTheme, dark }: { onToggleTheme: () => void; dark: boo )}
- Settings + Settings + )} + + {/* Mobile backdrop */} + {isMobile && sidebarOpen && ( +
setSidebarOpen(false)} + style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.4)", zIndex: 299 }} + /> + )} + + {/* Sidebar */} +
+ setSidebarOpen(false) : undefined} /> +
+ +
} /> } /> diff --git a/frontend/src/components/DoomScrollViewer/DoomScrollViewer.tsx b/frontend/src/components/DoomScrollViewer/DoomScrollViewer.tsx index ab8e5c8..b8180fc 100644 --- a/frontend/src/components/DoomScrollViewer/DoomScrollViewer.tsx +++ b/frontend/src/components/DoomScrollViewer/DoomScrollViewer.tsx @@ -11,6 +11,7 @@ export default function DoomScrollViewer({ items, onClose, onViewInLibrary }: Pr const [index, setIndex] = useState(0); const [fading, setFading] = useState(false); const wheelLock = useRef(false); + const touchStartY = useRef(null); const item = items[index]; @@ -34,11 +35,22 @@ export default function DoomScrollViewer({ items, onClose, onViewInLibrary }: Pr if (e.key === "ArrowUp") { e.preventDefault(); go(-1); } if (e.key === "Escape") onClose(); }; + const onTouchStart = (e: TouchEvent) => { touchStartY.current = e.touches[0].clientY; }; + const onTouchEnd = (e: TouchEvent) => { + if (touchStartY.current === null) return; + const delta = touchStartY.current - e.changedTouches[0].clientY; + if (Math.abs(delta) > 50) delta > 0 ? go(1) : go(-1); + touchStartY.current = null; + }; window.addEventListener("wheel", onWheel, { passive: true }); window.addEventListener("keydown", onKey); + window.addEventListener("touchstart", onTouchStart); + window.addEventListener("touchend", onTouchEnd); return () => { window.removeEventListener("wheel", onWheel); window.removeEventListener("keydown", onKey); + window.removeEventListener("touchstart", onTouchStart); + window.removeEventListener("touchend", onTouchEnd); }; }, [index, fading]); @@ -58,7 +70,6 @@ export default function DoomScrollViewer({ items, onClose, onViewInLibrary }: Pr transform: fading ? "translateY(-12px)" : "translateY(0)", }} > -
{item?.filename}
{item?.media_type === "image" && ( window.innerWidth >= 768); + const touchStartX = useRef(null); const { data: item } = useQuery({ queryKey: ["media", mediaId], @@ -29,8 +30,24 @@ export default function MediaViewer({ mediaId, siblings, onClose, onNavigate }: if (e.key === "ArrowLeft" && prevId) onNavigate(prevId); if (e.key === "ArrowRight" && nextId) onNavigate(nextId); } + const onTouchStart = (e: TouchEvent) => { touchStartX.current = e.touches[0].clientX; }; + const onTouchEnd = (e: TouchEvent) => { + if (touchStartX.current === null) return; + const delta = touchStartX.current - e.changedTouches[0].clientX; + if (Math.abs(delta) > 50) { + if (delta > 0 && nextId) onNavigate(nextId); + if (delta < 0 && prevId) onNavigate(prevId); + } + touchStartX.current = null; + }; window.addEventListener("keydown", onKey); - return () => window.removeEventListener("keydown", onKey); + window.addEventListener("touchstart", onTouchStart); + window.addEventListener("touchend", onTouchEnd); + return () => { + window.removeEventListener("keydown", onKey); + window.removeEventListener("touchstart", onTouchStart); + window.removeEventListener("touchend", onTouchEnd); + }; }, [prevId, nextId, onClose, onNavigate]); return (