From 6f33be6c75866bc40cdfc95beb8ba6e1714f1e42 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sat, 11 Feb 2023 13:05:36 +0900
Subject: [PATCH] =?UTF-8?q?enhance(client):=20MkNote=E3=81=AE=E3=83=AA?=
 =?UTF-8?q?=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE=E8=A1=A8?=
 =?UTF-8?q?=E7=A4=BA=E6=95=B0=E3=81=AF16=E3=81=AB=E5=88=B6=E9=99=90=20(#98?=
 =?UTF-8?q?41)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(client): MkNoteのリアクションの表示数は16に制限・リアクションの横の?ボタンでリアクション詳細

* info-circleにする

* - Layout Shiftが起こらないように
- 自分のリアクションは必ずつける

* https://github.com/misskey-dev/misskey/pull/9841#issuecomment-1423786235

* remove logger

* refactor

* refactor

Co-authored-by: acid-chicken <root@acid-chicken.com>

* Revert "https://github.com/misskey-dev/misskey/pull/9841#issuecomment-1423786235"

This reverts commit ec1315b1fb207e0c5b2a5f2f4a00de7379c7a29b.

* wip

* wip

* :art:

* fix

* fix

* fix

* :art:

* wip

* remove extras from MkNoteDetailed

* もっと!

* no v-if

* dashed

---------

Co-authored-by: acid-chicken <root@acid-chicken.com>
---
 packages/frontend/src/components/MkNote.vue   | 35 +++++++++++--
 .../components/MkReactionsViewer.reaction.vue |  2 +-
 .../src/components/MkReactionsViewer.vue      | 51 ++++++++++++++++---
 3 files changed, 76 insertions(+), 12 deletions(-)

diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index d830e0e47..e910fbab0 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -5,7 +5,7 @@
 	ref="el"
 	v-hotkey="keymap"
 	:class="$style.root"
-	:tabindex="!isDeleted ? '-1' : null"
+	:tabindex="!isDeleted ? '-1' : undefined"
 >
 	<MkNoteSub v-if="appearNote.reply && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/>
 	<div v-if="pinned" :class="$style.tip"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div>
@@ -77,7 +77,13 @@
 				<MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA>
 			</div>
 			<footer :class="$style.footer">
-				<MkReactionsViewer :note="appearNote"/>
+				<MkReactionsViewer :note="appearNote" :max-number="16">
+					<template v-slot:more>
+						<button class="_button" :class="$style.reactionDetailsButton" @click="showReactions">
+							{{ i18n.ts.more }}
+						</button>
+					</template>
+				</MkReactionsViewer>
 				<button :class="$style.footerButton" class="_button" @click="reply()">
 					<i class="ti ti-arrow-back-up"></i>
 					<p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ appearNote.repliesCount }}</p>
@@ -120,7 +126,7 @@
 </template>
 
 <script lang="ts" setup>
-import { computed, inject, onMounted, onUnmounted, reactive, ref, shallowRef, Ref } from 'vue';
+import { computed, inject, onMounted, onUnmounted, reactive, ref, shallowRef, Ref, defineAsyncComponent } from 'vue';
 import * as mfm from 'mfm-js';
 import * as misskey from 'misskey-js';
 import MkNoteSub from '@/components/MkNoteSub.vue';
@@ -196,7 +202,7 @@ const isLong = (appearNote.cw == null && appearNote.text != null && (
 const collapsed = ref(appearNote.cw == null && isLong);
 const isDeleted = ref(false);
 const muted = ref(checkWordMute(appearNote, $i, defaultStore.state.mutedWords));
-const translation = ref(null);
+const translation = ref<any>(null);
 const translating = ref(false);
 const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance);
 const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || appearNote.userId === $i.id);
@@ -361,6 +367,12 @@ function readPromo() {
 	});
 	isDeleted.value = true;
 }
+
+function showReactions(): void {
+	os.popup(defineAsyncComponent(() => import('@/components/MkReactedUsersDialog.vue')), {
+		noteId: appearNote.id,
+	}, {}, 'closed');
+}
 </script>
 
 <style lang="scss" module>
@@ -697,4 +709,19 @@ function readPromo() {
 	text-align: center;
 	opacity: 0.7;
 }
+
+.reactionDetailsButton {
+	display: inline-block;
+	height: 32px;
+	margin: 2px;
+	padding: 0 6px;
+	border: dashed 1px var(--divider);
+	border-radius: 4px;
+	background: transparent;
+	opacity: .8;
+
+	&:hover {
+		background: var(--X5);
+	}
+}
 </style>
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index 83fdf0f98..4abd2562d 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -107,7 +107,7 @@ useTooltip(buttonEl, async (showing) => {
 	border-radius: 4px;
 
 	&.canToggle {
-		background: rgba(0, 0, 0, 0.05);
+		background: var(--buttonBg);
 
 		&:hover {
 			background: rgba(0, 0, 0, 0.1);
diff --git a/packages/frontend/src/components/MkReactionsViewer.vue b/packages/frontend/src/components/MkReactionsViewer.vue
index 5981471c6..cdd6f528e 100644
--- a/packages/frontend/src/components/MkReactionsViewer.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.vue
@@ -7,23 +7,60 @@
 	:move-class="$store.state.animation ? $style.transition_x_move : ''"
 	tag="div" :class="$style.root"
 >
-	<XReaction v-for="(count, reaction) in note.reactions" :key="reaction" :reaction="reaction" :count="count" :is-initial="initialReactions.has(reaction)" :note="note"/>
+	<XReaction v-for="[reaction, count] in reactions" :key="reaction" :reaction="reaction" :count="count" :is-initial="initialReactions.has(reaction)" :note="note"/>
+	<slot v-if="hasMoreReactions" name="more" />
 </TransitionGroup>
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue';
 import * as misskey from 'misskey-js';
-import { $i } from '@/account';
 import XReaction from '@/components/MkReactionsViewer.reaction.vue';
+import { watch } from 'vue';
 
-const props = defineProps<{
-	note: misskey.entities.Note;
-}>();
+const props = withDefaults(defineProps<{
+    note: misskey.entities.Note;
+    maxNumber?: number;
+}>(), {
+    maxNumber: Infinity,
+});
 
 const initialReactions = new Set(Object.keys(props.note.reactions));
 
-const isMe = computed(() => $i && $i.id === props.note.userId);
+let reactions = $ref<[string, number][]>([]);
+let hasMoreReactions = $ref(false);
+
+if (props.note.myReaction && !Object.keys(reactions).includes(props.note.myReaction)) {
+	reactions[props.note.myReaction] = props.note.reactions[props.note.myReaction];
+}
+
+watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumber]) => {
+	let newReactions: [string, number][] = [];
+	hasMoreReactions = Object.keys(newSource).length > maxNumber;
+
+	for (let i = 0; i < reactions.length; i++) {
+		const reaction = reactions[i][0];
+		if (reaction in newSource && newSource[reaction] !== 0) {
+			reactions[i][1] = newSource[reaction];
+			newReactions.push(reactions[i]);
+		}
+	}
+
+	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 = newReactions;
+}, { immediate: true, deep: true });
 </script>
 
 <style lang="scss" module>