merge: add like button #32

Add Like Button
This commit is contained in:
Marie 2023-10-01 01:55:40 +02:00 committed by GitHub
commit e5d9eb3082
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 66 additions and 9 deletions

View file

@ -343,6 +343,9 @@ export class NoteEntityService implements OnModuleInit {
uri: note.uri ?? undefined, uri: note.uri ?? undefined,
url: note.url ?? undefined, url: note.url ?? undefined,
updatedAt: note.updatedAt != null ? note.updatedAt.toISOString() : undefined, updatedAt: note.updatedAt != null ? note.updatedAt.toISOString() : undefined,
...(meId ? {
myReaction: this.populateMyReaction(note, meId, options?._hint_),
} : {}),
...(opts.detail ? { ...(opts.detail ? {
clippedCount: note.clippedCount, clippedCount: note.clippedCount,
@ -358,10 +361,6 @@ export class NoteEntityService implements OnModuleInit {
}) : undefined, }) : undefined,
poll: note.hasPoll ? this.populatePoll(note, meId) : undefined, poll: note.hasPoll ? this.populatePoll(note, meId) : undefined,
...(meId ? {
myReaction: this.populateMyReaction(note, meId, options?._hint_),
} : {}),
} : {}), } : {}),
}); });

View file

@ -116,6 +116,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<button v-else class="_button" :class="$style.noteFooterButton" disabled> <button v-else class="_button" :class="$style.noteFooterButton" disabled>
<i class="ph-prohibit ph-bold ph-lg"></i> <i class="ph-prohibit ph-bold ph-lg"></i>
</button> </button>
<button v-if="appearNote.myReaction == null && appearNote.reactionAcceptance !== 'likeOnly'" ref="likeButton" :class="$style.noteFooterButton" class="_button" @mousedown="like()">
<i class="ph-heart ph-bold ph-lg"></i>
</button>
<button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.noteFooterButton" class="_button" @mousedown="react()"> <button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.noteFooterButton" class="_button" @mousedown="react()">
<i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ph-heart ph-bold ph-lg"></i> <i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ph-heart ph-bold ph-lg"></i>
<i v-else class="ph-smiley ph-bold ph-lg"></i> <i v-else class="ph-smiley ph-bold ph-lg"></i>
@ -252,6 +255,7 @@ const renoteButton = shallowRef<HTMLElement>();
const renoteTime = shallowRef<HTMLElement>(); const renoteTime = shallowRef<HTMLElement>();
const reactButton = shallowRef<HTMLElement>(); const reactButton = shallowRef<HTMLElement>();
const clipButton = shallowRef<HTMLElement>(); const clipButton = shallowRef<HTMLElement>();
const likeButton = shallowRef<HTMLElement>();
let appearNote = $computed(() => isRenote ? note.renote as Misskey.entities.Note : note); let appearNote = $computed(() => isRenote ? note.renote as Misskey.entities.Note : note);
const isMyRenote = $i && ($i.id === note.userId); const isMyRenote = $i && ($i.id === note.userId);
const showContent = ref(false); const showContent = ref(false);
@ -432,6 +436,22 @@ function react(viaKeyboard = false): void {
} }
} }
function like(): void {
pleaseLogin();
showMovedDialog();
os.api('notes/reactions/create', {
noteId: props.note.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);
os.popup(MkRippleEffect, { x, y }, {}, 'end');
}
}
function undoReact(note): void { function undoReact(note): void {
const oldReaction = note.myReaction; const oldReaction = note.myReaction;
if (!oldReaction) return; if (!oldReaction) return;

View file

@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
--> -->
<template> <template>
<div v-if="!muted" :class="[$style.root, { [$style.children]: depth > 1 }]"> <div ref="el" v-if="!muted" :class="[$style.root, { [$style.children]: depth > 1 }]">
<div :class="$style.main"> <div :class="$style.main">
<div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div> <div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>
<MkAvatar :class="$style.avatar" :user="note.user" link preview/> <MkAvatar :class="$style.avatar" :user="note.user" link preview/>
@ -38,6 +38,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<button v-else class="_button" :class="$style.noteFooterButton" disabled> <button v-else class="_button" :class="$style.noteFooterButton" disabled>
<i class="ph-prohibit ph-bold ph-lg"></i> <i class="ph-prohibit ph-bold ph-lg"></i>
</button> </button>
<button v-if="note.myReaction == null && note.reactionAcceptance !== 'likeOnly'" ref="likeButton" :class="$style.noteFooterButton" class="_button" @mousedown="like()">
<i class="ph-heart ph-bold ph-lg"></i>
</button>
<button v-if="note.myReaction == null" ref="reactButton" :class="$style.noteFooterButton" class="_button" @mousedown="react()"> <button v-if="note.myReaction == null" ref="reactButton" :class="$style.noteFooterButton" class="_button" @mousedown="react()">
<i v-if="note.reactionAcceptance === 'likeOnly'" class="ph-heart ph-bold ph-lg"></i> <i v-if="note.reactionAcceptance === 'likeOnly'" class="ph-heart ph-bold ph-lg"></i>
<i v-else class="ph-smiley ph-bold ph-lg"></i> <i v-else class="ph-smiley ph-bold ph-lg"></i>
@ -90,6 +93,8 @@ import { reactionPicker } from '@/scripts/reaction-picker.js';
import { claimAchievement } from '@/scripts/achievements.js'; import { claimAchievement } from '@/scripts/achievements.js';
import type { MenuItem } from '@/types/menu.js'; import type { MenuItem } from '@/types/menu.js';
import { getNoteMenu } from '@/scripts/get-note-menu.js'; import { getNoteMenu } from '@/scripts/get-note-menu.js';
import { useNoteCapture } from '@/scripts/use-note-capture.js';
const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i.id); const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i.id);
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
@ -102,10 +107,7 @@ const props = withDefaults(defineProps<{
depth: 1, depth: 1,
}); });
function focus() { const el = shallowRef<HTMLElement>();
el.value.focus();
}
const muted = ref(checkWordMute(props.note, $i, defaultStore.state.mutedWords)); const muted = ref(checkWordMute(props.note, $i, defaultStore.state.mutedWords));
const translation = ref(null); const translation = ref(null);
const translating = ref(false); const translating = ref(false);
@ -113,6 +115,26 @@ const isDeleted = ref(false);
const reactButton = shallowRef<HTMLElement>(); const reactButton = shallowRef<HTMLElement>();
const renoteButton = shallowRef<HTMLElement>(); const renoteButton = shallowRef<HTMLElement>();
const menuButton = shallowRef<HTMLElement>(); const menuButton = shallowRef<HTMLElement>();
const likeButton = shallowRef<HTMLElement>();
let appearNote = $computed(() => isRenote ? props.note.renote as Misskey.entities.Note : props.note);
const isRenote = (
props.note.renote != null &&
props.note.text == null &&
props.note.fileIds.length === 0 &&
props.note.poll == null
);
useNoteCapture({
rootEl: el,
note: $$(appearNote),
isDeletedRef: isDeleted,
});
function focus() {
el.value.focus();
}
function reply(viaKeyboard = false): void { function reply(viaKeyboard = false): void {
pleaseLogin(); pleaseLogin();
@ -157,6 +179,22 @@ function react(viaKeyboard = false): void {
} }
} }
function like(): void {
pleaseLogin();
showMovedDialog();
os.api('notes/reactions/create', {
noteId: props.note.id,
reaction: '❤️',
});
const el = reactButton.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);
os.popup(MkRippleEffect, { x, y }, {}, 'end');
}
}
function undoReact(note): void { function undoReact(note): void {
const oldReaction = note.myReaction; const oldReaction = note.myReaction;
if (!oldReaction) return; if (!oldReaction) return;