add: like button
This commit is contained in:
parent
8d4e99b3a9
commit
802ad0fa02
3 changed files with 66 additions and 9 deletions
|
@ -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_),
|
|
||||||
} : {}),
|
|
||||||
} : {}),
|
} : {}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue