diff --git a/web/pages/_document.tsx b/web/pages/_document.tsx index 081af4be..175e73d5 100644 --- a/web/pages/_document.tsx +++ b/web/pages/_document.tsx @@ -1,5 +1,4 @@ import { Html, Head, Main, NextScript } from "next/document"; -import Script from "next/script"; export default function Document() { return ( diff --git a/web/src/components/ConnectingView.module.css b/web/src/components/ConnectingView.module.css index 5acb0405..d68f6a91 100644 --- a/web/src/components/ConnectingView.module.css +++ b/web/src/components/ConnectingView.module.css @@ -16,7 +16,6 @@ background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent; - font-family: Inter; font-size: 20px; font-style: normal; font-weight: 700; @@ -26,20 +25,28 @@ .prompt { color: #fff; - font-family: Inter; font-size: 30px; font-style: normal; font-weight: 700; line-height: normal; + text-align: center; } .text { width: 402px; color: #bfbfbf; text-align: center; - font-family: Inter; font-size: 14px; font-style: normal; font-weight: 500; line-height: normal; } + +@media (max-width: 1023px) { + .container { + width: 90%; + } + .text { + width: 100%; + } +} diff --git a/web/src/components/ConnectingView.tsx b/web/src/components/ConnectingView.tsx index 7d694834..452d0a1e 100644 --- a/web/src/components/ConnectingView.tsx +++ b/web/src/components/ConnectingView.tsx @@ -1,21 +1,18 @@ -import styles from "./ConnectingView.module.css"; -import globalStyles from "./tokens/OwnedTokens.module.css"; +import { useContext, useState } from "react"; import { Flex, Spinner, Text } from "@chakra-ui/react"; -import { useContext, useEffect, useState } from "react"; + import Web3Context from "../contexts/Web3Context/context"; import { useGameContext } from "../contexts/GameContext"; import { chainByChainId } from "../contexts/Web3Context"; -const ConnectingView = ({ nextStep }: { nextStep: () => void }) => { +import { EthereumError } from "../types"; +import globalStyles from "./tokens/OwnedTokens.module.css"; +import styles from "./ConnectingView.module.css"; + +const ConnectingView = () => { const web3Provider = useContext(Web3Context); const [isSwitching, setIsSwitching] = useState(false); const { chainId } = useGameContext(); - useEffect(() => { - if (web3Provider.buttonText === "Connected" && web3Provider.chainId === chainId) { - nextStep(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [web3Provider.buttonText, web3Provider.chainId]); const switchToWyrm = async () => { const wyrmID = 322; @@ -26,8 +23,8 @@ const ConnectingView = ({ nextStep }: { nextStep: () => void }) => { method: "wallet_switchEthereumChain", params: [{ chainId: hexString }], }); - } catch (switchError: any) { - if (switchError.code === 4902) { + } catch (switchError: unknown) { + if ((switchError as EthereumError).code === 4902) { try { await window.ethereum.request({ method: "wallet_addEthereumChain", @@ -44,6 +41,8 @@ const ConnectingView = ({ nextStep }: { nextStep: () => void }) => { } catch (addError) { console.log(addError); } + } else { + console.log(switchError); } } setIsSwitching(false); diff --git a/web/src/components/GlobalStyles.module.css b/web/src/components/GlobalStyles.module.css index e1a2ee55..8da6f869 100644 --- a/web/src/components/GlobalStyles.module.css +++ b/web/src/components/GlobalStyles.module.css @@ -4,7 +4,6 @@ justify-content: center; align-items: center; gap: 5px; - /*align-self: stretch;*/ border: 0.5px solid #f1e3bf; background: #00a341; color: #fff; @@ -33,6 +32,26 @@ position: relative; } +.mobileButton { + display: flex; + padding: 4px 20px; + justify-content: center; + align-items: center; + gap: 5px; + width: 180px; + /*align-self: stretch;*/ + border: 0.5px solid #f1e3bf; + /*border-radius: 50%;*/ + background: #00a341; + color: #fff; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: normal; + height: 31px; + position: relative; +} + .commitButton:disabled { border: 1px solid #F1E3BF; opacity: 0.3; @@ -45,7 +64,6 @@ -webkit-text-fill-color: transparent; color: #FFF; text-align: center; - font-family: Inter; font-size: 24px; font-style: italic; font-weight: 700; @@ -64,7 +82,7 @@ padding: 8px 12px; border-radius: 4px; border: 1px solid #ccc; - box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); font-size: 0.9rem; } diff --git a/web/src/components/Playing.module.css b/web/src/components/Playing.module.css index 74dfd51e..4d98b416 100644 --- a/web/src/components/Playing.module.css +++ b/web/src/components/Playing.module.css @@ -1,21 +1,7 @@ .container { - /*display: flex;*/ - /*width: 648px;*/ - /*padding: 40px 20px;*/ - /*flex-direction: column;*/ - /*justify-content: center;*/ - /*align-items: center;*/ - /*width: 100%;*/ - /*gap: 40px;*/ - /*!*flex: 1 0 0;*!*/ - /*border: 1px solid #4d4d4d;*/ - /*background: rgba(41, 41, 41, 0.7);*/ display: flex; - /*width: 1440px;*/ - /*height: 800px;*/ flex-direction: column; align-items: flex-start; - /*background-color: #221111;*/ } .title { @@ -23,7 +9,6 @@ background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent; - font-family: Inter; font-size: 20px; font-style: normal; font-weight: 700; @@ -33,7 +18,6 @@ .prompt { color: #fff; - font-family: Inter; font-size: 30px; font-style: normal; font-weight: 700; diff --git a/web/src/components/Playing.tsx b/web/src/components/Playing.tsx index cf23b023..cb6bd993 100644 --- a/web/src/components/Playing.tsx +++ b/web/src/components/Playing.tsx @@ -1,12 +1,12 @@ -import styles from "./Playing.module.css"; -import { Flex, Text } from "@chakra-ui/react"; +import { Flex } from "@chakra-ui/react"; + import { useGameContext } from "../contexts/GameContext"; import SessionsView from "./sessions/SessionsView"; -import { useEffect } from "react"; import PlayView from "./playing/PlayView"; +import styles from "./Playing.module.css"; const Playing = () => { - const { selectedSession, updateContext, selectedToken, watchingToken } = useGameContext(); + const { selectedSession, selectedToken, watchingToken } = useGameContext(); return ( diff --git a/web/src/components/TitleScreen.tsx b/web/src/components/TitleScreen.tsx index 6438b459..3def0556 100644 --- a/web/src/components/TitleScreen.tsx +++ b/web/src/components/TitleScreen.tsx @@ -1,32 +1,17 @@ -import { useContext, useEffect, useState } from "react"; +import { useContext, useEffect } from "react"; +import { useQueryClient } from "react-query"; + import ConnectingView from "./ConnectingView"; import Web3Context from "../contexts/Web3Context/context"; import TitleScreenLayout from "./layout/TitleScreenLayout"; import PlayingLayout from "./layout/PlayingLayout"; import Playing from "./Playing"; -import { useRouter } from "next/router"; -import { useQuery, useQueryClient } from "react-query"; -// eslint-disable-next-line @typescript-eslint/no-var-requires -import queryCacheProps from "../hooks/hookCommon"; import { useGameContext } from "../contexts/GameContext"; -import OwnedTokens from "./tokens/OwnedTokens"; -import SessionsView from "./sessions/SessionsView"; -// eslint-disable-next-line @typescript-eslint/no-var-requires const TitleScreen = () => { - const [step, setStep] = useState(1); const web3ctx = useContext(Web3Context); - const router = useRouter(); - - const { selectedSession, contractAddress, selectedToken, updateContext, chainId } = - useGameContext(); - - // useEffect(() => { - // if (typeof router.query.session_id === "string") { - // updateContext({ sessionId: Number(router.query.session_id) }); - // } - // }, [router.query.session_id]); + const { chainId } = useGameContext(); const queryClient = useQueryClient(); @@ -39,7 +24,7 @@ const TitleScreen = () => { <> {web3ctx.buttonText !== "Connected" || web3ctx.chainId !== chainId ? ( - setStep(3)} /> + ) : ( <> @@ -53,4 +38,3 @@ const TitleScreen = () => { }; export default TitleScreen; -1; diff --git a/web/src/components/layout/Navbar.tsx b/web/src/components/layout/Navbar.tsx index f0f1601f..12435ce7 100644 --- a/web/src/components/layout/Navbar.tsx +++ b/web/src/components/layout/Navbar.tsx @@ -19,7 +19,7 @@ const Navbar = () => { justifyContent={"space-between"} pt={{ base: "15px", sm: "5px" }} gap={"15px"} - fontSize={{ sm: "16px", base: "14px" }} + fontSize={{ sm: "16px", base: "10px" }} > Fullcount diff --git a/web/src/components/layout/PlayingLayout.tsx b/web/src/components/layout/PlayingLayout.tsx index 5aa99025..e99b9c0e 100644 --- a/web/src/components/layout/PlayingLayout.tsx +++ b/web/src/components/layout/PlayingLayout.tsx @@ -11,6 +11,7 @@ const sounds = { clapping: `${FULLCOUNT_ASSETS_PATH}/sounds/clapping-male-crowd.wav`, hit: `${FULLCOUNT_ASSETS_PATH}/sounds/hard-hit.wav`, catch: `${FULLCOUNT_ASSETS_PATH}/sounds/ball-hit.wav`, + select: `${FULLCOUNT_ASSETS_PATH}/sounds/select.wav`, }; const PlayingLayout = ({ children }: { children: ReactNode }) => { @@ -19,7 +20,7 @@ const PlayingLayout = ({ children }: { children: ReactNode }) => { direction="column" minH={"100vh"} gap={"40px"} - p={"0 7% 60px 7%"} + p={{ lg: "0 7% 60px 7%", base: "0 10px 80px 10px" }} maxW={"1440px"} placeSelf={"center"} w={"100%"} @@ -33,6 +34,7 @@ const PlayingLayout = ({ children }: { children: ReactNode }) => { diff --git a/web/src/components/layout/SoundFxSlider.tsx b/web/src/components/layout/SoundFxSlider.tsx index 93a0eb41..48c6e78e 100644 --- a/web/src/components/layout/SoundFxSlider.tsx +++ b/web/src/components/layout/SoundFxSlider.tsx @@ -6,7 +6,7 @@ const SoundFxSlider = () => { return ( - Sound FX + Sound FX void; +}) => { + return ( + + {types.map((type, idx) => ( + setSelected(idx)} + cursor={isDisabled ? "default" : "pointer"} + > + {type} + + ))} + + ); +}; + +export default ActionTypeSelector; diff --git a/web/src/components/playing/BatterView2.tsx b/web/src/components/playing/BatterView2.tsx index cdaebbbb..314a8ae7 100644 --- a/web/src/components/playing/BatterView2.tsx +++ b/web/src/components/playing/BatterView2.tsx @@ -1,31 +1,38 @@ -import { Box, Flex, Spinner, Text } from "@chakra-ui/react"; -import globalStyles from "../GlobalStyles.module.css"; -import styles from "./PlayView.module.css"; -import GridComponent from "./GridComponent"; -import { useCallback, useContext, useEffect, useState } from "react"; -import { getRowCol, getSwingDescription, swingKind } from "./PlayView"; -import { signSwing } from "../Signing"; -import Web3Context from "../../contexts/Web3Context/context"; -import { useGameContext } from "../../contexts/GameContext"; +import { useContext, useEffect, useState } from "react"; import { useMutation, useQueryClient } from "react-query"; +import { Flex, Spinner, Text } from "@chakra-ui/react"; + +import { useGameContext } from "../../contexts/GameContext"; +import Web3Context from "../../contexts/Web3Context/context"; +import GridComponent from "./GridComponent"; +import RandomGenerator from "./RandomGenerator"; import useMoonToast from "../../hooks/useMoonToast"; import { SessionStatus } from "./PlayView"; import FullcountABIImported from "../../web3/abi/FullcountABI.json"; +import { getRowCol } from "./PlayView"; import { AbiItem } from "web3-utils"; -import { sendTransactionWithEstimate } from "../utils"; +import { signSwing } from "../../utils/signing"; +import { sendTransactionWithEstimate } from "../../utils/sendTransactions"; +import globalStyles from "../GlobalStyles.module.css"; +import styles from "./PlayView.module.css"; +import ActionTypeSelector from "./ActionTypeSelector"; +import { getSwingDescription } from "../../utils/messages"; + const FullcountABI = FullcountABIImported as unknown as AbiItem[]; const BatterView2 = ({ sessionStatus }: { sessionStatus: SessionStatus }) => { const [kind, setKind] = useState(0); const [gridIndex, setGridIndex] = useState(-1); const [isRevealed, setIsRevealed] = useState(false); - const [nonce, setNonce] = useState("0"); + const [nonce, setNonce] = useState(""); const [showTooltip, setShowTooltip] = useState(false); const web3ctx = useContext(Web3Context); const { selectedSession, contractAddress, selectedToken } = useGameContext(); const gameContract = new web3ctx.web3.eth.Contract(FullcountABI) as any; gameContract.options.address = contractAddress; + const swingKinds = ["Contact", "Power", "Take"]; + const handleCommit = async () => { if (gridIndex === -1 && kind !== 2) { setShowTooltip(true); @@ -72,11 +79,7 @@ const BatterView2 = ({ sessionStatus }: { sessionStatus: SessionStatus }) => { }); }; - const [movements, setMovements] = useState([]); - const [seed, setSeed] = useState(""); - useEffect(() => { - setMovements([]); const item = localStorage.getItem( `fullcount.xyz-${contractAddress}-${selectedSession?.sessionID}-${selectedToken?.id}` ?? "", @@ -88,29 +91,6 @@ const BatterView2 = ({ sessionStatus }: { sessionStatus: SessionStatus }) => { } }, [selectedSession]); - useEffect(() => { - if (movements.length > 499) { - window.removeEventListener("mousemove", handleMouseMove); - setSeed(generateSeed(movements)); - setMovements([]); - } - }, [movements.length]); - - const generateSeed = (movements: number[]): string => { - const dataString = movements.join(""); - const hash = web3ctx.web3.utils.sha3(dataString) || ""; // Use Web3 to hash the data string - const uint256Seed = "0x" + hash.substring(2, 66); // Adjust the substring to get 64 hex characters - setNonce(uint256Seed); - return uint256Seed; - }; - const handleMouseMove = useCallback((event: MouseEvent) => { - setMovements((prevMovements) => [...prevMovements, event.clientX, event.clientY]); - }, []); - const handleGenerate = () => { - window.addEventListener("mousemove", handleMouseMove); - setMovements((prevMovements) => [...prevMovements, 0, 0]); - }; - const toast = useMoonToast(); const queryClient = useQueryClient(); @@ -178,6 +158,13 @@ const BatterView2 = ({ sessionStatus }: { sessionStatus: SessionStatus }) => { }, ); + const typeChangeHandle = (value: number) => { + setKind(value); + if (value === 2) { + setGridIndex(-1); + } + }; + return ( @@ -186,36 +173,12 @@ const BatterView2 = ({ sessionStatus }: { sessionStatus: SessionStatus }) => { 1. Select the type of swing - - setKind(0)} - cursor={sessionStatus.didBatterCommit ? "default" : "pointer"} - > - {swingKind[0]} - - setKind(1)} - cursor={sessionStatus.didBatterCommit ? "default" : "pointer"} - > - {swingKind[1]} - - { - setKind(2); - setGridIndex(-1); - } - } - cursor={sessionStatus.didBatterCommit ? "default" : "pointer"} - > - {swingKind[2]} - - + 2. Choose where to swing @@ -233,33 +196,15 @@ const BatterView2 = ({ sessionStatus }: { sessionStatus: SessionStatus }) => { 3. Generate randomness - - Click on the button below and move mouse until the button is filled in - - {!seed && movements.length === 0 && !sessionStatus.didBatterCommit && ( - - )} - {seed && Generated} - {movements.length > 0 && sessionStatus.progress === 3 && !sessionStatus.didBatterCommit && ( - window.removeEventListener("mousemove", handleMouseMove)} - w={"180px"} - h={"31px"} - border={"1px solid white"} - position={"relative"} - > - - - move mouse - - )} + setNonce(value)} + /> {!sessionStatus.didBatterCommit ? ( + )} + {sessionStatus.didBatterCommit && + sessionStatus.didPitcherCommit && + !sessionStatus.didBatterReveal && + !isRevealed && ( + + )} + {sessionStatus.didBatterCommit && !sessionStatus.didPitcherCommit && ( + Waiting pitcher to commit + )} + {sessionStatus.didBatterReveal && !sessionStatus.didPitcherReveal && ( + Waiting pitcher to reveal + )} + + ); +}; + +export default BatterViewMobile; diff --git a/web/src/components/playing/HeatMap.tsx b/web/src/components/playing/HeatMap.tsx index 0e92ffe8..35890f65 100644 --- a/web/src/components/playing/HeatMap.tsx +++ b/web/src/components/playing/HeatMap.tsx @@ -30,8 +30,8 @@ const HeatMap = ({ borderBottomStyle={bottomBorder.includes(index) && showStrikeZone ? "solid" : "none"} > + {Array.from({ length: 25 }).map((_, i) => generateCell(i))} diff --git a/web/src/components/playing/InviteLink.tsx b/web/src/components/playing/InviteLink.tsx index b76b2d88..a5360efb 100644 --- a/web/src/components/playing/InviteLink.tsx +++ b/web/src/components/playing/InviteLink.tsx @@ -14,7 +14,7 @@ const InviteLink = ({ session, token }: { session: Session; token: Token }) => { const { onCopy, hasCopied } = useClipboard(path); return ( - + Waiting for Opponent. Invite Friend? @@ -27,7 +27,7 @@ const InviteLink = ({ session, token }: { session: Session; token: Token }) => { overflowX={"hidden"} textOverflow={"ellipsis"} whiteSpace={"nowrap"} - maxW={"500px"} + maxW={{ base: "295px", lg: "500px" }} > {path} diff --git a/web/src/components/playing/MainStat.module.css b/web/src/components/playing/MainStat.module.css index 7be86221..4a38d962 100644 --- a/web/src/components/playing/MainStat.module.css +++ b/web/src/components/playing/MainStat.module.css @@ -7,11 +7,17 @@ background: #232323; align-items: baseline; } + +.divider { + width: 0.5px; + height: 9px; + background: #4D4D4D; +} + + .data { color: #FFF; text-align: center; - leading-trim: both; - text-edge: cap; font-size: 14px; font-style: normal; font-weight: 500; @@ -24,8 +30,6 @@ margin-left: -7px; color: #BFBFBF; text-align: center; - leading-trim: both; - text-edge: cap; font-size: 8px; font-style: normal; font-weight: 300; @@ -35,8 +39,13 @@ } -.divider { - width: 0.5px; - height: 9px; - background: #4D4D4D; -} \ No newline at end of file +@media (max-width: 1023px) { + .data { + font-size: 8px; + } + .label { + font-size: 6px; + margin-left: 0px; + + } +} diff --git a/web/src/components/playing/MainStatMobile.tsx b/web/src/components/playing/MainStatMobile.tsx new file mode 100644 index 00000000..bb13fa8a --- /dev/null +++ b/web/src/components/playing/MainStatMobile.tsx @@ -0,0 +1,85 @@ +import styles from "./MainStat.module.css"; +import { PlayerStats } from "../../types"; +import { Box, Flex, SimpleGrid, Text } from "@chakra-ui/react"; + +const formatDecimal = (value: number) => { + if (!value) { + return ".000"; + } + const formattedNumber = value.toFixed(3); + + // removing the leading zero: + return formattedNumber.replace(/^0+/, ""); +}; + +const pitcherRecord = (stats: PlayerStats): string => { + const wins = + stats.points_data.pitching_data.strikeouts + stats.points_data.pitching_data.in_play_outs; + const losses = + stats.points_data.pitching_data.singles + + stats.points_data.pitching_data.doubles + + stats.points_data.pitching_data.triples + + stats.points_data.pitching_data.home_runs; + return `${wins}-${losses}`; +}; + +const MainStat = ({ stats, isPitcher }: { stats: PlayerStats; isPitcher: boolean }) => { + return ( + <> + {isPitcher && stats.points_data?.pitching_data && ( + + + {pitcherRecord(stats)} + + W-L + + + + + {formatDecimal(stats.points_data.pitching_data.earned_run_average)} + + ERA + + + + {String(stats.points_data.pitching_data.strikeouts)} + + SO + + + + {formatDecimal(stats.points_data.pitching_data.whip)} + + WHIP + + + )} + {!isPitcher && stats.points_data?.batting_data && ( + + + + {formatDecimal(stats.points_data.batting_data.batting_average)} + + AVG + + + {String(stats.points_data.batting_data.home_runs)} + HR + + + + {String(stats.points_data.batting_data.runs_batted_in)} + + RBI + + + {formatDecimal(stats.points_data.batting_data.ops)} + OPS + + + )} + + ); +}; + +export default MainStat; diff --git a/web/src/components/playing/Narrate.tsx b/web/src/components/playing/Narrate.tsx index c76e8e83..a42461e7 100644 --- a/web/src/components/playing/Narrate.tsx +++ b/web/src/components/playing/Narrate.tsx @@ -59,7 +59,7 @@ const Narrate = ({ return ( <> {narrate.data && ( - + {narrate.data.slice(0, length)} )} diff --git a/web/src/components/playing/Outcome.tsx b/web/src/components/playing/Outcome.tsx index ff9379cd..9f647c88 100644 --- a/web/src/components/playing/Outcome.tsx +++ b/web/src/components/playing/Outcome.tsx @@ -152,12 +152,23 @@ const Outcome = ({ justifyContent={"center"} position={"relative"} alignItems={"center"} + mx={"auto"} > - + {pitchSpeed[pitch.speed]} - + {swingKind[swing.kind]} { const [speed, setSpeed] = useState(0); const [gridIndex, setGridIndex] = useState(-1); const [isRevealed, setIsRevealed] = useState(false); - const [nonce, setNonce] = useState("0"); + const [nonce, setNonce] = useState(""); const web3ctx = useContext(Web3Context); const { selectedSession, contractAddress, selectedToken } = useGameContext(); const [showTooltip, setShowTooltip] = useState(false); const gameContract = new web3ctx.web3.eth.Contract(FullcountABI) as any; gameContract.options.address = contractAddress; + const pitchSpeeds = ["Fast", "Slow"]; + const handleCommit = async () => { if (gridIndex === -1) { setShowTooltip(true); @@ -42,71 +54,31 @@ const PitcherView = ({ sessionStatus }: { sessionStatus: SessionStatus }) => { getRowCol(gridIndex)[0], getRowCol(gridIndex)[1], ); - localStorage.setItem( - `fullcount.xyz-${contractAddress}-${selectedSession?.sessionID}-${selectedToken?.id}`, - JSON.stringify({ - nonce, - speed, - vertical: getRowCol(gridIndex)[0], - horizontal: getRowCol(gridIndex)[1], - }), - ); + const localStorageKey = getLocalStorageKey(contractAddress, selectedSession, selectedToken); + setLocalStorageItem(localStorageKey, { + nonce, + speed, + vertical: getRowCol(gridIndex)[0], + horizontal: getRowCol(gridIndex)[1], + }); commitPitch.mutate({ sign }); }; const handleReveal = async () => { - const item = - localStorage.getItem( - `fullcount.xyz-${contractAddress}-${selectedSession?.sessionID}-${selectedToken?.id}` ?? "", - ) ?? ""; - const reveal = JSON.parse(item); - revealPitch.mutate({ - nonce: reveal.nonce, - speed: reveal.speed, - vertical: reveal.vertical, - horizontal: reveal.horizontal, - }); + const localStorageKey = getLocalStorageKey(contractAddress, selectedSession, selectedToken); + const reveal = getLocalStorageItem(localStorageKey); + revealPitch.mutate(reveal); }; - const [movements, setMovements] = useState([]); - const [seed, setSeed] = useState(""); - useEffect(() => { - setMovements([]); - const item = - localStorage.getItem( - `fullcount.xyz-${contractAddress}-${selectedSession?.sessionID}-${selectedToken?.id}` ?? "", - ) ?? ""; - if (item) { - const reveal = JSON.parse(item); + const localStorageKey = getLocalStorageKey(contractAddress, selectedSession, selectedToken); + const reveal = getLocalStorageItem(localStorageKey); + if (reveal) { setSpeed(reveal.speed); setGridIndex(reveal.vertical * 5 + reveal.horizontal); } }, [selectedSession]); - useEffect(() => { - if (movements.length > 499) { - window.removeEventListener("mousemove", handleMouseMove); - setSeed(generateSeed(movements)); - setMovements([]); - } - }, [movements.length]); - - const generateSeed = (movements: number[]): string => { - const dataString = movements.join(""); - const hash = web3ctx.web3.utils.sha3(dataString) || ""; // Use Web3 to hash the data string - const uint256Seed = "0x" + hash.substring(2, 66); // Adjust the substring to get 64 hex characters - setNonce(uint256Seed); - return uint256Seed; - }; - const handleMouseMove = useCallback((event: MouseEvent) => { - setMovements((prevMovements) => [...prevMovements, event.clientX, event.clientY]); - }, []); - const handleGenerate = () => { - window.addEventListener("mousemove", handleMouseMove); - setMovements((prevMovements) => [...prevMovements, 0, 0]); - }; - const toast = useMoonToast(); const queryClient = useQueryClient(); @@ -183,22 +155,12 @@ const PitcherView = ({ sessionStatus }: { sessionStatus: SessionStatus }) => { 1. Select the type of pitch - - setSpeed(0)} - cursor={sessionStatus.didPitcherCommit ? "default" : "pointer"} - > - Fast - - setSpeed(1)} - cursor={sessionStatus.didPitcherCommit ? "default" : "pointer"} - > - Slow - - + setSpeed(value)} + /> 2. Choose where to pitch @@ -216,33 +178,15 @@ const PitcherView = ({ sessionStatus }: { sessionStatus: SessionStatus }) => { 3. Generate randomness - - Click on the button below and move mouse until the button is filled in - - {!seed && movements.length === 0 && !sessionStatus.didPitcherCommit && ( - - )} - {seed && Generated} - {movements.length > 0 && sessionStatus.progress === 3 && !sessionStatus.didPitcherCommit && ( - window.removeEventListener("mousemove", handleMouseMove)} - w={"180px"} - h={"31px"} - border={"1px solid white"} - position={"relative"} - > - - - move mouse - - )} + setNonce(value)} + /> {!sessionStatus.didPitcherCommit ? ( + )} + {sessionStatus.didPitcherCommit && + sessionStatus.didBatterCommit && + !sessionStatus.didPitcherReveal && + !isRevealed && ( + + )} + {sessionStatus.didPitcherCommit && !sessionStatus.didBatterCommit && ( + Waiting batter to commit + )} + {sessionStatus.didPitcherReveal && !sessionStatus.didBatterReveal && ( + Waiting batter to reveal + )} + + ); +}; + +export default PitcherViewMobile; diff --git a/web/src/components/playing/PlayView.tsx b/web/src/components/playing/PlayView.tsx index df35d7bc..bb583500 100644 --- a/web/src/components/playing/PlayView.tsx +++ b/web/src/components/playing/PlayView.tsx @@ -1,12 +1,11 @@ import { useGameContext } from "../../contexts/GameContext"; import PitcherView from "./PitcherView"; -import { Box, Flex, Image, Text } from "@chakra-ui/react"; +import { Flex, Image, useMediaQuery } from "@chakra-ui/react"; import Timer from "./Timer"; import { useQuery } from "react-query"; import { useContext, useEffect, useState } from "react"; import Web3Context from "../../contexts/Web3Context/context"; -import { PitchLocation, SwingLocation, Token } from "../../types"; -import { CloseIcon } from "@chakra-ui/icons"; +import { Token } from "../../types"; import Outcome from "./Outcome"; import BatterView2 from "./BatterView2"; import InviteLink from "./InviteLink"; @@ -16,12 +15,10 @@ import { FULLCOUNT_ASSETS_PATH, ZERO_ADDRESS } from "../../constants"; import { getTokenMetadata } from "../../utils/decoders"; // eslint-disable-next-line @typescript-eslint/no-var-requires const tokenABI = require("../../web3/abi/BLBABI.json"); -import styles from "./PlayView.module.css"; -import axios from "axios"; -import MainStat from "./MainStat"; -import HeatMap from "./HeatMap"; +import TokenView from "../tokens/TokenView"; import Narrate from "./Narrate"; -import { IoExitOutline } from "react-icons/all"; +import PitcherViewMobile from "./PitcherViewMobile"; +import BatterViewMobile from "./BatterViewMobile"; const FullcountABI = FullcountABIImported as unknown as AbiItem[]; @@ -57,40 +54,6 @@ export const verticalLocations = { 4: "Low Ball", }; -export const getPitchDescription = (s: number, h: number, v: number) => { - const isStrike = h === 0 || h === 4 || v === 4 || v === 0 ? "A ball" : "A strike"; - const speed = s === 0 ? "Fast" : "Slow"; - let point = ""; - if (v < 2) { - point = h < 2 ? ", high and inside" : h === 2 ? " and high" : ", high and outside"; - } - if (v === 2) { - point = h < 2 ? " and inside" : h === 2 ? " and down the middle" : " and outside"; - } - if (v > 2) { - point = h < 2 ? ", low and inside" : h === 2 ? " and low" : ", low and outside"; - } - return `${isStrike}: ${speed}${point}.`; -}; - -export const getSwingDescription = (k: number, h: number, v: number) => { - if (k === 2) { - return "Nope. You are taking the pitch."; - } - const kind = k === 0 ? "For contact" : "For power"; - let point = ""; - if (v < 2) { - point = h < 2 ? "high and inside" : h === 2 ? "high" : "high and outside"; - } - if (v === 2) { - point = h < 2 ? "inside" : h === 2 ? "down the middle" : "outside"; - } - if (v > 2) { - point = h < 2 ? "low and inside" : h === 2 ? "low" : "low and outside"; - } - return `${kind}; ${point}.`; -}; - export const pitchSpeed = { 0: "Fast", 1: "Slow", @@ -103,6 +66,8 @@ export const swingKind = { }; const PlayView = ({ selectedToken }: { selectedToken: Token }) => { + const [isSmallView] = useMediaQuery("(max-width: 1023px)"); + const { selectedSession, updateContext, contractAddress } = useGameContext(); const web3ctx = useContext(Web3Context); const gameContract = new web3ctx.web3.eth.Contract(FullcountABI) as any; @@ -207,82 +172,6 @@ const PlayView = ({ selectedToken }: { selectedToken: Token }) => { }, ); - const pitcherStats = useQuery( - ["pitcher_stat", pitcher], - async () => { - if (!pitcher) { - return; - } - const API_URL = "https://api.fullcount.xyz/stats"; - const stat = await axios.get(`${API_URL}/${pitcher.address}/${pitcher.id}`); - return stat.data; - }, - { - enabled: !!pitcher, - }, - ); - - const batterStats = useQuery( - ["batter_stat", batter], - async () => { - if (!batter) { - return; - } - const API_URL = "https://api.fullcount.xyz/stats"; - const stat = await axios.get(`${API_URL}/${batter.address}/${batter.id}`); - return stat.data; - }, - { - enabled: !!batter, - }, - ); - - const mockLocations = [ - 19, 11, 5, 1, 2, 45, 29, 13, 8, 6, 70, 59, 47, 23, 12, 40, 35, 40, 32, 31, 11, 12, 23, 24, 34, - ]; - - const pitchDistributions = useQuery( - ["pitch_distribution", pitcher], - async () => { - if (!pitcher) { - return; - } - const API_URL = "https://api.fullcount.xyz/pitch_distribution"; - const res = await axios.get(`${API_URL}/${pitcher.address}/${pitcher.id}`); - const counts = new Array(25).fill(0); - res.data.pitch_distribution.forEach( - (l: PitchLocation) => (counts[l.pitch_vertical * 5 + l.pitch_horizontal] = l.count), - ); - const total = counts.reduce((acc, value) => acc + value); - const rates = counts.map((value) => value / total); - return { rates, counts }; - }, - { - enabled: !!pitcher, - }, - ); - - const swingDistributions = useQuery( - ["swing_distribution", batter], - async () => { - if (!batter) { - return; - } - const API_URL = "https://api.fullcount.xyz/swing_distribution"; - const res = await axios.get(`${API_URL}/${batter.address}/${batter.id}`); - const counts = new Array(25).fill(0); - res.data.swing_distribution.forEach( - (l: SwingLocation) => (counts[l.swing_vertical * 5 + l.swing_horizontal] = l.count), - ); - const total = counts.reduce((acc, value) => acc + value); - const rates = counts.map((value) => value / total); - return { rates, counts }; - }, - { - enabled: !!batter, - }, - ); - useEffect(() => { setPitcher(isPitcher(selectedToken) ? selectedToken : opponent); setBatter(isPitcher(selectedToken) ? opponent : selectedToken); @@ -290,8 +179,20 @@ const PlayView = ({ selectedToken }: { selectedToken: Token }) => { return ( + {isSmallView && ( + + exit updateContext({ selectedSession: undefined, watchingToken: undefined })} + /> + + )} - + {!isSmallView && } {(sessionStatus.data?.progress === 3 || sessionStatus.data?.progress === 4 || @@ -307,19 +208,39 @@ const PlayView = ({ selectedToken }: { selectedToken: Token }) => { } /> )} - - exit updateContext({ selectedSession: undefined, watchingToken: undefined })} + {!isSmallView && ( + + exit + updateContext({ selectedSession: undefined, watchingToken: undefined }) + } + /> + + )} + + + {isSmallView && ( + + + vs + - + )} {sessionStatus.data && sessionStatus.data.progress > 2 && sessionStatus.data.progress < 6 && ( - + { /> )} - - - - {isPitcher(selectedToken) ? ( - <> - {selectedToken && ( - - {selectedToken?.name} - - {selectedToken.name} - - - )} - - ) : ( - <> - {opponent ? ( - - {opponent?.name} - - {opponent?.name} - - - ) : ( - - - - - )} - - )} - {pitcherStats.data ? ( - - ) : ( - - )} - - {pitchDistributions.data ? ( - - ) : ( - - )} - - + + {!isSmallView && ( + + )} {sessionStatus.data?.progress === 2 && selectedSession && selectedToken && ( )} @@ -392,10 +263,22 @@ const PlayView = ({ selectedToken }: { selectedToken: Token }) => { !sessionStatus.data?.isExpired && ( <> {isPitcher(selectedToken) && sessionStatus.data && ( - + <> + {isSmallView ? ( + + ) : ( + + )} + )} {!isPitcher(selectedToken) && sessionStatus.data && ( - + <> + {isSmallView ? ( + + ) : ( + + )} + )} )} @@ -417,61 +300,13 @@ const PlayView = ({ selectedToken }: { selectedToken: Token }) => { }} /> )} - - {!isPitcher(selectedToken) ? ( - <> - {selectedToken && ( - - {selectedToken?.name} - - {selectedToken.name} - - - )} - - ) : ( - <> - {opponent ? ( - - {opponent?.name} - - {opponent?.name} - - - ) : ( - - - - - )} - - )} - {batterStats.data ? ( - - ) : ( - - )} - - {swingDistributions.data ? ( - - ) : ( - - )} - + {!isSmallView && ( + + )} ); diff --git a/web/src/components/playing/RandomGenerator.tsx b/web/src/components/playing/RandomGenerator.tsx new file mode 100644 index 00000000..40f8feb8 --- /dev/null +++ b/web/src/components/playing/RandomGenerator.tsx @@ -0,0 +1,67 @@ +import { useCallback, useEffect, useState } from "react"; +import { Box, Flex, Text } from "@chakra-ui/react"; + +import Web3 from "web3"; + +import globalStyles from "../GlobalStyles.module.css"; +import styles from "./PlayView.module.css"; + +const MOVEMENTS_NUMBER = 500; + +const RandomGenerator = ({ + isActive, + onChange, +}: { + isActive: boolean; + onChange: (arg0: string) => void; +}) => { + const web3 = new Web3(); + + const [movements, setMovements] = useState([]); + + const handleGenerate = () => { + window.addEventListener("mousemove", handleMouseMove); + setMovements((prevMovements) => [...prevMovements, 0, 0]); + }; + const handleMouseMove = useCallback((event: MouseEvent) => { + setMovements((prevMovements) => [...prevMovements, event.clientX, event.clientY]); + }, []); + const generateSeed = (movements: number[]) => { + const dataString = movements.join(""); + const hash = web3.utils.sha3(dataString) || ""; // Use Web3 to hash the data string + const uint256Seed = "0x" + hash.substring(2, 66); // Adjust the substring to get 64 hex characters + onChange(uint256Seed); + }; + + useEffect(() => { + if (movements.length >= MOVEMENTS_NUMBER) { + window.removeEventListener("mousemove", handleMouseMove); + generateSeed(movements); + setMovements([]); + } + }, [movements.length]); + + return ( + <> + {isActive && ( + <> + {movements.length === 0 && ( + + )} + {movements.length > 0 && ( + + + + move mouse + + )} + + )} + {!isActive && Generated} + + ); +}; + +export default RandomGenerator; diff --git a/web/src/components/playing/RandomGeneratorMobile.tsx b/web/src/components/playing/RandomGeneratorMobile.tsx new file mode 100644 index 00000000..f870e1c0 --- /dev/null +++ b/web/src/components/playing/RandomGeneratorMobile.tsx @@ -0,0 +1,110 @@ +import { useCallback, useEffect, useState } from "react"; +import { Box, Flex, Text } from "@chakra-ui/react"; +import Web3 from "web3"; +import globalStyles from "../GlobalStyles.module.css"; +import styles from "./PlayView.module.css"; + +const MOVEMENTS_NUMBER = 30; + +const RandomGeneratorMobile = ({ + isActive, + onChange, +}: { + isActive: boolean; + onChange: (arg0: string) => void; +}) => { + const web3 = new Web3(); + + const [movements, setMovements] = useState<{ alpha: number; beta: number; gamma: number }[]>([]); + + const handleGenerate = () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (DeviceMotionEvent && typeof DeviceMotionEvent.requestPermission === "function") { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + DeviceMotionEvent.requestPermission(); + } + window.addEventListener("deviceorientation", handleOrientation); + setMovements([{ alpha: 0, beta: 0, gamma: 0 }]); + }; + + function handleOrientation(event: DeviceOrientationEvent) { + setMovements((prevMovements) => { + const round = (value: number | null) => (value ? Number((value / 10).toFixed(1)) : 0); + const newMove = { + alpha: round(event.alpha), + beta: round(event.beta), + gamma: round(event.gamma), + }; + if (!prevMovements.length) { + return [newMove]; + } + const { alpha, beta, gamma } = prevMovements.slice(-1)[0]; + + if (alpha !== newMove.alpha && beta !== newMove.beta && gamma !== newMove.gamma) { + return [...prevMovements, newMove]; + } + return prevMovements; + }); + } + + const generateSeed = (points: { alpha: number; beta: number; gamma: number }[]) => { + interface Coordinates { + alpha: number; + beta: number; + gamma: number; + } + + function separateCoordinates(coords: Coordinates[]): [number[], number[], number[]] { + const alphas = coords.map((coord) => coord.alpha); + const betas = coords.map((coord) => coord.beta); + const gammas = coords.map((coord) => coord.gamma); + + return [alphas, betas, gammas]; + } + + const dataString = separateCoordinates(points).join(""); + const hash = web3.utils.sha3(dataString) || ""; + const uint256Seed = "0x" + hash.substring(2, 66); + onChange(uint256Seed); + }; + + useEffect(() => { + return () => { + window.removeEventListener("deviceorientation", handleOrientation); + }; + }, []); + + useEffect(() => { + if (movements.length >= MOVEMENTS_NUMBER) { + window.removeEventListener("deviceorientation", handleOrientation); + generateSeed(movements); + setMovements([]); + } + }, [movements.length]); + + return ( + <> + {isActive && ( + <> + {movements.length === 0 && ( + + )} + {movements.length > 0 && ( + + + + rotate your device + + )} + + )} + {!isActive && Generated} + + ); +}; + +export default RandomGeneratorMobile; diff --git a/web/src/components/playing/Timer.module.css b/web/src/components/playing/Timer.module.css index a90c32a4..702d5936 100644 --- a/web/src/components/playing/Timer.module.css +++ b/web/src/components/playing/Timer.module.css @@ -26,14 +26,34 @@ border: 1px solid #FFF; background: #1B1B1B; flex-direction: column; + justify-content: center; } .countLeft { display: flex; padding: 10px 12px; align-items: center; + justify-content: space-between; gap: 15px; - flex: 1 0 0; + flex: 0 0 0; border: 1px solid #F1E3BF; background: #00B94A; +} + +@media (max-width: 1023px) { + .countLeft { + padding: 2px 4px; + gap: 3px; + } + .timerContainer { + padding: 2px 4px; + } + + .title { + font-size: 10px; + } + + .time { + font-size: 12px; + } } \ No newline at end of file diff --git a/web/src/components/playing/Timer.tsx b/web/src/components/playing/Timer.tsx index b1574b81..a0c542a1 100644 --- a/web/src/components/playing/Timer.tsx +++ b/web/src/components/playing/Timer.tsx @@ -41,29 +41,61 @@ const Timer: React.FC = ({ start, delay, isActive }) => { }, [start, delay]); return ( - + - + HOME - + 0 - + INN - + BOT 9 - + AWAY - + 1 @@ -74,7 +106,12 @@ const Timer: React.FC = ({ start, delay, isActive }) => { {minutesLeft} - + : @@ -83,9 +120,14 @@ const Timer: React.FC = ({ start, delay, isActive }) => { - + - + BALL @@ -95,7 +137,12 @@ const Timer: React.FC = ({ start, delay, isActive }) => { - + STRIKE @@ -104,7 +151,12 @@ const Timer: React.FC = ({ start, delay, isActive }) => { {" "} - + OUT diff --git a/web/src/components/sessions/InviteView.tsx b/web/src/components/sessions/InviteView.tsx index 44d7261b..84447c3c 100644 --- a/web/src/components/sessions/InviteView.tsx +++ b/web/src/components/sessions/InviteView.tsx @@ -10,7 +10,12 @@ const InviteView = ({ isOpen, onClose }: { isOpen: boolean; onClose: () => void return ( <> - + {sessions && sessions?.length !== 0 && !sessions?.find((s) => s.sessionID === invitedTo) && ( diff --git a/web/src/components/sessions/SelectToken.tsx b/web/src/components/sessions/SelectToken.tsx index dabacf47..8d6b0f8d 100644 --- a/web/src/components/sessions/SelectToken.tsx +++ b/web/src/components/sessions/SelectToken.tsx @@ -8,7 +8,12 @@ const SelectToken = ({ isOpen, onClose }: { isOpen: boolean; onClose: () => void return ( <> - + Bottom of the ninth.
Bases loaded. Full count. diff --git a/web/src/components/sessions/SessionView3.tsx b/web/src/components/sessions/SessionView3.tsx index 75222d05..5a0dc0b1 100644 --- a/web/src/components/sessions/SessionView3.tsx +++ b/web/src/components/sessions/SessionView3.tsx @@ -7,8 +7,9 @@ import Web3Context from "../../contexts/Web3Context/context"; import CharacterCardSmall from "../tokens/CharacterCardSmall"; import { useMutation, useQueryClient } from "react-query"; import useMoonToast from "../../hooks/useMoonToast"; -import { progressMessage, sendTransactionWithEstimate } from "../utils"; +import { progressMessage } from "../../utils/messages"; import SelectToken from "./SelectToken"; +import { sendTransactionWithEstimate } from "../../utils/sendTransactions"; // eslint-disable-next-line @typescript-eslint/no-var-requires const FullcountABI = require("../../web3/abi/FullcountABI.json"); @@ -97,18 +98,6 @@ const SessionView3 = ({ session }: { session: Session }) => { }, ); - const isTokenStaked = (token: Token) => { - return sessions?.find( - (s) => - (s.pair.pitcher?.id === token.id && - s.pair.pitcher?.address === token.address && - !s.pitcherLeftSession) || - (s.pair.batter?.id === token.id && - s.pair.batter?.address === token.address && - !s.batterLeftSession), - ); - }; - const handleClick = () => { if (!selectedToken) { updateContext({ invitedTo: session.sessionID }); @@ -138,7 +127,13 @@ const SessionView3 = ({ session }: { session: Session }) => { ]; return ( - + { {progressMessage(session)} - + {session.pair.pitcher ? ( @@ -189,7 +190,6 @@ const SessionView3 = ({ session }: { session: Session }) => { )} - {/**/} ); }; diff --git a/web/src/components/tokens/CharacterCard.tsx b/web/src/components/tokens/CharacterCard.tsx index a55d7342..23336fde 100644 --- a/web/src/components/tokens/CharacterCard.tsx +++ b/web/src/components/tokens/CharacterCard.tsx @@ -57,11 +57,15 @@ const CharacterCard = ({ }} cursor={isClickable ? "pointer" : "default"} > - - {""} + {""} {(showName || isActive || children) && ( - {showName && {token.name}} + {showName && {token.name}} {isActive && ( @@ -480,7 +476,7 @@ const OwnedTokens = ({ forJoin = false }: { forJoin?: boolean }) => { )} {selectedToken && selectedToken.isStaked && ( - + {selectedToken.tokenProgress !== 3 && selectedToken.tokenProgress !== 4 && ( + + {joinSession.isLoading && joinSession.variables?.token.id === token.id ? ( + + + + ) : ( + {""} { + updateContext({ selectedToken: token }); + if (forJoin && invitedTo) { + joinSession.mutate({ + sessionID: invitedTo, + token, + inviteCode, + }); + } + }} + /> )} - + ))} {ownedTokens.data && ownedTokens.data.length > 0 && ( { + const pitcherStats = useQuery( + ["pitcher_stat", token], + async () => { + if (!token) { + return; + } + const API_URL = "https://api.fullcount.xyz/stats"; + const stat = await axios.get(`${API_URL}/${token.address}/${token.id}`); + return stat.data; + }, + { + enabled: !!token && isPitcher, + }, + ); + + const batterStats = useQuery( + ["batter_stat", token], + async () => { + if (!token) { + return; + } + const API_URL = "https://api.fullcount.xyz/stats"; + const stat = await axios.get(`${API_URL}/${token.address}/${token.id}`); + return stat.data; + }, + { + enabled: !!token && !isPitcher, + }, + ); + + const pitchDistributions = useQuery( + ["pitch_distribution", token], + async () => { + if (!token) { + return; + } + const API_URL = "https://api.fullcount.xyz/pitch_distribution"; + const res = await axios.get(`${API_URL}/${token.address}/${token.id}`); + const counts = new Array(25).fill(0); + res.data.pitch_distribution.forEach( + (l: PitchLocation) => (counts[l.pitch_vertical * 5 + l.pitch_horizontal] = l.count), + ); + const total = counts.reduce((acc, value) => acc + value); + const rates = counts.map((value) => value / total); + return { rates, counts }; + }, + { + enabled: !!token && isPitcher, + }, + ); + + const swingDistributions = useQuery( + ["swing_distribution", token], + async () => { + if (!token) { + return; + } + const API_URL = "https://api.fullcount.xyz/swing_distribution"; + const res = await axios.get(`${API_URL}/${token.address}/${token.id}`); + const counts = new Array(25).fill(0); + res.data.swing_distribution.forEach( + (l: SwingLocation) => (counts[l.swing_vertical * 5 + l.swing_horizontal] = l.count), + ); + const total = counts.reduce((acc, value) => acc + value); + const rates = counts.map((value) => value / total); + return { rates, counts }; + }, + { + enabled: !!token && !isPitcher, + }, + ); + + const [isSmallView] = useMediaQuery("(max-width: 1023px)"); + + if (!token) { + return ( + + ); + } + + return ( + + + {token?.name} + + {token.name} + + + {isPitcher ? ( + + {pitcherStats.data && isSmallView && ( + + )} + {pitcherStats.data && !isSmallView && ( + + )} + + {pitchDistributions.data && ( + + )} + + ) : ( + + {batterStats.data && isSmallView && ( + + )} + {batterStats.data && !isSmallView && ( + + )} + + {swingDistributions.data && ( + + )} + + )} + + ); +}; +export default TokenView; diff --git a/web/src/services/pitchMutations.ts b/web/src/services/pitchMutations.ts new file mode 100644 index 00000000..6368f35a --- /dev/null +++ b/web/src/services/pitchMutations.ts @@ -0,0 +1,77 @@ +// pitchMutations.ts +import { MutationFunction, UseMutationResult, useMutation, MutateFunction } from "react-query"; +import { sendTransactionWithEstimate } from "../utils/sendTransactions"; + +const MESSAGE_ERROR_ACCOUNT_NOT_SET = "Account address isn't set"; + +interface Signature { + sign: string; +} + +interface RevealDetails { + nonce: string; + speed: number; + vertical: number; + horizontal: number; +} + +export function useCommitPitch( + gameContract: any, + selectedSession: any, + web3ctx: any, + onSuccessCallbacks: any, + onErrorCallback: any, +): UseMutationResult { + const mutationFn: MutationFunction = async ({ sign }: Signature) => { + if (!web3ctx.account) { + return new Promise((_, reject) => { + reject(new Error(MESSAGE_ERROR_ACCOUNT_NOT_SET)); + }); + } + return sendTransactionWithEstimate( + web3ctx.account, + gameContract.methods.commitPitch(selectedSession?.sessionID, sign), + ); + }; + + return useMutation(mutationFn, { + onSuccess: onSuccessCallbacks, + onError: onErrorCallback, + }); +} + +export function useRevealPitch( + gameContract: any, + selectedSession: any, + web3ctx: any, + onSuccessCallbacks: any, + onErrorCallback: any, +): UseMutationResult { + const mutationFn: MutateFunction = async ({ + nonce, + speed, + vertical, + horizontal, + }: RevealDetails) => { + if (!web3ctx.account) { + return new Promise((_, reject) => { + reject(new Error(MESSAGE_ERROR_ACCOUNT_NOT_SET)); + }); + } + return sendTransactionWithEstimate( + web3ctx.account, + gameContract.methods.revealPitch( + selectedSession?.sessionID, + nonce, + speed, + vertical, + horizontal, + ), + ); + }; + + return useMutation(mutationFn, { + onSuccess: onSuccessCallbacks, + onError: onErrorCallback, + }); +} diff --git a/web/src/types.d.ts b/web/src/types.d.ts index a7c5cb96..87be1a89 100644 --- a/web/src/types.d.ts +++ b/web/src/types.d.ts @@ -154,3 +154,8 @@ interface SwingLocation { count: number; swing_type: number; } + +interface EthereumError { + code: number; + message: string; +} diff --git a/web/src/components/utils.ts b/web/src/utils/messages.ts similarity index 59% rename from web/src/components/utils.ts rename to web/src/utils/messages.ts index 550f391b..65fa7479 100644 --- a/web/src/components/utils.ts +++ b/web/src/utils/messages.ts @@ -1,5 +1,4 @@ import { Session } from "../types"; -import { SECOND_REVEAL_PRICE_MULTIPLIER } from "../constants"; export const progressMessage = (session: Session) => { if (session.progress === 1) { @@ -46,10 +45,36 @@ export const progressMessage = (session: Session) => { } }; -export const sendTransactionWithEstimate = async (account: string, method: any) => { - const estimatedGas = await method.estimateGas({ from: account }); - return method.send({ - from: account, - gas: Math.ceil(SECOND_REVEAL_PRICE_MULTIPLIER * estimatedGas), - }); +export const getPitchDescription = (s: number, h: number, v: number) => { + const isStrike = h === 0 || h === 4 || v === 4 || v === 0 ? "A ball" : "A strike"; + const speed = s === 0 ? "Fast" : "Slow"; + let point = ""; + if (v < 2) { + point = h < 2 ? ", high and inside" : h === 2 ? " and high" : ", high and outside"; + } + if (v === 2) { + point = h < 2 ? " and inside" : h === 2 ? " and down the middle" : " and outside"; + } + if (v > 2) { + point = h < 2 ? ", low and inside" : h === 2 ? " and low" : ", low and outside"; + } + return `${isStrike}: ${speed}${point}.`; +}; + +export const getSwingDescription = (k: number, h: number, v: number) => { + if (k === 2) { + return "Nope. You are taking the pitch."; + } + const kind = k === 0 ? "For contact" : "For power"; + let point = ""; + if (v < 2) { + point = h < 2 ? "high and inside" : h === 2 ? "high" : "high and outside"; + } + if (v === 2) { + point = h < 2 ? "inside" : h === 2 ? "down the middle" : "outside"; + } + if (v > 2) { + point = h < 2 ? "low and inside" : h === 2 ? "low" : "low and outside"; + } + return `${kind}; ${point}.`; }; diff --git a/web/src/utils/sendTransactions.ts b/web/src/utils/sendTransactions.ts new file mode 100644 index 00000000..70d63972 --- /dev/null +++ b/web/src/utils/sendTransactions.ts @@ -0,0 +1,9 @@ +import { SECOND_REVEAL_PRICE_MULTIPLIER } from "../constants"; + +export const sendTransactionWithEstimate = async (account: string, method: any) => { + const estimatedGas = await method.estimateGas({ from: account }); + return method.send({ + from: account, + gas: Math.ceil(SECOND_REVEAL_PRICE_MULTIPLIER * estimatedGas), + }); +}; diff --git a/web/src/components/Signing.ts b/web/src/utils/signing.ts similarity index 95% rename from web/src/components/Signing.ts rename to web/src/utils/signing.ts index 96abd252..d903b434 100644 --- a/web/src/components/Signing.ts +++ b/web/src/utils/signing.ts @@ -55,13 +55,11 @@ export async function signPitch( }, }); - const result = await provider.request({ + return await provider.request({ method: "eth_signTypedData_v4", params: [account, msgParams], from: account, }); - - return result; } export async function signSwing( @@ -112,11 +110,9 @@ export async function signSwing( }, }); - const result = await provider.request({ + return await provider.request({ method: "eth_signTypedData_v4", params: [account, msgParams], from: account, }); - - return result; }