wip
This commit is contained in:
parent
6b7806a3ae
commit
7246f6529f
|
@ -210,7 +210,7 @@ import { extractUrlFromMfm } from '@/utility/extract-url-from-mfm.js';
|
|||
import { $i } from '@/i.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu, getRenoteMenu } from '@/utility/get-note-menu.js';
|
||||
import { useNoteCapture } from '@/use/use-note-capture.js';
|
||||
import { noteEvents, useNoteCapture } from '@/use/use-note-capture.js';
|
||||
import { deepClone } from '@/utility/clone.js';
|
||||
import { useTooltip } from '@/use/use-tooltip.js';
|
||||
import { claimAchievement } from '@/utility/achievements.js';
|
||||
|
@ -382,6 +382,11 @@ provide(DI.mfmEmojiReactCallback, (reaction) => {
|
|||
misskeyApi('notes/reactions/create', {
|
||||
noteId: appearNote.value.id,
|
||||
reaction: reaction,
|
||||
}).then(() => {
|
||||
noteEvents.emit(`reacted:${appearNote.value.id}`, {
|
||||
userId: $i!.id,
|
||||
reaction: reaction,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -480,6 +485,11 @@ function react(): void {
|
|||
misskeyApi('notes/reactions/create', {
|
||||
noteId: appearNote.value.id,
|
||||
reaction: '❤️',
|
||||
}).then(() => {
|
||||
noteEvents.emit(`reacted:${appearNote.value.id}`, {
|
||||
userId: $i!.id,
|
||||
reaction: '❤️',
|
||||
});
|
||||
});
|
||||
const el = reactButton.value;
|
||||
if (el && prefer.s.animation) {
|
||||
|
@ -513,7 +523,10 @@ function react(): void {
|
|||
noteId: appearNote.value.id,
|
||||
reaction: reaction,
|
||||
}).then(() => {
|
||||
// 別にthenを待たなくても良いかも(楽観的更新)
|
||||
noteEvents.emit(`reacted:${appearNote.value.id}`, {
|
||||
userId: $i!.id,
|
||||
reaction: reaction,
|
||||
});
|
||||
});
|
||||
|
||||
if (appearNote.value.text && appearNote.value.text.length > 100 && (Date.now() - new Date(appearNote.value.createdAt).getTime() < 1000 * 3)) {
|
||||
|
|
|
@ -242,7 +242,7 @@ import { extractUrlFromMfm } from '@/utility/extract-url-from-mfm.js';
|
|||
import { $i } from '@/i.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { getNoteClipMenu, getNoteMenu, getRenoteMenu } from '@/utility/get-note-menu.js';
|
||||
import { useNoteCapture } from '@/use/use-note-capture.js';
|
||||
import { noteEvents, useNoteCapture } from '@/use/use-note-capture.js';
|
||||
import { deepClone } from '@/utility/clone.js';
|
||||
import { useTooltip } from '@/use/use-tooltip.js';
|
||||
import { claimAchievement } from '@/utility/achievements.js';
|
||||
|
@ -343,6 +343,11 @@ provide(DI.mfmEmojiReactCallback, (reaction) => {
|
|||
misskeyApi('notes/reactions/create', {
|
||||
noteId: appearNote.value.id,
|
||||
reaction: reaction,
|
||||
}).then(() => {
|
||||
noteEvents.emit(`reacted:${appearNote.value.id}`, {
|
||||
userId: $i!.id,
|
||||
reaction: reaction,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -445,6 +450,11 @@ function react(): void {
|
|||
misskeyApi('notes/reactions/create', {
|
||||
noteId: appearNote.value.id,
|
||||
reaction: '❤️',
|
||||
}).then(() => {
|
||||
noteEvents.emit(`reacted:${appearNote.value.id}`, {
|
||||
userId: $i!.id,
|
||||
reaction: '❤️',
|
||||
});
|
||||
});
|
||||
const el = reactButton.value;
|
||||
if (el && prefer.s.animation) {
|
||||
|
@ -472,6 +482,11 @@ function react(): void {
|
|||
misskeyApi('notes/reactions/create', {
|
||||
noteId: appearNote.value.id,
|
||||
reaction: reaction,
|
||||
}).then(() => {
|
||||
noteEvents.emit(`reacted:${appearNote.value.id}`, {
|
||||
userId: $i!.id,
|
||||
reaction: reaction,
|
||||
});
|
||||
});
|
||||
if (appearNote.value.text && appearNote.value.text.length > 100 && (Date.now() - new Date(appearNote.value.createdAt).getTime() < 1000 * 3)) {
|
||||
claimAchievement('reactWithoutRead');
|
||||
|
|
|
@ -105,11 +105,7 @@ function onNotification(notification) {
|
|||
}
|
||||
|
||||
function reload() {
|
||||
return new Promise<void>((res) => {
|
||||
paginator.reload().then(() => {
|
||||
res();
|
||||
});
|
||||
});
|
||||
return paginator.reload();
|
||||
}
|
||||
|
||||
let connection: Misskey.ChannelConnection<Misskey.Channels['main']> | null = null;
|
||||
|
|
|
@ -9,6 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
:leaveActiveClass="prefer.s.animation ? $style.transition_fade_leaveActive : ''"
|
||||
:enterFromClass="prefer.s.animation ? $style.transition_fade_enterFrom : ''"
|
||||
:leaveToClass="prefer.s.animation ? $style.transition_fade_leaveTo : ''"
|
||||
:css="prefer.s.animation"
|
||||
mode="out-in"
|
||||
>
|
||||
<MkLoading v-if="paginator.fetching.value"/>
|
||||
|
|
|
@ -36,6 +36,7 @@ import { checkReactionPermissions } from '@/utility/check-reaction-permissions.j
|
|||
import { customEmojisMap } from '@/custom-emojis.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { DI } from '@/di.js';
|
||||
import { noteEvents } from '@/use/use-note-capture.js';
|
||||
|
||||
const props = defineProps<{
|
||||
reaction: string;
|
||||
|
@ -83,10 +84,21 @@ async function toggleReaction() {
|
|||
misskeyApi('notes/reactions/delete', {
|
||||
noteId: props.note.id,
|
||||
}).then(() => {
|
||||
noteEvents.emit(`unreacted:${props.note.id}`, {
|
||||
userId: $i!.id,
|
||||
reaction: props.reaction,
|
||||
emoji: emoji.value,
|
||||
});
|
||||
if (oldReaction !== props.reaction) {
|
||||
misskeyApi('notes/reactions/create', {
|
||||
noteId: props.note.id,
|
||||
reaction: props.reaction,
|
||||
}).then(() => {
|
||||
noteEvents.emit(`reacted:${props.note.id}`, {
|
||||
userId: $i!.id,
|
||||
reaction: props.reaction,
|
||||
emoji: emoji.value,
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -110,6 +122,12 @@ async function toggleReaction() {
|
|||
misskeyApi('notes/reactions/create', {
|
||||
noteId: props.note.id,
|
||||
reaction: props.reaction,
|
||||
}).then(() => {
|
||||
noteEvents.emit(`reacted:${props.note.id}`, {
|
||||
userId: $i!.id,
|
||||
reaction: props.reaction,
|
||||
emoji: emoji.value,
|
||||
});
|
||||
});
|
||||
if (props.note.text && props.note.text.length > 100 && (Date.now() - new Date(props.note.createdAt).getTime() < 1000 * 3)) {
|
||||
claimAchievement('reactWithoutRead');
|
||||
|
|
|
@ -12,11 +12,11 @@ import { $i } from '@/i.js';
|
|||
import { store } from '@/store.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
|
||||
const noteEvents = new EventEmitter<{
|
||||
reacted: Misskey.entities.Note;
|
||||
unreacted: Misskey.entities.Note;
|
||||
pollVoted: Misskey.entities.Note;
|
||||
deleted: Misskey.entities.Note;
|
||||
export const noteEvents = new EventEmitter<{
|
||||
[`reacted:${string}`]: (ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; }; }) => void;
|
||||
[`unreacted:${string}`]: (ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; }; }) => void;
|
||||
[`pollVoted:${string}`]: (ctx: { userId: Misskey.entities.User['id']; choice: string; }) => void;
|
||||
[`deleted:${string}`]: () => void;
|
||||
}>();
|
||||
|
||||
const fetchEvent = new EventEmitter<{
|
||||
|
@ -55,10 +55,6 @@ function pseudoNoteCapture(props: {
|
|||
const note = props.note;
|
||||
const pureNote = props.pureNote;
|
||||
|
||||
function onReacted(): void {
|
||||
|
||||
}
|
||||
|
||||
function onFetched(data: Pick<Misskey.entities.Note, 'reactions' | 'reactionEmojis'>): void {
|
||||
note.value.reactions = data.reactions;
|
||||
note.value.reactionCount = Object.values(data.reactions).reduce((a, b) => a + b, 0);
|
||||
|
@ -100,58 +96,33 @@ function realtimeNoteCapture(props: {
|
|||
|
||||
switch (type) {
|
||||
case 'reacted': {
|
||||
const reaction = body.reaction;
|
||||
|
||||
if (body.emoji && !(body.emoji.name in note.value.reactionEmojis)) {
|
||||
note.value.reactionEmojis[body.emoji.name] = body.emoji.url;
|
||||
}
|
||||
|
||||
// TODO: reactionsプロパティがない場合ってあったっけ? なければ || {} は消せる
|
||||
const currentCount = (note.value.reactions || {})[reaction] || 0;
|
||||
|
||||
note.value.reactions[reaction] = currentCount + 1;
|
||||
note.value.reactionCount += 1;
|
||||
|
||||
if ($i && (body.userId === $i.id)) {
|
||||
note.value.myReaction = reaction;
|
||||
}
|
||||
noteEvents.emit(`reacted:${id}`, {
|
||||
userId: body.userId,
|
||||
reaction: body.reaction,
|
||||
emoji: body.emoji,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'unreacted': {
|
||||
const reaction = body.reaction;
|
||||
|
||||
// TODO: reactionsプロパティがない場合ってあったっけ? なければ || {} は消せる
|
||||
const currentCount = (note.value.reactions || {})[reaction] || 0;
|
||||
|
||||
note.value.reactions[reaction] = Math.max(0, currentCount - 1);
|
||||
note.value.reactionCount = Math.max(0, note.value.reactionCount - 1);
|
||||
if (note.value.reactions[reaction] === 0) delete note.value.reactions[reaction];
|
||||
|
||||
if ($i && (body.userId === $i.id)) {
|
||||
note.value.myReaction = null;
|
||||
}
|
||||
noteEvents.emit(`unreacted:${id}`, {
|
||||
userId: body.userId,
|
||||
reaction: body.reaction,
|
||||
emoji: body.emoji,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'pollVoted': {
|
||||
const choice = body.choice;
|
||||
|
||||
const choices = [...note.value.poll.choices];
|
||||
choices[choice] = {
|
||||
...choices[choice],
|
||||
votes: choices[choice].votes + 1,
|
||||
...($i && (body.userId === $i.id) ? {
|
||||
isVoted: true,
|
||||
} : {}),
|
||||
};
|
||||
|
||||
note.value.poll.choices = choices;
|
||||
noteEvents.emit(`pollVoted:${id}`, {
|
||||
userId: body.userId,
|
||||
choice: body.choice,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'deleted': {
|
||||
props.isDeletedRef.value = true;
|
||||
noteEvents.emit(`deleted:${id}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -195,6 +166,80 @@ export function useNoteCapture(props: {
|
|||
pureNote: Ref<Misskey.entities.Note>;
|
||||
isDeletedRef: Ref<boolean>;
|
||||
}) {
|
||||
const note = props.note;
|
||||
noteEvents.on(`reacted:${note.value.id}`, onReacted);
|
||||
noteEvents.on(`unreacted:${note.value.id}`, onUnreacted);
|
||||
noteEvents.on(`pollVoted:${note.value.id}`, onPollVoted);
|
||||
noteEvents.on(`deleted:${note.value.id}`, onDeleted);
|
||||
|
||||
let latestReactedKey: string | null = null;
|
||||
let latestUnreactedKey: string | null = null;
|
||||
let latestPollVotedKey: string | null = null;
|
||||
|
||||
function onReacted(ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; }; }): void {
|
||||
console.log('reacted', ctx);
|
||||
const newReactedKey = `${ctx.userId}:${ctx.reaction}`;
|
||||
if (newReactedKey === latestReactedKey) return;
|
||||
latestReactedKey = newReactedKey;
|
||||
|
||||
if (ctx.emoji && !(ctx.emoji.name in note.value.reactionEmojis)) {
|
||||
note.value.reactionEmojis[ctx.emoji.name] = ctx.emoji.url;
|
||||
}
|
||||
|
||||
const currentCount = note.value.reactions[ctx.reaction] || 0;
|
||||
|
||||
note.value.reactions[ctx.reaction] = currentCount + 1;
|
||||
note.value.reactionCount += 1;
|
||||
|
||||
if ($i && (ctx.userId === $i.id)) {
|
||||
note.value.myReaction = ctx.reaction;
|
||||
}
|
||||
}
|
||||
|
||||
function onUnreacted(ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; }; }): void {
|
||||
const newUnreactedKey = `${ctx.userId}:${ctx.reaction}`;
|
||||
if (newUnreactedKey === latestUnreactedKey) return;
|
||||
latestUnreactedKey = newUnreactedKey;
|
||||
|
||||
const currentCount = note.value.reactions[ctx.reaction] || 0;
|
||||
|
||||
note.value.reactions[ctx.reaction] = Math.max(0, currentCount - 1);
|
||||
note.value.reactionCount = Math.max(0, note.value.reactionCount - 1);
|
||||
if (note.value.reactions[ctx.reaction] === 0) delete note.value.reactions[ctx.reaction];
|
||||
|
||||
if ($i && (ctx.userId === $i.id)) {
|
||||
note.value.myReaction = null;
|
||||
}
|
||||
}
|
||||
|
||||
function onPollVoted(ctx: { userId: Misskey.entities.User['id']; choice: string; }): void {
|
||||
const newPollVotedKey = `${ctx.userId}:${ctx.choice}`;
|
||||
if (newPollVotedKey === latestPollVotedKey) return;
|
||||
latestPollVotedKey = newPollVotedKey;
|
||||
|
||||
const choices = [...note.value.poll.choices];
|
||||
choices[ctx.choice] = {
|
||||
...choices[ctx.choice],
|
||||
votes: choices[ctx.choice].votes + 1,
|
||||
...($i && (ctx.userId === $i.id) ? {
|
||||
isVoted: true,
|
||||
} : {}),
|
||||
};
|
||||
|
||||
note.value.poll.choices = choices;
|
||||
}
|
||||
|
||||
function onDeleted(): void {
|
||||
props.isDeletedRef.value = true;
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
noteEvents.off(`reacted:${note.value.id}`, onReacted);
|
||||
noteEvents.off(`unreacted:${note.value.id}`, onUnreacted);
|
||||
noteEvents.off(`pollVoted:${note.value.id}`, onPollVoted);
|
||||
noteEvents.off(`deleted:${note.value.id}`, onDeleted);
|
||||
});
|
||||
|
||||
if ($i && store.s.realtimeMode) {
|
||||
realtimeNoteCapture(props);
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue