From ff684238f1cb70c3b4c144ca6a1388dd9b827373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ferenczy?= Date: Wed, 6 Dec 2023 14:04:13 +0100 Subject: [PATCH 01/27] feat: creates landing page --- src/pages/guess-the-guild.tsx | 63 +++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/pages/guess-the-guild.tsx diff --git a/src/pages/guess-the-guild.tsx b/src/pages/guess-the-guild.tsx new file mode 100644 index 00000000..b8bd08d1 --- /dev/null +++ b/src/pages/guess-the-guild.tsx @@ -0,0 +1,63 @@ +import { + Card, + Container, + Heading, + Text, + useBreakpointValue, + useColorModeValue, +} from "@chakra-ui/react" +import Layout from "components/common/Layout" + +const GuessTheGuild = (): JSX.Element => { + const bgColor = useColorModeValue("var(--chakra-colors-gray-800)", "#37373a") + const bgOpacity = useColorModeValue(0.06, 0.1) + const bgLinearPercentage = useBreakpointValue({ base: "50%", sm: "55%" }) + + return ( + <> + + + + + GuildGesser 1.0 + + + Are you an expert on Guilds?
Test your knowledge in our guild + guesser mini game! +
+
+
+
+ + ) +} + +export default GuessTheGuild From 8c746646b367185b4258b93fc6ad9f8bb87d60eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ferenczy?= Date: Wed, 6 Dec 2023 14:11:21 +0100 Subject: [PATCH 02/27] feat: creates start screen --- src/components/guess-the-guild/StartGame.tsx | 110 +++++++++++++++++++ src/pages/guess-the-guild.tsx | 17 +-- 2 files changed, 112 insertions(+), 15 deletions(-) create mode 100644 src/components/guess-the-guild/StartGame.tsx diff --git a/src/components/guess-the-guild/StartGame.tsx b/src/components/guess-the-guild/StartGame.tsx new file mode 100644 index 00000000..a5157204 --- /dev/null +++ b/src/components/guess-the-guild/StartGame.tsx @@ -0,0 +1,110 @@ +import { Divider, Heading, Text, VStack } from "@chakra-ui/react" +import Button from "components/common/Button" +import { useState } from "react" + +enum Difficulty { + Easy, + Medium, + Hard, +} + +const StartGame = () => { + const [difficulty, setDifficulty] = useState(Difficulty.Easy) + + return ( + + + GuildGesser 1.0 + + + Are you an expert on Guilds?
Test your knowledge in our guild guesser + mini game! +
+ + + + + + {" "} + Your highscore + + + 3000 + + + + + + + {" "} + Select Difficulty + + + + + + + + + Selection from the top {difficulty == 0 && "100"}{" "} + {difficulty == 1 && "500"} {difficulty == 2 && "1000"} guilds. +
+ Solving this difficulty awards {difficulty == 0 && "1x"}{" "} + {difficulty == 1 && "2x"} {difficulty == 2 && "3x"} the usual points. +
+
+ +
+ ) +} + +export default StartGame diff --git a/src/pages/guess-the-guild.tsx b/src/pages/guess-the-guild.tsx index b8bd08d1..303ea031 100644 --- a/src/pages/guess-the-guild.tsx +++ b/src/pages/guess-the-guild.tsx @@ -1,12 +1,11 @@ import { Card, Container, - Heading, - Text, useBreakpointValue, useColorModeValue, } from "@chakra-ui/react" import Layout from "components/common/Layout" +import StartGame from "components/guess-the-guild/StartGame" const GuessTheGuild = (): JSX.Element => { const bgColor = useColorModeValue("var(--chakra-colors-gray-800)", "#37373a") @@ -40,19 +39,7 @@ const GuessTheGuild = (): JSX.Element => { > - - GuildGesser 1.0 - - - Are you an expert on Guilds?
Test your knowledge in our guild - guesser mini game! -
+
From 3f5456ee650069d05a0ea4fd76c9d7b58b22b8ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ferenczy?= Date: Wed, 6 Dec 2023 14:23:09 +0100 Subject: [PATCH 03/27] feat: creates ui for 'guess name' mode --- src/components/guess-the-guild/GuessName.tsx | 77 ++++++++++++++++++++ src/components/guess-the-guild/StartGame.tsx | 6 +- src/pages/guess-the-guild.tsx | 5 +- 3 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 src/components/guess-the-guild/GuessName.tsx diff --git a/src/components/guess-the-guild/GuessName.tsx b/src/components/guess-the-guild/GuessName.tsx new file mode 100644 index 00000000..765ca8ab --- /dev/null +++ b/src/components/guess-the-guild/GuessName.tsx @@ -0,0 +1,77 @@ +import { Button, Divider, Heading, Text, VStack } from "@chakra-ui/react" +import GuildLogo from "components/common/GuildLogo" +import { useState } from "react" + +const GuessName = () => { + const [submitted, setSubmitted] = useState(false) + const [answer, setAnswer] = useState() + + return ( + <> + + + Guess the guild by the logo! + + + + {"???"} + + + + + + + + + + + + {submitted && ( + + )} + {!submitted && ( + + )} + + + ) +} + +export default GuessName diff --git a/src/components/guess-the-guild/StartGame.tsx b/src/components/guess-the-guild/StartGame.tsx index a5157204..4f0897bc 100644 --- a/src/components/guess-the-guild/StartGame.tsx +++ b/src/components/guess-the-guild/StartGame.tsx @@ -40,8 +40,7 @@ const StartGame = () => { textTransform="uppercase" noOfLines={1} > - {" "} - Your highscore + {"Your highscore"} { textTransform="uppercase" noOfLines={1} > - {" "} - Select Difficulty + {"Select Difficulty"} diff --git a/src/pages/guess-the-guild.tsx b/src/pages/guess-the-guild.tsx index 303ea031..8ee33ba1 100644 --- a/src/pages/guess-the-guild.tsx +++ b/src/pages/guess-the-guild.tsx @@ -5,7 +5,7 @@ import { useColorModeValue, } from "@chakra-ui/react" import Layout from "components/common/Layout" -import StartGame from "components/guess-the-guild/StartGame" +import GuessName from "components/guess-the-guild/GuessName" const GuessTheGuild = (): JSX.Element => { const bgColor = useColorModeValue("var(--chakra-colors-gray-800)", "#37373a") @@ -39,7 +39,8 @@ const GuessTheGuild = (): JSX.Element => { > - + {/* */} + From cb97999fddbdd962813d1c9c8175af97efc03df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ferenczy?= Date: Wed, 6 Dec 2023 14:40:22 +0100 Subject: [PATCH 04/27] feat: creates ui draft for 'assign logos' mode --- .../guess-the-guild/AssignLogos.tsx | 115 ++++++++++++++++++ src/pages/guess-the-guild.tsx | 5 +- 2 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 src/components/guess-the-guild/AssignLogos.tsx diff --git a/src/components/guess-the-guild/AssignLogos.tsx b/src/components/guess-the-guild/AssignLogos.tsx new file mode 100644 index 00000000..cde0b1a4 --- /dev/null +++ b/src/components/guess-the-guild/AssignLogos.tsx @@ -0,0 +1,115 @@ +import { + Box, + Card, + Divider, + HStack, + Heading, + Tag, + TagLabel, + TagLeftIcon, + Text, + VStack, + Wrap, + useColorModeValue, +} from "@chakra-ui/react" +import Button from "components/common/Button" +import GuildLogo from "components/common/GuildLogo" +import { Users } from "phosphor-react" +import { useState } from "react" +import pluralize from "utils/pluralize" + +const AssignLogos = () => { + const bgColor = useColorModeValue("gray.100", "whiteAlpha.200") + const borderColor = useColorModeValue("gray.300", "gray.500") + + const [submitted, setSubmitted] = useState(false) + + const avatarSize = 90 + + return ( + <> + + + Guess the guild by the logo! + + + + + + + + + + + + + + + Guild Name + + + + {pluralize(1234, "role")} + + + + + {new Intl.NumberFormat("en", { notation: "compact" }).format( + 1234 + )} + + + + + + + + + + {submitted && ( + + )} + {!submitted && ( + + )} + + + ) +} + +export default AssignLogos diff --git a/src/pages/guess-the-guild.tsx b/src/pages/guess-the-guild.tsx index 8ee33ba1..cf93f150 100644 --- a/src/pages/guess-the-guild.tsx +++ b/src/pages/guess-the-guild.tsx @@ -5,7 +5,7 @@ import { useColorModeValue, } from "@chakra-ui/react" import Layout from "components/common/Layout" -import GuessName from "components/guess-the-guild/GuessName" +import AssignLogos from "components/guess-the-guild/AssignLogos" const GuessTheGuild = (): JSX.Element => { const bgColor = useColorModeValue("var(--chakra-colors-gray-800)", "#37373a") @@ -40,7 +40,8 @@ const GuessTheGuild = (): JSX.Element => { {/* */} - + {/* */} + From 8c52cf0d6bc15d71f0f461983899147e41aa3a02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ferenczy?= Date: Wed, 6 Dec 2023 14:57:58 +0100 Subject: [PATCH 05/27] feat: creates ui for 'end game' screen --- src/components/guess-the-guild/EndGame.tsx | 76 ++++++++++++++++++++++ src/pages/guess-the-guild.tsx | 5 +- 2 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 src/components/guess-the-guild/EndGame.tsx diff --git a/src/components/guess-the-guild/EndGame.tsx b/src/components/guess-the-guild/EndGame.tsx new file mode 100644 index 00000000..3826872d --- /dev/null +++ b/src/components/guess-the-guild/EndGame.tsx @@ -0,0 +1,76 @@ +import { Alert, Button, Divider, Heading, Text, VStack } from "@chakra-ui/react" +import pluralize from "utils/pluralize" + +const EndGame = () => { + const score = 22 + const highscore = 30 + + const getMessage = () => { + if (score <= 10) return "Better luck next time!" + if (score <= 20) return "You're getting the hang of it!" + return "It looks like we're dealing with a professional 👀" + } + + return ( + <> + + + Game Over + + {getMessage()} + + + + Final Score + + + {score} points + + + {score > highscore && ( + <> + + 🎉 New Highscore! + + + )} + + {score <= highscore && ( + <> + + Only {pluralize(highscore - score + 1, "point")} to go + to beat your current highscore! + + + )} + + + + + + + ) +} + +export default EndGame diff --git a/src/pages/guess-the-guild.tsx b/src/pages/guess-the-guild.tsx index cf93f150..b1f328d2 100644 --- a/src/pages/guess-the-guild.tsx +++ b/src/pages/guess-the-guild.tsx @@ -5,7 +5,7 @@ import { useColorModeValue, } from "@chakra-ui/react" import Layout from "components/common/Layout" -import AssignLogos from "components/guess-the-guild/AssignLogos" +import EndGame from "components/guess-the-guild/EndGame" const GuessTheGuild = (): JSX.Element => { const bgColor = useColorModeValue("var(--chakra-colors-gray-800)", "#37373a") @@ -41,7 +41,8 @@ const GuessTheGuild = (): JSX.Element => { {/* */} {/* */} - + {/* */} + From 455d55fe338feec8e82f1d7987871cd5230e6455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ferenczy?= Date: Wed, 6 Dec 2023 15:03:25 +0100 Subject: [PATCH 06/27] fix: replaces chakra cards with common card component --- src/components/guess-the-guild/AssignLogos.tsx | 4 ++-- src/pages/guess-the-guild.tsx | 14 +++++--------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/components/guess-the-guild/AssignLogos.tsx b/src/components/guess-the-guild/AssignLogos.tsx index cde0b1a4..4ec74cc8 100644 --- a/src/components/guess-the-guild/AssignLogos.tsx +++ b/src/components/guess-the-guild/AssignLogos.tsx @@ -1,6 +1,5 @@ import { Box, - Card, Divider, HStack, Heading, @@ -13,6 +12,7 @@ import { useColorModeValue, } from "@chakra-ui/react" import Button from "components/common/Button" +import Card from "components/common/Card" import GuildLogo from "components/common/GuildLogo" import { Users } from "phosphor-react" import { useState } from "react" @@ -51,7 +51,7 @@ const AssignLogos = () => { - + { const bgColor = useColorModeValue("var(--chakra-colors-gray-800)", "#37373a") @@ -41,8 +37,8 @@ const GuessTheGuild = (): JSX.Element => { {/* */} {/* */} - {/* */} - + + {/* */} From 9869f47f4d25886e9f9161d1d56c045d17458196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ferenczy?= Date: Wed, 6 Dec 2023 15:12:13 +0100 Subject: [PATCH 07/27] feat: creates ui for score indicator --- .../guess-the-guild/ScoreIndicator.tsx | 47 +++++++++++++++++++ src/pages/guess-the-guild.tsx | 2 + 2 files changed, 49 insertions(+) create mode 100644 src/components/guess-the-guild/ScoreIndicator.tsx diff --git a/src/components/guess-the-guild/ScoreIndicator.tsx b/src/components/guess-the-guild/ScoreIndicator.tsx new file mode 100644 index 00000000..501e0f8d --- /dev/null +++ b/src/components/guess-the-guild/ScoreIndicator.tsx @@ -0,0 +1,47 @@ +import { Alert, HStack, Progress, Tag, chakra } from "@chakra-ui/react" +import Card from "components/common/Card" + +const ScoreIndicator = () => { + const score = 100 + const highscore = 200 + + const isHighscore = score > highscore + + return ( + + {!isHighscore && ( + <> + + + Score: {score} + + + Highscore: {highscore} + + + + + + )} + + {isHighscore && ( + <> + + 🎉 New Highscore!{" "} + + {score} points + + + + )} + + ) +} + +export default ScoreIndicator diff --git a/src/pages/guess-the-guild.tsx b/src/pages/guess-the-guild.tsx index 54588d4f..d1e5c9de 100644 --- a/src/pages/guess-the-guild.tsx +++ b/src/pages/guess-the-guild.tsx @@ -2,6 +2,7 @@ import { Container, useBreakpointValue, useColorModeValue } from "@chakra-ui/rea import Card from "components/common/Card" import Layout from "components/common/Layout" import AssignLogos from "components/guess-the-guild/AssignLogos" +import ScoreIndicator from "components/guess-the-guild/ScoreIndicator" const GuessTheGuild = (): JSX.Element => { const bgColor = useColorModeValue("var(--chakra-colors-gray-800)", "#37373a") @@ -34,6 +35,7 @@ const GuessTheGuild = (): JSX.Element => { textColor="white" > + {/* */} {/* */} From 35cbc09aa1128ec53e4f0bd98ea05bf90b91b6b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ferenczy?= Date: Wed, 6 Dec 2023 16:56:34 +0100 Subject: [PATCH 08/27] feat: answer validation on 'guess name' mode --- .../guess-the-guild/AnswerButton.tsx | 59 ++++++++++++++++ src/components/guess-the-guild/GuessName.tsx | 68 ++++++++----------- src/pages/guess-the-guild.tsx | 54 ++++++++++++++- 3 files changed, 139 insertions(+), 42 deletions(-) create mode 100644 src/components/guess-the-guild/AnswerButton.tsx diff --git a/src/components/guess-the-guild/AnswerButton.tsx b/src/components/guess-the-guild/AnswerButton.tsx new file mode 100644 index 00000000..e1981819 --- /dev/null +++ b/src/components/guess-the-guild/AnswerButton.tsx @@ -0,0 +1,59 @@ +import Button from "components/common/Button" +import { GuildBase } from "types" + +type Props = { + guild: GuildBase + selectedGuildId?: number + solutionGuild: GuildBase + isAnswerSubmitted: boolean + onSelect: (guildId: number) => void +} + +const AnswerButton = ({ + guild, + selectedGuildId, + solutionGuild, + isAnswerSubmitted, + onSelect, +}: Props) => { + let colorScheme = "gray" + let isDisabled = false + let variant = "solid" + let isActive = false + + if (isAnswerSubmitted) { + if (guild.id === selectedGuildId) { + colorScheme = solutionGuild.id === selectedGuildId ? "green" : "red" + } else if (guild.id === solutionGuild.id) { + colorScheme = "green" + variant = "outline" + } else { + isDisabled = true + colorScheme = "gray" + variant = "solid" + } + } else { + isActive = selectedGuildId === guild.id + } + + const handleClick = (guildId: number) => { + if (isAnswerSubmitted) return + onSelect(guildId) + } + + return ( + + ) +} + +export default AnswerButton diff --git a/src/components/guess-the-guild/GuessName.tsx b/src/components/guess-the-guild/GuessName.tsx index 765ca8ab..5f50ccbf 100644 --- a/src/components/guess-the-guild/GuessName.tsx +++ b/src/components/guess-the-guild/GuessName.tsx @@ -1,10 +1,18 @@ import { Button, Divider, Heading, Text, VStack } from "@chakra-ui/react" import GuildLogo from "components/common/GuildLogo" import { useState } from "react" +import { GuildBase } from "types" +import AnswerButton from "./AnswerButton" -const GuessName = () => { - const [submitted, setSubmitted] = useState(false) - const [answer, setAnswer] = useState() +const getRandomGuild = (guilds: GuildBase[]) => + guilds[Math.floor(Math.random()) * guilds.length] + +const GuessName = ({ guilds }: { guilds: GuildBase[] }) => { + const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false) + const [solutionGuild, setSolutionGuild] = useState( + getRandomGuild(guilds) + ) + const [selectedGuildId, setSelectedGuildId] = useState() return ( <> @@ -19,53 +27,35 @@ const GuessName = () => { - {"???"} + {isAnswerSubmitted ? solutionGuild.name : "???"} - - - - + {guilds.map((guild) => ( + setSelectedGuildId(guild.id)} + /> + ))} - {submitted && ( + {isAnswerSubmitted && ( )} - {!submitted && ( - )} diff --git a/src/pages/guess-the-guild.tsx b/src/pages/guess-the-guild.tsx index d1e5c9de..7d9968e0 100644 --- a/src/pages/guess-the-guild.tsx +++ b/src/pages/guess-the-guild.tsx @@ -1,14 +1,62 @@ import { Container, useBreakpointValue, useColorModeValue } from "@chakra-ui/react" import Card from "components/common/Card" import Layout from "components/common/Layout" -import AssignLogos from "components/guess-the-guild/AssignLogos" +import GuessName from "components/guess-the-guild/GuessName" import ScoreIndicator from "components/guess-the-guild/ScoreIndicator" +import { GuildBase } from "types" const GuessTheGuild = (): JSX.Element => { const bgColor = useColorModeValue("var(--chakra-colors-gray-800)", "#37373a") const bgOpacity = useColorModeValue(0.06, 0.1) const bgLinearPercentage = useBreakpointValue({ base: "50%", sm: "55%" }) + const mockGuilds: GuildBase[] = [ + { + id: 1, + name: "Guild1", + urlName: "guild1", + imageUrl: "", + roles: ["role1", "role2"], + platforms: ["DISCORD"], + memberCount: 12, + rolesCount: 2, + tags: ["FEATURED", "VERIFIED"], + }, + { + id: 2, + name: "Guild2", + urlName: "guild1", + imageUrl: "", + roles: ["role1", "role2"], + platforms: ["DISCORD"], + memberCount: 12, + rolesCount: 2, + tags: ["FEATURED", "VERIFIED"], + }, + { + id: 3, + name: "Guild3", + urlName: "guild1", + imageUrl: "", + roles: ["role1", "role2"], + platforms: ["DISCORD"], + memberCount: 12, + rolesCount: 2, + tags: ["FEATURED", "VERIFIED"], + }, + { + id: 4, + name: "Guild4", + urlName: "guild1", + imageUrl: "", + roles: ["role1", "role2"], + platforms: ["DISCORD"], + memberCount: 12, + rolesCount: 2, + tags: ["FEATURED", "VERIFIED"], + }, + ] + return ( <> { {/* */} - {/* */} - + + {/* */} {/* */} From 92c8123f5d1e1fde513eb66632371d8205087bff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ferenczy?= Date: Wed, 6 Dec 2023 17:01:13 +0100 Subject: [PATCH 09/27] feat: show alert for correct/incorrect answer --- src/components/guess-the-guild/GuessName.tsx | 27 +++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/components/guess-the-guild/GuessName.tsx b/src/components/guess-the-guild/GuessName.tsx index 5f50ccbf..992abd9c 100644 --- a/src/components/guess-the-guild/GuessName.tsx +++ b/src/components/guess-the-guild/GuessName.tsx @@ -1,4 +1,12 @@ -import { Button, Divider, Heading, Text, VStack } from "@chakra-ui/react" +import { + Alert, + AlertIcon, + Button, + Divider, + Heading, + Text, + VStack, +} from "@chakra-ui/react" import GuildLogo from "components/common/GuildLogo" import { useState } from "react" import { GuildBase } from "types" @@ -9,11 +17,11 @@ const getRandomGuild = (guilds: GuildBase[]) => const GuessName = ({ guilds }: { guilds: GuildBase[] }) => { const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false) - const [solutionGuild, setSolutionGuild] = useState( - getRandomGuild(guilds) - ) + const [solutionGuild] = useState(getRandomGuild(guilds)) const [selectedGuildId, setSelectedGuildId] = useState() + const isAnswerCorrect = selectedGuildId === solutionGuild.id + return ( <> @@ -45,6 +53,17 @@ const GuessName = ({ guilds }: { guilds: GuildBase[] }) => { + {isAnswerSubmitted && ( + + {" "} + {isAnswerCorrect ? "Your answer is correct!" : "Wrong answer!"} + + )} + {isAnswerSubmitted && ( )} - {!submitted && ( - )} diff --git a/src/components/guess-the-guild/assign-logos/GuildCardWithDropzone.tsx b/src/components/guess-the-guild/assign-logos/GuildCardWithDropzone.tsx index 82c83c4e..fef005db 100644 --- a/src/components/guess-the-guild/assign-logos/GuildCardWithDropzone.tsx +++ b/src/components/guess-the-guild/assign-logos/GuildCardWithDropzone.tsx @@ -1,5 +1,6 @@ import { HStack, + Icon, Tag, TagLabel, TagLeftIcon, @@ -9,7 +10,8 @@ import { useColorModeValue, } from "@chakra-ui/react" import Card from "components/common/Card" -import { Users } from "phosphor-react" +import GuildLogo from "components/common/GuildLogo" +import { ArrowDown, Users } from "phosphor-react" import { ReactNode } from "react" import { GuildBase } from "types" import pluralize from "utils/pluralize" @@ -19,48 +21,77 @@ const GuildCardWithDropzone = ({ guild, avatarSize, children, + isAnswerSubmitted, + isLogoCorrect, }: { guild: GuildBase avatarSize: number children: ReactNode + isAnswerSubmitted: boolean + isLogoCorrect: boolean }) => { const bgColor = useColorModeValue("gray.100", "whiteAlpha.200") return ( - - - - {children} - - - - {guild.name} - - - - {pluralize(guild.rolesCount, "role")} - - - - - {new Intl.NumberFormat("en", { notation: "compact" }).format( - guild.memberCount - )} - - - - - - + <> + + + + {children} + + + + {guild.name} + + + + {pluralize(guild.rolesCount, "role")} + + + + + {new Intl.NumberFormat("en", { notation: "compact" }).format( + guild.memberCount + )} + + + + + + + {isAnswerSubmitted && !isLogoCorrect && ( + <> + + + + + Correct Answer + + + + )} + ) } From 9adc8a7ac77746706dbbdacc5f051bb6a084574b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ferenczy?= Date: Wed, 6 Dec 2023 19:24:28 +0100 Subject: [PATCH 13/27] refactor: moves dnd logic to 'useDragAndDrop' hook --- .../guess-the-guild/AssignLogos.tsx | 88 +-------------- .../assign-logos/useDragAndDrop.tsx | 105 ++++++++++++++++++ 2 files changed, 110 insertions(+), 83 deletions(-) create mode 100644 src/components/guess-the-guild/assign-logos/useDragAndDrop.tsx diff --git a/src/components/guess-the-guild/AssignLogos.tsx b/src/components/guess-the-guild/AssignLogos.tsx index ae63bb7a..964ef05e 100644 --- a/src/components/guess-the-guild/AssignLogos.tsx +++ b/src/components/guess-the-guild/AssignLogos.tsx @@ -4,104 +4,26 @@ import Button from "components/common/Button" import GuildLogo from "components/common/GuildLogo" import React, { useState } from "react" import { GuildBase } from "types" -import { shuffleArray } from "utils/shuffleArray" import Draggable from "./assign-logos/Draggable" import GuildCardWithDropzone from "./assign-logos/GuildCardWithDropzone" import SourceDropzone from "./assign-logos/SourceDropzone" - -export const START_ZONE_ID = "source" - -type DropzoneDict = Record +import useDragAndDrop, { START_ZONE_ID } from "./assign-logos/useDragAndDrop" const AssignLogos = ({ guilds }: { guilds: GuildBase[] }) => { const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false) const avatarSize = 90 - const [movingGuild, setMovingGuild] = useState(null) - - const [startZone, setStartZone] = useState(shuffleArray(guilds)) - const initialDropzones = Object.fromEntries( - guilds.map((guild) => [guild.id, null]) - ) - const [dropzones, setDropzones] = useState(initialDropzones) - - const addToStartZone = (guild) => { - setStartZone((prev) => [...prev, guild]) - } - - const removeFromStartZone = (guildId) => { - setStartZone((prev) => prev.filter((g) => g.id !== guildId)) - } + const { startZone, dropzones, movingGuild, dragStart, dragEnd } = + useDragAndDrop(guilds) const handleDragStart = (event: DragStartEvent) => { if (isAnswerSubmitted) return - setMovingGuild(guilds.find((g) => g.id.toString() === event.active.id)) + dragStart(event) } const handleDragEnd = (event: DragEndEvent) => { if (isAnswerSubmitted) return - const { over } = event - - if (!over || !movingGuild) { - setMovingGuild(null) - return - } - - const sourceZoneId = findSourceZoneId(movingGuild) - const targetZoneId = over.id - - if (!isMoveAllowed(sourceZoneId, targetZoneId)) { - setMovingGuild(null) - return - } - - updateDropzones(sourceZoneId, targetZoneId) - updateStartZone(sourceZoneId, targetZoneId) - - setMovingGuild(null) - } - - const updateDropzones = (sourceZoneId, targetZoneId) => { - const updatedDropzones = { ...dropzones } - - if (targetZoneId !== START_ZONE_ID) { - updatedDropzones[targetZoneId] = guilds.find( - (guild) => guild.id === movingGuild.id - ) - } - - if (sourceZoneId !== START_ZONE_ID) { - updatedDropzones[sourceZoneId] = null - } - - setDropzones(updatedDropzones) - } - - const updateStartZone = (sourceZoneId, targetZoneId) => { - if (targetZoneId === START_ZONE_ID) { - addToStartZone(movingGuild) - } - if (sourceZoneId === START_ZONE_ID) { - removeFromStartZone(movingGuild.id) - } - } - - const isMoveAllowed = (sourceZoneId, targetZoneId) => { - if (sourceZoneId === targetZoneId) return false - - if (targetZoneId !== START_ZONE_ID && dropzones[targetZoneId]) return false - - return true - } - - const findSourceZoneId = (guild: GuildBase): string | number | null => { - const isInStartZone = startZone.some((g) => g.id === guild.id) - if (isInStartZone) return START_ZONE_ID - - const sourceDropzone = Object.entries(dropzones).find( - ([_, dzGuild]) => dzGuild?.id === guild.id - ) - return sourceDropzone ? sourceDropzone[0] : null + dragEnd(event) } const renderDraggableAvatar = (guild: GuildBase) => { diff --git a/src/components/guess-the-guild/assign-logos/useDragAndDrop.tsx b/src/components/guess-the-guild/assign-logos/useDragAndDrop.tsx new file mode 100644 index 00000000..41e189e5 --- /dev/null +++ b/src/components/guess-the-guild/assign-logos/useDragAndDrop.tsx @@ -0,0 +1,105 @@ +import { DragEndEvent, DragStartEvent } from "@dnd-kit/core" +import { useState } from "react" +import { GuildBase } from "types" +import { shuffleArray } from "utils/shuffleArray" + +export const START_ZONE_ID = "source" +type DropzoneDict = Record + +const useDragAndDrop = (guilds) => { + const [startZone, setStartZone] = useState(shuffleArray(guilds)) + const [movingGuild, setMovingGuild] = useState(null) + + const initialDropzones = Object.fromEntries( + guilds.map((guild) => [guild.id, null]) + ) + + const [dropzones, setDropzones] = useState(initialDropzones) + + const addToStartZone = (guild) => { + setStartZone((prev) => [...prev, guild]) + } + + const removeFromStartZone = (guildId) => { + setStartZone((prev) => prev.filter((g) => g.id !== guildId)) + } + + const dragStart = (event: DragStartEvent) => { + setMovingGuild(guilds.find((g) => g.id.toString() === event.active.id)) + } + + const dragEnd = (event: DragEndEvent) => { + const { over } = event + + if (!over || !movingGuild) { + setMovingGuild(null) + return + } + + const sourceZoneId = findSourceZoneId(movingGuild) + const targetZoneId = over.id + + if (!isMoveAllowed(sourceZoneId, targetZoneId)) { + setMovingGuild(null) + return + } + + updateDropzones(sourceZoneId, targetZoneId) + updateStartZone(sourceZoneId, targetZoneId) + + setMovingGuild(null) + } + + const updateDropzones = (sourceZoneId, targetZoneId) => { + const updatedDropzones = { ...dropzones } + + if (targetZoneId !== START_ZONE_ID) { + updatedDropzones[targetZoneId] = guilds.find( + (guild) => guild.id === movingGuild.id + ) + } + + if (sourceZoneId !== START_ZONE_ID) { + updatedDropzones[sourceZoneId] = null + } + + setDropzones(updatedDropzones) + } + + const updateStartZone = (sourceZoneId, targetZoneId) => { + if (targetZoneId === START_ZONE_ID) { + addToStartZone(movingGuild) + } + if (sourceZoneId === START_ZONE_ID) { + removeFromStartZone(movingGuild.id) + } + } + + const isMoveAllowed = (sourceZoneId, targetZoneId) => { + if (sourceZoneId === targetZoneId) return false + + if (targetZoneId !== START_ZONE_ID && dropzones[targetZoneId]) return false + + return true + } + + const findSourceZoneId = (guild: GuildBase): string | number | null => { + const isInStartZone = startZone.some((g) => g.id === guild.id) + if (isInStartZone) return START_ZONE_ID + + const sourceDropzone = Object.entries(dropzones).find( + ([_, dzGuild]) => dzGuild?.id === guild.id + ) + return sourceDropzone ? sourceDropzone[0] : null + } + + return { + startZone, + dropzones, + movingGuild, + dragStart, + dragEnd, + } +} + +export default useDragAndDrop From 256202ccbdf553ba4d53c960f519bfb150c3708d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ferenczy?= Date: Wed, 6 Dec 2023 19:29:51 +0100 Subject: [PATCH 14/27] refactor: extended typing on 'useDragAndDrop' hook --- .../assign-logos/useDragAndDrop.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/guess-the-guild/assign-logos/useDragAndDrop.tsx b/src/components/guess-the-guild/assign-logos/useDragAndDrop.tsx index 41e189e5..26f93b48 100644 --- a/src/components/guess-the-guild/assign-logos/useDragAndDrop.tsx +++ b/src/components/guess-the-guild/assign-logos/useDragAndDrop.tsx @@ -6,7 +6,7 @@ import { shuffleArray } from "utils/shuffleArray" export const START_ZONE_ID = "source" type DropzoneDict = Record -const useDragAndDrop = (guilds) => { +const useDragAndDrop = (guilds: GuildBase[]) => { const [startZone, setStartZone] = useState(shuffleArray(guilds)) const [movingGuild, setMovingGuild] = useState(null) @@ -16,11 +16,11 @@ const useDragAndDrop = (guilds) => { const [dropzones, setDropzones] = useState(initialDropzones) - const addToStartZone = (guild) => { + const addToStartZone = (guild: GuildBase) => { setStartZone((prev) => [...prev, guild]) } - const removeFromStartZone = (guildId) => { + const removeFromStartZone = (guildId: number) => { setStartZone((prev) => prev.filter((g) => g.id !== guildId)) } @@ -37,7 +37,7 @@ const useDragAndDrop = (guilds) => { } const sourceZoneId = findSourceZoneId(movingGuild) - const targetZoneId = over.id + const targetZoneId = over.id as string if (!isMoveAllowed(sourceZoneId, targetZoneId)) { setMovingGuild(null) @@ -50,7 +50,7 @@ const useDragAndDrop = (guilds) => { setMovingGuild(null) } - const updateDropzones = (sourceZoneId, targetZoneId) => { + const updateDropzones = (sourceZoneId: string, targetZoneId: string) => { const updatedDropzones = { ...dropzones } if (targetZoneId !== START_ZONE_ID) { @@ -66,7 +66,7 @@ const useDragAndDrop = (guilds) => { setDropzones(updatedDropzones) } - const updateStartZone = (sourceZoneId, targetZoneId) => { + const updateStartZone = (sourceZoneId: string, targetZoneId: string) => { if (targetZoneId === START_ZONE_ID) { addToStartZone(movingGuild) } @@ -75,7 +75,7 @@ const useDragAndDrop = (guilds) => { } } - const isMoveAllowed = (sourceZoneId, targetZoneId) => { + const isMoveAllowed = (sourceZoneId: string, targetZoneId: string): boolean => { if (sourceZoneId === targetZoneId) return false if (targetZoneId !== START_ZONE_ID && dropzones[targetZoneId]) return false @@ -83,7 +83,7 @@ const useDragAndDrop = (guilds) => { return true } - const findSourceZoneId = (guild: GuildBase): string | number | null => { + const findSourceZoneId = (guild: GuildBase): string | null => { const isInStartZone = startZone.some((g) => g.id === guild.id) if (isInStartZone) return START_ZONE_ID From 3cf73793d0fa2752596842cf408b7f8e6c28b011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ferenczy?= Date: Wed, 6 Dec 2023 19:45:59 +0100 Subject: [PATCH 15/27] refactor: improving responsiveness of 'assign logos' mode --- .../guess-the-guild/AssignLogos.tsx | 2 +- .../assign-logos/GuildCardWithDropzone.tsx | 2 +- .../assign-logos/SourceDropzone.tsx | 24 +++++++++++++++---- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/components/guess-the-guild/AssignLogos.tsx b/src/components/guess-the-guild/AssignLogos.tsx index 964ef05e..479cfb8c 100644 --- a/src/components/guess-the-guild/AssignLogos.tsx +++ b/src/components/guess-the-guild/AssignLogos.tsx @@ -11,7 +11,7 @@ import useDragAndDrop, { START_ZONE_ID } from "./assign-logos/useDragAndDrop" const AssignLogos = ({ guilds }: { guilds: GuildBase[] }) => { const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false) - const avatarSize = 90 + const avatarSize = { base: "60px", sm: "70px", md: "80px" } const { startZone, dropzones, movingGuild, dragStart, dragEnd } = useDragAndDrop(guilds) diff --git a/src/components/guess-the-guild/assign-logos/GuildCardWithDropzone.tsx b/src/components/guess-the-guild/assign-logos/GuildCardWithDropzone.tsx index fef005db..576d9272 100644 --- a/src/components/guess-the-guild/assign-logos/GuildCardWithDropzone.tsx +++ b/src/components/guess-the-guild/assign-logos/GuildCardWithDropzone.tsx @@ -25,7 +25,7 @@ const GuildCardWithDropzone = ({ isLogoCorrect, }: { guild: GuildBase - avatarSize: number + avatarSize: any children: ReactNode isAnswerSubmitted: boolean isLogoCorrect: boolean diff --git a/src/components/guess-the-guild/assign-logos/SourceDropzone.tsx b/src/components/guess-the-guild/assign-logos/SourceDropzone.tsx index ca2b71e4..0b58106f 100644 --- a/src/components/guess-the-guild/assign-logos/SourceDropzone.tsx +++ b/src/components/guess-the-guild/assign-logos/SourceDropzone.tsx @@ -1,4 +1,4 @@ -import { Box, useColorModeValue } from "@chakra-ui/react" +import { Box, useColorModeValue, useTheme } from "@chakra-ui/react" import { useDroppable } from "@dnd-kit/core" import { ReactNode } from "react" @@ -15,15 +15,31 @@ const SourceDropzone = ({ const borderColor = "transparent" const borderColorHover = useColorModeValue("green.400", "green.600") + const theme = useTheme() + const paddingValue = theme.space[2] + + const addPaddingToSize = (sizeValue) => `calc(${sizeValue} + 3*${paddingValue}) ` + + const minHeightWithPadding = () => { + const pSize = size + + return { + base: addPaddingToSize(pSize.base), + sm: addPaddingToSize(pSize.sm), + md: addPaddingToSize(pSize.md), + } + } + return ( Date: Wed, 6 Dec 2023 19:51:27 +0100 Subject: [PATCH 16/27] refactor: answer correct/wrong alert to separate component --- .../guess-the-guild/AssignLogos.tsx | 26 ++++++++++--------- src/components/guess-the-guild/GuessName.tsx | 23 +++------------- .../guess-the-guild/ResultAlert.tsx | 15 +++++++++++ 3 files changed, 33 insertions(+), 31 deletions(-) create mode 100644 src/components/guess-the-guild/ResultAlert.tsx diff --git a/src/components/guess-the-guild/AssignLogos.tsx b/src/components/guess-the-guild/AssignLogos.tsx index 479cfb8c..aec184af 100644 --- a/src/components/guess-the-guild/AssignLogos.tsx +++ b/src/components/guess-the-guild/AssignLogos.tsx @@ -1,9 +1,10 @@ -import { Alert, AlertIcon, Divider, Heading, VStack } from "@chakra-ui/react" +import { Divider, Heading, VStack } from "@chakra-ui/react" import { DndContext, DragEndEvent, DragOverlay, DragStartEvent } from "@dnd-kit/core" import Button from "components/common/Button" import GuildLogo from "components/common/GuildLogo" -import React, { useState } from "react" +import React, { useMemo, useState } from "react" import { GuildBase } from "types" +import ResultAlert from "./ResultAlert" import Draggable from "./assign-logos/Draggable" import GuildCardWithDropzone from "./assign-logos/GuildCardWithDropzone" import SourceDropzone from "./assign-logos/SourceDropzone" @@ -45,6 +46,15 @@ const AssignLogos = ({ guilds }: { guilds: GuildBase[] }) => { const isLogoCorrectForGuild = (guild: GuildBase) => dropzones[guild.id]?.id === guild?.id + const canSubmit = useMemo(() => { + for (const key of Object.keys(dropzones)) { + if (key !== "source" && dropzones[key] === null) { + return false + } + } + return true + }, [dropzones]) + return ( <> @@ -87,16 +97,7 @@ const AssignLogos = ({ guilds }: { guilds: GuildBase[] }) => { - {isAnswerSubmitted && ( - - {" "} - {isAnswerCorrect ? "Your answer is correct!" : "Wrong answer!"} - - )} + {isAnswerSubmitted && } {isAnswerSubmitted && ( - )} {!isAnswerSubmitted && ( )} + + {isAnswerSubmitted && isAnswerCorrect && ( + + )} + + {isAnswerSubmitted && !isAnswerCorrect && ( + + )} ) diff --git a/src/components/guess-the-guild/EndGame.tsx b/src/components/guess-the-guild/EndGame.tsx index 3826872d..14e357d7 100644 --- a/src/components/guess-the-guild/EndGame.tsx +++ b/src/components/guess-the-guild/EndGame.tsx @@ -1,7 +1,7 @@ import { Alert, Button, Divider, Heading, Text, VStack } from "@chakra-ui/react" import pluralize from "utils/pluralize" -const EndGame = () => { +const EndGame = ({ onRestart }: { onRestart: () => void }) => { const score = 22 const highscore = 30 @@ -65,7 +65,7 @@ const EndGame = () => { - diff --git a/src/components/guess-the-guild/GuessName.tsx b/src/components/guess-the-guild/GuessName.tsx index a53ecb6c..f2ba51a4 100644 --- a/src/components/guess-the-guild/GuessName.tsx +++ b/src/components/guess-the-guild/GuessName.tsx @@ -1,14 +1,15 @@ import { Button, Divider, Heading, Text, VStack } from "@chakra-ui/react" import GuildLogo from "components/common/GuildLogo" +import { GameModeProps } from "pages/guess-the-guild" import { useState } from "react" import { GuildBase } from "types" import AnswerButton from "./AnswerButton" import ResultAlert from "./ResultAlert" const getRandomGuild = (guilds: GuildBase[]) => - guilds[Math.floor(Math.random()) * guilds.length] + guilds[Math.floor(Math.random() * guilds.length)] -const GuessName = ({ guilds }: { guilds: GuildBase[] }) => { +const GuessName = ({ guilds, onNext, onExit }: GameModeProps) => { const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false) const [solutionGuild] = useState(getRandomGuild(guilds)) const [selectedGuildId, setSelectedGuildId] = useState() @@ -48,11 +49,6 @@ const GuessName = ({ guilds }: { guilds: GuildBase[] }) => { {isAnswerSubmitted && } - {isAnswerSubmitted && ( - - )} {!isAnswerSubmitted && ( )} + + {isAnswerSubmitted && isAnswerCorrect && ( + + )} + + {isAnswerSubmitted && !isAnswerCorrect && ( + + )} ) diff --git a/src/components/guess-the-guild/StartGame.tsx b/src/components/guess-the-guild/StartGame.tsx index 4f0897bc..f8154eba 100644 --- a/src/components/guess-the-guild/StartGame.tsx +++ b/src/components/guess-the-guild/StartGame.tsx @@ -8,7 +8,7 @@ enum Difficulty { Hard, } -const StartGame = () => { +const StartGame = ({ onStart }: { onStart: () => void }) => { const [difficulty, setDifficulty] = useState(Difficulty.Easy) return ( @@ -98,7 +98,7 @@ const StartGame = () => { {difficulty == 1 && "2x"} {difficulty == 2 && "3x"} the usual points. - diff --git a/src/pages/guess-the-guild.tsx b/src/pages/guess-the-guild.tsx index 5a17796c..1238a77f 100644 --- a/src/pages/guess-the-guild.tsx +++ b/src/pages/guess-the-guild.tsx @@ -2,14 +2,66 @@ import { Container, useBreakpointValue, useColorModeValue } from "@chakra-ui/rea import Card from "components/common/Card" import Layout from "components/common/Layout" import AssignLogos from "components/guess-the-guild/AssignLogos" +import EndGame from "components/guess-the-guild/EndGame" +import GuessName from "components/guess-the-guild/GuessName" import ScoreIndicator from "components/guess-the-guild/ScoreIndicator" +import StartGame from "components/guess-the-guild/StartGame" +import React, { useState } from "react" import { GuildBase } from "types" +export enum GameMode { + GuessNameMode, + AssignLogosMode, +} + +export enum GameState { + Start, + Game, + End, +} + +export type GameModeProps = { + guilds: GuildBase[] + onNext: () => void + onExit: () => void +} + const GuessTheGuild = (): JSX.Element => { const bgColor = useColorModeValue("var(--chakra-colors-gray-800)", "#37373a") const bgOpacity = useColorModeValue(0.06, 0.1) const bgLinearPercentage = useBreakpointValue({ base: "50%", sm: "55%" }) + const [round, setRound] = useState(0) + const [gameMode, setGameMode] = useState(GameMode.GuessNameMode) + const [gameState, setGameState] = useState(GameState.Start) + + const selectGameMode = () => { + if (Math.random() >= 0.5) { + setGameMode(GameMode.AssignLogosMode) + } else { + setGameMode(GameMode.GuessNameMode) + } + } + + const startGame = () => { + setGameState(GameState.Game) + selectGameMode() + } + + const nextGame = () => { + selectGameMode() + setRound(round + 1) + } + + const endGame = () => { + setGameState(GameState.End) + } + + const restartGame = () => { + setRound(0) + setGameState(GameState.Start) + } + const mockGuilds: GuildBase[] = [ { id: 1, @@ -85,10 +137,37 @@ const GuessTheGuild = (): JSX.Element => { - {/* */} - {/* */} - - {/* */} + {gameState === GameState.Start && ( + startGame()} /> + )} + + {gameState === GameState.Game && ( + + {gameMode === GameMode.AssignLogosMode && ( + <> + nextGame()} + onExit={() => endGame()} + /> + + )} + + {gameMode === GameMode.GuessNameMode && ( + <> + nextGame()} + onExit={() => endGame()} + /> + + )} + + )} + + {gameState === GameState.End && ( + restartGame()} /> + )} From a89756dc0470333ee1dc78107a02bf0aa80207b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ferenczy?= Date: Wed, 6 Dec 2023 20:43:29 +0100 Subject: [PATCH 18/27] feat: score calculation --- .../guess-the-guild/AssignLogos.tsx | 13 +- src/components/guess-the-guild/EndGame.tsx | 13 +- src/components/guess-the-guild/GuessName.tsx | 13 +- .../guess-the-guild/ScoreIndicator.tsx | 11 +- src/components/guess-the-guild/StartGame.tsx | 170 +++++++++--------- src/pages/guess-the-guild.tsx | 54 +++++- 6 files changed, 170 insertions(+), 104 deletions(-) diff --git a/src/components/guess-the-guild/AssignLogos.tsx b/src/components/guess-the-guild/AssignLogos.tsx index cbb11e60..51173e8d 100644 --- a/src/components/guess-the-guild/AssignLogos.tsx +++ b/src/components/guess-the-guild/AssignLogos.tsx @@ -11,7 +11,7 @@ import GuildCardWithDropzone from "./assign-logos/GuildCardWithDropzone" import SourceDropzone from "./assign-logos/SourceDropzone" import useDragAndDrop, { START_ZONE_ID } from "./assign-logos/useDragAndDrop" -const AssignLogos = ({ guilds, onNext, onExit }: GameModeProps) => { +const AssignLogos = ({ guilds, onNext, onExit, onCorrect }: GameModeProps) => { const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false) const avatarSize = { base: "60px", sm: "70px", md: "80px" } @@ -56,6 +56,11 @@ const AssignLogos = ({ guilds, onNext, onExit }: GameModeProps) => { return true }, [dropzones]) + const handleSubmit = () => { + setIsAnswerSubmitted(true) + if (isAnswerCorrect) onCorrect() + } + return ( <> @@ -105,20 +110,20 @@ const AssignLogos = ({ guilds, onNext, onExit }: GameModeProps) => { colorScheme="green" w="100%" isDisabled={!canSubmit} - onClick={() => setIsAnswerSubmitted(true)} + onClick={handleSubmit} > Submit )} {isAnswerSubmitted && isAnswerCorrect && ( - )} {isAnswerSubmitted && !isAnswerCorrect && ( - )} diff --git a/src/components/guess-the-guild/EndGame.tsx b/src/components/guess-the-guild/EndGame.tsx index 14e357d7..8b50f0c5 100644 --- a/src/components/guess-the-guild/EndGame.tsx +++ b/src/components/guess-the-guild/EndGame.tsx @@ -1,10 +1,15 @@ import { Alert, Button, Divider, Heading, Text, VStack } from "@chakra-ui/react" import pluralize from "utils/pluralize" -const EndGame = ({ onRestart }: { onRestart: () => void }) => { - const score = 22 - const highscore = 30 - +const EndGame = ({ + score, + highscore, + onRestart, +}: { + score: number + highscore: number + onRestart: () => void +}) => { const getMessage = () => { if (score <= 10) return "Better luck next time!" if (score <= 20) return "You're getting the hang of it!" diff --git a/src/components/guess-the-guild/GuessName.tsx b/src/components/guess-the-guild/GuessName.tsx index f2ba51a4..9a332565 100644 --- a/src/components/guess-the-guild/GuessName.tsx +++ b/src/components/guess-the-guild/GuessName.tsx @@ -9,13 +9,18 @@ import ResultAlert from "./ResultAlert" const getRandomGuild = (guilds: GuildBase[]) => guilds[Math.floor(Math.random() * guilds.length)] -const GuessName = ({ guilds, onNext, onExit }: GameModeProps) => { +const GuessName = ({ guilds, onNext, onExit, onCorrect }: GameModeProps) => { const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false) const [solutionGuild] = useState(getRandomGuild(guilds)) const [selectedGuildId, setSelectedGuildId] = useState() const isAnswerCorrect = selectedGuildId === solutionGuild.id + const handleSubmit = () => { + setIsAnswerSubmitted(true) + if (isAnswerCorrect) onCorrect() + } + return ( <> @@ -54,20 +59,20 @@ const GuessName = ({ guilds, onNext, onExit }: GameModeProps) => { colorScheme="green" w="100%" isDisabled={typeof selectedGuildId === "undefined"} - onClick={() => setIsAnswerSubmitted(true)} + onClick={handleSubmit} > Submit )} {isAnswerSubmitted && isAnswerCorrect && ( - )} {isAnswerSubmitted && !isAnswerCorrect && ( - )} diff --git a/src/components/guess-the-guild/ScoreIndicator.tsx b/src/components/guess-the-guild/ScoreIndicator.tsx index 501e0f8d..97a8ea46 100644 --- a/src/components/guess-the-guild/ScoreIndicator.tsx +++ b/src/components/guess-the-guild/ScoreIndicator.tsx @@ -1,10 +1,13 @@ import { Alert, HStack, Progress, Tag, chakra } from "@chakra-ui/react" import Card from "components/common/Card" -const ScoreIndicator = () => { - const score = 100 - const highscore = 200 - +const ScoreIndicator = ({ + score, + highscore, +}: { + score: number + highscore: number +}) => { const isHighscore = score > highscore return ( diff --git a/src/components/guess-the-guild/StartGame.tsx b/src/components/guess-the-guild/StartGame.tsx index f8154eba..c84dd6c8 100644 --- a/src/components/guess-the-guild/StartGame.tsx +++ b/src/components/guess-the-guild/StartGame.tsx @@ -1,59 +1,38 @@ import { Divider, Heading, Text, VStack } from "@chakra-ui/react" import Button from "components/common/Button" -import { useState } from "react" +import { Difficulty } from "pages/guess-the-guild" -enum Difficulty { - Easy, - Medium, - Hard, +type Props = { + onStart: () => void + difficulty: Difficulty + onDifficultyChange: (d: Difficulty) => void + highscore: number } -const StartGame = ({ onStart }: { onStart: () => void }) => { - const [difficulty, setDifficulty] = useState(Difficulty.Easy) +const StartGame = ({ + onStart, + difficulty, + onDifficultyChange, + highscore, +}: Props) => ( + + + GuildGesser 1.0 + + + Are you an expert on Guilds?
Test your knowledge in our guild guesser + mini game! +
- return ( - - - GuildGesser 1.0 - - - Are you an expert on Guilds?
Test your knowledge in our guild guesser - mini game! -
- - - - - - {"Your highscore"} - - - 3000 - - - - + + void }) => { textTransform="uppercase" noOfLines={1} > - {"Select Difficulty"} + {"Your highscore"} + + + {highscore} + + + - - - - + + {"Select Difficulty"} + - - Selection from the top {difficulty == 0 && "100"}{" "} - {difficulty == 1 && "500"} {difficulty == 2 && "1000"} guilds. -
- Solving this difficulty awards {difficulty == 0 && "1x"}{" "} - {difficulty == 1 && "2x"} {difficulty == 2 && "3x"} the usual points. -
-
- + + + + + Selection from the top {difficulty == 0 && "100"} {difficulty == 1 && "500"}{" "} + {difficulty == 2 && "1000"} guilds. +
+ Solving this difficulty awards {difficulty == 0 && "1x"}{" "} + {difficulty == 1 && "2x"} {difficulty == 2 && "3x"} the usual points. +
- ) -} + +
+) export default StartGame diff --git a/src/pages/guess-the-guild.tsx b/src/pages/guess-the-guild.tsx index 1238a77f..640d7235 100644 --- a/src/pages/guess-the-guild.tsx +++ b/src/pages/guess-the-guild.tsx @@ -6,7 +6,8 @@ import EndGame from "components/guess-the-guild/EndGame" import GuessName from "components/guess-the-guild/GuessName" import ScoreIndicator from "components/guess-the-guild/ScoreIndicator" import StartGame from "components/guess-the-guild/StartGame" -import React, { useState } from "react" +import useLocalStorage from "hooks/useLocalStorage" +import React, { useEffect, useState } from "react" import { GuildBase } from "types" export enum GameMode { @@ -24,6 +25,13 @@ export type GameModeProps = { guilds: GuildBase[] onNext: () => void onExit: () => void + onCorrect: () => void +} + +export enum Difficulty { + Easy, + Medium, + Hard, } const GuessTheGuild = (): JSX.Element => { @@ -34,6 +42,15 @@ const GuessTheGuild = (): JSX.Element => { const [round, setRound] = useState(0) const [gameMode, setGameMode] = useState(GameMode.GuessNameMode) const [gameState, setGameState] = useState(GameState.Start) + const [difficulty, setDifficulty] = useState(Difficulty.Easy) + + const [score, setScore] = useState(0) + const [highscore, setHighscore] = useState(0) + const [savedHighscore, setSavedHighscore] = useLocalStorage("highscore", 0) + + useEffect(() => { + setHighscore(savedHighscore) + }, []) const selectGameMode = () => { if (Math.random() >= 0.5) { @@ -109,6 +126,25 @@ const GuessTheGuild = (): JSX.Element => { }, ] + const addPoints = (points: number) => { + let pointsToAdd = points + if (difficulty === Difficulty.Medium) pointsToAdd *= 2 + if (difficulty === Difficulty.Hard) pointsToAdd *= 3 + const updatedPoints = points + pointsToAdd + setScore(updatedPoints) + updateHighscore(updatedPoints) + } + + const updateHighscore = (updatedPoints: number) => { + if (updatedPoints > highscore) { + setSavedHighscore(updatedPoints) + } + } + + const handleDifficultyChange = (diff: Difficulty) => { + setDifficulty(diff) + } + return ( <> { textColor="white" > - + {gameState === GameState.Game && ( + + )} + {gameState === GameState.Start && ( - startGame()} /> + startGame()} + highscore={highscore} + onDifficultyChange={handleDifficultyChange} + difficulty={difficulty} + /> )} {gameState === GameState.Game && ( @@ -149,6 +193,7 @@ const GuessTheGuild = (): JSX.Element => { guilds={mockGuilds} onNext={() => nextGame()} onExit={() => endGame()} + onCorrect={() => addPoints(2)} /> )} @@ -159,6 +204,7 @@ const GuessTheGuild = (): JSX.Element => { guilds={mockGuilds} onNext={() => nextGame()} onExit={() => endGame()} + onCorrect={() => addPoints(1)} /> )} @@ -166,7 +212,7 @@ const GuessTheGuild = (): JSX.Element => { )} {gameState === GameState.End && ( - restartGame()} /> + )} From e04d917b112cf640f7dbcb8210b06c779fc928f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ferenczy?= Date: Wed, 6 Dec 2023 20:56:50 +0100 Subject: [PATCH 19/27] refactor: moves game logic to 'useGameLogic' hook --- .../guess-the-guild/AssignLogos.tsx | 2 +- src/components/guess-the-guild/GuessName.tsx | 2 +- src/components/guess-the-guild/StartGame.tsx | 2 +- .../guess-the-guild/guess-the-guild-types.tsx | 25 ++++ .../guess-the-guild/useGameLogic.tsx | 69 +++++++++ src/pages/guess-the-guild.tsx | 141 ++++++------------ 6 files changed, 142 insertions(+), 99 deletions(-) create mode 100644 src/components/guess-the-guild/guess-the-guild-types.tsx create mode 100644 src/components/guess-the-guild/useGameLogic.tsx diff --git a/src/components/guess-the-guild/AssignLogos.tsx b/src/components/guess-the-guild/AssignLogos.tsx index 51173e8d..cd418812 100644 --- a/src/components/guess-the-guild/AssignLogos.tsx +++ b/src/components/guess-the-guild/AssignLogos.tsx @@ -2,7 +2,6 @@ import { Divider, Heading, VStack } from "@chakra-ui/react" import { DndContext, DragEndEvent, DragOverlay, DragStartEvent } from "@dnd-kit/core" import Button from "components/common/Button" import GuildLogo from "components/common/GuildLogo" -import { GameModeProps } from "pages/guess-the-guild" import React, { useMemo, useState } from "react" import { GuildBase } from "types" import ResultAlert from "./ResultAlert" @@ -10,6 +9,7 @@ import Draggable from "./assign-logos/Draggable" import GuildCardWithDropzone from "./assign-logos/GuildCardWithDropzone" import SourceDropzone from "./assign-logos/SourceDropzone" import useDragAndDrop, { START_ZONE_ID } from "./assign-logos/useDragAndDrop" +import { GameModeProps } from "./guess-the-guild-types" const AssignLogos = ({ guilds, onNext, onExit, onCorrect }: GameModeProps) => { const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false) diff --git a/src/components/guess-the-guild/GuessName.tsx b/src/components/guess-the-guild/GuessName.tsx index 9a332565..57da28f3 100644 --- a/src/components/guess-the-guild/GuessName.tsx +++ b/src/components/guess-the-guild/GuessName.tsx @@ -1,10 +1,10 @@ import { Button, Divider, Heading, Text, VStack } from "@chakra-ui/react" import GuildLogo from "components/common/GuildLogo" -import { GameModeProps } from "pages/guess-the-guild" import { useState } from "react" import { GuildBase } from "types" import AnswerButton from "./AnswerButton" import ResultAlert from "./ResultAlert" +import { GameModeProps } from "./guess-the-guild-types" const getRandomGuild = (guilds: GuildBase[]) => guilds[Math.floor(Math.random() * guilds.length)] diff --git a/src/components/guess-the-guild/StartGame.tsx b/src/components/guess-the-guild/StartGame.tsx index c84dd6c8..f938dae3 100644 --- a/src/components/guess-the-guild/StartGame.tsx +++ b/src/components/guess-the-guild/StartGame.tsx @@ -1,6 +1,6 @@ import { Divider, Heading, Text, VStack } from "@chakra-ui/react" import Button from "components/common/Button" -import { Difficulty } from "pages/guess-the-guild" +import { Difficulty } from "./guess-the-guild-types" type Props = { onStart: () => void diff --git a/src/components/guess-the-guild/guess-the-guild-types.tsx b/src/components/guess-the-guild/guess-the-guild-types.tsx new file mode 100644 index 00000000..eb210ff5 --- /dev/null +++ b/src/components/guess-the-guild/guess-the-guild-types.tsx @@ -0,0 +1,25 @@ +import { GuildBase } from "types" + +export enum GameMode { + GuessNameMode, + AssignLogosMode, +} + +export enum GameState { + Start, + Game, + End, +} + +export type GameModeProps = { + guilds: GuildBase[] + onNext: () => void + onExit: () => void + onCorrect: () => void +} + +export enum Difficulty { + Easy, + Medium, + Hard, +} diff --git a/src/components/guess-the-guild/useGameLogic.tsx b/src/components/guess-the-guild/useGameLogic.tsx new file mode 100644 index 00000000..e3142c64 --- /dev/null +++ b/src/components/guess-the-guild/useGameLogic.tsx @@ -0,0 +1,69 @@ +import useLocalStorage from "hooks/useLocalStorage" +import { useEffect, useState } from "react" +import { Difficulty, GameMode, GameState } from "./guess-the-guild-types" + +const useGameLogic = () => { + const [savedHighscore, setSavedHighscore] = useLocalStorage("highscore", 0) + const [score, setScore] = useState(0) + const [highscore, setHighscore] = useState(0) + const [round, setRound] = useState(0) + const [gameState, setGameState] = useState(GameState.Start) + const [gameMode, setGameMode] = useState(GameMode.GuessNameMode) + const [difficulty, setDifficulty] = useState(Difficulty.Easy) + + useEffect(() => { + setHighscore(savedHighscore) + }, []) + + const addPoints = (points: number) => { + let pointsToAdd = points + if (difficulty === Difficulty.Medium) pointsToAdd *= 2 + if (difficulty === Difficulty.Hard) pointsToAdd *= 3 + const updatedPoints = points + pointsToAdd + setScore(updatedPoints) + updateHighscore(updatedPoints) + } + + const updateHighscore = (updatedPoints: number) => { + if (updatedPoints > highscore) { + setSavedHighscore(updatedPoints) + } + } + + const restartGame = () => { + setScore(0) + setRound(0) + setHighscore(savedHighscore) + setGameState(GameState.Start) + } + + const startGame = () => { + setGameState(GameState.Game) + selectGameMode() + } + + const selectGameMode = () => { + if (Math.random() >= 0.5) { + setGameMode(GameMode.AssignLogosMode) + } else { + setGameMode(GameMode.GuessNameMode) + } + } + + const endGame = () => { + setGameState(GameState.End) + } + + const nextGame = () => { + selectGameMode() + setRound(round + 1) + } + + return { + state: { score, highscore, round, difficulty, gameMode, gameState }, + transition: { startGame, endGame, restartGame, nextGame }, + action: { setDifficulty, addPoints }, + } +} + +export default useGameLogic diff --git a/src/pages/guess-the-guild.tsx b/src/pages/guess-the-guild.tsx index 640d7235..6519edba 100644 --- a/src/pages/guess-the-guild.tsx +++ b/src/pages/guess-the-guild.tsx @@ -6,79 +6,22 @@ import EndGame from "components/guess-the-guild/EndGame" import GuessName from "components/guess-the-guild/GuessName" import ScoreIndicator from "components/guess-the-guild/ScoreIndicator" import StartGame from "components/guess-the-guild/StartGame" -import useLocalStorage from "hooks/useLocalStorage" -import React, { useEffect, useState } from "react" +import { + Difficulty, + GameMode, + GameState, +} from "components/guess-the-guild/guess-the-guild-types" +import useGameLogic from "components/guess-the-guild/useGameLogic" +import React from "react" import { GuildBase } from "types" -export enum GameMode { - GuessNameMode, - AssignLogosMode, -} - -export enum GameState { - Start, - Game, - End, -} - -export type GameModeProps = { - guilds: GuildBase[] - onNext: () => void - onExit: () => void - onCorrect: () => void -} - -export enum Difficulty { - Easy, - Medium, - Hard, -} - const GuessTheGuild = (): JSX.Element => { + const gameLogic = useGameLogic() + const bgColor = useColorModeValue("var(--chakra-colors-gray-800)", "#37373a") const bgOpacity = useColorModeValue(0.06, 0.1) const bgLinearPercentage = useBreakpointValue({ base: "50%", sm: "55%" }) - const [round, setRound] = useState(0) - const [gameMode, setGameMode] = useState(GameMode.GuessNameMode) - const [gameState, setGameState] = useState(GameState.Start) - const [difficulty, setDifficulty] = useState(Difficulty.Easy) - - const [score, setScore] = useState(0) - const [highscore, setHighscore] = useState(0) - const [savedHighscore, setSavedHighscore] = useLocalStorage("highscore", 0) - - useEffect(() => { - setHighscore(savedHighscore) - }, []) - - const selectGameMode = () => { - if (Math.random() >= 0.5) { - setGameMode(GameMode.AssignLogosMode) - } else { - setGameMode(GameMode.GuessNameMode) - } - } - - const startGame = () => { - setGameState(GameState.Game) - selectGameMode() - } - - const nextGame = () => { - selectGameMode() - setRound(round + 1) - } - - const endGame = () => { - setGameState(GameState.End) - } - - const restartGame = () => { - setRound(0) - setGameState(GameState.Start) - } - const mockGuilds: GuildBase[] = [ { id: 1, @@ -126,23 +69,16 @@ const GuessTheGuild = (): JSX.Element => { }, ] - const addPoints = (points: number) => { - let pointsToAdd = points - if (difficulty === Difficulty.Medium) pointsToAdd *= 2 - if (difficulty === Difficulty.Hard) pointsToAdd *= 3 - const updatedPoints = points + pointsToAdd - setScore(updatedPoints) - updateHighscore(updatedPoints) + const handleStart = () => { + gameLogic.transition.startGame() } - const updateHighscore = (updatedPoints: number) => { - if (updatedPoints > highscore) { - setSavedHighscore(updatedPoints) - } + const handleNext = () => { + gameLogic.transition.nextGame() } const handleDifficultyChange = (diff: Difficulty) => { - setDifficulty(diff) + gameLogic.action.setDifficulty(diff) } return ( @@ -171,48 +107,61 @@ const GuessTheGuild = (): JSX.Element => { textColor="white" > - {gameState === GameState.Game && ( - + {/* Show points indicator when the game is on */} + {gameLogic.state.gameState === GameState.Game && ( + )} - {gameState === GameState.Start && ( + {/* Start Screen */} + {gameLogic.state.gameState === GameState.Start && ( startGame()} - highscore={highscore} + onStart={handleStart} + highscore={gameLogic.state.highscore} onDifficultyChange={handleDifficultyChange} - difficulty={difficulty} + difficulty={gameLogic.state.difficulty} /> )} - {gameState === GameState.Game && ( - - {gameMode === GameMode.AssignLogosMode && ( + {/* Game Screen */} + {gameLogic.state.gameState === GameState.Game && ( + + {/* Name Guessing Mode */} + {gameLogic.state.gameMode === GameMode.AssignLogosMode && ( <> nextGame()} - onExit={() => endGame()} - onCorrect={() => addPoints(2)} + onNext={handleNext} + onExit={() => gameLogic.transition.endGame()} + onCorrect={() => gameLogic.action.addPoints(2)} /> )} - {gameMode === GameMode.GuessNameMode && ( + {/* Assign Logo Mode */} + {gameLogic.state.gameMode === GameMode.GuessNameMode && ( <> nextGame()} - onExit={() => endGame()} - onCorrect={() => addPoints(1)} + onNext={handleNext} + onExit={() => gameLogic.transition.endGame()} + onCorrect={() => gameLogic.action.addPoints(1)} /> )} )} - {gameState === GameState.End && ( - + {/* End Screen */} + {gameLogic.state.gameState === GameState.End && ( + )} From f1dcc2b0dc5bcfacbd639572d2b18556e2e01e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ferenczy?= Date: Wed, 6 Dec 2023 21:55:25 +0100 Subject: [PATCH 20/27] feat: fetching guilds from api --- .../guess-the-guild/AssignLogos.tsx | 6 +- src/components/guess-the-guild/GuessName.tsx | 2 +- .../guess-the-guild/useGameLogic.tsx | 2 +- src/pages/guess-the-guild.tsx | 119 +++++++++--------- 4 files changed, 66 insertions(+), 63 deletions(-) diff --git a/src/components/guess-the-guild/AssignLogos.tsx b/src/components/guess-the-guild/AssignLogos.tsx index cd418812..f18acc9b 100644 --- a/src/components/guess-the-guild/AssignLogos.tsx +++ b/src/components/guess-the-guild/AssignLogos.tsx @@ -84,7 +84,11 @@ const AssignLogos = ({ guilds, onNext, onExit, onCorrect }: GameModeProps) => { {movingGuild && !isAnswerSubmitted ? ( - + ) : null} diff --git a/src/components/guess-the-guild/GuessName.tsx b/src/components/guess-the-guild/GuessName.tsx index 57da28f3..bf70bbaa 100644 --- a/src/components/guess-the-guild/GuessName.tsx +++ b/src/components/guess-the-guild/GuessName.tsx @@ -33,7 +33,7 @@ const GuessName = ({ guilds, onNext, onExit, onCorrect }: GameModeProps) => { Guess the guild by the logo! - + {isAnswerSubmitted ? solutionGuild.name : "???"} diff --git a/src/components/guess-the-guild/useGameLogic.tsx b/src/components/guess-the-guild/useGameLogic.tsx index e3142c64..9f2a1f5c 100644 --- a/src/components/guess-the-guild/useGameLogic.tsx +++ b/src/components/guess-the-guild/useGameLogic.tsx @@ -19,7 +19,7 @@ const useGameLogic = () => { let pointsToAdd = points if (difficulty === Difficulty.Medium) pointsToAdd *= 2 if (difficulty === Difficulty.Hard) pointsToAdd *= 3 - const updatedPoints = points + pointsToAdd + const updatedPoints = score + pointsToAdd setScore(updatedPoints) updateHighscore(updatedPoints) } diff --git a/src/pages/guess-the-guild.tsx b/src/pages/guess-the-guild.tsx index 6519edba..a8d5238e 100644 --- a/src/pages/guess-the-guild.tsx +++ b/src/pages/guess-the-guild.tsx @@ -12,8 +12,9 @@ import { GameState, } from "components/guess-the-guild/guess-the-guild-types" import useGameLogic from "components/guess-the-guild/useGameLogic" -import React from "react" -import { GuildBase } from "types" +import useShowErrorToast from "hooks/useShowErrorToast" +import React, { useEffect, useState } from "react" +import useSWR from "swr" const GuessTheGuild = (): JSX.Element => { const gameLogic = useGameLogic() @@ -22,58 +23,48 @@ const GuessTheGuild = (): JSX.Element => { const bgOpacity = useColorModeValue(0.06, 0.1) const bgLinearPercentage = useBreakpointValue({ base: "50%", sm: "55%" }) - const mockGuilds: GuildBase[] = [ - { - id: 1, - name: "Guild1", - urlName: "guild1", - imageUrl: "", - roles: ["role1", "role2"], - platforms: ["DISCORD"], - memberCount: 12, - rolesCount: 2, - tags: ["FEATURED", "VERIFIED"], - }, - { - id: 2, - name: "Guild2", - urlName: "guild1", - imageUrl: "", - roles: ["role1", "role2"], - platforms: ["DISCORD"], - memberCount: 12, - rolesCount: 2, - tags: ["FEATURED", "VERIFIED"], - }, - { - id: 3, - name: "Guild3", - urlName: "guild1", - imageUrl: "", - roles: ["role1", "role2"], - platforms: ["DISCORD"], - memberCount: 12, - rolesCount: 2, - tags: ["FEATURED", "VERIFIED"], - }, - { - id: 4, - name: "Guild4", - urlName: "guild1", - imageUrl: "", - roles: ["role1", "role2"], - platforms: ["DISCORD"], - memberCount: 12, - rolesCount: 2, - tags: ["FEATURED", "VERIFIED"], - }, - ] + const nToFetch = + gameLogic.state.difficulty === Difficulty.Easy + ? 100 + : gameLogic.state.difficulty === Difficulty.Medium + ? 500 + : 1000 + const apiUrl = `/v2/guilds?sort=members&limit=${nToFetch}` + + const [guilds, setGuilds] = useState([]) + const [isLoading, setIsLoading] = useState(true) + + const { data: allGuilds, error } = useSWR(apiUrl, { + revalidateOnFocus: false, + refreshInterval: 86400000, // 24 hours + }) + + const selectGuilds = () => { + if (allGuilds) setGuilds(allGuilds.sort(() => 0.5 - Math.random()).slice(0, 4)) + } + + useEffect(() => { + if (allGuilds) { + setGuilds(allGuilds.sort(() => 0.5 - Math.random()).slice(0, 4)) + setIsLoading(false) + } else { + setIsLoading(true) + } + }, [allGuilds]) + + const showErrorToast = useShowErrorToast() + + useEffect(() => { + if (error) showErrorToast("Failed to load guilds! Please try again later.") + }, [error, showErrorToast]) const handleStart = () => { + selectGuilds() gameLogic.transition.startGame() } const handleNext = () => { + selectGuilds() gameLogic.transition.nextGame() } @@ -132,24 +123,32 @@ const GuessTheGuild = (): JSX.Element => { {/* Name Guessing Mode */} {gameLogic.state.gameMode === GameMode.AssignLogosMode && ( <> - gameLogic.transition.endGame()} - onCorrect={() => gameLogic.action.addPoints(2)} - /> + {!isLoading && ( + <> + gameLogic.transition.endGame()} + onCorrect={() => gameLogic.action.addPoints(2)} + /> + + )} )} {/* Assign Logo Mode */} {gameLogic.state.gameMode === GameMode.GuessNameMode && ( <> - gameLogic.transition.endGame()} - onCorrect={() => gameLogic.action.addPoints(1)} - /> + {!isLoading && ( + <> + gameLogic.transition.endGame()} + onCorrect={() => gameLogic.action.addPoints(1)} + /> + + )} )} From 6d4d89a61f7ffcdb0bb8c30995b940c9211e95f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ferenczy?= Date: Wed, 6 Dec 2023 22:09:43 +0100 Subject: [PATCH 21/27] feat: show skeleton on load --- .../guess-the-guild/SkeletonAssignLogos.tsx | 50 +++++++++++++++++++ .../guess-the-guild/SkeletonGuessName.tsx | 13 +++++ src/pages/guess-the-guild.tsx | 4 ++ 3 files changed, 67 insertions(+) create mode 100644 src/components/guess-the-guild/SkeletonAssignLogos.tsx create mode 100644 src/components/guess-the-guild/SkeletonGuessName.tsx diff --git a/src/components/guess-the-guild/SkeletonAssignLogos.tsx b/src/components/guess-the-guild/SkeletonAssignLogos.tsx new file mode 100644 index 00000000..5576ecae --- /dev/null +++ b/src/components/guess-the-guild/SkeletonAssignLogos.tsx @@ -0,0 +1,50 @@ +import { + Card, + HStack, + SkeletonCircle, + SkeletonText, + VStack, + useColorModeValue, +} from "@chakra-ui/react" + +const SkeletonAssignLogos = () => { + const bgColor = useColorModeValue("gray.100", "whiteAlpha.200") + + return ( + + + + + + + + + + + + + + + + + + + + .{" "} + + + + + + + + + + + + + + ) +} + +export default SkeletonAssignLogos diff --git a/src/components/guess-the-guild/SkeletonGuessName.tsx b/src/components/guess-the-guild/SkeletonGuessName.tsx new file mode 100644 index 00000000..cdea0deb --- /dev/null +++ b/src/components/guess-the-guild/SkeletonGuessName.tsx @@ -0,0 +1,13 @@ +import { Skeleton, SkeletonCircle, VStack } from "@chakra-ui/react" + +const SkeletonGuessName = () => ( + + + + + + + +) + +export default SkeletonGuessName diff --git a/src/pages/guess-the-guild.tsx b/src/pages/guess-the-guild.tsx index a8d5238e..b044f6a9 100644 --- a/src/pages/guess-the-guild.tsx +++ b/src/pages/guess-the-guild.tsx @@ -5,6 +5,8 @@ import AssignLogos from "components/guess-the-guild/AssignLogos" import EndGame from "components/guess-the-guild/EndGame" import GuessName from "components/guess-the-guild/GuessName" import ScoreIndicator from "components/guess-the-guild/ScoreIndicator" +import SkeletonAssignLogos from "components/guess-the-guild/SkeletonAssignLogos" +import SkeletonGuessName from "components/guess-the-guild/SkeletonGuessName" import StartGame from "components/guess-the-guild/StartGame" import { Difficulty, @@ -123,6 +125,7 @@ const GuessTheGuild = (): JSX.Element => { {/* Name Guessing Mode */} {gameLogic.state.gameMode === GameMode.AssignLogosMode && ( <> + {isLoading && } {!isLoading && ( <> { {/* Assign Logo Mode */} {gameLogic.state.gameMode === GameMode.GuessNameMode && ( <> + {isLoading && } {!isLoading && ( <> Date: Wed, 6 Dec 2023 22:13:03 +0100 Subject: [PATCH 22/27] feat: lift up animation on logo dragstart --- src/components/guess-the-guild/AssignLogos.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/components/guess-the-guild/AssignLogos.tsx b/src/components/guess-the-guild/AssignLogos.tsx index f18acc9b..78f25fcc 100644 --- a/src/components/guess-the-guild/AssignLogos.tsx +++ b/src/components/guess-the-guild/AssignLogos.tsx @@ -61,8 +61,19 @@ const AssignLogos = ({ guilds, onNext, onExit, onCorrect }: GameModeProps) => { if (isAnswerCorrect) onCorrect() } + const liftUpAnimation = ` + @keyframes liftUp { + from { + transform: scale(1.0); + } + to { + transform: scale(1.1); + } + }` + return ( <> + { w={avatarSize} h={avatarSize} imageUrl={movingGuild?.imageUrl} + animation={"liftUp 0.3s ease-in-out"} + transform={"scale(1.1)"} + boxShadow={"lg"} /> ) : null} From deaebee65ae8f89aa0f9eeaed985e8ff5ef8ebd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ferenczy?= Date: Wed, 6 Dec 2023 22:31:57 +0100 Subject: [PATCH 23/27] refactor: directory structure --- .../{ => assign-logos}/AssignLogos.tsx | 12 ++++++------ .../assign-logos/{ => components}/Draggable.tsx | 0 .../{ => components}/GuildCardWithDropzone.tsx | 0 .../components}/SkeletonAssignLogos.tsx | 0 .../assign-logos/{ => components}/SourceDropzone.tsx | 0 .../assign-logos/{ => components}/TargetDropzone.tsx | 0 .../assign-logos/{ => hooks}/useDragAndDrop.tsx | 0 .../guess-the-guild/{ => components}/ResultAlert.tsx | 0 .../{ => components}/ScoreIndicator.tsx | 0 .../guess-the-guild/{ => guess-name}/GuessName.tsx | 6 +++--- .../{ => guess-name/components}/AnswerButton.tsx | 0 .../components}/SkeletonGuessName.tsx | 0 .../guess-the-guild/{ => hooks}/useGameLogic.tsx | 2 +- src/pages/guess-the-guild.tsx | 12 ++++++------ 14 files changed, 16 insertions(+), 16 deletions(-) rename src/components/guess-the-guild/{ => assign-logos}/AssignLogos.tsx (92%) rename src/components/guess-the-guild/assign-logos/{ => components}/Draggable.tsx (100%) rename src/components/guess-the-guild/assign-logos/{ => components}/GuildCardWithDropzone.tsx (100%) rename src/components/guess-the-guild/{ => assign-logos/components}/SkeletonAssignLogos.tsx (100%) rename src/components/guess-the-guild/assign-logos/{ => components}/SourceDropzone.tsx (100%) rename src/components/guess-the-guild/assign-logos/{ => components}/TargetDropzone.tsx (100%) rename src/components/guess-the-guild/assign-logos/{ => hooks}/useDragAndDrop.tsx (100%) rename src/components/guess-the-guild/{ => components}/ResultAlert.tsx (100%) rename src/components/guess-the-guild/{ => components}/ScoreIndicator.tsx (100%) rename src/components/guess-the-guild/{ => guess-name}/GuessName.tsx (93%) rename src/components/guess-the-guild/{ => guess-name/components}/AnswerButton.tsx (100%) rename src/components/guess-the-guild/{ => guess-name/components}/SkeletonGuessName.tsx (100%) rename src/components/guess-the-guild/{ => hooks}/useGameLogic.tsx (96%) diff --git a/src/components/guess-the-guild/AssignLogos.tsx b/src/components/guess-the-guild/assign-logos/AssignLogos.tsx similarity index 92% rename from src/components/guess-the-guild/AssignLogos.tsx rename to src/components/guess-the-guild/assign-logos/AssignLogos.tsx index 78f25fcc..df1e81ae 100644 --- a/src/components/guess-the-guild/AssignLogos.tsx +++ b/src/components/guess-the-guild/assign-logos/AssignLogos.tsx @@ -4,12 +4,12 @@ import Button from "components/common/Button" import GuildLogo from "components/common/GuildLogo" import React, { useMemo, useState } from "react" import { GuildBase } from "types" -import ResultAlert from "./ResultAlert" -import Draggable from "./assign-logos/Draggable" -import GuildCardWithDropzone from "./assign-logos/GuildCardWithDropzone" -import SourceDropzone from "./assign-logos/SourceDropzone" -import useDragAndDrop, { START_ZONE_ID } from "./assign-logos/useDragAndDrop" -import { GameModeProps } from "./guess-the-guild-types" +import ResultAlert from "../components/ResultAlert" +import { GameModeProps } from "../guess-the-guild-types" +import Draggable from "./components/Draggable" +import GuildCardWithDropzone from "./components/GuildCardWithDropzone" +import SourceDropzone from "./components/SourceDropzone" +import useDragAndDrop, { START_ZONE_ID } from "./hooks/useDragAndDrop" const AssignLogos = ({ guilds, onNext, onExit, onCorrect }: GameModeProps) => { const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false) diff --git a/src/components/guess-the-guild/assign-logos/Draggable.tsx b/src/components/guess-the-guild/assign-logos/components/Draggable.tsx similarity index 100% rename from src/components/guess-the-guild/assign-logos/Draggable.tsx rename to src/components/guess-the-guild/assign-logos/components/Draggable.tsx diff --git a/src/components/guess-the-guild/assign-logos/GuildCardWithDropzone.tsx b/src/components/guess-the-guild/assign-logos/components/GuildCardWithDropzone.tsx similarity index 100% rename from src/components/guess-the-guild/assign-logos/GuildCardWithDropzone.tsx rename to src/components/guess-the-guild/assign-logos/components/GuildCardWithDropzone.tsx diff --git a/src/components/guess-the-guild/SkeletonAssignLogos.tsx b/src/components/guess-the-guild/assign-logos/components/SkeletonAssignLogos.tsx similarity index 100% rename from src/components/guess-the-guild/SkeletonAssignLogos.tsx rename to src/components/guess-the-guild/assign-logos/components/SkeletonAssignLogos.tsx diff --git a/src/components/guess-the-guild/assign-logos/SourceDropzone.tsx b/src/components/guess-the-guild/assign-logos/components/SourceDropzone.tsx similarity index 100% rename from src/components/guess-the-guild/assign-logos/SourceDropzone.tsx rename to src/components/guess-the-guild/assign-logos/components/SourceDropzone.tsx diff --git a/src/components/guess-the-guild/assign-logos/TargetDropzone.tsx b/src/components/guess-the-guild/assign-logos/components/TargetDropzone.tsx similarity index 100% rename from src/components/guess-the-guild/assign-logos/TargetDropzone.tsx rename to src/components/guess-the-guild/assign-logos/components/TargetDropzone.tsx diff --git a/src/components/guess-the-guild/assign-logos/useDragAndDrop.tsx b/src/components/guess-the-guild/assign-logos/hooks/useDragAndDrop.tsx similarity index 100% rename from src/components/guess-the-guild/assign-logos/useDragAndDrop.tsx rename to src/components/guess-the-guild/assign-logos/hooks/useDragAndDrop.tsx diff --git a/src/components/guess-the-guild/ResultAlert.tsx b/src/components/guess-the-guild/components/ResultAlert.tsx similarity index 100% rename from src/components/guess-the-guild/ResultAlert.tsx rename to src/components/guess-the-guild/components/ResultAlert.tsx diff --git a/src/components/guess-the-guild/ScoreIndicator.tsx b/src/components/guess-the-guild/components/ScoreIndicator.tsx similarity index 100% rename from src/components/guess-the-guild/ScoreIndicator.tsx rename to src/components/guess-the-guild/components/ScoreIndicator.tsx diff --git a/src/components/guess-the-guild/GuessName.tsx b/src/components/guess-the-guild/guess-name/GuessName.tsx similarity index 93% rename from src/components/guess-the-guild/GuessName.tsx rename to src/components/guess-the-guild/guess-name/GuessName.tsx index bf70bbaa..ccd14cfe 100644 --- a/src/components/guess-the-guild/GuessName.tsx +++ b/src/components/guess-the-guild/guess-name/GuessName.tsx @@ -2,9 +2,9 @@ import { Button, Divider, Heading, Text, VStack } from "@chakra-ui/react" import GuildLogo from "components/common/GuildLogo" import { useState } from "react" import { GuildBase } from "types" -import AnswerButton from "./AnswerButton" -import ResultAlert from "./ResultAlert" -import { GameModeProps } from "./guess-the-guild-types" +import ResultAlert from "../components/ResultAlert" +import { GameModeProps } from "../guess-the-guild-types" +import AnswerButton from "./components/AnswerButton" const getRandomGuild = (guilds: GuildBase[]) => guilds[Math.floor(Math.random() * guilds.length)] diff --git a/src/components/guess-the-guild/AnswerButton.tsx b/src/components/guess-the-guild/guess-name/components/AnswerButton.tsx similarity index 100% rename from src/components/guess-the-guild/AnswerButton.tsx rename to src/components/guess-the-guild/guess-name/components/AnswerButton.tsx diff --git a/src/components/guess-the-guild/SkeletonGuessName.tsx b/src/components/guess-the-guild/guess-name/components/SkeletonGuessName.tsx similarity index 100% rename from src/components/guess-the-guild/SkeletonGuessName.tsx rename to src/components/guess-the-guild/guess-name/components/SkeletonGuessName.tsx diff --git a/src/components/guess-the-guild/useGameLogic.tsx b/src/components/guess-the-guild/hooks/useGameLogic.tsx similarity index 96% rename from src/components/guess-the-guild/useGameLogic.tsx rename to src/components/guess-the-guild/hooks/useGameLogic.tsx index 9f2a1f5c..1b9e3554 100644 --- a/src/components/guess-the-guild/useGameLogic.tsx +++ b/src/components/guess-the-guild/hooks/useGameLogic.tsx @@ -1,6 +1,6 @@ import useLocalStorage from "hooks/useLocalStorage" import { useEffect, useState } from "react" -import { Difficulty, GameMode, GameState } from "./guess-the-guild-types" +import { Difficulty, GameMode, GameState } from "../guess-the-guild-types" const useGameLogic = () => { const [savedHighscore, setSavedHighscore] = useLocalStorage("highscore", 0) diff --git a/src/pages/guess-the-guild.tsx b/src/pages/guess-the-guild.tsx index b044f6a9..7f8e2a4f 100644 --- a/src/pages/guess-the-guild.tsx +++ b/src/pages/guess-the-guild.tsx @@ -1,19 +1,19 @@ import { Container, useBreakpointValue, useColorModeValue } from "@chakra-ui/react" import Card from "components/common/Card" import Layout from "components/common/Layout" -import AssignLogos from "components/guess-the-guild/AssignLogos" import EndGame from "components/guess-the-guild/EndGame" -import GuessName from "components/guess-the-guild/GuessName" -import ScoreIndicator from "components/guess-the-guild/ScoreIndicator" -import SkeletonAssignLogos from "components/guess-the-guild/SkeletonAssignLogos" -import SkeletonGuessName from "components/guess-the-guild/SkeletonGuessName" import StartGame from "components/guess-the-guild/StartGame" +import AssignLogos from "components/guess-the-guild/assign-logos/AssignLogos" +import SkeletonAssignLogos from "components/guess-the-guild/assign-logos/components/SkeletonAssignLogos" +import ScoreIndicator from "components/guess-the-guild/components/ScoreIndicator" +import GuessName from "components/guess-the-guild/guess-name/GuessName" +import SkeletonGuessName from "components/guess-the-guild/guess-name/components/SkeletonGuessName" import { Difficulty, GameMode, GameState, } from "components/guess-the-guild/guess-the-guild-types" -import useGameLogic from "components/guess-the-guild/useGameLogic" +import useGameLogic from "components/guess-the-guild/hooks/useGameLogic" import useShowErrorToast from "hooks/useShowErrorToast" import React, { useEffect, useState } from "react" import useSWR from "swr" From 8a62b4c04434af4e838e03d93066bdd73a036653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ferenczy?= Date: Wed, 6 Dec 2023 22:52:45 +0100 Subject: [PATCH 24/27] refactor: moves magic numbers to consts --- src/components/guess-the-guild/StartGame.tsx | 9 ++++---- .../guess-the-guild/hooks/useGameLogic.tsx | 20 ++++++++++++++---- src/pages/guess-the-guild.tsx | 21 +++++++++++-------- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/components/guess-the-guild/StartGame.tsx b/src/components/guess-the-guild/StartGame.tsx index f938dae3..5c03fe41 100644 --- a/src/components/guess-the-guild/StartGame.tsx +++ b/src/components/guess-the-guild/StartGame.tsx @@ -1,6 +1,8 @@ import { Divider, Heading, Text, VStack } from "@chakra-ui/react" import Button from "components/common/Button" +import { DIFFICULTY_GUILD_POOL_SIZE } from "pages/guess-the-guild" import { Difficulty } from "./guess-the-guild-types" +import { DIFFICULTY_MULTIPLIERS } from "./hooks/useGameLogic" type Props = { onStart: () => void @@ -94,11 +96,10 @@ const StartGame = ({ - Selection from the top {difficulty == 0 && "100"} {difficulty == 1 && "500"}{" "} - {difficulty == 2 && "1000"} guilds. + Selection from the top {DIFFICULTY_GUILD_POOL_SIZE[difficulty]} guilds.
- Solving this difficulty awards {difficulty == 0 && "1x"}{" "} - {difficulty == 1 && "2x"} {difficulty == 2 && "3x"} the usual points. + Solving this difficulty awards {DIFFICULTY_MULTIPLIERS[difficulty]}x the + usual points.
diff --git a/src/components/guess-the-guild/assign-logos/components/TargetDropzone.tsx b/src/components/guess-the-guild/assign-logos/components/TargetDropzone.tsx index 216e1e5c..02f11f7a 100644 --- a/src/components/guess-the-guild/assign-logos/components/TargetDropzone.tsx +++ b/src/components/guess-the-guild/assign-logos/components/TargetDropzone.tsx @@ -29,6 +29,7 @@ const TargetDropzone = ({ boxSizing="content-box" transition="0.5s" ref={setNodeRef} + data-test={`dropzone-${id}`} > {children} diff --git a/src/components/guess-the-guild/guess-name/GuessName.tsx b/src/components/guess-the-guild/guess-name/GuessName.tsx index ccd14cfe..0fe5f537 100644 --- a/src/components/guess-the-guild/guess-name/GuessName.tsx +++ b/src/components/guess-the-guild/guess-name/GuessName.tsx @@ -56,6 +56,7 @@ const GuessName = ({ guilds, onNext, onExit, onCorrect }: GameModeProps) => { {!isAnswerSubmitted && ( )} {isAnswerSubmitted && !isAnswerCorrect && ( - )} diff --git a/src/components/guess-the-guild/guess-name/components/AnswerButton.tsx b/src/components/guess-the-guild/guess-name/components/AnswerButton.tsx index e1981819..a4718b15 100644 --- a/src/components/guess-the-guild/guess-name/components/AnswerButton.tsx +++ b/src/components/guess-the-guild/guess-name/components/AnswerButton.tsx @@ -50,6 +50,7 @@ const AnswerButton = ({ variant={variant} isActive={isActive} onClick={() => handleClick(guild.id)} + data-test="answer-button" > {guild.name}