diff --git a/package.json b/package.json index 1c09685..1086e63 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "react-dom": "^18", "react-fade-in": "^2.0.1", "react-fast-marquee": "^1.6.5", + "react-qr-code": "^2.0.15", "rehype-slug": "^6.0.0", "remark-gfm": "^4.0.0", "tailwind-merge": "^2.3.0", diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx index 9ac1063..7a72fec 100644 --- a/src/app/(main)/layout.tsx +++ b/src/app/(main)/layout.tsx @@ -43,9 +43,9 @@ import NewDomainDialog from "@/components/misc/NewDomainDialog"; import ThemedToaster from "@/components/misc/ThemedToaster"; import UnofficalDialog from "@/components/misc/UnofficalDialog"; import { - Breadcrumb, - BreadcrumbList, - BreadcrumbPage, + Breadcrumb, + BreadcrumbList, + BreadcrumbPage, } from "@/components/ui/breadcrumb"; import { TooltipProvider } from "@/components/ui/tooltip"; import { banner } from "@/config/banner"; @@ -55,79 +55,74 @@ import { Inter as interFont } from "next/font/google"; import Link from "next/link"; export const extraMetadata = { - twitter: { - images: [ - { - url: "/imgs/icon-cf.png", - }, - ], - }, - themeColor: "#000000", - openGraph: { - images: [ - { - url: "/imgs/icon-cf.png", - }, - ], - }, + twitter: { + images: [ + { + url: "/imgs/icon-cf.png", + }, + ], + }, + themeColor: "#000000", + openGraph: { + images: [ + { + url: "/imgs/icon-cf.png", + }, + ], + }, } satisfies Metadata; export const viewport: Viewport = { - themeColor: "black", - colorScheme: "dark", + themeColor: "black", + colorScheme: "dark", }; const inter = interFont({ variable: "--font-inter", subsets: ["latin"] }); export default async function RootLayout({ - children, + children, }: Readonly<{ - children: React.ReactNode; + children: React.ReactNode; }>) { - return ( - - - - {banner.isBanner && ( -
- {banner.bannerText} -
- )} -
-
- - - - - - - - - - -
- -
-
- - {children} -
{" "} - - - - - - -
-
-
- ); + return ( + + + + {banner.isBanner && ( +
+ {banner.bannerText} +
+ )} +
+
+ + + + + + + + + + +
+ +
+
+ + {children} +
{" "} + + + + + + +
+
+
+ ); } diff --git a/src/app/(main)/server/[server]/page.tsx b/src/app/(main)/server/[server]/page.tsx index 6921cf3..b79e3e2 100644 --- a/src/app/(main)/server/[server]/page.tsx +++ b/src/app/(main)/server/[server]/page.tsx @@ -130,7 +130,7 @@ export default function ServerPage({ params }: { params: { server: string } }) { return (
-
+
diff --git a/src/app/(main)/server/[server]/statistics/page.tsx b/src/app/(main)/server/[server]/statistics/page.tsx index c242b98..f7090a3 100644 --- a/src/app/(main)/server/[server]/statistics/page.tsx +++ b/src/app/(main)/server/[server]/statistics/page.tsx @@ -37,80 +37,80 @@ import { Separator } from "@/components/ui/separator"; import type { Metadata, ResolvingMetadata } from "next"; type Props = { - params: { server: string }; + params: { server: string }; }; export async function generateMetadata( - { params }: Props, - parent: ResolvingMetadata, + { params }: Props, + parent: ResolvingMetadata ): Promise { - // read route params - const { server } = params; - const json = await ( - await fetch("https://api.minehut.com/server/" + server + "?byName=true") - ).json(); + // read route params + const { server } = params; + const json = await ( + await fetch("https://api.minehut.com/server/" + server + "?byName=true") + ).json(); - return { - title: - json.server == null - ? "Server doesn't exist | MHSF" - : json.server.name + - ", " + - (json.server.online - ? json.server.playerCount + - (json.server.maxPlayers != 10 - ? "/" + json.server.maxPlayers - : "") + - " online" - : "Offline") + - " | MHSF", - description: - json.server == null - ? `The server ${server} doesn't exist.` - : `View ${server} on Minehut Server Finder!`, - authors: json.server == null ? undefined : { name: json.server.owner }, - applicationName: "MHSF (Minehut Server Finder)", - icons: - json.server == null - ? undefined - : "https://mcapi.marveldc.me/item/" + - (json.server.icon == undefined ? "OAK_SIGN" : json.server.icon) + - "?width=64&height=64", - openGraph: { - type: "profile", - siteName: "MHSF (Minehut Server Finder)", - images: [ - { - url: - "https://mcapi.marveldc.me/item/" + - json.server.icon + - "?width=64&height=64", - }, - { - url: "/favicon.ico", - }, - ], - }, - }; + return { + title: + json.server == null + ? "Server doesn't exist | MHSF" + : json.server.name + + ", " + + (json.server.online + ? json.server.playerCount + + (json.server.maxPlayers != 10 + ? "/" + json.server.maxPlayers + : "") + + " online" + : "Offline") + + " | MHSF", + description: + json.server == null + ? `The server ${server} doesn't exist.` + : `View ${server} on Minehut Server Finder!`, + authors: json.server == null ? undefined : { name: json.server.owner }, + applicationName: "MHSF (Minehut Server Finder)", + icons: + json.server == null + ? undefined + : "https://mcapi.marveldc.me/item/" + + (json.server.icon == undefined ? "OAK_SIGN" : json.server.icon) + + "?width=64&height=64", + openGraph: { + type: "profile", + siteName: "MHSF (Minehut Server Finder)", + images: [ + { + url: + "https://mcapi.marveldc.me/item/" + + json.server.icon + + "?width=64&height=64", + }, + { + url: "/favicon.ico", + }, + ], + }, + }; } export default function ServerPage({ params }: { params: { server: string } }) { - return ( -
- -
- - -
- - -
-
- -
-
-
-
-
- ); + return ( +
+ +
+ + +
+ + +
+
+ +
+
+
+
+
+ ); } diff --git a/src/app/globals.css b/src/app/globals.css index c023a5d..cf1c4bb 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -147,6 +147,37 @@ /* }*/ /*}*/ + +@layer base { + ::view-transition-old(root), + ::view-transition-new(root) { + animation: none; + mix-blend-mode: normal; + } + + ::view-transition-old(root) { + z-index: 1; + } + + ::view-transition-new(root) { + z-index: 2; + } + + @keyframes clip-down { + from { + clip-path: inset(0 0 100% 0); + } + to { + clip-path: inset(0 0 0 0); + } + } + + .dark::view-transition-new(root), + .light::view-transition-new(root) { + animation: 0.7s clip-down; + } +} + .backdrop-blur { -webkit-backdrop-filter: blur(8px) !important; backdrop-filter: blur(8px) !important; diff --git a/src/components/AfterServerView.tsx b/src/components/AfterServerView.tsx index 1989357..940a608 100644 --- a/src/components/AfterServerView.tsx +++ b/src/components/AfterServerView.tsx @@ -37,7 +37,7 @@ import { getIndexFromRarity, getMinehutIcons, } from "@/lib/types/server-icon"; -import { Copy, ExternalLink, Info } from "lucide-react"; +import { Copy, Info, QrCode, Share2 } from "lucide-react"; import { useTheme } from "next-themes"; import { useEffect, useState } from "react"; import FadeIn from "react-fade-in/lib/FadeIn"; @@ -56,6 +56,9 @@ import { import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip"; import { Drawer, DrawerContent, DrawerHeader, DrawerTitle } from "./ui/drawer"; import EmbedSelector from "./feat/EmbedSelector"; +import { Separator } from "./ui/separator"; +import QRCodeGenerator from "./feat/QRCodeGen"; +import NoItems from "./misc/NoItems"; export default function AfterServerView({ server }: { server: string }) { const [description, setDescription] = useState(""); @@ -64,6 +67,7 @@ export default function AfterServerView({ server }: { server: string }) { const [icons, setIcons] = useState(); const { resolvedTheme } = useTheme(); const [loading, setLoading] = useState(true); + const [qrCodeOpen, setQrCodeOpen] = useState(false); const [view, setView] = useState( description !== "" || discord !== "" ? "desc" : "extra" ); @@ -104,6 +108,14 @@ export default function AfterServerView({ server }: { server: string }) { + + + + QR Code generator + + + +
@@ -135,9 +147,10 @@ export default function AfterServerView({ server }: { server: string }) { > Purchased Icons +
@@ -168,9 +181,16 @@ export default function AfterServerView({ server }: { server: string }) { > Purchased Icons +
+ +
+
@@ -450,6 +470,7 @@ export default function AfterServerView({ server }: { server: string }) { ownership, they may or may not available at that certain moment either.

+ {serverObject?.purchased_icons.length == 0 && } {serverObject?.purchased_icons.map((icon) => ( - - - -
-
- -
-
- ); + const [color, setColor] = useState(""); + return ( +
+ + +
+
+ +
+
+ ); } diff --git a/src/components/NewChart.tsx b/src/components/NewChart.tsx index be22d17..dcd75b9 100644 --- a/src/components/NewChart.tsx +++ b/src/components/NewChart.tsx @@ -196,7 +196,7 @@ export function NewChart({ server }: { server: string }) { ); } -function convert(value: number) { +export function convert(value: number) { var result: string = value.toString(); if (value >= 1000000) { result = Math.floor(value / 1000000) + "m"; diff --git a/src/components/ServerView.tsx b/src/components/ServerView.tsx index 98abd39..7a96c23 100644 --- a/src/components/ServerView.tsx +++ b/src/components/ServerView.tsx @@ -31,15 +31,11 @@ "use client"; import { useState, useEffect } from "react"; import { - Card, CardContent, CardDescription, - CardFooter, - CardHeader, CardTitle, BetterHeader, } from "@/components/ui/card"; -import { motion } from "framer-motion"; import { Tooltip, TooltipContent, @@ -47,14 +43,22 @@ import { } from "@/components/ui/tooltip"; import { Badge } from "./ui/badge"; import ServerSingle from "@/lib/single"; -import { SignedIn, SignedOut } from "@clerk/nextjs"; -import SignInPopoverButton from "./clerk/SignInPopoverButton"; -import { Star, X } from "lucide-react"; -import { favoriteServer, isFavorited } from "@/lib/api"; -import { LoadingButton } from "./ui/loading-button"; +import { motion } from "framer-motion"; +import { Cake, Check, Heart, Star, Users, X } from "lucide-react"; +import { + favoriteServer, + getCommunityServerFavorites, + isFavorited, +} from "@/lib/api"; import { useTheme } from "next-themes"; import { Skeleton } from "./ui/skeleton"; import FadeIn from "react-fade-in/lib/FadeIn"; +import { Button } from "./ui/button"; +import IconDisplay from "./IconDisplay"; +import { useClerk, useUser } from "@clerk/nextjs"; +import { LoaderIcon } from "react-hot-toast"; +import { Separator } from "./ui/separator"; +import { convert } from "@/components/NewChart"; export default function ServerView(props: { server: string }) { const [single, setSingle] = useState(new ServerSingle(props.server)); @@ -64,6 +68,9 @@ export default function ServerView(props: { server: string }) { const [loadingFavorite, setLoadingFavorite] = useState(false); const [randomText, setRandomText] = useState(""); const [lastOnline, setLastOnline] = useState(0); + const { isSignedIn } = useUser(); + const [communityFavorited, setCommunityFavorited] = useState(0); + const clerk = useClerk(); const [format, setFormat] = useState(""); const [description, setDescription] = useState(""); const allText = [""]; @@ -91,16 +98,19 @@ export default function ServerView(props: { server: string }) { setLastOnline(online); } }); + getCommunityServerFavorites(single.grabOffline()?.name as string).then( + (b) => { + setCommunityFavorited(b); + } + ); }); }, []); if (loading) { return ( <> -
- - - +
+
); @@ -125,8 +135,14 @@ export default function ServerView(props: { server: string }) {
)} -
- +
+
+ +
+
{single.grabOnline() == undefined && @@ -173,103 +189,114 @@ export default function ServerView(props: { server: string }) { )} - {single.getAuthor() != undefined && ( -

by {single.getAuthor()}

+ {single.getAuthor() != undefined ? ( +

+ by {single.getAuthor()}{" "} + +

+ ) : ( +

+ by Anonymous{" "} + +

)}
-

- Time: -
- Last online{" "} - - - - {timeConverter(single.grabOffline()?.last_online)} - - - - {single.grabOffline()?.last_online} in Unix - time - - {" "} -
- Created on{" "} - - - {timeConverter(single.grabOffline()?.creation)} - - - {single.grabOffline()?.creation} in Unix time - - +

+ <> + + {convert(communityFavorited)} + + + <> + {" "} + {convert(single.grabOffline()?.joins as number)} + + + <> + {" "} + {timeConverter(single.grabOffline()?.creation)} +

- - - - Favorite the server? - - By favoriting the server, you can see it later.{" "} - - You need to sign in to favorite a server. - - - - - - - - - { - setLoadingFavorite(true); - favoriteServer(single.grabOffline()?.name as string).then( - () => { - setFavorited(!favorited); - setLoadingFavorite(false); - } - ); - }} - > - {favorited && ( - - - - )} - {!favorited && ( - - - - )} - {favorited && "Unf"} - {!favorited && "F"}avorite Server - - - - - - This is unlike voting. The{" "} - amount of people who favorited are public, but the server - doesn{"'"}t know who favorited, as Favorites are completely - anonymous. - - - +
diff --git a/src/components/ThemeProvider.tsx b/src/components/ThemeProvider.tsx index c8ee787..96026f2 100644 --- a/src/components/ThemeProvider.tsx +++ b/src/components/ThemeProvider.tsx @@ -31,7 +31,7 @@ "use client"; import * as React from "react"; -import { ThemeProvider as NextThemesProvider } from "next-themes"; +import { ThemeProvider as NextThemesProvider, useTheme } from "next-themes"; import { type ThemeProviderProps } from "next-themes/dist/types"; export function ThemeProvider({ children, ...props }: ThemeProviderProps) { @@ -45,3 +45,57 @@ export function ThemeProvider({ children, ...props }: ThemeProviderProps) { return {children}; } + +interface UseThemeTransitionResult { + theme: string | undefined; + changeTheme: (changeTheme: string) => () => void; + mounted: boolean; +} + +export function useThemeTransition(): UseThemeTransitionResult { + const { theme, setTheme, systemTheme } = useTheme(); + const [mounted, setMounted] = React.useState(false); + + React.useEffect(() => { + setMounted(true); + }, []); + + const changeTheme = (changeTheme: string) => { + if (!mounted) return; + + const resolvedTheme = theme === "system" ? systemTheme : changeTheme; + + if (document.startViewTransition) { + document.startViewTransition(() => { + const root = document.documentElement; + root.style.setProperty( + "--current-background", + `var(--${resolvedTheme}-background)` + ); + root.style.setProperty( + "--current-foreground", + `var(--${resolvedTheme}-foreground)` + ); + setTheme(changeTheme); + }); + } else { + setTheme(changeTheme); + } + }; + + React.useEffect(() => { + if (mounted && theme) { + const root = document.documentElement; + root.style.setProperty( + "--current-background", + `var(--${theme}-background)` + ); + root.style.setProperty( + "--current-foreground", + `var(--${theme}-foreground)` + ); + } + }, [mounted, theme]); + + return { theme, changeTheme, mounted }; +} diff --git a/src/components/ThemeSwitcher.tsx b/src/components/ThemeSwitcher.tsx index ea06c01..e2758ed 100644 --- a/src/components/ThemeSwitcher.tsx +++ b/src/components/ThemeSwitcher.tsx @@ -29,10 +29,7 @@ */ "use client"; - -import * as React from "react"; import { Moon, Sun } from "lucide-react"; -import { useTheme } from "next-themes"; import { Button } from "@/components/ui/button"; import { @@ -41,27 +38,28 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import { useThemeTransition } from "./ThemeProvider"; export function ModeToggle() { - const { setTheme } = useTheme(); + const { changeTheme } = useThemeTransition(); return ( - - setTheme("light")}> + changeTheme("light")}> Light - setTheme("dark")}> + changeTheme("dark")}> Dark - setTheme("system")}> + changeTheme("system")}> System diff --git a/src/components/feat/AchievementList.tsx b/src/components/feat/AchievementList.tsx index 6f80fa1..77c5d0d 100644 --- a/src/components/feat/AchievementList.tsx +++ b/src/components/feat/AchievementList.tsx @@ -36,6 +36,7 @@ import { Card, CardContent } from "../ui/card"; import { Skeleton } from "../ui/skeleton"; import A from "../misc/Link"; import { formalNames } from "@/config/achievements"; +import NoItems from "../misc/NoItems"; export default function AchievementList({ server }: { server: string }) { const [achievements, setAchievements] = useState< @@ -70,6 +71,7 @@ export default function AchievementList({ server }: { server: string }) { Achievements are earned automatically when the server is online. See{" "} Docs:Advanced/Achievements + {achievements.length === 0 && } {achievements .filter( (value, index) => listify(achievements).indexOf(value.type) === index @@ -109,8 +111,6 @@ export default function AchievementList({ server }: { server: string }) { ); } - - type WithInterval = K & { interval: number; }; diff --git a/src/components/feat/QRCodeGen.tsx b/src/components/feat/QRCodeGen.tsx new file mode 100644 index 0000000..2147520 --- /dev/null +++ b/src/components/feat/QRCodeGen.tsx @@ -0,0 +1,57 @@ +/* + * MHSF, Minehut Server List + * All external content is rather licensed under the ECA Agreement + * located here: https://mhsf.app/docs/legal/external-content-agreement + * + * All code under MHSF is licensed under the MIT License + * by open source contributors + * + * Copyright (c) 2024 dvelo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +"use client"; + +import QRCode from "react-qr-code"; +import { DrawerFooter, DrawerTrigger } from "../ui/drawer"; +import { Button } from "../ui/button"; +import { useTheme } from "next-themes"; + +export default function QRCodeGenerator({ server }: { server: string }) { + const { resolvedTheme } = useTheme(); + + return ( +
+ + + + + + +
+ ); +} diff --git a/src/components/misc/NoItems.tsx b/src/components/misc/NoItems.tsx new file mode 100644 index 0000000..4a79d7d --- /dev/null +++ b/src/components/misc/NoItems.tsx @@ -0,0 +1,48 @@ +/* + * MHSF, Minehut Server List + * All external content is rather licensed under the ECA Agreement + * located here: https://mhsf.app/docs/legal/external-content-agreement + * + * All code under MHSF is licensed under the MIT License + * by open source contributors + * + * Copyright (c) 2024 dvelo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +"use client"; + +import { DatabaseZap } from "lucide-react"; + +export default function NoItems() { + return ( + <> +
+ +

+ Huh, we tried to find something, but nothing was found. +

+
+ + ); +} diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 9838605..d3b29c9 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -50,6 +50,8 @@ const buttonVariants = cva( ghost: "hover:bg-accent hover:text-accent-foreground focus:ring-4 focus:ring-neutral-100 focus:ring-offset-current dark:focus:ring-neutral-900 duration-150 ease-in-out transition-all", link: "text-primary underline-offset-4 hover:underline", + favorite: + "text-black rounded-lg hover:bg-primary/90 focus:ring-4 focus:ring-yellow-400/60 focus:ring-offset-current dark:focus:ring-yellow-400/60 duration-150 ease-in-out transition-all bg-gradient-to-bl from-yellow-300 via-yellow-500 to-yellow-100", }, size: { default: "h-10 px-4 py-2", diff --git a/src/config/version.tsx b/src/config/version.tsx index a87dd62..d76dd17 100644 --- a/src/config/version.tsx +++ b/src/config/version.tsx @@ -55,8 +55,30 @@ const FeatureList = ({ ); }; -export const version = "1.4.0"; +export const version = "1.6.0"; export const changelog: { name: string; id: string; changelog: ReactNode }[] = [ + { + id: "h9jr2cbxn7qwfvt5uypsdg", + name: "v1.6.0", + changelog: ( + + Version 1.6.0 (November 17th 2024) + + } + /> + ), + }, { id: "r9swempc7kaqd2j84nutv5", name: "v1.5.0", diff --git a/src/lib/head.ts b/src/lib/head.ts new file mode 100644 index 0000000..3165e09 --- /dev/null +++ b/src/lib/head.ts @@ -0,0 +1,36 @@ +/* + * MHSF, Minehut Server List + * All external content is rather licensed under the ECA Agreement + * located here: https://mhsf.app/docs/legal/external-content-agreement + * + * All code under MHSF is licensed under the MIT License + * by open source contributors + * + * Copyright (c) 2024 dvelo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +export async function getMinecraftHead(username: string) { + const uuidRequest = await fetch("https://api.mojang.com/users/profiles/minecraft/" + username); + const uuid = (await uuidRequest.json()).id; + + return `https://crafatar.com/avatars/${uuid}`; +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 7de6b74..db00a94 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6847,6 +6847,11 @@ punycode@^2.1.0, punycode@^2.3.0: resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +qr.js@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/qr.js/-/qr.js-0.0.0.tgz#cace86386f59a0db8050fa90d9b6b0e88a1e364f" + integrity sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" @@ -6923,6 +6928,14 @@ react-markdown@^9.0.1: unist-util-visit "^5.0.0" vfile "^6.0.0" +react-qr-code@^2.0.15: + version "2.0.15" + resolved "https://registry.yarnpkg.com/react-qr-code/-/react-qr-code-2.0.15.tgz#fbfc12952c504bcd64275647e9d1ea63251742ce" + integrity sha512-MkZcjEXqVKqXEIMVE0mbcGgDpkfSdd8zhuzXEl9QzYeNcw8Hq2oVIzDLWuZN2PQBwM5PWjc2S31K8Q1UbcFMfw== + dependencies: + prop-types "^15.8.1" + qr.js "0.0.0" + react-remove-scroll-bar@^2.3.3, react-remove-scroll-bar@^2.3.4, react-remove-scroll-bar@^2.3.6: version "2.3.6" resolved "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz"