"use client"; import Image from "next/image"; import { Separator } from "@app/components/ui/separator"; import { useSupporterStatusContext } from "@app/hooks/useSupporterStatusContext"; import { useState } from "react"; import { Popover, PopoverContent, PopoverTrigger } from "@app/components/ui/popover"; import { Button } from "./ui/button"; import { Credenza, CredenzaBody, CredenzaClose, CredenzaContent, CredenzaDescription, CredenzaFooter, CredenzaHeader, CredenzaTitle } from "./Credenza"; import { z } from "zod"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "./ui/form"; import { Input } from "./ui/input"; import { toast } from "@app/hooks/useToast"; import { createApiClient, formatAxiosError } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { AxiosResponse } from "axios"; import { ValidateSupporterKeyResponse } from "@server/routers/supporterKey"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "./ui/card"; import { Check, ExternalLink } from "lucide-react"; import confetti from "canvas-confetti"; const formSchema = z.object({ githubUsername: z .string() .nonempty({ message: "GitHub username is required" }), key: z.string().nonempty({ message: "Supporter key is required" }) }); export default function SupporterStatus() { const { supporterStatus, updateSupporterStatus } = useSupporterStatusContext(); const [supportOpen, setSupportOpen] = useState(false); const [keyOpen, setKeyOpen] = useState(false); const [purchaseOptionsOpen, setPurchaseOptionsOpen] = useState(false); const api = createApiClient(useEnvContext()); const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { githubUsername: "", key: "" } }); async function hide() { await api.post("/supporter-key/hide"); updateSupporterStatus({ visible: false }); } async function onSubmit(values: z.infer) { try { const res = await api.post< AxiosResponse >("/supporter-key/validate", { githubUsername: values.githubUsername, key: values.key }); const data = res.data.data; if (!data || !data.valid) { toast({ variant: "destructive", title: "Invalid Key", description: "Your supporter key is invalid." }); return; } // Trigger the toast toast({ variant: "default", title: "Valid Key", description: "Your supporter key has been validated. Thank you for your support!" }); // Fireworks-style confetti const duration = 5 * 1000; // 5 seconds const animationEnd = Date.now() + duration; const defaults = { startVelocity: 30, spread: 360, ticks: 60, zIndex: 0, colors: ["#FFA500", "#FF4500", "#FFD700"] // Orange hues }; function randomInRange(min: number, max: number) { return Math.random() * (max - min) + min; } const interval = setInterval(() => { const timeLeft = animationEnd - Date.now(); if (timeLeft <= 0) { clearInterval(interval); return; } const particleCount = 50 * (timeLeft / duration); // Launch confetti from two random horizontal positions confetti({ ...defaults, particleCount, origin: { x: randomInRange(0.1, 0.3), y: Math.random() - 0.2 } }); confetti({ ...defaults, particleCount, origin: { x: randomInRange(0.7, 0.9), y: Math.random() - 0.2 } }); }, 250); setPurchaseOptionsOpen(false); setKeyOpen(false); updateSupporterStatus({ visible: false }); } catch (error) { toast({ variant: "destructive", title: "Error", description: formatAxiosError( error, "Failed to validate supporter key." ) }); return; } } return ( <> { setPurchaseOptionsOpen(val); }} > Support Development and Adopt a Pangolin!

Purchase a supporter key to help us continue developing Pangolin. Your contribution allows us commit more time to maintain and add new features to the application for everyone. We will never use this to paywall features.

You will also get to adopt and meet your very own pet Pangolin!

Payments are processed via GitHub. Afterward, you can retrieve your key on{" "} our website {" "} and redeem it here.{" "} Learn more.

Please select the option that best suits you.

Full Supporter

$95

  • For the whole server
  • Lifetime purchase
  • Supporter status
Limited Supporter

$25

  • For 5 or less users
  • Lifetime purchase
  • Supporter status
{supporterStatus?.tier !== "Limited Supporter" ? ( ) : ( )}
{ setKeyOpen(val); }} > Enter Supporter Key Meet your very own pet Pangolin!
( GitHub Username )} /> ( Supporter Key )} />
{supporterStatus?.visible ? ( ) : null} ); }