From f3c7871745854667a6521eb06f638a35c1973818 Mon Sep 17 00:00:00 2001 From: yoavs8 Date: Thu, 12 Mar 2026 19:25:55 +0200 Subject: [PATCH 1/5] a --- apps/scouting/frontend/package.json | 6 +- .../src/scouter/components/startFeedback.ts | 60 ++++ .../src/scouter/components/stopwatch.tsx | 46 ++- package-lock.json | 326 ++++++++++-------- 4 files changed, 268 insertions(+), 170 deletions(-) create mode 100644 apps/scouting/frontend/src/scouter/components/startFeedback.ts diff --git a/apps/scouting/frontend/package.json b/apps/scouting/frontend/package.json index b129e6e3..5ab4c045 100644 --- a/apps/scouting/frontend/package.json +++ b/apps/scouting/frontend/package.json @@ -14,13 +14,15 @@ "author": "", "license": "ISC", "dependencies": { + "@capacitor/core": "^8.2.0", + "@capacitor/haptics": "^8.0.1", "@tailwindcss/vite": "^4.1.16", "heatmap.js": "^2.0.5", + "http-proxy-middleware": "^3.0.5", "react": "^19.1.1", "react-dom": "^19.1.1", "tailwindcss": "^4.1.16", - "tsx": "^4.21.0", - "http-proxy-middleware": "^3.0.5" + "tsx": "^4.21.0" }, "devDependencies": { "@eslint/js": "^9.36.0", diff --git a/apps/scouting/frontend/src/scouter/components/startFeedback.ts b/apps/scouting/frontend/src/scouter/components/startFeedback.ts new file mode 100644 index 00000000..fd09ca41 --- /dev/null +++ b/apps/scouting/frontend/src/scouter/components/startFeedback.ts @@ -0,0 +1,60 @@ +/** + * Plays start feedback: native haptics on Capacitor apps (iOS + Android), + * otherwise vibration (Android) + short audio click (iOS + Android in browser). + */ +import { Capacitor } from "@capacitor/core"; +import { Haptics, ImpactStyle } from "@capacitor/haptics"; + +let audioContext: AudioContext | null = null; + +function playClick(): void { + try { + const Ctx = typeof window !== "undefined" && (window.AudioContext || (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext); + if (!Ctx) return; + if (!audioContext) { + audioContext = new Ctx(); + } + const ctx = audioContext; + const play = () => { + const osc = ctx.createOscillator(); + const gain = ctx.createGain(); + osc.connect(gain); + gain.connect(ctx.destination); + osc.frequency.value = 520; + osc.type = "sine"; + gain.gain.setValueAtTime(0.12, ctx.currentTime); + gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.04); + osc.start(ctx.currentTime); + osc.stop(ctx.currentTime + 0.04); + }; + if (ctx.state === "suspended") { + ctx.resume().then(play).catch(() => {}); + } else { + play(); + } + } catch { + /* ignore */ + } +} + +export function playStartFeedback(): void { + if (typeof window === "undefined") return; + + try { + if (Capacitor.isNativePlatform()) { + void Haptics.impact({ style: ImpactStyle.Medium }); + return; + } + } catch { + /* not in Capacitor native app */ + } + + try { + if (typeof navigator.vibrate === "function") { + navigator.vibrate(100); + } + } catch { + /* ignore */ + } + playClick(); +} diff --git a/apps/scouting/frontend/src/scouter/components/stopwatch.tsx b/apps/scouting/frontend/src/scouter/components/stopwatch.tsx index b6bb5340..3831159a 100644 --- a/apps/scouting/frontend/src/scouter/components/stopwatch.tsx +++ b/apps/scouting/frontend/src/scouter/components/stopwatch.tsx @@ -2,6 +2,7 @@ import type React from "react"; import { useEffect, useRef, useState, type Dispatch } from "react"; import type { Interval } from "@repo/scouting_types"; +import { playStartFeedback } from "./startFeedback"; const MILLLISECONDS_IN_A_SECOND = 1000; const SECOND_IN_A_MINUTE = 60; @@ -31,13 +32,12 @@ const Stopwatch: React.FC = ({ const [elapsedTime, setElapsedTime] = useState(INITIAL_TIME_MILLISECONDS); const startTimeRef = useRef(INITIAL_TIME_MILLISECONDS); - const startCurrentCycleTime = useRef(INITIAL_TIME_MILLISECONDS); - - const reset = () => { - setElapsedTime(INITIAL_TIME_MILLISECONDS); - setIsRunning(false); - }; + const buttonRef = useRef(null); + const disabledRef = useRef(disabled); + const isRunningRef = useRef(isRunning); + disabledRef.current = disabled; + isRunningRef.current = isRunning; const calculateSeconds = () => { return Math.floor( @@ -70,10 +70,23 @@ const Stopwatch: React.FC = ({ setElapsedTime(Date.now() - startTimeRef.current); }, CYCLE_TIME_MILLISECONDS); + return () => clearInterval(intervalId); + }, [isRunning]); + + useEffect(() => { + const el = buttonRef.current; + if (!el) return; + const onGesture = () => { + if (disabledRef.current || isRunningRef.current) return; + playStartFeedback(); + }; + el.addEventListener("touchstart", onGesture, { capture: true }); + el.addEventListener("mousedown", onGesture, { capture: true }); return () => { - clearInterval(intervalId); + el.removeEventListener("touchstart", onGesture, { capture: true }); + el.removeEventListener("mousedown", onGesture, { capture: true }); }; - }, [isRunning]); + }, []); const start = () => { if (isRunning || disabled) { @@ -81,7 +94,6 @@ const Stopwatch: React.FC = ({ } const relativeTime = getCurrentRelativeTime(); startCurrentCycleTime.current = relativeTime; - startTimeRef.current = Date.now() - elapsedTime; setIsRunning(true); onStart?.(); @@ -91,16 +103,14 @@ const Stopwatch: React.FC = ({ if (!isRunning) { return; } - const cycleStopwatchCounter: Interval = { start: startCurrentCycleTime.current, end: getCurrentRelativeTime(), }; - addCycleTimeSeconds(cycleStopwatchCounter); + setElapsedTime(INITIAL_TIME_MILLISECONDS); setIsRunning(false); - reset(); onStop?.(); }; @@ -113,17 +123,17 @@ const Stopwatch: React.FC = ({ }`} >
{formatTime()}
diff --git a/package-lock.json b/package-lock.json index ea37c7ec..5816ca17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -87,6 +87,8 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@capacitor/core": "^8.2.0", + "@capacitor/haptics": "^8.0.1", "@tailwindcss/vite": "^4.1.16", "heatmap.js": "^2.0.5", "http-proxy-middleware": "^3.0.5", @@ -481,6 +483,24 @@ "node": ">=6.9.0" } }, + "node_modules/@capacitor/core": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-8.2.0.tgz", + "integrity": "sha512-oKaoNeNtH2iIZMDFVrb1atoyRECDGHcfLMunJ5KWN8DtvpVBeeA4c41e20NTuhMxw1cSYbpq2PV2hb+/9CJxlQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@capacitor/haptics": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@capacitor/haptics/-/haptics-8.0.1.tgz", + "integrity": "sha512-8v8rowLBMeb3CryqoQvXndwyUsoi4pPXf0qFw7IGA4D32Uk7+K6juN2SjRowqunoovkvvbFmU9TD7JIAz2zmFw==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": ">=8.0.0" + } + }, "node_modules/@dotenv-run/cli": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/@dotenv-run/cli/-/cli-1.3.6.tgz", @@ -531,9 +551,9 @@ "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", - "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", + "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", "cpu": [ "ppc64" ], @@ -547,9 +567,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", - "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", "cpu": [ "arm" ], @@ -563,9 +583,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", - "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", + "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", "cpu": [ "arm64" ], @@ -579,9 +599,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", - "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", "cpu": [ "x64" ], @@ -595,9 +615,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", - "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", "cpu": [ "arm64" ], @@ -611,9 +631,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", - "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", + "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", "cpu": [ "x64" ], @@ -627,9 +647,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", - "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", + "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", "cpu": [ "arm64" ], @@ -643,9 +663,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", - "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", + "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", "cpu": [ "x64" ], @@ -659,9 +679,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", - "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", + "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", "cpu": [ "arm" ], @@ -675,9 +695,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", - "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", "cpu": [ "arm64" ], @@ -691,9 +711,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", - "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", + "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", "cpu": [ "ia32" ], @@ -707,9 +727,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", - "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", + "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", "cpu": [ "loong64" ], @@ -723,9 +743,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", - "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", + "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", "cpu": [ "mips64el" ], @@ -739,9 +759,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", - "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", "cpu": [ "ppc64" ], @@ -755,9 +775,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", - "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", + "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", "cpu": [ "riscv64" ], @@ -771,9 +791,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", - "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", + "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", "cpu": [ "s390x" ], @@ -787,9 +807,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", - "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", + "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", "cpu": [ "x64" ], @@ -803,9 +823,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", - "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", + "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", "cpu": [ "arm64" ], @@ -819,9 +839,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", - "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", "cpu": [ "x64" ], @@ -835,9 +855,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", - "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", + "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", "cpu": [ "arm64" ], @@ -851,9 +871,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", - "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", + "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", "cpu": [ "x64" ], @@ -867,9 +887,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", - "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", "cpu": [ "arm64" ], @@ -883,9 +903,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", - "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", "cpu": [ "x64" ], @@ -899,9 +919,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", - "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", + "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", "cpu": [ "arm64" ], @@ -915,9 +935,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", - "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", + "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", "cpu": [ "ia32" ], @@ -931,9 +951,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", - "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", "cpu": [ "x64" ], @@ -2117,9 +2137,9 @@ } }, "node_modules/@types/node": { - "version": "25.3.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.5.tgz", - "integrity": "sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA==", + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", "license": "MIT", "dependencies": { "undici-types": "~7.18.0" @@ -2196,9 +2216,9 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz", - "integrity": "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", "license": "MIT", "dependencies": { "@babel/core": "^7.29.0", @@ -2212,7 +2232,7 @@ "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/@zxing/browser": { @@ -2306,9 +2326,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", - "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", + "version": "2.10.7", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.7.tgz", + "integrity": "sha512-1ghYO3HnxGec0TCGBXiDLVns4eCSx4zJpxnHrlqFQajmhfKMQBzUGDdkMK7fUW7PTHTeLf+j87aTuKuuwWzMGw==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -2434,9 +2454,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001777", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001777.tgz", - "integrity": "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ==", + "version": "1.0.30001778", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001778.tgz", + "integrity": "sha512-PN7uxFL+ExFJO61aVmP1aIEG4i9whQd4eoSCebav62UwDyp5OHh06zN4jqKSMePVgxHifCw1QJxdRkA1Pisekg==", "funding": [ { "type": "opencollective", @@ -2732,9 +2752,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.307", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.307.tgz", - "integrity": "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg==", + "version": "1.5.313", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.313.tgz", + "integrity": "sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA==", "license": "ISC" }, "node_modules/encodeurl": { @@ -2805,9 +2825,9 @@ } }, "node_modules/esbuild": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", - "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", + "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", "hasInstallScript": true, "license": "MIT", "bin": { @@ -2817,32 +2837,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.3", - "@esbuild/android-arm": "0.27.3", - "@esbuild/android-arm64": "0.27.3", - "@esbuild/android-x64": "0.27.3", - "@esbuild/darwin-arm64": "0.27.3", - "@esbuild/darwin-x64": "0.27.3", - "@esbuild/freebsd-arm64": "0.27.3", - "@esbuild/freebsd-x64": "0.27.3", - "@esbuild/linux-arm": "0.27.3", - "@esbuild/linux-arm64": "0.27.3", - "@esbuild/linux-ia32": "0.27.3", - "@esbuild/linux-loong64": "0.27.3", - "@esbuild/linux-mips64el": "0.27.3", - "@esbuild/linux-ppc64": "0.27.3", - "@esbuild/linux-riscv64": "0.27.3", - "@esbuild/linux-s390x": "0.27.3", - "@esbuild/linux-x64": "0.27.3", - "@esbuild/netbsd-arm64": "0.27.3", - "@esbuild/netbsd-x64": "0.27.3", - "@esbuild/openbsd-arm64": "0.27.3", - "@esbuild/openbsd-x64": "0.27.3", - "@esbuild/openharmony-arm64": "0.27.3", - "@esbuild/sunos-x64": "0.27.3", - "@esbuild/win32-arm64": "0.27.3", - "@esbuild/win32-ia32": "0.27.3", - "@esbuild/win32-x64": "0.27.3" + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" } }, "node_modules/escalade": { @@ -4655,6 +4675,12 @@ "node": ">=14.0.0" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/tsx": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", @@ -4675,27 +4701,27 @@ } }, "node_modules/turbo": { - "version": "2.8.14", - "resolved": "https://registry.npmjs.org/turbo/-/turbo-2.8.14.tgz", - "integrity": "sha512-UCTxeMNYT1cKaHiIFdLCQ7ulI+jw5i5uOnJOrRXsgUD7G3+OjlUjwVd7JfeVt2McWSVGjYA3EVW/v1FSsJ5DtA==", + "version": "2.8.16", + "resolved": "https://registry.npmjs.org/turbo/-/turbo-2.8.16.tgz", + "integrity": "sha512-u6e9e3cTTpE2adQ1DYm3A3r8y3LAONEx1jYvJx6eIgSY4bMLxIxs0riWzI0Z/IK903ikiUzRPZ2c1Ph5lVLkhA==", "dev": true, "license": "MIT", "bin": { "turbo": "bin/turbo" }, "optionalDependencies": { - "turbo-darwin-64": "2.8.14", - "turbo-darwin-arm64": "2.8.14", - "turbo-linux-64": "2.8.14", - "turbo-linux-arm64": "2.8.14", - "turbo-windows-64": "2.8.14", - "turbo-windows-arm64": "2.8.14" + "turbo-darwin-64": "2.8.16", + "turbo-darwin-arm64": "2.8.16", + "turbo-linux-64": "2.8.16", + "turbo-linux-arm64": "2.8.16", + "turbo-windows-64": "2.8.16", + "turbo-windows-arm64": "2.8.16" } }, "node_modules/turbo-darwin-64": { - "version": "2.8.14", - "resolved": "https://registry.npmjs.org/turbo-darwin-64/-/turbo-darwin-64-2.8.14.tgz", - "integrity": "sha512-9sFi7n2lLfEsGWi5OEoA/eTtQU2BPKtzSYKqufMtDeRmqMT9vKjbv9gJCRkllSVE9BOXA0qXC3diyX8V8rKIKw==", + "version": "2.8.16", + "resolved": "https://registry.npmjs.org/turbo-darwin-64/-/turbo-darwin-64-2.8.16.tgz", + "integrity": "sha512-KWa4hUMWrpADC6Q/wIHRkBLw6X6MV9nx6X7hSXbTrrMz0KdaKhmfudUZ3sS76bJFmgArBU25cSc0AUyyrswYxg==", "cpu": [ "x64" ], @@ -4707,9 +4733,9 @@ ] }, "node_modules/turbo-darwin-arm64": { - "version": "2.8.14", - "resolved": "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-2.8.14.tgz", - "integrity": "sha512-aS4yJuy6A1PCLws+PJpZP0qCURG8Y5iVx13z/WAbKyeDTY6W6PiGgcEllSaeLGxyn++382ztN/EZH85n2zZ6VQ==", + "version": "2.8.16", + "resolved": "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-2.8.16.tgz", + "integrity": "sha512-NBgaqBDLQSZlJR4D5XCkQq6noaO0RvIgwm5eYFJYL3bH5dNu8o0UBpq7C5DYnQI8+ybyoHFjT5/icN4LeUYLow==", "cpu": [ "arm64" ], @@ -4721,9 +4747,9 @@ ] }, "node_modules/turbo-linux-64": { - "version": "2.8.14", - "resolved": "https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-2.8.14.tgz", - "integrity": "sha512-XC6wPUDJkakjhNLaS0NrHDMiujRVjH+naEAwvKLArgqRaFkNxjmyNDRM4eu3soMMFmjym6NTxYaF74rvET+Orw==", + "version": "2.8.16", + "resolved": "https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-2.8.16.tgz", + "integrity": "sha512-VYPdcCRevI9kR/hr1H1xwXy7QQt/jNKiim1e1mjANBXD2E9VZWMkIL74J1Huad5MbU3/jw7voHOqDPLJPC2p6w==", "cpu": [ "x64" ], @@ -4735,9 +4761,9 @@ ] }, "node_modules/turbo-linux-arm64": { - "version": "2.8.14", - "resolved": "https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-2.8.14.tgz", - "integrity": "sha512-ChfE7isyVNjZrVSPDwcfqcHLG/FuIBbOFxnt1FM8vSuBGzHAs8AlTdwFNIxlEMJfZ8Ad9mdMxdmsCUPIWiQ6cg==", + "version": "2.8.16", + "resolved": "https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-2.8.16.tgz", + "integrity": "sha512-beq8tgUVI3uwkQkXJMiOr/hfxQRw54M3elpBwqgYFfemiK5LhCjjcwO0DkE8GZZfElBIlk+saMAQOZy3885wNQ==", "cpu": [ "arm64" ], @@ -4749,9 +4775,9 @@ ] }, "node_modules/turbo-windows-64": { - "version": "2.8.14", - "resolved": "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-2.8.14.tgz", - "integrity": "sha512-FTbIeQL1ycLFW2t9uQNMy+bRSzi3Xhwun/e7ZhFBdM+U0VZxxrtfYEBM9CHOejlfqomk6Jh7aRz0sJoqYn39Hg==", + "version": "2.8.16", + "resolved": "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-2.8.16.tgz", + "integrity": "sha512-Ig7b46iUgiOIkea/D3Z7H+zNzvzSnIJcLYFpZLA0RxbUTrbLhv9qIPwv3pT9p/abmu0LXVKHxaOo+p26SuDhzw==", "cpu": [ "x64" ], @@ -4763,9 +4789,9 @@ ] }, "node_modules/turbo-windows-arm64": { - "version": "2.8.14", - "resolved": "https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-2.8.14.tgz", - "integrity": "sha512-KgZX12cTyhY030qS7ieT8zRkhZZE2VWJasDFVUSVVn17nR7IShpv68/7j5UqJNeRLIGF1XPK0phsP5V5yw3how==", + "version": "2.8.16", + "resolved": "https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-2.8.16.tgz", + "integrity": "sha512-fOWjbEA2PiE2HEnFQrwNZKYEdjewyPc2no9GmrXklZnTCuMsxeCN39aVlKpKpim03Zq/ykIuvApGwq8ZbfS2Yw==", "cpu": [ "arm64" ], From c76dd0a5a2b28bcefc36fe84b948f474d3a98bee Mon Sep 17 00:00:00 2001 From: yoavs8 Date: Thu, 26 Mar 2026 17:32:17 +0200 Subject: [PATCH 2/5] a --- .../src/scouter/components/startFeedback.ts | 11 ++++++++-- .../src/scouter/components/stopwatch.tsx | 22 +------------------ 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/apps/scouting/frontend/src/scouter/components/startFeedback.ts b/apps/scouting/frontend/src/scouter/components/startFeedback.ts index fd09ca41..9f55bd5e 100644 --- a/apps/scouting/frontend/src/scouter/components/startFeedback.ts +++ b/apps/scouting/frontend/src/scouter/components/startFeedback.ts @@ -9,7 +9,11 @@ let audioContext: AudioContext | null = null; function playClick(): void { try { - const Ctx = typeof window !== "undefined" && (window.AudioContext || (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext); + const Ctx = + typeof window !== "undefined" && + (window.AudioContext || + (window as unknown as { webkitAudioContext?: typeof AudioContext }) + .webkitAudioContext); if (!Ctx) return; if (!audioContext) { audioContext = new Ctx(); @@ -28,7 +32,10 @@ function playClick(): void { osc.stop(ctx.currentTime + 0.04); }; if (ctx.state === "suspended") { - ctx.resume().then(play).catch(() => {}); + ctx + .resume() + .then(play) + .catch(() => {}); } else { play(); } diff --git a/apps/scouting/frontend/src/scouter/components/stopwatch.tsx b/apps/scouting/frontend/src/scouter/components/stopwatch.tsx index 3831159a..332ca419 100644 --- a/apps/scouting/frontend/src/scouter/components/stopwatch.tsx +++ b/apps/scouting/frontend/src/scouter/components/stopwatch.tsx @@ -33,11 +33,6 @@ const Stopwatch: React.FC = ({ const startTimeRef = useRef(INITIAL_TIME_MILLISECONDS); const startCurrentCycleTime = useRef(INITIAL_TIME_MILLISECONDS); - const buttonRef = useRef(null); - const disabledRef = useRef(disabled); - const isRunningRef = useRef(isRunning); - disabledRef.current = disabled; - isRunningRef.current = isRunning; const calculateSeconds = () => { return Math.floor( @@ -73,25 +68,11 @@ const Stopwatch: React.FC = ({ return () => clearInterval(intervalId); }, [isRunning]); - useEffect(() => { - const el = buttonRef.current; - if (!el) return; - const onGesture = () => { - if (disabledRef.current || isRunningRef.current) return; - playStartFeedback(); - }; - el.addEventListener("touchstart", onGesture, { capture: true }); - el.addEventListener("mousedown", onGesture, { capture: true }); - return () => { - el.removeEventListener("touchstart", onGesture, { capture: true }); - el.removeEventListener("mousedown", onGesture, { capture: true }); - }; - }, []); - const start = () => { if (isRunning || disabled) { return; } + playStartFeedback(); const relativeTime = getCurrentRelativeTime(); startCurrentCycleTime.current = relativeTime; startTimeRef.current = Date.now() - elapsedTime; @@ -123,7 +104,6 @@ const Stopwatch: React.FC = ({ }`} >
Date: Fri, 27 Mar 2026 21:02:53 +0300 Subject: [PATCH 3/5] b --- apps/scouting/frontend/package.json | 2 - .../src/scouter/components/startFeedback.ts | 18 +- package-lock.json | 373 +----------------- 3 files changed, 5 insertions(+), 388 deletions(-) diff --git a/apps/scouting/frontend/package.json b/apps/scouting/frontend/package.json index 5ab4c045..afe2ca9a 100644 --- a/apps/scouting/frontend/package.json +++ b/apps/scouting/frontend/package.json @@ -14,8 +14,6 @@ "author": "", "license": "ISC", "dependencies": { - "@capacitor/core": "^8.2.0", - "@capacitor/haptics": "^8.0.1", "@tailwindcss/vite": "^4.1.16", "heatmap.js": "^2.0.5", "http-proxy-middleware": "^3.0.5", diff --git a/apps/scouting/frontend/src/scouter/components/startFeedback.ts b/apps/scouting/frontend/src/scouter/components/startFeedback.ts index 9f55bd5e..e4c7d13f 100644 --- a/apps/scouting/frontend/src/scouter/components/startFeedback.ts +++ b/apps/scouting/frontend/src/scouter/components/startFeedback.ts @@ -1,10 +1,7 @@ /** - * Plays start feedback: native haptics on Capacitor apps (iOS + Android), - * otherwise vibration (Android) + short audio click (iOS + Android in browser). + * Browser feedback when a stopwatch cycle starts. + * Uses vibration when available and always attempts a short click sound. */ -import { Capacitor } from "@capacitor/core"; -import { Haptics, ImpactStyle } from "@capacitor/haptics"; - let audioContext: AudioContext | null = null; function playClick(): void { @@ -47,18 +44,9 @@ function playClick(): void { export function playStartFeedback(): void { if (typeof window === "undefined") return; - try { - if (Capacitor.isNativePlatform()) { - void Haptics.impact({ style: ImpactStyle.Medium }); - return; - } - } catch { - /* not in Capacitor native app */ - } - try { if (typeof navigator.vibrate === "function") { - navigator.vibrate(100); + navigator.vibrate([120, 40, 120]); } } catch { /* ignore */ diff --git a/package-lock.json b/package-lock.json index 5816ca17..dae6d1a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -87,8 +87,6 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@capacitor/core": "^8.2.0", - "@capacitor/haptics": "^8.0.1", "@tailwindcss/vite": "^4.1.16", "heatmap.js": "^2.0.5", "http-proxy-middleware": "^3.0.5", @@ -483,24 +481,6 @@ "node": ">=6.9.0" } }, - "node_modules/@capacitor/core": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-8.2.0.tgz", - "integrity": "sha512-oKaoNeNtH2iIZMDFVrb1atoyRECDGHcfLMunJ5KWN8DtvpVBeeA4c41e20NTuhMxw1cSYbpq2PV2hb+/9CJxlQ==", - "license": "MIT", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@capacitor/haptics": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@capacitor/haptics/-/haptics-8.0.1.tgz", - "integrity": "sha512-8v8rowLBMeb3CryqoQvXndwyUsoi4pPXf0qFw7IGA4D32Uk7+K6juN2SjRowqunoovkvvbFmU9TD7JIAz2zmFw==", - "license": "MIT", - "peerDependencies": { - "@capacitor/core": ">=8.0.0" - } - }, "node_modules/@dotenv-run/cli": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/@dotenv-run/cli/-/cli-1.3.6.tgz", @@ -1323,356 +1303,6 @@ "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", "license": "MIT" }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", - "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", - "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", - "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", - "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", - "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", - "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", - "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", - "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", - "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", - "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", - "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", - "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", - "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", - "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", - "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", - "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", - "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", - "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", - "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", - "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "peer": true - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", - "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "peer": true - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", - "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", - "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", - "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", - "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true - }, "node_modules/@tailwindcss/node": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.1.tgz", @@ -4679,7 +4309,8 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "optional": true }, "node_modules/tsx": { "version": "4.21.0", From ae3bebae5a24aa7eabbb562b958da0f70e8da4a7 Mon Sep 17 00:00:00 2001 From: yoavs8 Date: Sun, 12 Apr 2026 17:24:42 +0300 Subject: [PATCH 4/5] skib --- .../src/scouter/components/startFeedback.ts | 50 ++++++++++--------- .../src/scouter/components/stopwatch.tsx | 17 ++++--- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/apps/scouting/frontend/src/scouter/components/startFeedback.ts b/apps/scouting/frontend/src/scouter/components/startFeedback.ts index e4c7d13f..4f390045 100644 --- a/apps/scouting/frontend/src/scouter/components/startFeedback.ts +++ b/apps/scouting/frontend/src/scouter/components/startFeedback.ts @@ -2,40 +2,44 @@ * Browser feedback when a stopwatch cycle starts. * Uses vibration when available and always attempts a short click sound. */ + +type AudioContextedWindow = Window & { webkitAudioContext?: typeof AudioContext }; + +const VIBRATE_PULSE_MS = 120; +const VIBRATE_PAUSE_MS = 40; + let audioContext: AudioContext | null = null; +const playTouchSound = (osc: OscillatorNode, gain: GainNode) => { + osc.connect(gain); + gain.connect(audioContext!.destination); + osc.frequency.value = 520; + osc.type = "sine"; + gain.gain.setValueAtTime(0.12, audioContext!.currentTime); + gain.gain.exponentialRampToValueAtTime(0.001, audioContext!.currentTime + 0.04); + osc.start(audioContext!.currentTime); + osc.stop(audioContext!.currentTime + 0.04); +}; + function playClick(): void { try { - const Ctx = - typeof window !== "undefined" && - (window.AudioContext || - (window as unknown as { webkitAudioContext?: typeof AudioContext }) - .webkitAudioContext); + if (typeof window === "undefined") return; + const audioWindow = window as AudioContextedWindow; + const Ctx = audioWindow.AudioContext ?? audioWindow.webkitAudioContext; if (!Ctx) return; if (!audioContext) { audioContext = new Ctx(); } - const ctx = audioContext; const play = () => { - const osc = ctx.createOscillator(); - const gain = ctx.createGain(); - osc.connect(gain); - gain.connect(ctx.destination); - osc.frequency.value = 520; - osc.type = "sine"; - gain.gain.setValueAtTime(0.12, ctx.currentTime); - gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.04); - osc.start(ctx.currentTime); - osc.stop(ctx.currentTime + 0.04); + const osc = audioContext!.createOscillator(); + const gain = audioContext!.createGain(); + playTouchSound(osc, gain); }; - if (ctx.state === "suspended") { - ctx - .resume() - .then(play) - .catch(() => {}); - } else { + if (audioContext.state !== "suspended") { play(); + return; } + audioContext.resume().then(play); } catch { /* ignore */ } @@ -46,7 +50,7 @@ export function playStartFeedback(): void { try { if (typeof navigator.vibrate === "function") { - navigator.vibrate([120, 40, 120]); + navigator.vibrate([VIBRATE_PULSE_MS, VIBRATE_PAUSE_MS, VIBRATE_PULSE_MS]); } } catch { /* ignore */ diff --git a/apps/scouting/frontend/src/scouter/components/stopwatch.tsx b/apps/scouting/frontend/src/scouter/components/stopwatch.tsx index 332ca419..151ed702 100644 --- a/apps/scouting/frontend/src/scouter/components/stopwatch.tsx +++ b/apps/scouting/frontend/src/scouter/components/stopwatch.tsx @@ -80,6 +80,11 @@ const Stopwatch: React.FC = ({ onStart?.(); }; + const reset = () => { + setElapsedTime(INITIAL_TIME_MILLISECONDS); + setIsRunning(false); + }; + const stop = () => { if (!isRunning) { return; @@ -90,8 +95,7 @@ const Stopwatch: React.FC = ({ }; addCycleTimeSeconds(cycleStopwatchCounter); - setElapsedTime(INITIAL_TIME_MILLISECONDS); - setIsRunning(false); + reset(); onStop?.(); }; @@ -110,10 +114,11 @@ const Stopwatch: React.FC = ({ font-mono font-semibold shadow-lg transition-all duration-150 ${disabled ? "bg-slate-800 text-slate-900" : isRunning ? "bg-emerald-500 text-white scale-95" : "bg-slate-800 text-green-400 hover:bg-slate-700"} `} - onPointerDown={start} - onPointerUp={stop} - onPointerLeave={stop} - onPointerCancel={stop} + onMouseDown={start} + onMouseUp={stop} + onMouseLeave={stop} + onTouchStart={start} + onTouchEnd={stop} > {formatTime()}
From 6c90d7be72d14db0c76c94bb979824522cac4111 Mon Sep 17 00:00:00 2001 From: yoavs8 Date: Sun, 12 Apr 2026 18:00:22 +0300 Subject: [PATCH 5/5] udhfidsfusdfsguygi --- .../src/scouter/components/startFeedback.ts | 68 ++++++++++++------- .../src/scouter/components/stopwatch.tsx | 1 + 2 files changed, 46 insertions(+), 23 deletions(-) diff --git a/apps/scouting/frontend/src/scouter/components/startFeedback.ts b/apps/scouting/frontend/src/scouter/components/startFeedback.ts index 4f390045..ef50047b 100644 --- a/apps/scouting/frontend/src/scouter/components/startFeedback.ts +++ b/apps/scouting/frontend/src/scouter/components/startFeedback.ts @@ -3,47 +3,69 @@ * Uses vibration when available and always attempts a short click sound. */ -type AudioContextedWindow = Window & { webkitAudioContext?: typeof AudioContext }; +type AudioContextedWindow = Window & { + AudioContext?: typeof AudioContext; + webkitAudioContext?: typeof AudioContext; +}; const VIBRATE_PULSE_MS = 120; const VIBRATE_PAUSE_MS = 40; -let audioContext: AudioContext | null = null; +const CLICK_FREQUENCY_HZ = 520; +const CLICK_GAIN_PEAK = 0.12; +const CLICK_GAIN_FLOOR = 0.001; +const CLICK_DURATION_S = 0.04; + +const getAudioContext = (() => { + let ctx: AudioContext | null = null; + return (): AudioContext | null => { + if (typeof window === "undefined") return null; + const audioWindow = window as AudioContextedWindow; + const Ctx = audioWindow.AudioContext ?? audioWindow.webkitAudioContext; + if (!Ctx) return null; + if (!ctx) { + ctx = new Ctx(); + } + return ctx; + }; +})(); -const playTouchSound = (osc: OscillatorNode, gain: GainNode) => { +const playTouchSound = ( + osc: OscillatorNode, + gain: GainNode, + ctx: AudioContext, +) => { osc.connect(gain); - gain.connect(audioContext!.destination); - osc.frequency.value = 520; + gain.connect(ctx.destination); + osc.frequency.value = CLICK_FREQUENCY_HZ; osc.type = "sine"; - gain.gain.setValueAtTime(0.12, audioContext!.currentTime); - gain.gain.exponentialRampToValueAtTime(0.001, audioContext!.currentTime + 0.04); - osc.start(audioContext!.currentTime); - osc.stop(audioContext!.currentTime + 0.04); + gain.gain.setValueAtTime(CLICK_GAIN_PEAK, ctx.currentTime); + gain.gain.exponentialRampToValueAtTime( + CLICK_GAIN_FLOOR, + ctx.currentTime + CLICK_DURATION_S, + ); + osc.start(ctx.currentTime); + osc.stop(ctx.currentTime + CLICK_DURATION_S); }; -function playClick(): void { +const playClick = (): void => { try { - if (typeof window === "undefined") return; - const audioWindow = window as AudioContextedWindow; - const Ctx = audioWindow.AudioContext ?? audioWindow.webkitAudioContext; - if (!Ctx) return; - if (!audioContext) { - audioContext = new Ctx(); - } + const ctx = getAudioContext(); + if (!ctx) return; const play = () => { - const osc = audioContext!.createOscillator(); - const gain = audioContext!.createGain(); - playTouchSound(osc, gain); + const osc = ctx.createOscillator(); + const gain = ctx.createGain(); + playTouchSound(osc, gain, ctx); }; - if (audioContext.state !== "suspended") { + if (ctx.state !== "suspended") { play(); return; } - audioContext.resume().then(play); + ctx.resume().then(play); } catch { /* ignore */ } -} +}; export function playStartFeedback(): void { if (typeof window === "undefined") return; diff --git a/apps/scouting/frontend/src/scouter/components/stopwatch.tsx b/apps/scouting/frontend/src/scouter/components/stopwatch.tsx index 151ed702..9bc212e4 100644 --- a/apps/scouting/frontend/src/scouter/components/stopwatch.tsx +++ b/apps/scouting/frontend/src/scouter/components/stopwatch.tsx @@ -95,6 +95,7 @@ const Stopwatch: React.FC = ({ }; addCycleTimeSeconds(cycleStopwatchCounter); + setIsRunning(false); reset(); onStop?.(); };