From dab6cf09d5e175ea77525a194d544da66115213b Mon Sep 17 00:00:00 2001
From: FLY_MC <me@flymc.cc>
Date: Thu, 13 Mar 2025 20:42:06 +0800
Subject: [PATCH] frontend: reset reactions viewer

---
 .../components/MkReactionsViewer.details.vue  |  8 +-
 .../components/MkReactionsViewer.reaction.vue | 29 +-----
 .../src/components/MkReactionsViewer.vue      | 94 +++++--------------
 3 files changed, 32 insertions(+), 99 deletions(-)

diff --git a/packages/frontend/src/components/MkReactionsViewer.details.vue b/packages/frontend/src/components/MkReactionsViewer.details.vue
index e4e33324a6..d24e0b15bf 100644
--- a/packages/frontend/src/components/MkReactionsViewer.details.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.details.vue
@@ -41,11 +41,9 @@ const emit = defineEmits<{
 }>();
 
 function getReactionName(reaction: string): string {
-	if (reaction.startsWith(':')) {
-		const match = reaction.match(/^:([^@]+)(?:@[^:]+)?:$/);
-		if (match) {
-			return `:${match[1]}:`;
-		}
+	const trimLocal = reaction.replace('@.', '');
+	if (trimLocal.startsWith(':')) {
+		return trimLocal;
 	}
 	return getEmojiName(reaction);
 }
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index 259a395730..20486c9611 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	ref="buttonEl"
 	v-ripple="canToggle"
 	class="_button"
-	:class="[$style.root, { [$style.reacted]: isReacted, [$style.canToggle]: canToggle, [$style.small]: prefer.s.reactionsDisplaySize === 'small', [$style.large]: prefer.s.reactionsDisplaySize === 'large' }]"
+	:class="[$style.root, { [$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle, [$style.small]: prefer.s.reactionsDisplaySize === 'small', [$style.large]: prefer.s.reactionsDisplaySize === 'large' }]"
 	@click.stop="toggleReaction()"
 	@contextmenu.prevent.stop="menu"
 >
@@ -26,7 +26,7 @@ import XDetails from '@/components/MkReactionsViewer.details.vue';
 import MkReactionIcon from '@/components/MkReactionIcon.vue';
 import * as os from '@/os.js';
 import { misskeyApi, misskeyApiGet } from '@/utility/misskey-api.js';
-import { useTooltip } from '@/utility/use-tooltip.js';
+import { useTooltip } from '@/use/use-tooltip.js';
 import { $i } from '@/account.js';
 import MkReactionEffect from '@/components/MkReactionEffect.vue';
 import { claimAchievement } from '@/utility/achievements.js';
@@ -59,27 +59,18 @@ const canToggle = computed(() => {
 });
 const canGetInfo = computed(() => !props.reaction.match(/@\w/) && props.reaction.includes(':'));
 
-const isReacted = computed(() => {
-	if (!props.note.myReaction) return false;
-
-	return normalizeReaction(props.note.myReaction) === normalizeReaction(props.reaction);
-});
-
 async function toggleReaction() {
 	if (!canToggle.value) return;
 
 	const oldReaction = props.note.myReaction;
 	if (oldReaction) {
-		const normalizedOldReaction = normalizeReaction(oldReaction);
-		const normalizedNewReaction = normalizeReaction(props.reaction);
-
 		const confirm = await os.confirm({
 			type: 'warning',
-			text: normalizedOldReaction !== normalizedNewReaction ? i18n.ts.changeReactionConfirm : i18n.ts.cancelReactionConfirm,
+			text: oldReaction !== props.reaction ? i18n.ts.changeReactionConfirm : i18n.ts.cancelReactionConfirm,
 		});
 		if (confirm.canceled) return;
 
-		if (normalizedOldReaction !== normalizedNewReaction) {
+		if (oldReaction !== props.reaction) {
 			sound.playMisskeySfx('reaction');
 		}
 
@@ -91,7 +82,7 @@ async function toggleReaction() {
 		misskeyApi('notes/reactions/delete', {
 			noteId: props.note.id,
 		}).then(() => {
-			if (normalizedOldReaction !== normalizedNewReaction) {
+			if (oldReaction !== props.reaction) {
 				misskeyApi('notes/reactions/create', {
 					noteId: props.note.id,
 					reaction: props.reaction,
@@ -184,16 +175,6 @@ if (!mock) {
 		});
 	}, 100);
 }
-
-function normalizeReaction(reaction) {
-	if (reaction.startsWith(':') && reaction.endsWith(':')) {
-		const match = reaction.match(/^:([^@]+)(?:@[^:]+)?:$/);
-		if (match) {
-			return `:${match[1]}:`;
-		}
-	}
-	return reaction;
-}
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/components/MkReactionsViewer.vue b/packages/frontend/src/components/MkReactionsViewer.vue
index e08a959a8a..bb60db8d34 100644
--- a/packages/frontend/src/components/MkReactionsViewer.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.vue
@@ -11,7 +11,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 	:leaveToClass="prefer.s.animation ? $style.transition_x_leaveTo : ''"
 	:moveClass="prefer.s.animation ? $style.transition_x_move : ''"
 	tag="div" :class="$style.root"
-	@click.stop
 >
 	<XReaction v-for="[reaction, count] in reactions" :key="reaction" :reaction="reaction" :count="count" :isInitial="initialReactions.has(reaction)" :note="note" @reactionToggled="onMockToggleReaction"/>
 	<slot v-if="hasMoreReactions" name="more"/>
@@ -37,96 +36,51 @@ const emit = defineEmits<{
 	(ev: 'mockUpdateMyReaction', emoji: string, delta: number): void;
 }>();
 
-const initialReactions = ref(new Set<string>());
+const initialReactions = new Set(Object.keys(props.note.reactions));
 
 const reactions = ref<[string, number][]>([]);
 const hasMoreReactions = ref(false);
 
-function normalizeReaction(reaction) {
-	if (reaction.startsWith(':') && reaction.endsWith(':')) {
-		const match = reaction.match(/^:([^@]+)(?:@[^:]+)?:$/);
-		if (match) {
-			return `:${match[1]}:`;
-		}
-	}
-	return reaction;
+if (props.note.myReaction && !Object.keys(reactions.value).includes(props.note.myReaction)) {
+	reactions.value[props.note.myReaction] = props.note.reactions[props.note.myReaction];
 }
 
-watch(() => props.note.myReaction, (newMyReaction) => {
-	if (newMyReaction && !Object.keys(reactions.value).includes(newMyReaction)) {
-		reactions.value[newMyReaction] = props.note.reactions[newMyReaction];
-	}
-}, { immediate: true });
-
 function onMockToggleReaction(emoji: string, count: number) {
 	if (!mock) return;
 
-	const i = reactions.value.findIndex((item) => {
-		return normalizeReaction(item[0]) === normalizeReaction(emoji);
-	});
+	const i = reactions.value.findIndex((item) => item[0] === emoji);
 	if (i < 0) return;
 
 	emit('mockUpdateMyReaction', emoji, (count - reactions.value[i][1]));
 }
 
 watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumber]) => {
-	initialReactions.value = new Set(Object.keys(newSource));
-
-	const normalizedCounts = new Map<string, number>();
-	const normalizedOriginals = new Map<string, string>();
-
-	for (const [reaction, count] of Object.entries(newSource)) {
-		const normalized = normalizeReaction(reaction);
-		const currentCount = normalizedCounts.get(normalized) || 0;
-		normalizedCounts.set(normalized, currentCount + count);
-
-		if (!normalizedOriginals.has(normalized) || !reaction.includes('@')) {
-			normalizedOriginals.set(normalized, reaction);
-		}
-	}
-
 	let newReactions: [string, number][] = [];
-
-	for (let i = 0; i < reactions.value.length; i++) {
-		const [reaction] = reactions.value[i];
-		const normalized = normalizeReaction(reaction);
-
-		if (normalizedCounts.has(normalized) && normalizedCounts.get(normalized)! > 0) {
-			newReactions.push([
-				normalizedOriginals.get(normalized)!,
-				normalizedCounts.get(normalized)!,
-			]);
-			normalizedCounts.delete(normalized);
-		}
-	}
-
-	const remainingEntries = Array.from(normalizedCounts.entries())
-		.filter(([, count]) => count > 0)
-		.sort(([, a], [, b]) => b - a);
-
-	for (const [normalized, count] of remainingEntries) {
-		if (newReactions.length < maxNumber) {
-			newReactions.push([normalizedOriginals.get(normalized)!, count]);
-		}
-	}
-
 	hasMoreReactions.value = Object.keys(newSource).length > maxNumber;
 
-	if (props.note.myReaction) {
-		const normalizedMyReaction = normalizeReaction(props.note.myReaction);
-		const alreadyIncluded = newReactions.some(([x]) =>
-			normalizeReaction(x) === normalizedMyReaction,
-		);
-
-		if (!alreadyIncluded && newSource[props.note.myReaction]) {
-			newReactions.push([
-				props.note.myReaction,
-				newSource[props.note.myReaction],
-			]);
+	for (let i = 0; i < reactions.value.length; i++) {
+		const reaction = reactions.value[i][0];
+		if (reaction in newSource && newSource[reaction] !== 0) {
+			reactions.value[i][1] = newSource[reaction];
+			newReactions.push(reactions.value[i]);
 		}
 	}
 
-	reactions.value = newReactions.slice(0, props.maxNumber);
+	const newReactionsNames = newReactions.map(([x]) => x);
+	newReactions = [
+		...newReactions,
+		...Object.entries(newSource)
+			.sort(([, a], [, b]) => b - a)
+			.filter(([y], i) => i < maxNumber && !newReactionsNames.includes(y)),
+	];
+
+	newReactions = newReactions.slice(0, props.maxNumber);
+
+	if (props.note.myReaction && !newReactions.map(([x]) => x).includes(props.note.myReaction)) {
+		newReactions.push([props.note.myReaction, newSource[props.note.myReaction]]);
+	}
+
+	reactions.value = newReactions;
 }, { immediate: true, deep: true });
 </script>