diff --git a/package-lock.json b/package-lock.json index 4f866634..1bd22c78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,8 @@ "prettier": "^3.2.4", "rimraf": "^5.0.5", "ts-jest": "^29.1.2", - "typescript": "^5.4.2" + "typescript": "^5.4.2", + "use-sound": "^4.0.1" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -8894,6 +8895,12 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/howler": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/howler/-/howler-2.2.4.tgz", + "integrity": "sha512-iARIBPgcQrwtEr+tALF+rapJ8qSc+Set2GJQl7xT1MQzWaVkFebdJhR3alVlSiUf5U7nAANKuj3aWpwerocD5w==", + "dev": true + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -15428,6 +15435,18 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/use-sound": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/use-sound/-/use-sound-4.0.1.tgz", + "integrity": "sha512-hykJ86kNcu6y/FzlSHcQxhjSGMslZx2WlfLpZNoPbvueakv4OF3xPxEtGV2YmculrIaH0tPp9LtG4jgy17xMWg==", + "dev": true, + "dependencies": { + "howler": "^2.1.3" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 7d6c27d7..121847cc 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,8 @@ "prettier": "^3.2.4", "rimraf": "^5.0.5", "ts-jest": "^29.1.2", - "typescript": "^5.4.2" + "typescript": "^5.4.2", + "use-sound": "^4.0.1" }, "workspaces": [ "packages/*", diff --git a/packages/frontend/public/sfx/timer_expired.wav b/packages/frontend/public/sfx/timer_expired.wav new file mode 100644 index 00000000..e2662a7c Binary files /dev/null and b/packages/frontend/public/sfx/timer_expired.wav differ diff --git a/packages/frontend/src/retro/components/buttons/ToggleTimerDialogButton.tsx b/packages/frontend/src/retro/components/buttons/ToggleTimerDialogButton.tsx index 1437d083..9065bff6 100644 --- a/packages/frontend/src/retro/components/buttons/ToggleTimerDialogButton.tsx +++ b/packages/frontend/src/retro/components/buttons/ToggleTimerDialogButton.tsx @@ -10,6 +10,7 @@ import { useTimer } from "../../hooks/useTimer"; import { TimerStatus } from "../../types/retroTypes"; import { WiggleActionButton } from "./WiggleActionButton"; import useTimedEffect from "../../hooks/useTimedEffect"; +import useSound from "use-sound"; export function ToggleTimerDialogButton() { const { isOpen, closeDialog, openDialog } = useDialog(); @@ -18,13 +19,19 @@ export function ToggleTimerDialogButton() { const { user } = useUserContext(); const { isEffectActive, startEffect } = useTimedEffect({ effectLength: 3000 }); - const { minutes, seconds, remainingTimeLabel } = useTimer({ onTimerFinish: startEffect }); + const { minutes, seconds, remainingTimeLabel } = useTimer({ onTimerFinish: handleTimerExpired }); + const [playTimeExpiredSound] = useSound("/sfx/timer_expired.wav"); function handleOpenDialog() { if (!isModerator(user)) return; openDialog(); } + function handleTimerExpired() { + playTimeExpiredSound(); + startEffect(); + } + if (!isModerator(user) && timerStatus === TimerStatus.STOPPED && !isEffectActive) return null; return (