Browse Source

Add mute button

Kirk Trombley 3 years ago
parent
commit
45df5f2157

+ 9 - 1
client/src/App.jsx

@@ -15,7 +15,7 @@ import {
   PRE_GAME,
   PRE_ROUND,
 } from "./domain/constants";
-import { dispatch, useGameState } from "./domain/gameStore";
+import { dispatch, useGameState, useIsMuted } from "./domain/gameStore";
 import Loading from "./components/util/Loading";
 
 const needsHeaderFooter = {
@@ -94,6 +94,7 @@ export const State = ({ show, children, setTransitioning }) => {
 const App = () => {
   const [loading, setLoading] = useState(true);
   const [transitioning, setTransitioning] = useState(true);
+  const muted = useIsMuted();
   const gameState = useGameState();
   useEffect(() => {
     const url = new URL(window.location.href);
@@ -113,6 +114,13 @@ const App = () => {
 
   return (
     <StrictMode>
+      <button
+        type="button"
+        onClick={dispatch.muteToggle}
+        className={styles.volume}
+      >
+        {muted ? "🔇" : "🔊"}
+      </button>
       <div className={styles.page}>
         <Header show={needsHF} />
         <State show={loading} setTransitioning={setTransitioning}>

+ 10 - 0
client/src/App.module.css

@@ -31,3 +31,13 @@
   top: 50%;
   transform: translate(-50%, -50%);
 }
+
+.volume {
+  position: absolute;
+  top: 4px;
+  left: 4px;
+  text-align: center;
+  vertical-align: center;
+  font-size: 1em;
+  z-index: 100;
+}

+ 5 - 7
client/src/components/screens/GamePanel/GuessPane/GuessPane.module.css

@@ -50,23 +50,21 @@
   align-items: center;
 }
 
-.gungame_panel {
+.gungamePanel {
   background-color: #333;
   width: 90%;
   height: 90%;
+  box-sizing: border-box;
+  padding-top: 2em;
+  padding-bottom: 10em;
   display: flex;
   flex-flow: column nowrap;
   justify-content: space-between;
   align-items: center;
 }
 
-.gungame_label {
+.gungameLabel {
   text-align: center;
-  margin-top: 2em;
-}
-
-.gungame_canvas {
-  margin-bottom: 8em;
 }
 
 @media only screen and (min-width: 600px) and (min-height: 600px) {

+ 4 - 2
client/src/components/screens/GamePanel/RaceMode.jsx

@@ -3,6 +3,7 @@ import ms from "pretty-ms";
 import styles from "./GamePanel.module.css";
 import { useFirstSubmitter, useIsFaded } from "./hooks";
 import chime from "../../../assets/chime.mp3";
+import { useIsMuted } from "../../../domain/gameStore";
 
 const chimeAudio = new Audio(chime);
 chimeAudio.volume = 0.25;
@@ -10,13 +11,14 @@ chimeAudio.volume = 0.25;
 const RaceMode = ({ rate, cutoffTime }) => {
   const first = useFirstSubmitter(rate, cutoffTime);
   const faded = useIsFaded(first);
+  const muted = useIsMuted();
   const cutoff = first !== null;
 
   useEffect(() => {
-    if (cutoff) {
+    if (cutoff && !muted) {
       chimeAudio.play();
     }
-  }, [cutoff]);
+  }, [cutoff, muted]);
 
   if (!cutoff) {
     return <></>;

+ 22 - 14
client/src/components/util/KillFeed/KillFeed.jsx

@@ -1,5 +1,10 @@
 import { useEffect, useState } from "react";
-import { dispatch, useGameId, usePlayerName } from "../../../domain/gameStore";
+import {
+  dispatch,
+  useGameId,
+  useIsMuted,
+  usePlayerName,
+} from "../../../domain/gameStore";
 import { usePlayers } from "../../../hooks/useGameInfo";
 import styles from "./KillFeed.module.css";
 import hitmarker from "../../../assets/hitmarker.svg";
@@ -12,6 +17,7 @@ import hitsound from "../../../assets/hitsound.wav";
 const shownItems = new Set();
 
 const KillFeed = () => {
+  const muted = useIsMuted();
   const playerName = usePlayerName();
   const gameId = useGameId();
   const players = usePlayers();
@@ -49,19 +55,21 @@ const KillFeed = () => {
     };
   }, [shownItemsState, gameId, players, playerName]);
   useEffect(() => {
-    display.forEach(() => {
-      const audio = new Audio(hitsound);
-      audio.volume = 0.5;
-      // delay up to half a second so overlapping sounds better
-      const delayedPlay = () =>
-        setTimeout(() => audio.play(), Math.random() * 500);
-      audio.addEventListener("canplaythrough", delayedPlay);
-      // clean up after ourselves in the hopes that the browser actually deletes this audio element
-      audio.addEventListener("ended", () =>
-        audio.removeEventListener("canplaythrough", delayedPlay)
-      );
-    });
-  }, [display]);
+    if (!muted) {
+      display.forEach(() => {
+        const audio = new Audio(hitsound);
+        audio.volume = 0.5;
+        // delay up to half a second so overlapping sounds better
+        const delayedPlay = () =>
+          setTimeout(() => audio.play(), Math.random() * 500);
+        audio.addEventListener("canplaythrough", delayedPlay);
+        // clean up after ourselves in the hopes that the browser actually deletes this audio element
+        audio.addEventListener("ended", () =>
+          audio.removeEventListener("canplaythrough", delayedPlay)
+        );
+      });
+    }
+  }, [display, muted]);
   return (
     <>
       {display.map(() => (

+ 5 - 0
client/src/domain/gameStore.js

@@ -22,6 +22,7 @@ import {
 
 const [hooks, actions, watch] = create(store => ({
   // state
+  isMuted: false,
   gameId: null,
   playerName: null,
   lastRound: {
@@ -42,6 +43,9 @@ const [hooks, actions, watch] = create(store => ({
   panoStartPov: null,
   // actions
   /* eslint-disable no-param-reassign */
+  muteToggle: () => {
+    store.isMuted = !store.isMuted;
+  },
   setPlayerName: name => {
     store.playerName = name;
   },
@@ -143,6 +147,7 @@ if (process.env.REACT_APP_MONITOR_STORE === "true") {
 }
 
 export const {
+  useIsMuted,
   useGameId,
   usePlayerName,
   useLastRound,