From 24a0d3cd6f526879e18e00b60245c24a373d9e15 Mon Sep 17 00:00:00 2001
From: FLY_MC <me@flymc.cc>
Date: Wed, 12 Mar 2025 05:43:09 +0800
Subject: [PATCH] frontend: fix merge reactions

---
 .../components/MkReactionsViewer.details.vue  |  8 ++++---
 .../components/MkReactionsViewer.reaction.vue | 19 ++++++++++++++---
 .../src/components/MkReactionsViewer.vue      | 21 +++++++++++++++++--
 3 files changed, 40 insertions(+), 8 deletions(-)

diff --git a/packages/frontend/src/components/MkReactionsViewer.details.vue b/packages/frontend/src/components/MkReactionsViewer.details.vue
index d24e0b15bf..e4e33324a6 100644
--- a/packages/frontend/src/components/MkReactionsViewer.details.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.details.vue
@@ -41,9 +41,11 @@ const emit = defineEmits<{
 }>();
 
 function getReactionName(reaction: string): string {
-	const trimLocal = reaction.replace('@.', '');
-	if (trimLocal.startsWith(':')) {
-		return trimLocal;
+	if (reaction.startsWith(':')) {
+		const match = reaction.match(/^:([^@]+)(?:@[^:]+)?:$/);
+		if (match) {
+			return `:${match[1]}:`;
+		}
 	}
 	return getEmojiName(reaction);
 }
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index f35c938d02..47d5c4a84f 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -64,13 +64,16 @@ async function toggleReaction() {
 
 	const oldReaction = props.note.myReaction;
 	if (oldReaction) {
+		const normalizedOldReaction = normalizeReaction(oldReaction);
+		const normalizedNewReaction = normalizeReaction(props.reaction);
+
 		const confirm = await os.confirm({
 			type: 'warning',
-			text: oldReaction !== props.reaction ? i18n.ts.changeReactionConfirm : i18n.ts.cancelReactionConfirm,
+			text: normalizedOldReaction !== normalizedNewReaction ? i18n.ts.changeReactionConfirm : i18n.ts.cancelReactionConfirm,
 		});
 		if (confirm.canceled) return;
 
-		if (oldReaction !== props.reaction) {
+		if (normalizedOldReaction !== normalizedNewReaction) {
 			sound.playMisskeySfx('reaction');
 		}
 
@@ -82,7 +85,7 @@ async function toggleReaction() {
 		misskeyApi('notes/reactions/delete', {
 			noteId: props.note.id,
 		}).then(() => {
-			if (oldReaction !== props.reaction) {
+			if (normalizedOldReaction !== normalizedNewReaction) {
 				misskeyApi('notes/reactions/create', {
 					noteId: props.note.id,
 					reaction: props.reaction,
@@ -175,6 +178,16 @@ 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 0fed933b56..7edfcc6a72 100644
--- a/packages/frontend/src/components/MkReactionsViewer.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.vue
@@ -41,6 +41,16 @@ const initialReactions = ref(new Set<string>());
 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;
+}
+
 watch(() => props.note.myReaction, (newMyReaction) => {
 	if (newMyReaction && !Object.keys(reactions.value).includes(newMyReaction)) {
 		reactions.value[newMyReaction] = props.note.reactions[newMyReaction];
@@ -50,7 +60,9 @@ watch(() => props.note.myReaction, (newMyReaction) => {
 function onMockToggleReaction(emoji: string, count: number) {
 	if (!mock) return;
 
-	const i = reactions.value.findIndex((item) => item[0] === emoji);
+	const i = reactions.value.findIndex((item) => {
+		return normalizeReaction(item[0]) === normalizeReaction(emoji);
+	});
 	if (i < 0) return;
 
 	emit('mockUpdateMyReaction', emoji, (count - reactions.value[i][1]));
@@ -80,7 +92,12 @@ watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumbe
 	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]]);
+		const normalizedMyReaction = normalizeReaction(props.note.myReaction);
+		const alreadyIncluded = newReactions.some(([x]) => normalizeReaction(x) === normalizedMyReaction);
+
+		if (!alreadyIncluded) {
+			newReactions.push([props.note.myReaction, newSource[props.note.myReaction]]);
+		}
 	}
 
 	reactions.value = newReactions;