From dc5b4a04026f88367e484933334028ac417c9d67 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 6 Jan 2023 13:25:49 +0900
Subject: [PATCH] enhance(client): show fireworks when visit user who today is
 birthday

Resolve #9476
---
 CHANGELOG.md                              |  1 +
 packages/frontend/package.json            |  1 +
 packages/frontend/src/pages/user/home.vue | 34 +++++++++++++++++++++++
 yarn.lock                                 |  8 ++++++
 4 files changed, 44 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 556fe12868..6cda5537dc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -80,6 +80,7 @@ You should also include the user name that made the change.
 - Client: add user list widget @syuilo
 - Client: add heatmap of daily active users to about page @syuilo
 - Client: introduce fluent emoji @syuilo
+- Client: show fireworks when visit user who today is birthday @syuilo
 - Client: show bot warning on screen when logged in as bot account @syuilo
 - Client: improve overall performance of client @syuilo
 - Client: ui tweaks @syuilo
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index d151a7b3af..98230d6ceb 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -20,6 +20,7 @@
 		"blurhash": "2.0.4",
 		"broadcast-channel": "4.19.1",
 		"browser-image-resizer": "git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.3",
+		"canvas-confetti": "^1.6.0",
 		"chart.js": "4.1.1",
 		"chartjs-adapter-date-fns": "3.0.0",
 		"chartjs-chart-matrix": "^1.3.0",
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index 11a7ee3b8a..d9d7fc826c 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -110,6 +110,7 @@
 <script lang="ts" setup>
 import { defineAsyncComponent, computed, inject, onMounted, onUnmounted, watch } from 'vue';
 import calcAge from 's-age';
+import confetti from 'canvas-confetti';
 import * as misskey from 'misskey-js';
 import XUserTimeline from './index.timeline.vue';
 import XNote from '@/components/MkNote.vue';
@@ -155,6 +156,29 @@ const age = $computed(() => {
 	return calcAge(props.user.birthday);
 });
 
+function birthdayEffect() {
+	const duration = 1000 * 5;
+	const animationEnd = Date.now() + duration;
+	const defaults = { startVelocity: 30, spread: 360, ticks: 60, zIndex: os.claimZIndex('high') };
+
+	function randomInRange(min, max) {
+		return Math.random() * (max - min) + min;
+	}
+
+	const interval = setInterval(() => {
+		const timeLeft = animationEnd - Date.now();
+
+		if (timeLeft <= 0) {
+			return clearInterval(interval);
+		}
+
+		const particleCount = 50 * (timeLeft / duration);
+		// since particles fall down, start a bit higher than random
+		confetti(Object.assign({}, defaults, { particleCount, origin: { x: randomInRange(0.1, 0.3), y: Math.random() - 0.2 } }));
+		confetti(Object.assign({}, defaults, { particleCount, origin: { x: randomInRange(0.7, 0.9), y: Math.random() - 0.2 } }));
+	}, 250);
+}
+
 function menu(ev) {
 	os.popupMenu(getUserMenu(props.user, router), ev.currentTarget ?? ev.target);
 }
@@ -180,6 +204,16 @@ function parallax() {
 onMounted(() => {
 	window.requestAnimationFrame(parallaxLoop);
 	narrow = rootEl!.clientWidth < 1000;
+
+	if (props.user.birthday) {
+		const m = new Date().getMonth() + 1;
+		const d = new Date().getDate();
+		const bm = parseInt(props.user.birthday.split('-')[1]);
+		const bd = parseInt(props.user.birthday.split('-')[2]);
+		if (m === bm && d === bd) {
+			birthdayEffect();
+		}
+	}
 });
 
 onUnmounted(() => {
diff --git a/yarn.lock b/yarn.lock
index 208f0724d5..49ae07dd00 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4833,6 +4833,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"canvas-confetti@npm:^1.6.0":
+  version: 1.6.0
+  resolution: "canvas-confetti@npm:1.6.0"
+  checksum: be19e3be736ab28aa8af7b53cba9f4146f5714edadbf4d5a7d7b1899914dc59a8ac5574260fe6b7d9995c51df5787b0b707adfbb72dbacbc61fc03f9f2b25291
+  languageName: node
+  linkType: hard
+
 "caseless@npm:~0.12.0":
   version: 0.12.0
   resolution: "caseless@npm:0.12.0"
@@ -8052,6 +8059,7 @@ __metadata:
     blurhash: 2.0.4
     broadcast-channel: 4.19.1
     browser-image-resizer: "git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.3"
+    canvas-confetti: ^1.6.0
     chart.js: 4.1.1
     chartjs-adapter-date-fns: 3.0.0
     chartjs-chart-matrix: ^1.3.0