feat: fade-in + skeletons

This commit is contained in:
dvelo 2024-08-18 01:15:27 -05:00
parent d810a48dc8
commit f0584f0dfb
10 changed files with 933 additions and 827 deletions

@ -1,6 +1,6 @@
{ {
"name": "mh-stats", "name": "mh-stats",
"version": "0.10.0", "version": "0.10.2",
"private": true, "private": true,
"packageManager": "yarn@1.22.22", "packageManager": "yarn@1.22.22",
"scripts": { "scripts": {

@ -20,6 +20,7 @@ import { Inter as interFont } from "next/font/google";
import { CommandBarer } from "@/components/CommandBar"; import { CommandBarer } from "@/components/CommandBar";
import ThemedToaster from "@/components/misc/ThemedToaster"; import ThemedToaster from "@/components/misc/ThemedToaster";
import UnofficalDialog from "@/components/misc/UnofficalDialog"; import UnofficalDialog from "@/components/misc/UnofficalDialog";
import ClientFadeIn from "@/components/ClientFadeIn";
const inter = interFont({ variable: "--font-inter", subsets: ["latin"] }); const inter = interFont({ variable: "--font-inter", subsets: ["latin"] });
export default async function RootLayout({ export default async function RootLayout({
@ -63,7 +64,7 @@ export default async function RootLayout({
</div> </div>
<div> <div>
<NextTopLoader /> <NextTopLoader />
{children} <ClientFadeIn>{children}</ClientFadeIn>
</div>{" "} </div>{" "}
<ThemedToaster /> <ThemedToaster />
<CommandBarer /> <CommandBarer />

@ -4,11 +4,13 @@ import { useEffect, useState } from "react";
import { Card, CardDescription, CardHeader, CardTitle } from "./ui/card"; import { Card, CardDescription, CardHeader, CardTitle } from "./ui/card";
import Markdown from "react-markdown"; import Markdown from "react-markdown";
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
import FadeIn from "react-fade-in/lib/FadeIn";
export default function AfterServerView({ server }: { server: string }) { export default function AfterServerView({ server }: { server: string }) {
const [description, setDescription] = useState(""); const [description, setDescription] = useState("");
const [discord, setDiscord] = useState(""); const [discord, setDiscord] = useState("");
const { resolvedTheme } = useTheme(); const { resolvedTheme } = useTheme();
const [loading, setLoading] = useState(true);
useEffect(() => { useEffect(() => {
getCustomization(server).then((b) => { getCustomization(server).then((b) => {
@ -16,10 +18,14 @@ export default function AfterServerView({ server }: { server: string }) {
setDescription(b.description == null ? "" : b.description); setDescription(b.description == null ? "" : b.description);
setDiscord(b.discord == null ? "" : b.discord); setDiscord(b.discord == null ? "" : b.discord);
} }
setLoading(false);
}); });
}, []); }, []);
if (loading) return <></>;
return ( return (
<>
<FadeIn>
<div className="grid sm:grid-cols-4 pl-4 pr-4 gap-3.5"> <div className="grid sm:grid-cols-4 pl-4 pr-4 gap-3.5">
{description != "" && ( {description != "" && (
<Card className="sm:col-span-3"> <Card className="sm:col-span-3">
@ -47,5 +53,7 @@ export default function AfterServerView({ server }: { server: string }) {
<br /> <br />
</div> </div>
</FadeIn>
</>
); );
} }

@ -0,0 +1,12 @@
"use client";
import FadeIn from "react-fade-in";
export default function ClientFadeIn({
children,
delay = 0,
}: {
children: React.ReactNode;
delay?: number;
}) {
return <FadeIn delay={delay}>{children}</FadeIn>;
}

@ -18,6 +18,8 @@ import { Separator } from "./ui/separator";
import { Card, CardDescription, CardHeader, CardTitle } from "./ui/card"; import { Card, CardDescription, CardHeader, CardTitle } from "./ui/card";
import Link from "next/link"; import Link from "next/link";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { Skeleton } from "./ui/skeleton";
import FadeIn from "react-fade-in/lib/FadeIn";
export default function FavoriteSortView() { export default function FavoriteSortView() {
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@ -55,7 +57,23 @@ export default function FavoriteSortView() {
}, []); }, []);
if (loading) { if (loading) {
return <Spinner className="flex items-center" />; return (
<>
<div className="grid grid-cols-2 gap-4">
<Skeleton className="h-[112px] rounded-xl" />
<Skeleton className="h-[112px] rounded-xl" />
</div>
<br />
<Separator />
<br />
<div className="grid grid-cols-4 gap-4">
<Skeleton className="h-[450px] rounded-xl" />
<Skeleton className="h-[450px] rounded-xl" />
<Skeleton className="h-[450px] rounded-xl" />
<Skeleton className="h-[450px] rounded-xl" />
</div>
</>
);
} }
return ( return (
@ -200,6 +218,7 @@ export default function FavoriteSortView() {
} }
style={{ overflow: "hidden", paddingLeft: 6 }} style={{ overflow: "hidden", paddingLeft: 6 }}
> >
<FadeIn>
<div className="grid sm:grid-cols-4 gap-4"> <div className="grid sm:grid-cols-4 gap-4">
{allItems.map((v) => { {allItems.map((v) => {
if (v.favorites == 0) { if (v.favorites == 0) {
@ -250,8 +269,8 @@ export default function FavoriteSortView() {
</Link> </Link>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
Open up the server page to see more information about Open up the server page to see more information
the server about the server
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</CardDescription> </CardDescription>
@ -260,6 +279,7 @@ export default function FavoriteSortView() {
); );
})} })}
</div> </div>
</FadeIn>
</InfiniteScroll> </InfiniteScroll>
</div> </div>
); );

@ -10,6 +10,8 @@ import toast from "react-hot-toast";
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip"; import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
import { getAccountFavorites } from "@/lib/api"; import { getAccountFavorites } from "@/lib/api";
import { useRouter } from "@/lib/useRouter"; import { useRouter } from "@/lib/useRouter";
import { Skeleton } from "./ui/skeleton";
import FadeIn from "react-fade-in/lib/FadeIn";
export default function FavoritesView() { export default function FavoritesView() {
const [apiFavorites, setApiFavorites] = useState<any>([]); const [apiFavorites, setApiFavorites] = useState<any>([]);
@ -40,8 +42,20 @@ export default function FavoritesView() {
if (loading) { if (loading) {
return ( return (
<> <>
<Spinner className="flex items-center" /> <div className="grid grid-cols-4 gap-4">
<br /> <Skeleton className="h-[147px] rounded-xl" />
<Skeleton className="h-[147px] rounded-xl" />
<Skeleton className="h-[147px] rounded-xl" />
<Skeleton className="h-[147px] rounded-xl" />
<Skeleton className="h-[147px] rounded-xl" />
<Skeleton className="h-[147px] rounded-xl" />
<Skeleton className="h-[147px] rounded-xl" />
<Skeleton className="h-[147px] rounded-xl" />
<Skeleton className="h-[147px] rounded-xl" />
<Skeleton className="h-[147px] rounded-xl" />
<Skeleton className="h-[147px] rounded-xl" />
<Skeleton className="h-[147px] rounded-xl" />
</div>
</> </>
); );
} }
@ -54,6 +68,7 @@ export default function FavoritesView() {
Your favorites are empty. Maybe favorite a server! Your favorites are empty. Maybe favorite a server!
</div> </div>
)} )}
<FadeIn>
<div className="grid sm:grid-cols-4 gap-4"> <div className="grid sm:grid-cols-4 gap-4">
{apiFavorites.map((server: ServerResponse) => ( {apiFavorites.map((server: ServerResponse) => (
<Card key={server.name}> <Card key={server.name}>
@ -101,6 +116,7 @@ export default function FavoritesView() {
</Card> </Card>
))} ))}
</div> </div>
</FadeIn>
</> </>
); );
} }

@ -19,6 +19,8 @@ import {
import { useEffectOnce } from "@/lib/useEffectOnce"; import { useEffectOnce } from "@/lib/useEffectOnce";
import { ServerResponse } from "@/lib/types/mh-server"; import { ServerResponse } from "@/lib/types/mh-server";
import { getCommunityServerFavorites, getShortTermData } from "@/lib/api"; import { getCommunityServerFavorites, getShortTermData } from "@/lib/api";
import { Skeleton } from "./ui/skeleton";
import FadeIn from "react-fade-in/lib/FadeIn";
const chartConfig = { const chartConfig = {
player_count: { player_count: {
@ -37,6 +39,7 @@ export function NewChart({ server }: { server: string }) {
const [chartData, setChartData] = React.useState<any>([]); const [chartData, setChartData] = React.useState<any>([]);
const [joins, setJoins] = React.useState<any>(0); const [joins, setJoins] = React.useState<any>(0);
const [loading, setLoading] = React.useState(true);
const [favorites, setFavorites] = React.useState<any>(0); const [favorites, setFavorites] = React.useState<any>(0);
const allNums = { player_count: joins, favorites }; const allNums = { player_count: joins, favorites };
@ -52,11 +55,20 @@ export function NewChart({ server }: { server: string }) {
}); });
} }
); );
setLoading(false);
} }
); );
}); });
if (loading)
return ( return (
<>
<Skeleton className="w-full h-[437px]" />
</>
);
return (
<FadeIn>
<Card className="w-full"> <Card className="w-full">
<CardHeader className="flex flex-col items-stretch space-y-0 border-b p-0 sm:flex-row"> <CardHeader className="flex flex-col items-stretch space-y-0 border-b p-0 sm:flex-row">
<div className="flex flex-1 flex-col justify-center gap-1 px-6 py-5 sm:py-6"> <div className="flex flex-1 flex-col justify-center gap-1 px-6 py-5 sm:py-6">
@ -145,6 +157,7 @@ export function NewChart({ server }: { server: string }) {
</ChartContainer> </ChartContainer>
</CardContent> </CardContent>
</Card> </Card>
</FadeIn>
); );
} }

@ -46,6 +46,8 @@ import {
MenubarSubTrigger, MenubarSubTrigger,
MenubarTrigger, MenubarTrigger,
} from "@/components/ui/menubar"; } from "@/components/ui/menubar";
import ClientFadeIn from "./ClientFadeIn";
import { Skeleton } from "./ui/skeleton";
export default function ServerList() { export default function ServerList() {
const [loading, setLoading]: any = useState(true); const [loading, setLoading]: any = useState(true);
@ -119,18 +121,27 @@ export default function ServerList() {
if (loading) { if (loading) {
return ( return (
<> <>
<Spinner className="flex items-center" /> <div className="grid grid-cols-3 gap-4 max-lg:grid-cols-2">
<Skeleton className="h-[112px] rounded-xl" />
<Skeleton className="h-[112px] rounded-xl" />
<Skeleton className="h-[112px] rounded-xl" />
</div>
<br /> <br />
<div <Separator />
className="flex justify-center" <br />
dangerouslySetInnerHTML={{ __html: randomText }} <div className="grid grid-cols-4 gap-4">
></div> <Skeleton className="h-[450px] rounded-xl" />
<Skeleton className="h-[450px] rounded-xl" />
<Skeleton className="h-[450px] rounded-xl" />
<Skeleton className="h-[450px] rounded-xl" />
</div>
</> </>
); );
} }
return ( return (
<> <>
<ClientFadeIn>
<div className="max-lg:grid-cols-2 grid grid-cols-3 gap-4 "> <div className="max-lg:grid-cols-2 grid grid-cols-3 gap-4 ">
<Stat <Stat
title="Players online" title="Players online"
@ -170,9 +181,9 @@ export default function ServerList() {
The server amount is over 3.2k, meaning that new servers The server amount is over 3.2k, meaning that new servers
have to go into a queue before being able to be online.{" "} have to go into a queue before being able to be online.{" "}
<br /> <br />
(the server count isn't entirely accurate, so sometimes you (the server count isn't entirely accurate, so sometimes
might not go into a queue even when the server count is at you might not go into a queue even when the server count
3.2k) is at 3.2k)
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
)} )}
@ -206,8 +217,10 @@ export default function ServerList() {
icon={Sun} icon={Sun}
/> />
</div> </div>
</ClientFadeIn>
<br /> <br />
<Separator /> <Separator />
<ClientFadeIn delay={100}>
<Menubar className="mt-3 ml-2 border rounded p-2"> <Menubar className="mt-3 ml-2 border rounded p-2">
<MenubarMenu> <MenubarMenu>
<MenubarTrigger>Servers</MenubarTrigger> <MenubarTrigger>Servers</MenubarTrigger>
@ -557,9 +570,15 @@ export default function ServerList() {
<MenubarSubTrigger>Grid</MenubarSubTrigger> <MenubarSubTrigger>Grid</MenubarSubTrigger>
<MenubarSubContent> <MenubarSubContent>
<MenubarRadioGroup value={ipr} onValueChange={setIPR}> <MenubarRadioGroup value={ipr} onValueChange={setIPR}>
<MenubarRadioItem value="4">4 items per row</MenubarRadioItem> <MenubarRadioItem value="4">
<MenubarRadioItem value="5">5 items per row</MenubarRadioItem> 4 items per row
<MenubarRadioItem value="6">6 items per row</MenubarRadioItem> </MenubarRadioItem>
<MenubarRadioItem value="5">
5 items per row
</MenubarRadioItem>
<MenubarRadioItem value="6">
6 items per row
</MenubarRadioItem>
</MenubarRadioGroup> </MenubarRadioGroup>
</MenubarSubContent> </MenubarSubContent>
</MenubarSub> </MenubarSub>
@ -584,6 +603,7 @@ export default function ServerList() {
</MenubarContent> </MenubarContent>
</MenubarMenu> </MenubarMenu>
</Menubar> </Menubar>
</ClientFadeIn>
<Dialog open={random} onOpenChange={setRandom}> <Dialog open={random} onOpenChange={setRandom}>
<DialogContent> <DialogContent>
@ -695,6 +715,7 @@ export default function ServerList() {
} }
style={{ overflow: "hidden !important", paddingLeft: 6 }} style={{ overflow: "hidden !important", paddingLeft: 6 }}
> >
<ClientFadeIn delay={200}>
<div className={" grid " + "grid-cols-" + ipr + " gap-4"}> <div className={" grid " + "grid-cols-" + ipr + " gap-4"}>
{servers.map((b: any) => ( {servers.map((b: any) => (
<> <>
@ -702,6 +723,7 @@ export default function ServerList() {
</> </>
))} ))}
</div> </div>
</ClientFadeIn>
</InfiniteScroll> </InfiniteScroll>
</> </>
); );

@ -24,6 +24,8 @@ import { Star, X } from "lucide-react";
import { favoriteServer, isFavorited } from "@/lib/api"; import { favoriteServer, isFavorited } from "@/lib/api";
import { LoadingButton } from "./ui/loading-button"; import { LoadingButton } from "./ui/loading-button";
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
import { Skeleton } from "./ui/skeleton";
import FadeIn from "react-fade-in/lib/FadeIn";
export default function ServerView(props: { server: string }) { export default function ServerView(props: { server: string }) {
const [single, setSingle] = useState(new ServerSingle(props.server)); const [single, setSingle] = useState(new ServerSingle(props.server));
@ -66,12 +68,11 @@ export default function ServerView(props: { server: string }) {
if (loading) { if (loading) {
return ( return (
<> <>
<Spinner className="flex items-center" /> <div className="grid p-4 sm:grid-cols-3 gap-4">
<br /> <Skeleton className="sm:col-span-2 h-[245px]" />
<div
className="flex justify-center" <Skeleton className="h-[245px]" />
dangerouslySetInnerHTML={{ __html: randomText }} </div>
></div>
</> </>
); );
} }
@ -94,7 +95,7 @@ export default function ServerView(props: { server: string }) {
</div> </div>
</div> </div>
)} )}
<FadeIn>
<div className="grid p-4 sm:grid-cols-3 gap-4"> <div className="grid p-4 sm:grid-cols-3 gap-4">
<Card className="sm:col-span-2"> <Card className="sm:col-span-2">
<BetterHeader> <BetterHeader>
@ -159,7 +160,8 @@ export default function ServerView(props: { server: string }) {
</code> </code>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
<code>{single.grabOffline()?.last_online}</code> in Unix time <code>{single.grabOffline()?.last_online}</code> in Unix
time
</TooltipContent> </TooltipContent>
</Tooltip>{" "} </Tooltip>{" "}
<br /> <br />
@ -239,6 +241,7 @@ export default function ServerView(props: { server: string }) {
</CardFooter> </CardFooter>
</Card> </Card>
</div> </div>
</FadeIn>
</> </>
); );
} }

@ -1,7 +1,7 @@
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
export const version = "b-0.10.0"; export const version = "b-0.10.2";
const User = ({ user }: { user: string }) => ( const User = ({ user }: { user: string }) => (
<span className="cursor-pointer bg-[rgba(255,165,0,0.25);] rounded p-[2.5px]"> <span className="cursor-pointer bg-[rgba(255,165,0,0.25);] rounded p-[2.5px]">
@ -44,6 +44,17 @@ export const Changelog = () => (
</code> </code>
</div> </div>
<br /> <br />
<div>
<strong className="flex items-center">
Version b-0.10.2 (August 18th 2024)
</strong>
<ul>
<li> Content fades-in on load</li>
<li> Instead of using spinners, now we are using Skeletons</li>
</ul>
</div>
<br />
<br />
<div> <div>
<strong className="flex items-center"> <strong className="flex items-center">
Version b-0.10.0 (August 17th 2024) Version b-0.10.0 (August 17th 2024)