Add vpinball: a browser Visual Pinball player
Next.js app that plays real .vpx tables via @valknar/vpinball-wasm (WebAssembly Visual Pinball). Demo mode with the bundled default table, drag-and-drop custom table upload, a cabinet-styled UI with a coin-door HUD (fullscreen/stats/info/eject), a custom 404 page, PWA support (hand-rolled service worker), and a static Docker Compose deployment behind nginx. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012hQoM3jJT1Lx7CMTciMzvD
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
/** True once no pointer/keyboard activity has been seen for `timeoutMs`. */
|
||||
export function useIdleTimer(timeoutMs = 3000): boolean {
|
||||
const [isIdle, setIsIdle] = useState(false);
|
||||
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const reset = () => {
|
||||
setIsIdle(false);
|
||||
if (timerRef.current) clearTimeout(timerRef.current);
|
||||
timerRef.current = setTimeout(() => setIsIdle(true), timeoutMs);
|
||||
};
|
||||
|
||||
reset();
|
||||
const events: Array<keyof WindowEventMap> = [
|
||||
"mousemove",
|
||||
"pointerdown",
|
||||
"touchstart",
|
||||
"keydown",
|
||||
];
|
||||
events.forEach((event) => window.addEventListener(event, reset, { passive: true }));
|
||||
|
||||
return () => {
|
||||
if (timerRef.current) clearTimeout(timerRef.current);
|
||||
events.forEach((event) => window.removeEventListener(event, reset));
|
||||
};
|
||||
}, [timeoutMs]);
|
||||
|
||||
return isIdle;
|
||||
}
|
||||
Reference in New Issue
Block a user