diff --git a/package-lock.json b/package-lock.json
index c9e0a334..443d478d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -33,6 +33,7 @@
"@tanstack/react-table": "8.20.6",
"axios": "1.7.9",
"better-sqlite3": "11.7.0",
+ "canvas-confetti": "^1.9.3",
"class-variance-authority": "0.7.1",
"clsx": "2.1.1",
"cmdk": "1.0.4",
@@ -5384,6 +5385,16 @@
],
"license": "CC-BY-4.0"
},
+ "node_modules/canvas-confetti": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.9.3.tgz",
+ "integrity": "sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==",
+ "license": "ISC",
+ "funding": {
+ "type": "donate",
+ "url": "https://www.paypal.me/kirilvatev"
+ }
+ },
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
diff --git a/package.json b/package.json
index 08cb73aa..c853d004 100644
--- a/package.json
+++ b/package.json
@@ -44,6 +44,7 @@
"@tanstack/react-table": "8.20.6",
"axios": "1.7.9",
"better-sqlite3": "11.7.0",
+ "canvas-confetti": "^1.9.3",
"class-variance-authority": "0.7.1",
"clsx": "2.1.1",
"cmdk": "1.0.4",
diff --git a/src/app/components/SupporterMessage.tsx b/src/app/components/SupporterMessage.tsx
new file mode 100644
index 00000000..678b1bdd
--- /dev/null
+++ b/src/app/components/SupporterMessage.tsx
@@ -0,0 +1,34 @@
+"use client";
+
+import React from "react";
+import confetti from "canvas-confetti";
+
+export default function SupporterMessage({ tier }: { tier: string }) {
+ return (
+
+
{
+ // Get the bounding box of the element
+ const rect = (
+ e.target as HTMLElement
+ ).getBoundingClientRect();
+
+ // Calculate the origin based on the top center of the box
+ confetti({
+ particleCount: 100,
+ spread: 70,
+ origin: {
+ x: (rect.left + rect.width / 2) / window.innerWidth, // Horizontal center of the box
+ y: rect.top / window.innerHeight // Top of the box
+ }
+ });
+ }}
+ >
+
+ Thank you for supporting Pangolin as a {tier}!
+
+
+
+ );
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index ce75dc27..8d472673 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -12,6 +12,7 @@ import SupportStatusProvider from "@app/providers/SupporterStatusProvider";
import { createApiClient, internal, priv } from "@app/lib/api";
import { AxiosResponse } from "axios";
import { IsSupporterKeyVisibleResponse } from "@server/routers/supporterKey";
+import SupporterMessage from "./components/SupporterMessage";
export const metadata: Metadata = {
title: `Dashboard - Pangolin`,
@@ -114,15 +115,9 @@ export default async function RootLayout({
)}
{supporterData?.tier && (
-
-
-
- Thank you for supporting
- Pangolin as a{" "}
- {supporterData.tier}!
-
-
-
+
)}
diff --git a/src/types/canvas-confetti.d.ts b/src/types/canvas-confetti.d.ts
new file mode 100644
index 00000000..c88a6443
--- /dev/null
+++ b/src/types/canvas-confetti.d.ts
@@ -0,0 +1,19 @@
+declare module "canvas-confetti" {
+ export interface ConfettiOptions {
+ particleCount?: number;
+ angle?: number;
+ spread?: number;
+ startVelocity?: number;
+ decay?: number;
+ gravity?: number;
+ drift?: number;
+ ticks?: number;
+ origin?: { x?: number; y?: number };
+ colors?: string[];
+ shapes?: string[];
+ scalar?: number;
+ zIndex?: number;
+ }
+
+ export default function confetti(options?: ConfettiOptions): Promise;
+}
\ No newline at end of file