From 5bef683d0db7adf90b30cd826538b99ce693e791 Mon Sep 17 00:00:00 2001 From: Anton Mushnin Date: Tue, 30 Apr 2024 21:52:39 +0300 Subject: [PATCH 1/5] mapping events to sounds filenames --- web/src/components/layout/SoundMapping.ts | 34 +++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 web/src/components/layout/SoundMapping.ts diff --git a/web/src/components/layout/SoundMapping.ts b/web/src/components/layout/SoundMapping.ts new file mode 100644 index 00000000..8ca6d044 --- /dev/null +++ b/web/src/components/layout/SoundMapping.ts @@ -0,0 +1,34 @@ +import { SoundSource } from "../../hooks/useSound"; + +export const soundMapping: Record = { + menuItem: "button-click.mp3", + menuButton: "button-click.mp3", + soundVolume: "button-click.mp3", + launchButton: "button-click.mp3", + modeSelector: "button-click.mp3", + characterSelector: "button-click.mp3", + batButton: "button-click.mp3", + pitchButton: "button-click.mp3", + viewSelector: "button-click.mp3", + cancelButton: "button-click.mp3", + createButton: "button-click.mp3", + imageSelector: "button-click.mp3", + addCharacter: "button-click.mp3", + pitchHistoryOpen: "button-click.mp3", + pitchHistoryClose: "button-click.mp3", + grid: "button-click.mp3", + typeSelector: "button-click.mp3", + heatmapClick: "button-click.mp3", + commitButton: "button-click.mp3", + homeButton: "button-click.mp3", + loss: "loss.mp3", + win: "win.mp3", + heartbeat: "heartbeat.mp3", + stadium: "stadium.mp3", + contactHit: "", + powerHit: "", + walk: "", + catch: "", + error: "error.mp3", + notification: "notification.mp3", +}; From dee397296f63c5f4d237383a4ae8c10ff55a9bb2 Mon Sep 17 00:00:00 2001 From: Anton Mushnin Date: Tue, 30 Apr 2024 21:52:58 +0300 Subject: [PATCH 2/5] useSound hook --- web/src/hooks/useSound.tsx | 52 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 web/src/hooks/useSound.tsx diff --git a/web/src/hooks/useSound.tsx b/web/src/hooks/useSound.tsx new file mode 100644 index 00000000..6c61c3c9 --- /dev/null +++ b/web/src/hooks/useSound.tsx @@ -0,0 +1,52 @@ +import { soundMapping } from "../components/layout/SoundMapping"; +import { useGameContext } from "../contexts/GameContext"; + +export type SoundSource = + | "menuItem" + | "menuButton" + | "soundVolume" + | "launchButton" + | "modeSelector" + | "characterSelector" + | "batButton" + | "pitchButton" + | "viewSelector" + | "cancelButton" + | "createButton" + | "imageSelector" + | "addCharacter" + | "pitchHistoryOpen" + | "pitchHistoryClose" + | "grid" + | "typeSelector" + | "heatmapClick" + | "commitButton" + | "homeButton" + | "loss" + | "win" + | "heartbeat" + | "stadium" + | "contactHit" + | "powerHit" + | "walk" + | "catch" + | "error" + | "notification"; + +// Custom hook to play sound +export const useSound = () => { + const { soundVolume } = useGameContext(); + return (source: SoundSource) => { + const soundName = soundMapping[source]; + if (soundName && soundVolume) { + const soundElement = document.getElementById(soundName) as HTMLAudioElement; + if (!soundElement) { + console.warn("Sound element not found:", soundName, source); + return; + } + soundElement.currentTime = 0; + soundElement.volume = soundVolume / 100; + soundElement.play(); + } + }; +}; From d9662fca9eaa0b5841226315c714c03a2009e38d Mon Sep 17 00:00:00 2001 From: Anton Mushnin Date: Tue, 30 Apr 2024 21:53:06 +0300 Subject: [PATCH 3/5] new icon --- web/src/components/icons/VolumeOffIcon.tsx | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 web/src/components/icons/VolumeOffIcon.tsx diff --git a/web/src/components/icons/VolumeOffIcon.tsx b/web/src/components/icons/VolumeOffIcon.tsx new file mode 100644 index 00000000..d63497cc --- /dev/null +++ b/web/src/components/icons/VolumeOffIcon.tsx @@ -0,0 +1,23 @@ +import React from "react"; + +const VolumeOffIcon: React.FC> = (props) => ( + + + + + +); + +export default VolumeOffIcon; From a48996e60a4e6e7a81a962ba773f9374b7b38bed Mon Sep 17 00:00:00 2001 From: Anton Mushnin Date: Tue, 30 Apr 2024 21:55:38 +0300 Subject: [PATCH 4/5] sound control --- web/src/components/layout/Navbar.module.css | 7 +++ web/src/components/layout/Navbar.tsx | 55 ++++++++++++++++++++- web/src/components/layout/SoundFxSlider.tsx | 25 ++++++---- web/src/components/layout/layout.tsx | 9 ++++ 4 files changed, 85 insertions(+), 11 deletions(-) diff --git a/web/src/components/layout/Navbar.module.css b/web/src/components/layout/Navbar.module.css index 7855bca0..5078e9a1 100644 --- a/web/src/components/layout/Navbar.module.css +++ b/web/src/components/layout/Navbar.module.css @@ -62,6 +62,13 @@ margin-top: 5px; } +.soundSlider { + position: absolute; + z-index: 20; + right: 0; + margin-top: 5px; +} + .menuItem { color: #262019; font-family: Pangolin, cursive; diff --git a/web/src/components/layout/Navbar.tsx b/web/src/components/layout/Navbar.tsx index 5b885e33..cde8b087 100644 --- a/web/src/components/layout/Navbar.tsx +++ b/web/src/components/layout/Navbar.tsx @@ -9,12 +9,21 @@ import { FEEDBACK_FORM_URL, FULLCOUNT_ASSETS_PATH } from "../../constants"; import { setLocalStorageItem } from "../../utils/localStorage"; import { useGameContext } from "../../contexts/GameContext"; import router from "next/router"; +import VolumeOn from "../icons/VolumeOn"; +import SoundFxSlider from "./SoundFxSlider"; +import VolumeOffIcon from "../icons/VolumeOffIcon"; +import { useSound } from "../../hooks/useSound"; const Navbar = () => { const { user } = useUser(); const [isMenuOpen, setIsMenuOpen] = useState(false); - const menuRef = useRef(null); // Ref for the menu element, typed as HTMLDivElement + const menuRef = useRef(null); + const [isSoundSliderOpen, setIsSoundSliderOpen] = useState(false); + const soundSliderRef = useRef(null); + + const { soundVolume } = useGameContext(); + const playSound = useSound(); const handleClickOutside = (event: MouseEvent) => { if (menuRef.current && !menuRef.current.contains(event.target as Node)) { @@ -22,14 +31,22 @@ const Navbar = () => { event.stopPropagation(); } }; + const handleClickOutsideSlider = (event: MouseEvent) => { + if (soundSliderRef.current && !soundSliderRef.current.contains(event.target as Node)) { + setIsSoundSliderOpen(false); + event.stopPropagation(); + } + }; const { joinedNotification, updateContext } = useGameContext(); const handleNotificationClick = () => { + playSound("menuItem"); setLocalStorageItem("joinedNotification", !joinedNotification); updateContext({ joinedNotification: !joinedNotification }); }; const handleLeaderboardClick = () => { + playSound("menuItem"); // sendReport('Leaderboards', {}, ["type:click","click:leaderboards"]) router.push("/leaderboards"); }; @@ -43,6 +60,15 @@ const Navbar = () => { }; }, [isMenuOpen]); + useEffect(() => { + if (isSoundSliderOpen) { + document.addEventListener("click", handleClickOutsideSlider, true); + } + return () => { + document.removeEventListener("click", handleClickOutsideSlider, true); + }; + }, [isSoundSliderOpen]); + const { logout, isLoading: isLoggingOut } = useLogout(); return ( @@ -64,7 +90,30 @@ const Navbar = () => { {/* */} {/**/}
-
setIsMenuOpen(!isMenuOpen)}> +
{ + playSound("menuButton"); + setIsSoundSliderOpen(!isSoundSliderOpen); + }} + > + {soundVolume === 0 ? : } +
+ {isSoundSliderOpen &&
} + {isSoundSliderOpen && ( +
+ +
+ )} +
+
+
{ + playSound("menuButton"); + setIsMenuOpen(!isMenuOpen); + }} + >
{isMenuOpen &&
} @@ -82,6 +131,7 @@ const Navbar = () => {
{ + playSound("menuItem"); window.open(FEEDBACK_FORM_URL, "_blank", "noopener,noreferrer"); }} > @@ -91,6 +141,7 @@ const Navbar = () => {
{ + playSound("menuItem"); if (!isLoggingOut) { logout(); } diff --git a/web/src/components/layout/SoundFxSlider.tsx b/web/src/components/layout/SoundFxSlider.tsx index 48c6e78e..cd38c1b3 100644 --- a/web/src/components/layout/SoundFxSlider.tsx +++ b/web/src/components/layout/SoundFxSlider.tsx @@ -1,26 +1,33 @@ import { Flex, Slider, SliderFilledTrack, SliderThumb, SliderTrack, Text } from "@chakra-ui/react"; import { useGameContext } from "../../contexts/GameContext"; +import { useSound } from "../../hooks/useSound"; const SoundFxSlider = () => { const { soundVolume, updateContext } = useGameContext(); + const playSound = useSound(); return ( - - Sound FX + + + Sound FX + updateContext({ soundVolume: val })} + w={"60px"} + onChange={(val) => { + playSound("soundVolume"); + updateContext({ soundVolume: val }); + }} > - - + + diff --git a/web/src/components/layout/layout.tsx b/web/src/components/layout/layout.tsx index 8b0319c0..6bac1ad0 100644 --- a/web/src/components/layout/layout.tsx +++ b/web/src/components/layout/layout.tsx @@ -4,6 +4,7 @@ import { Flex } from "@chakra-ui/react"; import { FULLCOUNT_ASSETS, FULLCOUNT_ASSETS_PATH } from "../../constants"; import React from "react"; +import { soundMapping } from "./SoundMapping"; export const siteTitle = "Fullcount - baseball game"; @@ -42,6 +43,14 @@ export default function Layout({ src={`${FULLCOUNT_ASSETS_PATH}/sounds/clapping-male-crowd.wav`} preload={"auto"} /> + {Array.from(new Set(Object.values(soundMapping))).map((fileName) => ( +
From 65f6f975226b4e7878cbd78c20d0e6fa4d607a14 Mon Sep 17 00:00:00 2001 From: Anton Mushnin Date: Tue, 30 Apr 2024 21:56:14 +0300 Subject: [PATCH 5/5] adding sounds to events --- web/src/components/HomePage/ModeSelector.tsx | 4 +++ web/src/components/HomePage/PracticeView.tsx | 13 +++++-- web/src/components/HomePage/PvpView.tsx | 18 ++++++++-- web/src/components/LaunchForm.tsx | 6 ++++ web/src/components/Playing.tsx | 5 +-- web/src/components/TitleScreen.tsx | 1 + web/src/components/atbat/AtBatView.tsx | 6 ++-- web/src/components/atbat/AtBatView2.tsx | 3 ++ web/src/components/atbat/ExitDialog.tsx | 5 +++ .../components/atbat/OnboardingCharacter.tsx | 12 +++++-- web/src/components/atbat/Outcome2.tsx | 10 ++++-- web/src/components/atbat/Score.tsx | 10 +++++- web/src/components/atbat/ScoreForDesktop.tsx | 10 +++++- web/src/components/campaign/CampaignView.tsx | 12 +++++-- web/src/components/campaign/CharacterCard.tsx | 5 ++- web/src/components/layout/PlayingLayout.tsx | 35 ++++++++++--------- .../leaderboard/LeaderboardLayout.tsx | 10 +++++- .../leaderboard/LeaderboardView.tsx | 7 +++- web/src/components/playing/GridComponent.tsx | 4 ++- web/src/components/playing/HeatMap.tsx | 12 +++++-- web/src/components/playing/PlayView.tsx | 8 ++++- web/src/components/playing/PlayerView.tsx | 4 +++ web/src/components/playing/PlayerView2.tsx | 4 +++ .../components/practice/PracticeSelect.tsx | 23 ++++++++++-- .../components/tokens/CreateCharacterForm.tsx | 20 +++++++++-- .../components/tokens/NewCharacterButton.tsx | 4 +++ web/src/components/tokens/PlayButtons.tsx | 4 +++ web/src/components/tokens/Roster.tsx | 11 ++++-- 28 files changed, 216 insertions(+), 50 deletions(-) diff --git a/web/src/components/HomePage/ModeSelector.tsx b/web/src/components/HomePage/ModeSelector.tsx index 6880d17f..767c52f9 100644 --- a/web/src/components/HomePage/ModeSelector.tsx +++ b/web/src/components/HomePage/ModeSelector.tsx @@ -1,6 +1,7 @@ import styles from "./ModeSelector.module.css"; import { useGameContext } from "../../contexts/GameContext"; import { sendReport } from "../../utils/humbug"; +import { useSound } from "../../hooks/useSound"; const modes = [ { @@ -19,7 +20,10 @@ const modes = [ const ModeSelector = () => { const { selectedMode, updateContext } = useGameContext(); + + const playSound = useSound(); const handleClick = (modeIdx: number) => { + playSound("modeSelector"); sendReport(`Mode selected: ${modes[modeIdx].title}`, {}, [ "type:click", `click:${modes[modeIdx].title}`, diff --git a/web/src/components/HomePage/PracticeView.tsx b/web/src/components/HomePage/PracticeView.tsx index 021b12e3..6abc928b 100644 --- a/web/src/components/HomePage/PracticeView.tsx +++ b/web/src/components/HomePage/PracticeView.tsx @@ -3,6 +3,7 @@ import { AtBat, OwnedToken } from "../../types"; import { useGameContext } from "../../contexts/GameContext"; import { useRouter } from "next/router"; import CoachCard from "../practice/CoachCard"; +import { useSound } from "../../hooks/useSound"; const BOTS_ADDRESS = "0xbb1dDc1eB50959c4c59b62F3f6Dbf9CbB6156Bc8"; const rachel = ["11", "12", "13"]; @@ -33,6 +34,8 @@ export const isCoach = (address: string, id: string): boolean => { const PvpView = ({ atBats }: { atBats: AtBat[] }) => { const router = useRouter(); const { updateContext } = useGameContext(); + const playSound = useSound(); + const handlePlay = (atBat: AtBat) => { if (atBat.pitcher) { const team = pitchers.find((t) => getDefault(t) === atBat.pitcher?.id); @@ -81,7 +84,10 @@ const PvpView = ({ atBats }: { atBats: AtBat[] }) => { return openAtBat.pitcher ? ( handlePlay(openAtBat)} + onClick={() => { + playSound("batButton"); + handlePlay(openAtBat); + }} description={pitcherDescription} isPitcher={true} key={idx} @@ -105,7 +111,10 @@ const PvpView = ({ atBats }: { atBats: AtBat[] }) => { return openAtBat.batter ? ( handlePlay(openAtBat)} + onClick={() => { + playSound("pitchButton"); + handlePlay(openAtBat); + }} description={batterDescription} isPitcher={false} key={idx} diff --git a/web/src/components/HomePage/PvpView.tsx b/web/src/components/HomePage/PvpView.tsx index 80dd73b6..e7d197d3 100644 --- a/web/src/components/HomePage/PvpView.tsx +++ b/web/src/components/HomePage/PvpView.tsx @@ -14,12 +14,15 @@ import useUser from "../../contexts/UserContext"; import { isCampaignToken } from "../campaign/teams"; import { isCoach } from "./PracticeView"; import { sendReport } from "../../utils/humbug"; +import { useSound } from "../../hooks/useSound"; const views = ["Open", "My games", "Other"]; const PvpView = ({ atBats, tokens }: { atBats: AtBat[]; tokens: OwnedToken[] }) => { const { selectedToken } = useGameContext(); const toast = useMoonToast(); const { user } = useUser(); + const playSound = useSound(); + const queryClient = useQueryClient(); const joinSession = useMutation( async ({ @@ -113,7 +116,10 @@ const PvpView = ({ atBats, tokens }: { atBats: AtBat[]; tokens: OwnedToken[] }) {views.map((v, idx) => (
setSelectedView(idx)} + onClick={() => { + playSound("viewSelector"); + setSelectedView(idx); + }} key={idx} > {v} @@ -167,7 +173,10 @@ const PvpView = ({ atBats, tokens }: { atBats: AtBat[]; tokens: OwnedToken[] }) handlePlay(openAtBat)} + onClick={() => { + playSound("batButton"); + handlePlay(openAtBat); + }} isLoading={ joinSession.variables?.sessionID === openAtBat.lastSessionId && joinSession.isLoading @@ -195,7 +204,10 @@ const PvpView = ({ atBats, tokens }: { atBats: AtBat[]; tokens: OwnedToken[] }) handlePlay(openAtBat)} + onClick={() => { + playSound("pitchButton"); + handlePlay(openAtBat); + }} isLoading={ joinSession.variables?.sessionID === openAtBat.lastSessionId && joinSession.isLoading diff --git a/web/src/components/LaunchForm.tsx b/web/src/components/LaunchForm.tsx index fae6f71b..73e78792 100644 --- a/web/src/components/LaunchForm.tsx +++ b/web/src/components/LaunchForm.tsx @@ -9,20 +9,26 @@ import { import { useGameContext } from "../contexts/GameContext"; import router from "next/router"; import { sendReport } from "../utils/humbug"; +import { useSound } from "../hooks/useSound"; const buttons = ["PVP", "CAMPAIGN", "PRACTICE"]; const LaunchForm = ({ onClose }: { onClose: () => void }) => { const { updateContext } = useGameContext(); + const playSound = useSound(); + const handleClick = (selectedMode: number) => { + playSound("launchButton"); updateContext({ selectedMode }); onClose(); }; const handleDemoClick = () => { + playSound("launchButton"); sendReport("Playing demo", {}, ["type:click", "click:play_demo"]); router.push("/atbat"); }; const handleTrailerClick = () => { + playSound("launchButton"); sendReport("Playing trailer", {}, ["type:click", "click:play_trailer"]); window.open(TRAILER_LINK, "_blank", "noopener,noreferrer"); }; diff --git a/web/src/components/Playing.tsx b/web/src/components/Playing.tsx index 74b26387..1d956596 100644 --- a/web/src/components/Playing.tsx +++ b/web/src/components/Playing.tsx @@ -16,12 +16,12 @@ import HomePage from "./HomePage/HomePage"; import { getAtBats } from "../services/fullcounts"; import React, { useEffect, useState } from "react"; import { FULLCOUNT_ASSETS_PATH } from "../constants"; -import { playSound } from "../utils/notifications"; import { getContracts } from "../utils/getWeb3Contracts"; import { getMulticallResults } from "../utils/multicall"; import { AbiItem } from "web3-utils"; import FullcountABIImported from "../web3/abi/FullcountABI.json"; +import { useSound } from "../hooks/useSound"; const FullcountABI = FullcountABIImported as unknown as AbiItem[]; const Playing = () => { @@ -37,6 +37,7 @@ const Playing = () => { joinedNotification, } = useGameContext(); const { user } = useUser(); + const playSound = useSound(); const ownedTokens = useQuery( ["owned_tokens", user], @@ -99,7 +100,7 @@ const Playing = () => { result.some((t) => t.address === ts.address && t.id === ts.id && t.progress === "3"), ) ) { - playSound("clapping"); + playSound("stadium"); } return result; diff --git a/web/src/components/TitleScreen.tsx b/web/src/components/TitleScreen.tsx index 29508377..dade513f 100644 --- a/web/src/components/TitleScreen.tsx +++ b/web/src/components/TitleScreen.tsx @@ -12,6 +12,7 @@ import LoadingView from "./HomePage/LoadingView"; import LaunchForm from "./LaunchForm"; import MoonstreamLogo2 from "./icons/MoonstreamLogo2"; import { useGameContext } from "../contexts/GameContext"; + const TitleScreen = () => { const { user, isLoading } = useUser(); const [isLogging, setIsLogging] = useState(false); // login or signUp diff --git a/web/src/components/atbat/AtBatView.tsx b/web/src/components/atbat/AtBatView.tsx index 30c786e8..d24e8bab 100644 --- a/web/src/components/atbat/AtBatView.tsx +++ b/web/src/components/atbat/AtBatView.tsx @@ -17,10 +17,10 @@ import ExitIcon from "../icons/ExitIcon"; import TokenCard from "./TokenCard"; import ScoreForDesktop from "./ScoreForDesktop"; import { sendReport } from "../../utils/humbug"; -import { playSound } from "../../utils/notifications"; import ExitDialog from "./ExitDialog"; import useUser from "../../contexts/UserContext"; import { fetchFullcountPlayerTokens } from "../../tokenInterfaces/FullcountPlayerAPI"; +import { useSound } from "../../hooks/useSound"; export const outcomes = [ "In Progress", @@ -68,6 +68,7 @@ const AtBatView: React.FC = () => { const [isBigView] = useMediaQuery("(min-width: 1024px)"); const { isOpen, onOpen, onClose } = useDisclosure(); const { user } = useUser(); + const playSound = useSound(); useEffect(() => { window.scrollTo(0, 0); @@ -169,7 +170,7 @@ const AtBatView: React.FC = () => { const { gameContract } = getContracts(); const progress = await gameContract.methods.sessionProgress(currentSessionId).call(); if (Number(currentSessionProgress.data) === 2 && Number(progress) === 3) { - playSound("joinedNotification"); + playSound("stadium"); } return progress; }, @@ -186,6 +187,7 @@ const AtBatView: React.FC = () => { }; const handleExitClick = () => { + playSound("homeButton"); if ( atBatState.data?.atBat.pitches.length === 1 && atBatState.data.atBat.pitches[0].progress == 2 diff --git a/web/src/components/atbat/AtBatView2.tsx b/web/src/components/atbat/AtBatView2.tsx index 94ec7872..0c11094b 100644 --- a/web/src/components/atbat/AtBatView2.tsx +++ b/web/src/components/atbat/AtBatView2.tsx @@ -15,6 +15,7 @@ import { getAtBat, initialAtBatState, selectedToken } from "./OnboardingAPI"; import { outcomeType, sessionOutcomes } from "./AtBatView"; import OnboardingCharacter from "./OnboardingCharacter"; import { useGameContext } from "../../contexts/GameContext"; +import { useSound } from "../../hooks/useSound"; const AtBatView2: React.FC = () => { const router = useRouter(); @@ -27,6 +28,7 @@ const AtBatView2: React.FC = () => { const [name, setName] = useState("Guest_0420"); const [image, setImage] = useState(blbImage(7)); const { updateContext } = useGameContext(); + const playSound = useSound(); useEffect(() => { window.scrollTo(0, 0); @@ -104,6 +106,7 @@ const AtBatView2: React.FC = () => {
{ + playSound("homeButton"); updateContext({ isLaunching: false }); router.push("/"); }} diff --git a/web/src/components/atbat/ExitDialog.tsx b/web/src/components/atbat/ExitDialog.tsx index 7f2e88cf..a411b7d2 100644 --- a/web/src/components/atbat/ExitDialog.tsx +++ b/web/src/components/atbat/ExitDialog.tsx @@ -8,6 +8,7 @@ import { CANT_ABORT_SESSION_MSG } from "../../messages"; import useMoonToast from "../../hooks/useMoonToast"; import { useEffect, useRef } from "react"; import useUser from "../../contexts/UserContext"; +import { useSound } from "../../hooks/useSound"; const ExitDialog = ({ token, @@ -21,6 +22,8 @@ const ExitDialog = ({ const toast = useMoonToast(); const queryClient = useQueryClient(); const { user } = useUser(); + const playSound = useSound(); + const updateTokenInCache = (token: Token, sessionId: number) => { const currentTokens = queryClient.getQueryData(["owned_tokens", user]); const currentAtBats = queryClient.getQueryData<{ tokens: Token[]; atBats: AtBat[] }>([ @@ -95,9 +98,11 @@ const ExitDialog = ({ }, []); const handleCloseClick = () => { + playSound("homeButton"); closeAtBat.mutate({ token, sessionId }); }; const handleKeepClick = () => { + playSound("homeButton"); router.push("/"); }; return ( diff --git a/web/src/components/atbat/OnboardingCharacter.tsx b/web/src/components/atbat/OnboardingCharacter.tsx index 143384a1..00f5565e 100644 --- a/web/src/components/atbat/OnboardingCharacter.tsx +++ b/web/src/components/atbat/OnboardingCharacter.tsx @@ -4,6 +4,7 @@ import styles from "../tokens/CreateNewCharacter.module.css"; import localStyles from "./OnboardingCharacter.module.css"; import React, { useEffect, useState } from "react"; import { blbImage } from "../../constants"; +import { useSound } from "../../hooks/useSound"; const NUMBER_OF_IMAGES = 8; const names = [ "Joe Expo", @@ -30,6 +31,7 @@ const OnboardingCharacter = ({ }) => { const [name, setName] = useState(names[7]); const [imageIndex, setImageIndex] = useState(7); + const playSound = useSound(); useEffect(() => { setName(names[7]); @@ -59,7 +61,10 @@ const OnboardingCharacter = ({ alt={`img${idx}`} src={blbImage(idx)} className={imageIndex === idx ? styles.selectedImage : styles.image} - onClick={() => setImageIndex(idx)} + onClick={() => { + playSound("imageSelector"); + setImageIndex(idx); + }} /> ))}
@@ -68,7 +73,10 @@ const OnboardingCharacter = ({ diff --git a/web/src/components/atbat/Outcome2.tsx b/web/src/components/atbat/Outcome2.tsx index 9dbc3063..4391a90a 100644 --- a/web/src/components/atbat/Outcome2.tsx +++ b/web/src/components/atbat/Outcome2.tsx @@ -7,6 +7,7 @@ import { FULLCOUNT_ASSETS_PATH } from "../../constants"; import { useGameContext } from "../../contexts/GameContext"; import { outcomes, outcomeType } from "./AtBatView"; import Tip from "./Tip"; +import { useSound } from "../../hooks/useSound"; export const sessionOutcomeType = ( tokens: Token[], @@ -43,9 +44,14 @@ const Outcome2 = ({ showTips?: boolean; }) => { const { selectedToken } = useGameContext(); + const playSound = useSound(); + useEffect(() => { - console.log(sessionStatus); - }, [sessionStatus]); + if (atBat.outcome !== 0 && forToken) { + playSound(outcomeType([forToken], atBat) === "positive" ? "win" : "loss"); + } + }, []); + return (
diff --git a/web/src/components/atbat/Score.tsx b/web/src/components/atbat/Score.tsx index 34c0a677..e1369ff8 100644 --- a/web/src/components/atbat/Score.tsx +++ b/web/src/components/atbat/Score.tsx @@ -7,6 +7,7 @@ import { useState } from "react"; import ChevronUp from "../icons/ChevronUp"; import ChevronDown from "../icons/ChevronDown"; import PitchHistory from "./PitchHistory"; +import { useSound } from "../../hooks/useSound"; const Score = ({ atBat, @@ -19,11 +20,18 @@ const Score = ({ }) => { const { secondsPerPhase } = useGameContext(); const [isHistoryOpen, setIsHistoryOpen] = useState(openHistory); + const playSound = useSound(); return (
{isHistoryOpen && } -
setIsHistoryOpen(!isHistoryOpen)}> +
{ + playSound(isHistoryOpen ? "pitchHistoryClose" : "pitchHistoryOpen"); + setIsHistoryOpen(!isHistoryOpen); + }} + >
{`Pitch ${atBat.numberOfSessions}`}
{isHistoryOpen ? : }
diff --git a/web/src/components/atbat/ScoreForDesktop.tsx b/web/src/components/atbat/ScoreForDesktop.tsx index c3a0326c..99833f67 100644 --- a/web/src/components/atbat/ScoreForDesktop.tsx +++ b/web/src/components/atbat/ScoreForDesktop.tsx @@ -7,6 +7,7 @@ import PitchHistory from "./PitchHistory"; import ChevronUpLarge from "../icons/ChevronUpLarge"; import ChevronDownLarge from "../icons/ChevronDownLarge"; import DotsCounterLarge from "../sessions/DotsCounterLarge"; +import { useSound } from "../../hooks/useSound"; const ScoreForDesktop = ({ atBat, @@ -19,11 +20,18 @@ const ScoreForDesktop = ({ }) => { const { secondsPerPhase } = useGameContext(); const [isHistoryOpen, setIsHistoryOpen] = useState(openHistory); + const playSound = useSound(); return (
{isHistoryOpen && } -
setIsHistoryOpen(!isHistoryOpen)}> +
{ + playSound(isHistoryOpen ? "pitchHistoryClose" : "pitchHistoryOpen"); + setIsHistoryOpen(!isHistoryOpen); + }} + >
{`Pitch ${atBat.numberOfSessions}`}
{isHistoryOpen ? : }
diff --git a/web/src/components/campaign/CampaignView.tsx b/web/src/components/campaign/CampaignView.tsx index 677450a0..38c3d650 100644 --- a/web/src/components/campaign/CampaignView.tsx +++ b/web/src/components/campaign/CampaignView.tsx @@ -15,10 +15,12 @@ import { getCharacterName, } from "./teams"; import axios from "axios"; +import { useSound } from "../../hooks/useSound"; const CampaignView = ({ atBats }: { atBats: AtBat[] }) => { const { selectedToken } = useGameContext(); const [isPitching, setIsPitching] = useState(true); + const playSound = useSound(); const stats = useQuery( ["stats", selectedToken?.id, selectedToken?.address], @@ -98,13 +100,19 @@ const CampaignView = ({ atBats }: { atBats: AtBat[] }) => {
setIsPitching(true)} + onClick={() => { + playSound("modeSelector"); + setIsPitching(true); + }} > pitchers
setIsPitching(false)} + onClick={() => { + playSound("modeSelector"); + setIsPitching(false); + }} > batters
diff --git a/web/src/components/campaign/CharacterCard.tsx b/web/src/components/campaign/CharacterCard.tsx index 94f60b33..7446e0a2 100644 --- a/web/src/components/campaign/CharacterCard.tsx +++ b/web/src/components/campaign/CharacterCard.tsx @@ -10,6 +10,7 @@ import { Spinner, Image } from "@chakra-ui/react"; import { Character } from "./teams"; import CharacterProgress from "./CharacterProgress"; import { sendReport } from "../../utils/humbug"; +import { useSound } from "../../hooks/useSound"; const CharacterCard = ({ character, @@ -29,6 +30,8 @@ const CharacterCard = ({ const queryClient = useQueryClient(); const { user } = useUser(); const toast = useMoonToast(); + const playSound = useSound(); + const joinSession = useMutation( async ({ sessionID, @@ -69,7 +72,6 @@ const CharacterCard = ({ }, ); queryClient.setQueryData(["owned_tokens", user], (oldData: OwnedToken[] | undefined) => { - console.log(oldData); if (!oldData) { return []; } @@ -106,6 +108,7 @@ const CharacterCard = ({ ); const handlePlay = (atBat: AtBat | undefined) => { + playSound(atBat?.pitcher ? "batButton" : "pitchButton"); if (selectedToken && atBat) { joinSession.mutate({ sessionID: atBat.lastSessionId ?? 0, diff --git a/web/src/components/layout/PlayingLayout.tsx b/web/src/components/layout/PlayingLayout.tsx index fc4c6d00..c212dd11 100644 --- a/web/src/components/layout/PlayingLayout.tsx +++ b/web/src/components/layout/PlayingLayout.tsx @@ -2,19 +2,20 @@ import React, { ReactNode, useEffect } from "react"; import { Flex } from "@chakra-ui/react"; import Navbar from "./Navbar"; -import { FULLCOUNT_ASSETS_PATH } from "../../constants"; +import { FULLCOUNT_ASSETS, FULLCOUNT_ASSETS_PATH } from "../../constants"; import { useGameContext } from "../../contexts/GameContext"; import { getLocalStorageItem } from "../../utils/localStorage"; const sounds = { - whoosh: `${FULLCOUNT_ASSETS_PATH}/sounds/whoosh.wav`, - heartbeat: `${FULLCOUNT_ASSETS_PATH}/sounds/heartbeat.wav`, - swoosh: `${FULLCOUNT_ASSETS_PATH}/sounds/windy-swoosh.wav`, - swing: `${FULLCOUNT_ASSETS_PATH}/sounds/fast-swoosh.wav`, - clapping: `${FULLCOUNT_ASSETS_PATH}/sounds/clapping-male-crowd.wav`, - hit: `${FULLCOUNT_ASSETS_PATH}/sounds/eelke-hit-01.wav`, - catch: `${FULLCOUNT_ASSETS_PATH}/sounds/martinimeniscus-glove-catch-6-ff009.wav`, - select: `${FULLCOUNT_ASSETS_PATH}/sounds/select.wav`, + // whoosh: `${FULLCOUNT_ASSETS_PATH}/sounds/whoosh.wav`, + // heartbeat: `${FULLCOUNT_ASSETS_PATH}/sounds/heartbeat.wav`, + // swoosh: `${FULLCOUNT_ASSETS_PATH}/sounds/windy-swoosh.wav`, + // swing: `${FULLCOUNT_ASSETS_PATH}/sounds/fast-swoosh.wav`, + // clapping: `${FULLCOUNT_ASSETS_PATH}/sounds/clapping-male-crowd.wav`, + // hit: `${FULLCOUNT_ASSETS_PATH}/sounds/eelke-hit-01.wav`, + // catch: `${FULLCOUNT_ASSETS_PATH}/sounds/martinimeniscus-glove-catch-6-ff009.wav`, + // select: `${FULLCOUNT_ASSETS_PATH}/sounds/select.wav`, + // "button-click.mp3": `${FULLCOUNT_ASSETS}/sounds/button-click.mp3`, }; const PlayingLayout = ({ children }: { children: ReactNode }) => { @@ -36,14 +37,14 @@ const PlayingLayout = ({ children }: { children: ReactNode }) => { maxW={"1240px"} > -