From bafcb9387833cde238ebf5f4670cfcd33734b222 Mon Sep 17 00:00:00 2001
From: fly_mc <me@flymc.cc>
Date: Tue, 15 Oct 2024 14:08:20 +0800
Subject: [PATCH] tweak Mknote & MkDetailed

---
 packages/frontend/src/components/MkNote.vue   | 58 +++++++++++++++----
 .../src/components/MkNoteDetailed.vue         | 40 ++++++++++++-
 2 files changed, 84 insertions(+), 14 deletions(-)

diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 828ad2e872..39a4ef45ac 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -46,11 +46,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<Mfm :text="getNoteSummary(appearNote)" :plain="true" :nowrap="true" :author="appearNote.user" :nyaize="'respect'" :class="$style.collapsedRenoteTargetText" @click="renoteCollapsed = false"/>
 	</div>
 	<article v-else :class="$style.article" @contextmenu.stop="onContextmenu">
+		<div style="display: flex; padding-bottom: 10px;">
 		<div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div>
 		<MkAvatar :class="$style.avatar" :user="appearNote.user" :link="!mock" :preview="!mock"/>
 		<div :class="$style.main">
 			<MkNoteHeader :note="appearNote" :mini="true"/>
 			<MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/>
+		</div>
+	    </div>
 			<div style="container-type: inline-size;">
 				<p v-if="appearNote.cw != null" :class="$style.cw">
 					<Mfm
@@ -125,11 +128,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<button v-else :class="$style.footerButton" class="_button" disabled>
 					<i class="ti ti-ban"></i>
 				</button>
+				<button v-if="appearNote.myReaction == null && appearNote.reactionAcceptance !== 'likeOnly'" ref="likeButton" :class="$style.footerButton" class="_button" @click.stop @click="like()">
+					<i class="ti ti-heart"></i>
+				</button>
 				<button ref="reactButton" :class="$style.footerButton" class="_button" @click="toggleReact()">
 					<i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReaction != null" class="ti ti-heart-filled" style="color: var(--MI_THEME-love);"></i>
 					<i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--MI_THEME-accent);"></i>
 					<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
-					<i v-else class="ti ti-plus"></i>
+					<i v-else class="ti ti-mood-plus"></i>
 					<p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.reactionCount) }}</p>
 				</button>
 				<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" class="_button" @mousedown.prevent="clip()">
@@ -139,7 +145,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<i class="ti ti-dots"></i>
 				</button>
 			</footer>
-		</div>
+		
 	</article>
 </div>
 <div v-else-if="!hardMuted" :class="$style.muted" @click="muted = false">
@@ -259,6 +265,7 @@ const renoteButton = shallowRef<HTMLElement>();
 const renoteTime = shallowRef<HTMLElement>();
 const reactButton = shallowRef<HTMLElement>();
 const clipButton = shallowRef<HTMLElement>();
+const likeButton = shallowRef<HTMLElement>();
 const appearNote = computed(() => getAppearNote(note.value));
 const galleryEl = shallowRef<InstanceType<typeof MkMediaList>>();
 const isMyRenote = $i && ($i.id === note.value.userId);
@@ -441,6 +448,28 @@ function reply(): void {
 	});
 }
 
+function like(): void {
+	pleaseLogin(undefined, pleaseLoginContext.value);
+	showMovedDialog();
+	sound.playMisskeySfx('reaction');
+	if (props.mock) {
+		return;
+	}
+	misskeyApi('notes/reactions/create', {
+		noteId: appearNote.value.id,
+		reaction: '❤️',
+	});
+	const el = likeButton.value as HTMLElement | null | undefined;
+	if (el) {
+		const rect = el.getBoundingClientRect();
+		const x = rect.left + (el.offsetWidth / 2);
+		const y = rect.top + (el.offsetHeight / 2);
+		const { dispose } = os.popup(MkRippleEffect, { x, y }, {
+			end: () => dispose(),
+		});
+	}
+}
+
 function react(): void {
 	pleaseLogin(undefined, pleaseLoginContext.value);
 	showMovedDialog();
@@ -642,8 +671,13 @@ function emitUpdReaction(emoji: string, delta: number) {
 	}
 
 	.footer {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
 		position: relative;
 		z-index: 1;
+		margin-top: 0.4em;
+		max-width: 400px;
 	}
 
 	&:hover > .article > .main > .footer > .footerButton {
@@ -786,7 +820,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 
 .article {
 	position: relative;
-	display: flex;
+	//display: flex;
 	padding: 28px 32px;
 }
 
@@ -806,8 +840,8 @@ function emitUpdReaction(emoji: string, delta: number) {
 	margin: 0 14px 0 0;
 	width: 58px;
 	height: 58px;
-	position: sticky !important;
-	top: calc(22px + var(--MI-stickyTop, 0px));
+	//position: sticky !important;
+	//top: calc(22px + var(--MI-stickyTop, 0px));
 	left: 0;
 }
 
@@ -920,7 +954,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 	opacity: 0.7;
 
 	&:not(:last-child) {
-		margin-right: 28px;
+		margin-right: 1.5em;
 	}
 
 	&:hover {
@@ -963,7 +997,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 	}
 
 	.article {
-		padding: 20px 22px;
+		padding: 23px 25px;
 	}
 
 	.footer {
@@ -986,7 +1020,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 	}
 
 	.article {
-		padding: 14px 16px;
+		padding: 22px 24px;
 	}
 }
 
@@ -995,7 +1029,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 		margin: 0 10px 0 0;
 		width: 46px;
 		height: 46px;
-		top: calc(14px + var(--MI-stickyTop, 0px));
+		//top: calc(14px + var(--MI-stickyTop, 0px));
 	}
 }
 
@@ -1003,7 +1037,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 	.root:not(.showActionsOnlyHover) {
 		.footerButton {
 			&:not(:last-child) {
-				margin-right: 18px;
+				margin-right: 0.2em;
 			}
 		}
 	}
@@ -1013,7 +1047,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 	.root:not(.showActionsOnlyHover) {
 		.footerButton {
 			&:not(:last-child) {
-				margin-right: 12px;
+				margin-right: 0.1em;
 			}
 		}
 	}
@@ -1035,7 +1069,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 	.root:not(.showActionsOnlyHover) {
 		.footerButton {
 			&:not(:last-child) {
-				margin-right: 8px;
+				margin-right: 0.1em;
 			}
 		}
 	}
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index ab70510517..ba28c2e8ea 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -150,11 +150,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<button v-else class="_button" :class="$style.noteFooterButton" disabled>
 				<i class="ti ti-ban"></i>
 			</button>
+			<button v-if="appearNote.myReaction == null && appearNote.reactionAcceptance !== 'likeOnly'" ref="likeButton" :class="$style.noteFooterButton" class="_button" @mousedown="like()">
+				<i class="ti ti-heart"></i>
+			</button>
 			<button ref="reactButton" :class="$style.noteFooterButton" class="_button" @click="toggleReact()">
 				<i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReaction != null" class="ti ti-heart-filled" style="color: var(--MI_THEME-love);"></i>
 				<i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--MI_THEME-accent);"></i>
 				<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
-				<i v-else class="ti ti-plus"></i>
+				<i v-else class="ti ti-mood-plus"></i>
 				<p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.reactionCount) }}</p>
 			</button>
 			<button
@@ -309,6 +312,7 @@ const renoteButton = shallowRef<HTMLElement>();
 const renoteTime = shallowRef<HTMLElement>();
 const reactButton = shallowRef<HTMLElement>();
 const clipButton = shallowRef<HTMLElement>();
+const likeButton = shallowRef<HTMLElement>();
 const historyMenuButton = shallowRef<HTMLElement>();
 const appearNote = computed(() => getAppearNote(note.value));
 const galleryEl = shallowRef<InstanceType<typeof MkMediaList>>();
@@ -455,6 +459,28 @@ function reply(): void {
 	});
 }
 
+function like(): void {
+	pleaseLogin(undefined, pleaseLoginContext.value);
+	showMovedDialog();
+	sound.playMisskeySfx('reaction');
+	if (props.mock) {
+		return;
+	}
+	misskeyApi('notes/reactions/create', {
+		noteId: appearNote.value.id,
+		reaction: '❤️',
+	});
+	const el = likeButton.value as HTMLElement | null | undefined;
+	if (el) {
+		const rect = el.getBoundingClientRect();
+		const x = rect.left + (el.offsetWidth / 2);
+		const y = rect.top + (el.offsetHeight / 2);
+		const { dispose } = os.popup(MkRippleEffect, { x, y }, {
+			end: () => dispose(),
+		});
+	}
+}
+
 function react(): void {
 	pleaseLogin(undefined, pleaseLoginContext.value);
 	showMovedDialog();
@@ -650,6 +676,16 @@ onMounted(() => {
 	}
 }
 
+.footer {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		position: relative;
+		z-index: 1;
+		margin-top: 0.4em;
+		max-width: 400px;
+}
+
 .replyTo {
 	opacity: 0.7;
 	padding-bottom: 0;
@@ -823,7 +859,7 @@ onMounted(() => {
 	opacity: 0.7;
 
 	&:not(:last-child) {
-		margin-right: 28px;
+		margin-right: 1.5em;
 	}
 
 	&:hover {