diff --git a/package-lock.json b/package-lock.json index e3d112a1fb9d00bd734d8c6899d50217003d6f13..af9fa642afd8acebf01405c436e164ac2e77de19 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10194,6 +10194,11 @@ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, + "node_modules/howler": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/howler/-/howler-2.2.4.tgz", + "integrity": "sha512-iARIBPgcQrwtEr+tALF+rapJ8qSc+Set2GJQl7xT1MQzWaVkFebdJhR3alVlSiUf5U7nAANKuj3aWpwerocD5w==" + }, "node_modules/html-minifier-terser": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", @@ -15506,6 +15511,17 @@ } } }, + "node_modules/use-sound": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/use-sound/-/use-sound-4.0.3.tgz", + "integrity": "sha512-L205pEUFIrLsGYsCUKHQVCt0ajs//YQOFbEQeNwaWaqQj3y3st4SuR+rvpMHLmv8hgTcfUFlvMQawZNI3OE18w==", + "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", @@ -16285,7 +16301,8 @@ "react-dom": "^18.2.0", "react-toastify": "^10.0.5", "react-zoom-pan-pinch": "^3.4.1", - "socket.io-client": "^4.7.4" + "socket.io-client": "^4.7.4", + "use-sound": "^4.0.3" }, "devDependencies": { "@tsconfig/vite-react": "^3.0.0", diff --git a/packages/client/package.json b/packages/client/package.json index 82d621eb071ee11ee6ff7e2049fa157391792914..c38aa9a9561ac2979271b671259d217ed1afd178 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -38,7 +38,8 @@ "react-dom": "^18.2.0", "react-toastify": "^10.0.5", "react-zoom-pan-pinch": "^3.4.1", - "socket.io-client": "^4.7.4" + "socket.io-client": "^4.7.4", + "use-sound": "^4.0.3" }, "devDependencies": { "@tsconfig/vite-react": "^3.0.0", diff --git a/packages/client/src/components/Header/HeaderLeft.tsx b/packages/client/src/components/Header/HeaderLeft.tsx index 231e3f5c2fb92bdb5e4082d02c6c7aa7aae5c49e..df31df14fdbf784009715e55bc9a0cebe1eb4848 100644 --- a/packages/client/src/components/Header/HeaderLeft.tsx +++ b/packages/client/src/components/Header/HeaderLeft.tsx @@ -4,15 +4,27 @@ import { AccountStanding } from "./AccountStanding"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Debug } from "@sc07-canvas/lib/src/debug"; import { faInfoCircle, faTools } from "@fortawesome/free-solid-svg-icons"; - +import African3 from '../../sounds/African3.mp3' +import useSound from "use-sound"; + export const HeaderLeft = () => { - const { setInfoSidebar } = useAppContext(); + const { setInfoSidebar, uiClickSound } = useAppContext(); + + const [African3Sound] = useSound( + African3, + { volume: 0.5 } + ); return (
{import.meta.env.DEV && ( diff --git a/packages/client/src/components/Overlay/OverlaySettings.tsx b/packages/client/src/components/Overlay/OverlaySettings.tsx index fdd369d0508336ddec576f2a44a9c55de060e2d6..4cbca070d3bf35e2aa8b61a8fd756287288586ac 100644 --- a/packages/client/src/components/Overlay/OverlaySettings.tsx +++ b/packages/client/src/components/Overlay/OverlaySettings.tsx @@ -1,10 +1,17 @@ import { Slider, Spinner, Switch } from "@nextui-org/react"; import { useAppContext } from "../../contexts/AppContext"; +import useSound from "use-sound"; +import African3 from '../../sounds/African3.mp3' export const OverlaySettings = () => { - const { blankOverlay, setBlankOverlay, heatmapOverlay, setHeatmapOverlay, pixelPulses, setPixelPulses } = + const { blankOverlay, setBlankOverlay, heatmapOverlay, setHeatmapOverlay, pixelPulses, setPixelPulses, uiClickSound } = useAppContext(); + const [African3Sound] = useSound( + African3, + { volume: 0.5 } + ); + return (
@@ -15,7 +22,12 @@ export const OverlaySettings = () => { - setBlankOverlay((vv) => ({ ...vv, enabled: v })) + { + setBlankOverlay((vv) => ({ ...vv, enabled: v })) + if (v && uiClickSound) { + African3Sound() + } + } } > Blank Canvas Overlay @@ -37,7 +49,12 @@ export const OverlaySettings = () => { - setHeatmapOverlay((vv) => ({ ...vv, enabled: v })) + { + setHeatmapOverlay((vv) => ({ ...vv, enabled: v })) + if (v && uiClickSound) { + African3Sound() + } + } } > {heatmapOverlay.loading && } @@ -60,7 +77,12 @@ export const OverlaySettings = () => { - setPixelPulses(v) + { + setPixelPulses(v) + if (v && uiClickSound) { + African3Sound() + } + } } > New Pixel Pulses diff --git a/packages/client/src/components/Settings/AudioSettings.tsx b/packages/client/src/components/Settings/AudioSettings.tsx new file mode 100644 index 0000000000000000000000000000000000000000..7f11392c29b259820eb0ca6fc3839956d09df22e --- /dev/null +++ b/packages/client/src/components/Settings/AudioSettings.tsx @@ -0,0 +1,291 @@ +import { useAppContext } from "../../contexts/AppContext"; +import useSound from 'use-sound'; +import Coffee1 from '../../sounds/Coffee1.mp3' +import Coffee2 from '../../sounds/Coffee2.mp3' +import African1 from '../../sounds/African1.mp3' +import African2 from '../../sounds/African2.mp3' +import African3 from '../../sounds/African3.mp3' +import African4 from '../../sounds/African4.mp3' +import WoodBlock1 from '../../sounds/Wood Block1.mp3' +import WoodBlock2 from '../../sounds/Wood Block2.mp3' +import WoodBlock3 from '../../sounds/Wood Block3.mp3' +import { Switch } from "@nextui-org/react"; +import { useEffect, useState } from "react"; +import network from "../../lib/network"; +import { Pixel } from "@sc07-canvas/lib/src/net"; + +export const AudioSettings = () => { + const { + placeSound, + setPlaceSound, + globalPlaceSound, + setGlobalPlaceSound, + disconnectSound, + setDisconnectSound, + reconnectSound, + setReconnectSound, + pixelAvailableSound, + setPixelAvailableSound, + pixelUndoSound, + setPixelUndoSound, + uiClickSound, + setUiClickSound + } = useAppContext(); + + const [Coffee1Sound] = useSound( + Coffee1, + { volume: 0.5 } + ); + + const [Coffee2Sound] = useSound( + Coffee2, + { volume: 0.5 } + ); + + const [African1Sound] = useSound( + African1, + { volume: 0.5 } + ); + + const [African2Sound] = useSound( + African2, + { volume: 0.5 } + ); + + const [African3Sound] = useSound( + African3, + { volume: 0.5 } + ); + + const [African4Sound] = useSound( + African4, + { volume: 0.5 } + ); + + const [WoodBlock1Sound] = useSound( + WoodBlock1, + { volume: 0.1 } + ); + + const [WoodBlock2Sound] = useSound( + WoodBlock2, + { volume: 0.1 } + ); + + const [WoodBlock3Sound] = useSound( + WoodBlock3, + { volume: 0.1 } + ); + + const [pixelsAvailable, setPixelsAvailable] = useState(0) + const [lastPixelPlaced, setLastPixelPlaced] = useState(0) + + const WoodSounds = [WoodBlock1Sound, WoodBlock2Sound, WoodBlock3Sound] + + useEffect(() => { + if (!pixelAvailableSound) { + return; + } + + function handleAvailable(number: number) { + if (number > pixelsAvailable) { + Coffee2Sound() + } + + setPixelsAvailable(number) + } + + network.socket.on("availablePixels", handleAvailable); + + return () => { + network.socket.off("availablePixels", handleAvailable); + }; + }, [pixelAvailableSound]); + + useEffect(() => { + if (!placeSound) { + return; + } + + function handlePlace(time: number) { + if (time < lastPixelPlaced) { + Coffee1Sound() + } + setLastPixelPlaced(time) + } + + network.socket.on("pixelLastPlaced", handlePlace); + + return () => { + network.socket.off("pixelLastPlaced", handlePlace); + }; + }, [placeSound]); + + useEffect(() => { + if (!pixelUndoSound) { + return; + } + + function handleUndo(data: { available: false } | { available: true; expireAt: number }) { + if (!data.available) { + African1Sound() + } + } + + network.socket.on("undo", handleUndo); + + return () => { + network.socket.off("undo", handleUndo); + }; + }, [pixelUndoSound]); + + useEffect(() => { + if (!globalPlaceSound) { + return; + } + + function handlePlace({ }: Pixel) { + WoodSounds[Math.floor(Math.random() * WoodSounds.length)]() + } + + network.socket.on("pixel", handlePlace); + + return () => { + network.socket.off("pixel", handlePlace); + }; + }, [globalPlaceSound]); + + useEffect(() => { + if (!disconnectSound) { + return; + } + + function handleDisconnect() { + African2Sound() + } + + network.socket.on("disconnect", handleDisconnect); + + return () => { + network.socket.off("disconnect", handleDisconnect); + }; + }, [disconnectSound]); + + useEffect(() => { + if (!reconnectSound) { + return; + } + + function handleConnect() { + African4Sound() + } + + network.socket.on("connect", handleConnect); + + return () => { + network.socket.off("connect", handleConnect); + }; + }, [reconnectSound]); + + return ( +
+
+

Audio

+

Sounds that play on events

+
+
+ + { + setPixelAvailableSound(v) + if (v) { + Coffee2Sound() + } + } + } + > + Pixel Available + + + { + setPlaceSound(v) + if (v) { + Coffee1Sound() + } + } + } + > + Pixel Place + + + { + setPixelUndoSound(v) + if (v) { + African1Sound() + } + } + } + > + Pixel Undo + + + { + setUiClickSound(v) + if (v) { + African3Sound() + } + } + } + > + UI Click + + + { + setDisconnectSound(v) + if (v) { + African2Sound() + } + } + } + > + Disconnect + + + { + setReconnectSound(v) + if (v) { + African4Sound() + } + } + } + > + Reconnect + + + { + setGlobalPlaceSound(v) + if (v) { + WoodSounds[Math.floor(Math.random() * WoodSounds.length)]() + } + } + } + > + Global Pixel Place ⚠️ + +
+
+ ); +}; diff --git a/packages/client/src/components/Settings/ChatSettings.tsx b/packages/client/src/components/Settings/ChatSettings.tsx index 865335573b138cda000681977b632a3cda07026a..3e0583cdf0bbe7eb00cd10e0ea801f9867444c5b 100644 --- a/packages/client/src/components/Settings/ChatSettings.tsx +++ b/packages/client/src/components/Settings/ChatSettings.tsx @@ -1,11 +1,19 @@ import { Switch } from "@nextui-org/react"; import { useAppContext } from "../../contexts/AppContext"; import React, { lazy } from "react"; +import African3 from '../../sounds/African3.mp3' +import useSound from "use-sound"; + const InnerChatSettings = lazy(() => import("../Chat/InnerChatSettings")); export const ChatSettings = () => { - const { loadChat, setLoadChat } = useAppContext(); + const { loadChat, setLoadChat, uiClickSound } = useAppContext(); + + const [African3Sound] = useSound( + African3, + { volume: 0.5 } + ); return (
@@ -14,7 +22,12 @@ export const ChatSettings = () => { { + setLoadChat(bool) + if (uiClickSound && bool) { + African3Sound() + } + }} />

Chat

diff --git a/packages/client/src/components/Settings/SettingsSidebar.tsx b/packages/client/src/components/Settings/SettingsSidebar.tsx index 7334faa70bd32453c9e64d33e1625d0a4f2354f9..43d57907a97023e86cf39b27dcafefe81d1e7b73 100644 --- a/packages/client/src/components/Settings/SettingsSidebar.tsx +++ b/packages/client/src/components/Settings/SettingsSidebar.tsx @@ -5,6 +5,7 @@ import { Button, Divider } from "@nextui-org/react"; import { TemplateSettings } from "./TemplateSettings"; import { ChatSettings } from "./ChatSettings"; import { OverlaySettings } from "../Overlay/OverlaySettings"; +import { AudioSettings } from "./AudioSettings"; export const SettingsSidebar = () => { const { settingsSidebar, setSettingsSidebar, setShowKeybinds } = useAppContext(); @@ -18,6 +19,8 @@ export const SettingsSidebar = () => { + +
diff --git a/packages/client/src/contexts/AppContext.tsx b/packages/client/src/contexts/AppContext.tsx index 6651ad3386b9e0f739623537bfc0f3d47ae6a9fe..781c99bcb155108c46593491680e0c79aef01470 100644 --- a/packages/client/src/contexts/AppContext.tsx +++ b/packages/client/src/contexts/AppContext.tsx @@ -25,6 +25,21 @@ interface IAppContext { loadChat: boolean; setLoadChat: (v: boolean) => void; + placeSound: boolean; + setPlaceSound: (v: boolean) => void; + globalPlaceSound: boolean; + setGlobalPlaceSound: (v: boolean) => void; + disconnectSound: boolean; + setDisconnectSound: (v: boolean) => void; + reconnectSound: boolean; + setReconnectSound: (v: boolean) => void; + pixelAvailableSound: boolean; + setPixelAvailableSound: (v: boolean) => void; + pixelUndoSound: boolean; + setPixelUndoSound: (v: boolean) => void; + uiClickSound: boolean; + setUiClickSound: (v: boolean) => void; + infoSidebar: boolean; setInfoSidebar: (v: boolean) => void; settingsSidebar: boolean; @@ -105,6 +120,15 @@ export const AppContext = ({ children }: PropsWithChildren) => { const [pixels, setPixels] = useState({ available: 0 }); const [undo, setUndo] = useState<{ available: true; expireAt: number }>(); + // --- audio --- + const [placeSound, setPlaceSound] = useState(false); + const [globalPlaceSound, setGlobalPlaceSound] = useState(false); + const [disconnectSound, setDisconnectSound] = useState(false); + const [reconnectSound, setReconnectSound] = useState(false); + const [pixelAvailableSound, setPixelAvailableSound] = useState(false); + const [pixelUndoSound, setPixelUndoSound] = useState(false); + const [uiClickSound, setUiClickSound] = useState(false); + // overlays visible const [infoSidebar, setInfoSidebar] = useState(false); const [settingsSidebar, setSettingsSidebar] = useState(false); @@ -240,6 +264,20 @@ export const AppContext = ({ children }: PropsWithChildren) => { setInfoSidebar, showModModal, setShowModModal, + placeSound, + setPlaceSound, + globalPlaceSound, + setGlobalPlaceSound, + disconnectSound, + setDisconnectSound, + reconnectSound, + setReconnectSound, + pixelAvailableSound, + setPixelAvailableSound, + pixelUndoSound, + setPixelUndoSound, + uiClickSound, + setUiClickSound }} > {!config && ( diff --git a/packages/client/src/sounds/African1.mp3 b/packages/client/src/sounds/African1.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..7f121c6bbde3e62a1bf4a1dbf91b90047eb945d7 Binary files /dev/null and b/packages/client/src/sounds/African1.mp3 differ diff --git a/packages/client/src/sounds/African2.mp3 b/packages/client/src/sounds/African2.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..2faf5a2903d172df3d7bfe1d2ddb2932a58d577d Binary files /dev/null and b/packages/client/src/sounds/African2.mp3 differ diff --git a/packages/client/src/sounds/African3.mp3 b/packages/client/src/sounds/African3.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..396b719b03dee6a7fa21065c5e2ec7dc360b3999 Binary files /dev/null and b/packages/client/src/sounds/African3.mp3 differ diff --git a/packages/client/src/sounds/African4.mp3 b/packages/client/src/sounds/African4.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..a47b96e76c82e54c9875413ece9e049c8e401977 Binary files /dev/null and b/packages/client/src/sounds/African4.mp3 differ diff --git a/packages/client/src/sounds/Coffee1.mp3 b/packages/client/src/sounds/Coffee1.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..4cd1b6e5afc5196358eae7cc399ef7276710d765 Binary files /dev/null and b/packages/client/src/sounds/Coffee1.mp3 differ diff --git a/packages/client/src/sounds/Coffee2.mp3 b/packages/client/src/sounds/Coffee2.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..db581ebc8220617f22e7982acc4aa01c0f623625 Binary files /dev/null and b/packages/client/src/sounds/Coffee2.mp3 differ diff --git a/packages/client/src/sounds/LICENSE.txt b/packages/client/src/sounds/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..ba6b920f48ffba1ca4cf67488ca02519898db3ef --- /dev/null +++ b/packages/client/src/sounds/LICENSE.txt @@ -0,0 +1,3 @@ +Sounds are by Ellr under CC BY 4.0 +Source: https://ellr.itch.io/universal-ui-soundpack +License: https://creativecommons.org/licenses/by/4.0/ \ No newline at end of file diff --git a/packages/client/src/sounds/Wood Block1.mp3 b/packages/client/src/sounds/Wood Block1.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..1302628eaa20d2688a12f6c43dd311d58d140e2a Binary files /dev/null and b/packages/client/src/sounds/Wood Block1.mp3 differ diff --git a/packages/client/src/sounds/Wood Block2.mp3 b/packages/client/src/sounds/Wood Block2.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..b1b623a1ef9ee08deed19d329480909c0b141c13 Binary files /dev/null and b/packages/client/src/sounds/Wood Block2.mp3 differ diff --git a/packages/client/src/sounds/Wood Block3.mp3 b/packages/client/src/sounds/Wood Block3.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..fb516286d0b31ad3354b0650d3a0cf89c889b4b1 Binary files /dev/null and b/packages/client/src/sounds/Wood Block3.mp3 differ