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 `[](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: `[](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
-
-
-
-
- * {userError}
-
-
-
-
-
-
-
-
- {outputText()}
-
-
-
-
-
-
- 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}
+
+
+
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")],
};