From f2801f2ae7f31058b3bd62ee41e94cae0831e14d Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 8 Nov 2023 09:15:42 +0000 Subject: [PATCH] feat(ssr): server side render parts of the navbar --- src/components/Container.tsx | 2 +- src/components/Navbar.tsx | 162 ----------------- src/components/navbar/FriendsButton.tsx | 66 +++++++ src/components/navbar/Navbar.tsx | 56 ++++++ src/components/navbar/NavbarButton.tsx | 26 +++ src/components/navbar/YouButton.tsx | 29 +++ src/components/player/PlayerInfo.tsx | 169 +----------------- .../player/PlayerInfoExtraLabels.tsx | 42 +++++ .../player/PlayerSettingsButtons.tsx | 137 ++++++++++++++ 9 files changed, 362 insertions(+), 327 deletions(-) delete mode 100644 src/components/Navbar.tsx create mode 100644 src/components/navbar/FriendsButton.tsx create mode 100644 src/components/navbar/Navbar.tsx create mode 100644 src/components/navbar/NavbarButton.tsx create mode 100644 src/components/navbar/YouButton.tsx create mode 100644 src/components/player/PlayerInfoExtraLabels.tsx create mode 100644 src/components/player/PlayerSettingsButtons.tsx diff --git a/src/components/Container.tsx b/src/components/Container.tsx index 14f4730..f4afd33 100644 --- a/src/components/Container.tsx +++ b/src/components/Container.tsx @@ -1,6 +1,6 @@ import Image from "next/image"; import Footer from "./Footer"; -import Navbar from "./Navbar"; +import Navbar from "./navbar/Navbar"; export default function Container({ children }: { children: React.ReactNode }) { return ( diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx deleted file mode 100644 index b71b166..0000000 --- a/src/components/Navbar.tsx +++ /dev/null @@ -1,162 +0,0 @@ -"use client"; - -import { useSettingsStore } from "@/store/settingsStore"; -import useStore from "@/utils/useStore"; -import { - CogIcon, - MagnifyingGlassIcon, - ServerIcon, - UserIcon, -} from "@heroicons/react/20/solid"; -import { GlobeAltIcon, TvIcon } from "@heroicons/react/24/outline"; -import Link from "next/link"; -import Avatar from "./Avatar"; -import { Button } from "./ui/button"; -import { Card } from "./ui/card"; -import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; - -interface ButtonProps { - text: string; - icon?: JSX.Element; - href?: string; - ariaLabel: string; -} - -function NavbarButton({ text, icon, href, ariaLabel }: ButtonProps) { - return ( - - <> - {icon} -

{text}

- -
- ); -} - -function FriendsButton() { - const settingsStore = useStore(useSettingsStore, (state) => state); - - return ( - - - } - /> - - - {settingsStore?.friends.length == 0 ? ( -
-
-

No friends

-

- Add new friends by clicking below -

-
- - - - -
- ) : ( - settingsStore?.friends.map((friend) => { - return ( - -
- -
-

#{friend.rank}

-

{friend.name}

-
-
- - ); - }) - )} -
-
- ); -} - -export default function Navbar() { - const settingsStore = useStore(useSettingsStore, (state) => state); - - return ( - <> - - {settingsStore !== undefined && settingsStore.player && ( - - } - href={`/player/${settingsStore.player.id}/top/1`} - /> - )} - - - {/* TODO: fix hydration error? */} - {/* - - - - Click to view your friends - */} - - } - href="/ranking/global/1" - /> - } - href="/overlay/builder" - /> - } - href="/analytics" - /> - -
- - } - href="/search" - /> - } - href="/settings" - /> - - - ); -} diff --git a/src/components/navbar/FriendsButton.tsx b/src/components/navbar/FriendsButton.tsx new file mode 100644 index 0000000..dc43b48 --- /dev/null +++ b/src/components/navbar/FriendsButton.tsx @@ -0,0 +1,66 @@ +"use client"; + +import { useSettingsStore } from "@/store/settingsStore"; +import useStore from "@/utils/useStore"; +import { UserIcon } from "@heroicons/react/20/solid"; +import Link from "next/link"; +import Avatar from "../Avatar"; +import { Button } from "../ui/button"; +import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover"; +import NavbarButton from "./NavbarButton"; + +export default function FriendsButton() { + const settingsStore = useStore(useSettingsStore, (state) => state); + + return ( + + + } + /> + + + {settingsStore?.friends.length == 0 ? ( +
+
+

No friends

+

+ Add new friends by clicking below +

+
+ + + + +
+ ) : ( + settingsStore?.friends.map((friend) => { + return ( + +
+ +
+

#{friend.rank}

+

{friend.name}

+
+
+ + ); + }) + )} +
+
+ ); +} diff --git a/src/components/navbar/Navbar.tsx b/src/components/navbar/Navbar.tsx new file mode 100644 index 0000000..3de7fb1 --- /dev/null +++ b/src/components/navbar/Navbar.tsx @@ -0,0 +1,56 @@ +import { + CogIcon, + MagnifyingGlassIcon, + ServerIcon, +} from "@heroicons/react/20/solid"; +import { GlobeAltIcon, TvIcon } from "@heroicons/react/24/outline"; +import { Card } from "../ui/card"; +import FriendsButton from "./FriendsButton"; +import NavbarButton from "./NavbarButton"; +import YouButton from "./YouButton"; + +export default function Navbar() { + return ( + <> + + + + + + } + href="/ranking/global/1" + /> + } + href="/overlay/builder" + /> + } + href="/analytics" + /> + +
+ + } + href="/search" + /> + } + href="/settings" + /> + + + ); +} diff --git a/src/components/navbar/NavbarButton.tsx b/src/components/navbar/NavbarButton.tsx new file mode 100644 index 0000000..c896a2c --- /dev/null +++ b/src/components/navbar/NavbarButton.tsx @@ -0,0 +1,26 @@ +interface ButtonProps { + text: string; + icon?: JSX.Element; + href?: string; + ariaLabel: string; +} + +export default function NavbarButton({ + text, + icon, + href, + ariaLabel, +}: ButtonProps) { + return ( + + <> + {icon} +

{text}

+ +
+ ); +} diff --git a/src/components/navbar/YouButton.tsx b/src/components/navbar/YouButton.tsx new file mode 100644 index 0000000..a3110f7 --- /dev/null +++ b/src/components/navbar/YouButton.tsx @@ -0,0 +1,29 @@ +"use client"; + +import { useSettingsStore } from "@/store/settingsStore"; +import { useStore } from "zustand"; +import Avatar from "../Avatar"; +import NavbarButton from "./NavbarButton"; + +export default function YouButton() { + const settingsStore = useStore(useSettingsStore, (state) => state); + + if (!settingsStore || !settingsStore.player) { + return null; + } + + return ( + + } + href={`/player/${settingsStore.player.id}/top/1`} + /> + ); +} diff --git a/src/components/player/PlayerInfo.tsx b/src/components/player/PlayerInfo.tsx index 32cfa69..4d54755 100644 --- a/src/components/player/PlayerInfo.tsx +++ b/src/components/player/PlayerInfo.tsx @@ -1,120 +1,19 @@ -"use client"; - import { ScoresaberPlayer } from "@/schemas/scoresaber/player"; -import { useScoresaberScoresStore } from "@/store/scoresaberScoresStore"; -import { useSettingsStore } from "@/store/settingsStore"; import { formatNumber } from "@/utils/numberUtils"; -import { getAveragePp, getHighestPpPlay } from "@/utils/scoresaber/scores"; import { normalizedRegionName } from "@/utils/utils"; -import { - GlobeAsiaAustraliaIcon, - HomeIcon, - UserIcon, - XMarkIcon, -} from "@heroicons/react/20/solid"; -import dynamic from "next/dynamic"; -import { useEffect, useRef, useState } from "react"; -import { toast } from "react-toastify"; -import { useStore } from "zustand"; +import { GlobeAsiaAustraliaIcon } from "@heroicons/react/20/solid"; import Avatar from "../Avatar"; -import Button from "../Button"; import Card from "../Card"; import CountyFlag from "../CountryFlag"; import Label from "../Label"; - -const PPGainLabel = dynamic(() => import("./PPGainLabel")); +import PlayerInfoExtraLabels from "./PlayerInfoExtraLabels"; +import PlayerSettingsButtons from "./PlayerSettingsButtons"; type PlayerInfoProps = { playerData: ScoresaberPlayer; }; export default function PlayerInfo({ playerData }: PlayerInfoProps) { - const [mounted, setMounted] = useState(false); - const playerId = playerData.id; - const settingsStore = useStore(useSettingsStore, (store) => store); - const playerScoreStore = useStore(useScoresaberScoresStore, (store) => store); - - // Whether we have scores for this player in the local database - const hasLocalScores = playerScoreStore?.exists(playerId); - - const toastId: any = useRef(null); - - useEffect(() => { - setMounted(true); - }, []); - - async function claimProfile() { - settingsStore?.setProfile(playerData); - addProfile(false); - } - - async function addFriend() { - const friend = await settingsStore?.addFriend(playerData.id); - if (!friend) { - toast.error(`Failed to add ${playerData.name} as a friend`); - return; - } - addProfile(true); - } - - async function removeFriend() { - settingsStore?.removeFriend(playerData.id); - - toast.success(`Successfully removed ${playerData.name} as a friend`); - } - - async function addProfile(isFriend: boolean) { - if (!useScoresaberScoresStore.getState().exists(playerId)) { - if (!isFriend) { - toast.success(`Successfully set ${playerData.name} as your profile`); - } else { - toast.success(`Successfully added ${playerData.name} as a friend`); - } - - const reponse = await playerScoreStore?.addOrUpdatePlayer( - playerId, - (page, totalPages) => { - const autoClose = page == totalPages ? 5000 : false; - - if (page == 1) { - toastId.current = toast.info( - `Fetching scores for ${playerData.name} page ${page}/${totalPages}`, - { - autoClose: autoClose, - progress: page / totalPages, - }, - ); - } else { - if (page != totalPages) { - toast.update(toastId.current, { - progress: page / totalPages, - render: `Fetching scores for ${playerData.name} page ${page}/${totalPages}`, - autoClose: autoClose, - }); - } else { - toast.update(toastId.current, { - progress: 0, - render: `Successfully fetched scores for ${playerData.name}`, - autoClose: autoClose, - type: "success", - }); - } - } - - console.log( - `Fetching scores for ${playerId} (${page}/${totalPages})`, - ); - }, - ); - if (reponse?.error) { - toast.error("Failed to fetch scores"); - console.log(reponse.message); - return; - } - } - } - - const isOwnProfile = settingsStore.player?.id == playerId; const scoreStats = playerData.scoreStats; return ( @@ -129,42 +28,7 @@ export default function PlayerInfo({ playerData }: PlayerInfoProps) { {/* Settings Buttons */}
- {mounted && ( - <> - {!isOwnProfile && ( -
@@ -261,30 +125,7 @@ export default function PlayerInfo({ playerData }: PlayerInfoProps) { value={formatNumber(scoreStats.replaysWatched)} /> - {hasLocalScores && ( - <> -
diff --git a/src/components/player/PlayerInfoExtraLabels.tsx b/src/components/player/PlayerInfoExtraLabels.tsx new file mode 100644 index 0000000..f3db9fb --- /dev/null +++ b/src/components/player/PlayerInfoExtraLabels.tsx @@ -0,0 +1,42 @@ +"use client"; + +import { useScoresaberScoresStore } from "@/store/scoresaberScoresStore"; +import { formatNumber } from "@/utils/numberUtils"; +import { getAveragePp, getHighestPpPlay } from "@/utils/scoresaber/scores"; +import { useStore } from "zustand"; +import Label from "../Label"; +import PPGainLabel from "./PPGainLabel"; + +type PlayerInfoExtraLabelsProps = { + playerId: string; +}; + +export default function PlayerInfoExtraLabels({ + playerId, +}: PlayerInfoExtraLabelsProps) { + const playerScoreStore = useStore(useScoresaberScoresStore, (store) => store); + const hasLocalScores = playerScoreStore.exists(playerId); + + if (!hasLocalScores) { + return null; + } + + return ( + <> +