From 4a9c696be4a6a806d9d1750f0092141484ce3eb1 Mon Sep 17 00:00:00 2001 From: cnrad Date: Mon, 11 Nov 2024 22:16:26 -0500 Subject: [PATCH] chore(landing): general cleanup + ui touchup --- .gitignore | 1 + .prettierrc | 8 +- .vscode/settings.json | 4 - src/app/api/[...id]/route.ts | 138 +++--- src/app/globals.css | 97 ---- src/app/layout.tsx | 37 +- src/app/page.tsx | 835 +++++++++++++-------------------- src/components/ui/button.tsx | 57 --- src/components/ui/checkbox.tsx | 30 -- src/components/ui/dialog.tsx | 125 ----- src/components/ui/input.tsx | 25 - src/components/ui/popover.tsx | 31 +- src/components/ui/select.tsx | 164 ------- src/utils/actions.ts | 14 +- src/utils/parameter.ts | 205 -------- src/utils/parameters.ts | 191 ++++++++ tailwind.config.ts | 17 +- 17 files changed, 635 insertions(+), 1344 deletions(-) delete mode 100644 .vscode/settings.json delete mode 100644 src/components/ui/button.tsx delete mode 100644 src/components/ui/checkbox.tsx delete mode 100644 src/components/ui/dialog.tsx delete mode 100644 src/components/ui/input.tsx delete mode 100644 src/components/ui/select.tsx delete mode 100644 src/utils/parameter.ts create mode 100644 src/utils/parameters.ts diff --git a/.gitignore b/.gitignore index 0563835..86f0706 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ yarn-error.log* # local env files .env*.local +.env # vercel .vercel diff --git a/.prettierrc b/.prettierrc index 5577937..cc003b5 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,10 @@ { "plugins": ["prettier-plugin-tailwindcss"], - "tabWidth": 4 + "quoteProps": "consistent", + "printWidth": 120, + "bracketSpacing": true, + "singleQuote": false, + "useTabs": false, + "arrowParens": "avoid", + "tabWidth": 2 } diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index a100adc..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "editor.tabSize": 4, - "prettier.tabWidth": 4 -} diff --git a/src/app/api/[...id]/route.ts b/src/app/api/[...id]/route.ts index 2b2f208..c6c5745 100644 --- a/src/app/api/[...id]/route.ts +++ b/src/app/api/[...id]/route.ts @@ -5,81 +5,75 @@ import { NextRequest } from "next/server"; export const dynamic = "force-dynamic"; -export async function GET( - req: NextRequest, - options: { params: Promise<{ id: string[] }> }, -) { - const userId = (await options.params).id.join("/"); +export async function GET(req: NextRequest, options: { params: Promise<{ id: string[] }> }) { + const userId = (await options.params).id.join("/"); - if (!userId) - return Response.json( - { - data: { - error: "No ID provided.", - }, - success: false, - }, - { - status: 400, - }, - ); - - if (!isSnowflake(userId)) - return Response.json( - { - data: { - error: "The ID you provide is not a valid snowflake.", - }, - success: false, - }, - { - status: 400, - }, - ); - - let getUser: any = {}; - - getUser.data = await fetch(`https://api.lanyard.rest/v1/users/${userId}`, { - cache: "no-store", - }).then(async (res) => { - const data = await res.json(); - - if (!data.success) { - getUser.error = data.error; - } - - return data; - }); - - if (getUser.error) { - return Response.json( - { - data: getUser.error, - success: false, - }, - { - status: 400, - }, - ); - } - - const params: Parameters = Object.fromEntries( - req.nextUrl.searchParams.entries(), + if (!userId) + return Response.json( + { + data: { + error: "No ID provided.", + }, + success: false, + }, + { + status: 400, + }, ); - try { - let user = await redis.hget("users", userId); - if (!user) await redis.hset("users", userId, "true"); - } catch { - null; + if (!isSnowflake(userId)) + return Response.json( + { + data: { + error: "The ID you provide is not a valid snowflake.", + }, + success: false, + }, + { + status: 400, + }, + ); + + let getUser: any = {}; + + getUser.data = await fetch(`https://api.lanyard.rest/v1/users/${userId}`, { + cache: "no-store", + }).then(async res => { + const data = await res.json(); + + if (!data.success) { + getUser.error = data.error; } - return new Response(await renderCard(getUser.data, params), { - headers: { - "Content-Type": "image/svg+xml; charset=utf-8", - "content-security-policy": - "default-src 'none'; img-src * data:; style-src 'unsafe-inline'", - }, - status: 200, - }); + return data; + }); + + if (getUser.error) { + return Response.json( + { + data: getUser.error, + success: false, + }, + { + status: 400, + }, + ); + } + + const params: Parameters = Object.fromEntries(req.nextUrl.searchParams.entries()); + + try { + let user = await redis.hget("users", userId); + if (!user) await redis.hset("users", userId, "true"); + } catch { + null; + } + + return new Response(await renderCard(getUser.data, params), { + headers: { + "Content-Type": "image/svg+xml; charset=utf-8", + "content-security-policy": "default-src 'none'; img-src * data:; style-src 'unsafe-inline'", + }, + status: 200, + }); } diff --git a/src/app/globals.css b/src/app/globals.css index 7c1309c..75bcdce 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -13,103 +13,6 @@ body { font-family: "Poppins", sans-serif; } -.input { - text-align: left; - border-radius: 8px; - border: none; - width: 100%; - font-size: 0.9rem; - padding: 0.45rem 0.75rem; - color: #aaabaf; - border: solid 1px rgba(255, 255, 255, 0.2); - background: #000; - box-shadow: 0px 3px 15px rgba(0, 0, 0, 0.2); - transition: all ease-in-out 0.1s; - - &:focus { - outline: 0; - border-color: rgba(255, 255, 255, 0.5); - } -} - -.output { - color: #aaabaf; - word-break: break-word; - border-radius: 8px; - border: solid 1px #333; - padding: 8px; - background: #000; - box-shadow: 0px 3px 15px rgba(0, 0, 0, 0.2); - font-family: Monospace, sans-serif; -} - -.action { - font-size: 0.9rem; - padding: 5px 25px; - border-radius: 6px; - cursor: pointer; - color: #888; - border: solid 1px #333; - background: transparent; - transition: all ease-in-out 0.1s; - - &:hover { - color: #e6e6e6; - border-color: #e6e6e6; - } - &:active { - color: #fff; - border-color: #fff; - } -} - -.active { - color: #fff; - border-color: #fff; -} - -.stat { - position: fixed; - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - line-height: 1rem; - bottom: 1rem; - left: 50%; - transform: translate(-50%, 0); - background: #000; - padding: 1rem 1.25rem; - color: #fff; - border-radius: 0.55rem; - text-align: center; - box-shadow: 0 2px 15px -10px #a21caf; - min-width: 400px; - - @media (max-width: 400px) { - font-size: 14px; - min-width: 365px; - padding: 0.75rem 1rem; - } - - &:before { - content: ""; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: 0.55rem; - border: 2px solid transparent; - background: linear-gradient(45deg, #be123c, #6b21a8, #3730a3) border-box; - -webkit-mask: - linear-gradient(#fff 0 0) padding-box, - linear-gradient(#fff 0 0); - -webkit-mask-composite: xor; - mask-composite: exclude; - } -} - @layer base { :root { --radius: 0.5rem; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 709a61e..642824b 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -4,35 +4,28 @@ import "./globals.css"; import { Poppins } from "next/font/google"; const poppins = Poppins({ - weight: ["400", "500", "600", "700"], - subsets: ["latin"], - display: "swap", + weight: ["400", "500", "600", "700"], + subsets: ["latin"], + display: "swap", }); export const metadata: Metadata = { + title: "Lanyard for GitHub Profile", + description: "Utilize Lanyard to display your Discord Presence in your GitHub Profile", + openGraph: { title: "Lanyard for GitHub Profile", - description: - "Utilize Lanyard to display your Discord Presence in your GitHub Profile", - openGraph: { - title: "Lanyard for GitHub Profile", - description: - "Utilize Lanyard to display your Discord Presence in your GitHub Profile", - }, + description: "Utilize Lanyard to display your Discord Presence in your GitHub Profile", + }, }; export default function RootLayout({ - children, + children, }: Readonly<{ - children: React.ReactNode; + children: React.ReactNode; }>) { - return ( - - - {children} - - - ); + return ( + + {children} + + ); } diff --git a/src/app/page.tsx b/src/app/page.tsx index f8fbdca..16bd689 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,523 +1,352 @@ "use client"; -import React, { useState, useRef, useMemo, JSX } from "react"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; +import React, { useState, useMemo, JSX, useRef, useEffect } from "react"; import { motion } from "framer-motion"; - -import { useSmoothCount } from "use-smooth-count"; import useSWR from "swr"; - -import { getUserCount, isUserMonitored } from "@/utils/actions"; +import { getUserCount } from "@/utils/actions"; import { isSnowflake } from "@/utils/snowflake"; import Link from "next/link"; -import { parameterInfo } from "@/utils/parameter"; - +import { PARAMETERS } from "@/utils/parameters"; import * as Icon from "lucide-react"; -import { Checkbox } from "@/components/ui/checkbox"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; -import { Input } from "@/components/ui/input"; -import { filterLetters } from "@/lib/utils"; -import { Button } from "@/components/ui/button"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import { cn, filterLetters } from "@/lib/utils"; export default function Home() { - const originUrl = useMemo( - () => - typeof window !== "undefined" - ? window.location.origin - : "https://lanyard.cnrad.dev", - [], - ); - const [userId, setUserId] = useState(null); - const [userError, setUserError] = useState(); - const [userData, setUserData] = useState<{ userId: string } | null>(null); - const [copyState, setCopyState] = useState("Copy"); - const [outputType, setOutputType] = useState<"markdown" | "html" | "url">( - "markdown", - ); - const [isLoading, setIsLoading] = useState(true); - const [onImageLoaded, setOnImageLoaded] = useState(false); + const originUrl = process.env.NODE_ENV === "development" ? "http://localhost:3001" : "https://lanyard.cnrad.dev"; - const [option, setOption] = useState< - Array<{ name: string; value: string }> - >([]); + const [userId, setUserId] = useState(""); + const [userError, setUserError] = useState(); + const [copyState, setCopyState] = useState("Copy"); + const [outputType, setOutputType] = useState<"markdown" | "html" | "url">("markdown"); + const [isLoaded, setIsLoaded] = useState(false); + const [options, setOptions] = useState>({}); - const userCount = useSWR("getUserCount", getUserCount); - const countRef = useRef(null); - useSmoothCount({ - ref: countRef, - target: userCount.data || 0, - duration: 3, - curve: [0, 1, 0, 1], - }); + const userCount = useSWR("getUserCount", getUserCount); - const url = `${originUrl}/api/${userData?.userId}${option.length > 0 ? `?${option.map((o) => `${o.name}=${o.value}`).join("&")}` : ""}`; + async function onLoadDiscordId(userId: string) { + setUserId(userId); + setIsLoaded(false); + setUserError(undefined); - function outputText() { - if (outputType === "html") { - return ``; - } else if (outputType === "url") { - return `${url}`; - } else { - return `[![Discord Presence](${url})](https://discord.com/users/${userData?.userId})`; - } + if (userId.length < 1) return; + if (userId.length > 0 && !isSnowflake(userId)) return setUserError("Invalid Discord ID"); + } + + const url = `${originUrl}/api/${userId}${ + Object.keys(options).length > 0 + ? `?${Object.keys(options) + .map(option => `${option}=${options[option]}`) + .join("&")}` + : "" + }`; + + const copyContent = { + markdown: `[![Discord Presence](${url})](https://discord.com/users/${userId})`, + html: ``, + url: `${url}`, + }; + + const [isOptionsOpen, setIsOptionsOpen] = useState(false); + const optionsTriggerRef = useRef(null); + const optionsContentRef = useRef(null); + + useEffect(() => { + function handleOptionsClickOutside(event: MouseEvent) { + if ( + isOptionsOpen && + optionsContentRef.current && + !optionsContentRef.current.contains(event.target as Node) && + !optionsTriggerRef.current?.contains(event.target as Node) + ) { + setIsOptionsOpen(false); + } } - function copy() { - navigator.clipboard.writeText(outputText()); - setCopyState("Copied!"); + document.addEventListener("mousedown", handleOptionsClickOutside); + return () => { + document.removeEventListener("mousedown", handleOptionsClickOutside); + }; + }, [isOptionsOpen]); - setTimeout(() => setCopyState("Copy"), 1500); - } + return ( + <> +
+
+

🏷️ lanyard-profile-readme

+

Uses Lanyard to display your Discord Presence anywhere.

- async function submitDiscordId() { - setIsLoading(true); - setOnImageLoaded(false); - setUserData(null); - setUserError(undefined); +
+ onLoadDiscordId(e.target.value)} + value={userId || ""} + placeholder="Enter your Discord ID" + /> - if (!userId) return setUserError("Please enter a Discord ID"); - - if (!isSnowflake(userId)) return setUserError("Invalid Discord ID"); - - if ((await isUserMonitored(userId)) === false) - return setUserError( - <> - User is not being monitored by Lanyard, please join{" "} - - this server - {" "} - and try again. - , - ); - - setUserData({ userId }); - setIsLoading(false); - } - - function modifyOption( - data: - | { - type: "string"; - name: string; - data: string; - event: React.ChangeEvent; - } - | { - type: "list"; - name: string; - data: string; - } - | { - type: "boolean"; - name: string; - data: string | boolean; - }, - ) { - if (data.type === "string") { - const filteredValue = encodeURIComponent( - filterLetters( - data.data, - ( - parameterInfo.find( - (p) => - p.type === "string" && - p.parameter === data.name, - ) as any - ).options.omit, - ), - ); - - setOption((prev) => { - if (data.data === "") { - return prev?.filter((o) => o.name !== data.name) || []; - } else { - if (prev?.find((o) => o.name === data.name)) { - return prev.map((o) => { - if (o.name === data.name) { - o.value = filteredValue; - } - return o; - }); - } else { - return prev - ? [ - ...prev, - { - name: data.name, - value: filteredValue, - }, - ] - : [ - { - name: data.name, - value: filteredValue, - }, - ]; - } - } - }); - } else if (data.type === "list") { - setOption((prev) => { - if (prev?.find((o) => o.name === data.name)) { - return prev.map((o) => { - if (o.name === data.name) { - o.value = data.data; - } - return o; - }); - } else { - return prev - ? [...prev, { name: data.name, value: data.data }] - : [{ name: data.name, value: data.data }]; - } - }); - } else if (data.type === "boolean") { - setOption((prev) => { - if (prev?.find((o) => o.name === data.name)) { - return prev - .map((opt) => { - if (opt.name === data.name) { - const options = parameterInfo.find( - (p) => p.parameter === data.name, - )?.options as { defaultBool?: boolean }; - - if ( - data.data === - (options?.defaultBool! || false) - ) { - return null; - } else { - if (opt.name === data.name) { - opt.value = data.data.toString(); - } - return opt; - } - } else { - return opt; - } - }) - .filter((opt) => opt !== null); - } else { - return prev - ? [ - ...prev, - { name: data.name, value: data.data.toString() }, - ] - : [{ name: data.name, value: data.data.toString() }]; - } - }); - } - } - - return ( - <> -
-
-

- lanyard profile readme 🏷️ -

-

- Utilize Lanyard to display your Discord Presence in your - GitHub Profile -

-
-
{ - e.preventDefault(); - - submitDiscordId(); - }} - > - setUserId(e.target.value)} - value={userId || ""} - placeholder="Enter your Discord ID" - /> - -
- - * {userError} - - -
- - - -
-
- {outputText()} -
-
- - - - Option - - - - Option - - Select an option to enable/disable - features to your Lanyard Profile - - - -
- {parameterInfo.map((item, idx) => { - return ( -
-
-

- {item.title} -

- - - - - - { - item.description - } - - -
- {item.type === "string" && ( - - modifyOption({ - type: "string", - name: item.parameter, - data: e - .target - .value, - event: e, - }) - } - value={decodeURIComponent( - option?.find( - (o) => - o.name === - item.parameter, - )?.value || "", - )} - className="text-sm sm:text-base" - /> - )} - {item.type === - "boolean" && ( - - modifyOption({ - type: "boolean", - name: item.parameter, - data: bool, - }) - } - checked={ - option?.find( - (o) => - o.name === - item.parameter, - )?.value === - "true" - ? true - : option?.find( - ( - o, - ) => - o.name === - item.parameter, - ) - ?.value === - "false" - ? false - : item - .options - ?.defaultBool || - false - } - /> - )} - {item.type === "list" && ( - - )} -
- ); - })} -
- - - -
-
-
-
- setOnImageLoaded(true)} - suppressHydrationWarning - /> -
-
-
-
- setIsOptionsOpen(p => !p)} + className="group flex min-h-[2.25rem] min-w-[2.25rem] items-center justify-center rounded-lg border border-white/10 bg-stone-900/50 transition-colors duration-150 ease-out hover:border-white/40" > - Lanyard Profile Readme has{" "} -
{" "} - total users! - - - ); + + +
+ + +
+ {PARAMETERS.filter(item => item.type !== "boolean").map(item => { + return ( +
+
+

{item.title}

+ + + + + + {item.description} + + +
+ + {item.type === "string" && ( + { + const filteredValue = encodeURIComponent( + filterLetters( + e.target.value, + (PARAMETERS.find(p => p.parameter === item.parameter) as any).options.omit, + ), + ); + + setOptions(prev => ({ + ...prev, + [item.parameter]: filteredValue, + })); + }} + value={decodeURIComponent((options[item.parameter] as string) || "")} + /> + )} + + {item.type === "list" && ( +
+ + +
+ )} +
+ ); + })} +
+ + {/* Separated for easier styling/readability */} +
+ {PARAMETERS.filter(item => item.type === "boolean").map(item => { + return ( +
+ + setOptions(prev => ({ + ...prev, + [item.parameter]: e.target.checked.toString(), + })) + } + /> + +

p.parameter === item.parameter)?.deprecated + ? "line-through" + : "none", + }} + > + {item.title} +

+ + + + + + + {item.description} + + +
+ ); + })} +
+
+ + {!isLoaded ? ( + + {userError} + + ) : null} + + + Your Lanyard Banner setIsLoaded(true)} + onError={() => + userId.length > 0 && isSnowflake(userId) + ? setUserError( + <> + User is not monitored by Lanyard, please join{" "} + + the server + {" "} + and try again. + , + ) + : null + } + /> + +
+ {(["markdown", "html", "url"] as const).map(type => ( + + ))} +
+ +
+ {copyContent[outputType]} +
+ +
+ +
+
+
+
+ + {userCount.data && ( + + Currently at  + + {userCount.data?.toLocaleString()} + +  total users! + + )} + + ); } diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx deleted file mode 100644 index cca3833..0000000 --- a/src/components/ui/button.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import * as React from "react"; -import { Slot } from "@radix-ui/react-slot"; -import { cva, type VariantProps } from "class-variance-authority"; - -import { cn } from "@/lib/utils"; - -const buttonVariants = cva( - "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 focus-visible:ring-stone-300", - { - variants: { - variant: { - default: - "shadow bg-stone-50 text-stone-900 hover:bg-stone-50/90", - destructive: - "shadow-sm bg-red-900 text-stone-50 hover:bg-red-900/90", - outline: - "border shadow-sm text-stone-900 border-stone-800 bg-stone-950 hover:bg-stone-800 hover:text-stone-50", - secondary: - "shadow-sm bg-stone-800 text-stone-50 hover:bg-stone-800/80", - ghost: "hover:bg-stone-800 hover:text-stone-50", - link: "underline-offset-4 hover:underline text-stone-50", - }, - size: { - default: "h-9 px-4 py-2", - sm: "h-8 rounded-md px-3 text-xs", - lg: "h-10 rounded-md px-8", - icon: "h-9 w-9", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - }, -); - -export interface ButtonProps - extends React.ButtonHTMLAttributes, - VariantProps { - asChild?: boolean; -} - -const Button = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button"; - return ( - - ); - }, -); -Button.displayName = "Button"; - -export { Button, buttonVariants }; diff --git a/src/components/ui/checkbox.tsx b/src/components/ui/checkbox.tsx deleted file mode 100644 index 7d6baef..0000000 --- a/src/components/ui/checkbox.tsx +++ /dev/null @@ -1,30 +0,0 @@ -"use client"; - -import * as React from "react"; -import * as CheckboxPrimitive from "@radix-ui/react-checkbox"; -import { CheckIcon } from "@radix-ui/react-icons"; - -import { cn } from "@/lib/utils"; - -const Checkbox = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - - - -)); -Checkbox.displayName = CheckboxPrimitive.Root.displayName; - -export { Checkbox }; diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx deleted file mode 100644 index 95248db..0000000 --- a/src/components/ui/dialog.tsx +++ /dev/null @@ -1,125 +0,0 @@ -"use client"; - -import * as React from "react"; -import * as DialogPrimitive from "@radix-ui/react-dialog"; -import { Cross2Icon } from "@radix-ui/react-icons"; - -import { cn } from "@/lib/utils"; - -const Dialog = DialogPrimitive.Root; - -const DialogTrigger = DialogPrimitive.Trigger; - -const DialogPortal = DialogPrimitive.Portal; - -const DialogClose = DialogPrimitive.Close; - -const DialogOverlay = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; - -const DialogContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - - {children} - - - Close - - - -)); -DialogContent.displayName = DialogPrimitive.Content.displayName; - -const DialogHeader = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-); -DialogHeader.displayName = "DialogHeader"; - -const DialogFooter = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-); -DialogFooter.displayName = "DialogFooter"; - -const DialogTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -DialogTitle.displayName = DialogPrimitive.Title.displayName; - -const DialogDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -DialogDescription.displayName = DialogPrimitive.Description.displayName; - -export { - Dialog, - DialogPortal, - DialogOverlay, - DialogTrigger, - DialogClose, - DialogContent, - DialogHeader, - DialogFooter, - DialogTitle, - DialogDescription, -}; diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx deleted file mode 100644 index 4bb5f30..0000000 --- a/src/components/ui/input.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import * as React from "react"; - -import { cn } from "@/lib/utils"; - -export interface InputProps - extends React.InputHTMLAttributes {} - -const Input = React.forwardRef( - ({ className, type, ...props }, ref) => { - return ( - - ); - }, -); -Input.displayName = "Input"; - -export { Input }; diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx index 128cf26..afb20c8 100644 --- a/src/components/ui/popover.tsx +++ b/src/components/ui/popover.tsx @@ -6,27 +6,24 @@ import * as PopoverPrimitive from "@radix-ui/react-popover"; import { cn } from "@/lib/utils"; const Popover = PopoverPrimitive.Root; - const PopoverTrigger = PopoverPrimitive.Trigger; - const PopoverAnchor = PopoverPrimitive.Anchor; - const PopoverContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( - - - + + + )); PopoverContent.displayName = PopoverPrimitive.Content.displayName; diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx deleted file mode 100644 index 352bfcb..0000000 --- a/src/components/ui/select.tsx +++ /dev/null @@ -1,164 +0,0 @@ -"use client"; - -import * as React from "react"; -import { - CaretSortIcon, - CheckIcon, - ChevronDownIcon, - ChevronUpIcon, -} from "@radix-ui/react-icons"; -import * as SelectPrimitive from "@radix-ui/react-select"; - -import { cn } from "@/lib/utils"; - -const Select = SelectPrimitive.Root; - -const SelectGroup = SelectPrimitive.Group; - -const SelectValue = SelectPrimitive.Value; - -const SelectTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - span]:line-clamp-1", - className, - )} - {...props} - > - {children} - - - - -)); -SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; - -const SelectScrollUpButton = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - -)); -SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName; - -const SelectScrollDownButton = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - -)); -SelectScrollDownButton.displayName = - SelectPrimitive.ScrollDownButton.displayName; - -const SelectContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, position = "popper", ...props }, ref) => ( - - - - - {children} - - - - -)); -SelectContent.displayName = SelectPrimitive.Content.displayName; - -const SelectLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -SelectLabel.displayName = SelectPrimitive.Label.displayName; - -const SelectItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - - - - - {children} - -)); -SelectItem.displayName = SelectPrimitive.Item.displayName; - -const SelectSeparator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -SelectSeparator.displayName = SelectPrimitive.Separator.displayName; - -export { - Select, - SelectGroup, - SelectValue, - SelectTrigger, - SelectContent, - SelectLabel, - SelectItem, - SelectSeparator, - SelectScrollUpButton, - SelectScrollDownButton, -}; diff --git a/src/utils/actions.ts b/src/utils/actions.ts index e704820..85dd948 100644 --- a/src/utils/actions.ts +++ b/src/utils/actions.ts @@ -2,16 +2,8 @@ import redis from "@/utils/redis"; export async function getUserCount() { - let users = await redis.hgetall("users"); - let count = Object.keys(users); + let users = await redis.hgetall("users"); + let count = Object.keys(users); - return count.length; -} - -export async function isUserMonitored(userId: string) { - const user = await fetch(`https://api.lanyard.rest/v1/users/${userId}`, { - cache: "no-store", - }).then((res) => res.json()); - - return user.success === true; + return count.length; } diff --git a/src/utils/parameter.ts b/src/utils/parameter.ts deleted file mode 100644 index c82441e..0000000 --- a/src/utils/parameter.ts +++ /dev/null @@ -1,205 +0,0 @@ -export type Parameters = { - theme?: string; - bg?: string; - clanbg?: string; - animated?: string; - animatedDecoration?: string; - hideDiscrim?: string; - hideStatus?: string; - hideTimestamp?: string; - hideBadges?: string; - hideProfile?: string; - hideActivity?: string; - hideSpotify?: string; - hideClan?: string; - hideDecoration?: string; - ignoreAppId?: string; - showDisplayName?: string; - borderRadius?: string; - idleMessage?: string; -}; - -export const parameterInfo: Array< - | { - parameter: string; - type: "boolean"; - title: string; - description?: string; - options?: { - defaultBool?: boolean; - }; - } - | { - parameter: string; - type: "string"; - title: string; - description?: string; - options?: { - placeholder?: string; - omit?: string[]; - }; - } - | { - parameter: string; - type: "list"; - title: string; - description?: string; - options: { - list: Array<{ - name: string; - value: string; - }>; - }; - } -> = [ - { - parameter: "theme", - type: "list", - title: "Theme", - description: - 'This will change the background and the font colors, but the background can be overridden with the "Background Color" parameter.', - options: { - list: [ - { - name: "Light", - value: "light", - }, - { - name: "Dark", - value: "dark", - }, - ], - }, - }, - { - parameter: "bg", - type: "string", - title: "Background Color", - description: - "This will change the background color. Must be in hex format. Omit the # symbol.", - options: { - placeholder: "1A1C1F", - omit: ["#"], - }, - }, - { - parameter: "borderRadius", - type: "string", - title: "Border Radius", - description: - "This will change the border radius of the card. Must have a size unit (px, rem, em, and more).", - options: { - placeholder: "10px", - }, - }, - { - parameter: "animated", - type: "boolean", - title: "Toggle Animated Avatar", - description: - "If you have an animated avatar, but don't want it animated, this is the right option.", - options: { - defaultBool: true, - }, - }, - { - parameter: "idleMessage", - type: "string", - title: "Idle Message", - description: - "If you don't want the default \"I'm not currently doing anything!\" as your idle message, this is the right option.", - options: { - placeholder: "I'm not currently doing anything!", - }, - }, - { - parameter: "showDisplayName", - type: "boolean", - title: "Show Display Name", - description: - "If you'd like to show your global display name as well as your username, this is the right option.", - }, - { - parameter: "animatedDecoration", - type: "boolean", - title: "Toggle Animated Avatar Decoration", - description: - "If you have an Animated Avatar Decoration, but don't want it animated, this is the right option.", - options: { - defaultBool: true, - }, - }, - { - parameter: "hideDecoration", - type: "boolean", - title: "Hide Avatar Decoration", - description: - "If you don't want people seeing your Avatar Decoration, this is the right option.", - }, - { - parameter: "hideStatus", - type: "boolean", - title: "Hide Status", - description: - "If you don't want people seeing your status, this is the right option.", - }, - { - parameter: "hideTimestamp", - type: "boolean", - title: "Hide Elapsed Time", - description: - "If you don't want people seeing the elapsed time on an activity, this is the right option.", - }, - { - parameter: "hideClan", - type: "boolean", - title: "Hide Clan Tag", - description: - "If you don't want people seeing your Guild Tag (formerly known as Clans), this is the right option.", - }, - { - parameter: "hideBadges", - type: "boolean", - title: "Hide Badges", - description: - "If you don't want people seeing your Badges, this is the right option.", - }, - { - parameter: "hideProfile", - type: "boolean", - title: "Hide Profile", - description: - "If you don't want people seeing your Profile, this is the right option.", - }, - { - parameter: "hideActivity", - type: "boolean", - title: "Hide Activity", - description: - "If you don't want people seeing your activity, this is the right option.", - }, - { - parameter: "hideSpotify", - type: "boolean", - title: "Hide Spotify", - description: - "If you don't want people seeing your Spotify activity, this is the right option.", - }, - { - parameter: "ignoreAppId", - type: "string", - title: "Hide App by ID", - description: - "If you don't want to display a specific application, this is the right option. IDs separate by ','", - options: { - placeholder: "1302143410907648071, 1302132259368861759", - }, - }, - { - parameter: "hideDiscrim", - type: "boolean", - title: "Hide Discriminator (DEPRECATED)", - description: - "If you don't want people seeing your Discriminator, this is the right option. (DEPRECATED)", - }, -]; diff --git a/src/utils/parameters.ts b/src/utils/parameters.ts new file mode 100644 index 0000000..983523f --- /dev/null +++ b/src/utils/parameters.ts @@ -0,0 +1,191 @@ +export type Parameters = { + theme?: string; + bg?: string; + clanbg?: string; + animated?: string; + animatedDecoration?: string; + hideDiscrim?: string; + hideStatus?: string; + hideTimestamp?: string; + hideBadges?: string; + hideProfile?: string; + hideActivity?: string; + hideSpotify?: string; + hideClan?: string; + hideDecoration?: string; + ignoreAppId?: string; + showDisplayName?: string; + borderRadius?: string; + idleMessage?: string; +}; + +export const PARAMETERS: Array< + { deprecated?: boolean } & ( + | { + parameter: string; + type: "boolean"; + title: string; + description?: string; + options?: { + defaultBool?: boolean; + }; + } + | { + parameter: string; + type: "string"; + title: string; + description?: string; + options?: { + placeholder?: string; + omit?: string[]; + }; + } + | { + parameter: string; + type: "list"; + title: string; + description?: string; + options: { + list: Array<{ + name: string; + value: string; + }>; + }; + } + ) +> = [ + { + parameter: "theme", + type: "list", + title: "Theme", + description: "Changes the background and text colors. Can be overridden with the `bg` parameter.", + options: { + list: [ + { + name: "Light", + value: "light", + }, + { + name: "Dark", + value: "dark", + }, + ], + }, + }, + { + parameter: "bg", + type: "string", + title: "Background Color", + description: "Changes the background color to a hex color (no octothorpe).", + options: { + placeholder: "1A1C1F", + omit: ["#"], + }, + }, + { + parameter: "borderRadius", + type: "string", + title: "Border Radius", + description: "Changes the border radius of the card. Follows the CSS spec (px, rem, etc.).", + options: { + placeholder: "10px", + }, + }, + { + parameter: "animated", + type: "boolean", + title: "Disable Animated Avatar", + description: "Disables an animated avatar.", + options: { + defaultBool: true, + }, + }, + { + parameter: "idleMessage", + type: "string", + title: "Idle Message", + description: 'Changes the idle message. Defaults to "I\'m not currently doing anything!".', + options: { + placeholder: "I'm not currently doing anything!", + }, + }, + { + parameter: "showDisplayName", + type: "boolean", + title: "Show Display Name", + description: "Shows your global display name alongside your username.", + }, + { + parameter: "animatedDecoration", + type: "boolean", + title: "Disable Animated Avatar Decoration", + description: "Disables animated avatar decorations.", + options: { + defaultBool: true, + }, + }, + { + parameter: "hideDecoration", + type: "boolean", + title: "Hide Avatar Decoration", + description: "Hides any avatar decorations.", + }, + { + parameter: "hideStatus", + type: "boolean", + title: "Hide Status", + description: "Hides your custom Discord status.", + }, + { + parameter: "hideTimestamp", + type: "boolean", + title: "Hide Activity Time", + description: "Hides the time spent on an activity.", + }, + { + parameter: "hideClan", + type: "boolean", + title: "Hide Clan Tag", + description: "Hides your Guild Tag (formerly Clan Tag)", + }, + { + parameter: "hideBadges", + type: "boolean", + title: "Hide Badges", + description: "Hides your profile badges.", + }, + { + parameter: "hideProfile", + type: "boolean", + title: "Hide Profile", + description: "Hides your profile, keeps your activity.", + }, + { + parameter: "hideActivity", + type: "boolean", + title: "Hide Activity", + description: "Hides your activity, keeps your profile.", + }, + { + parameter: "hideSpotify", + type: "boolean", + title: "Hide Spotify", + description: "Hides your Spotify activity only.", + }, + { + parameter: "ignoreAppId", + type: "string", + title: "Hide App by ID", + description: "Hide apps by their respective ID, as a comma-separated list.", + options: { + placeholder: "1302143410907648071, 1302132259368861759", + }, + }, + { + parameter: "hideDiscrim", + type: "boolean", + title: "Hide Discriminator", + description: "Hides your discriminator. (DEPRECATED, RIP)", + deprecated: true, + }, +].sort((a, b) => b.type.localeCompare(a.type)); diff --git a/tailwind.config.ts b/tailwind.config.ts index 9152996..cc2433a 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -8,17 +8,12 @@ const config: Config = { "./src/app/**/*.{js,ts,jsx,tsx,mdx}", ], theme: { - extend: { - colors: { - background: 'var(--background)', - foreground: 'var(--foreground)' - }, - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)' - } - } + extend: { + colors: { + background: "var(--background)", + foreground: "var(--foreground)", + }, + }, }, plugins: [require("tailwindcss-animate")], };