wip
This commit is contained in:
parent
607196e863
commit
d01a49d19a
|
@ -6,11 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<template>
|
||||
<div
|
||||
v-if="!hardMuted && muted === false"
|
||||
v-show="!isDeleted"
|
||||
ref="rootEl"
|
||||
v-hotkey="keymap"
|
||||
:class="[$style.root, { [$style.showActionsOnlyHover]: prefer.s.showNoteActionsOnlyHover, [$style.skipRender]: prefer.s.skipNoteRender }]"
|
||||
:tabindex="isDeleted ? '-1' : '0'"
|
||||
tabindex="0"
|
||||
>
|
||||
<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>
|
||||
|
@ -241,6 +240,7 @@ import { getAppearNote } from '@/utility/get-appear-note.js';
|
|||
import { prefer } from '@/preferences.js';
|
||||
import { getPluginHandlers } from '@/plugin.js';
|
||||
import { DI } from '@/di.js';
|
||||
import { globalEvents } from '@/events.js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
note: Misskey.entities.Note;
|
||||
|
@ -273,10 +273,6 @@ if (noteViewInterruptors.length > 0) {
|
|||
for (const interruptor of noteViewInterruptors) {
|
||||
try {
|
||||
result = await interruptor.handler(result!) as Misskey.entities.Note | null;
|
||||
if (result === null) {
|
||||
isDeleted.value = true;
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
@ -308,7 +304,6 @@ const parsed = computed(() => appearNote.text ? mfm.parse(appearNote.text) : nul
|
|||
const urls = computed(() => parsed.value ? extractUrlFromMfm(parsed.value).filter((url) => appearNote.renote?.url !== url && appearNote.renote?.uri !== url) : null);
|
||||
const isLong = shouldCollapsed(appearNote, urls.value ?? []);
|
||||
const collapsed = ref(appearNote.cw == null && isLong);
|
||||
const isDeleted = ref(false);
|
||||
const muted = ref(checkMute(appearNote, $i?.mutedWords));
|
||||
const hardMuted = ref(props.withHardMute && checkMute(appearNote, $i?.hardMutedWords, true));
|
||||
const showSoftWordMutedWord = computed(() => prefer.s.showSoftWordMutedWord);
|
||||
|
@ -595,7 +590,7 @@ function onContextmenu(ev: MouseEvent): void {
|
|||
ev.preventDefault();
|
||||
react();
|
||||
} else {
|
||||
const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, isDeleted, currentClip: currentClip?.value });
|
||||
const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, currentClip: currentClip?.value });
|
||||
os.contextMenu(menu, ev).then(focus).finally(cleanup);
|
||||
}
|
||||
}
|
||||
|
@ -605,7 +600,7 @@ function showMenu(): void {
|
|||
return;
|
||||
}
|
||||
|
||||
const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, isDeleted, currentClip: currentClip?.value });
|
||||
const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, currentClip: currentClip?.value });
|
||||
os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup);
|
||||
}
|
||||
|
||||
|
@ -614,7 +609,7 @@ async function clip(): Promise<void> {
|
|||
return;
|
||||
}
|
||||
|
||||
os.popupMenu(await getNoteClipMenu({ note: note, isDeleted, currentClip: currentClip?.value }), clipButton.value).then(focus);
|
||||
os.popupMenu(await getNoteClipMenu({ note: note, currentClip: currentClip?.value }), clipButton.value).then(focus);
|
||||
}
|
||||
|
||||
function showRenoteMenu(): void {
|
||||
|
@ -630,8 +625,9 @@ function showRenoteMenu(): void {
|
|||
action: () => {
|
||||
misskeyApi('notes/delete', {
|
||||
noteId: note.id,
|
||||
}).then(() => {
|
||||
globalEvents.emit('noteDeleted', note.id);
|
||||
});
|
||||
isDeleted.value = true;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -682,7 +678,6 @@ function readPromo() {
|
|||
misskeyApi('promo/read', {
|
||||
noteId: appearNote.id,
|
||||
});
|
||||
isDeleted.value = true;
|
||||
}
|
||||
|
||||
function emitUpdReaction(emoji: string, delta: number) {
|
||||
|
|
|
@ -5,12 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<template>
|
||||
<div
|
||||
v-if="!muted"
|
||||
v-show="!isDeleted"
|
||||
v-if="!muted && !isDeleted"
|
||||
ref="rootEl"
|
||||
v-hotkey="keymap"
|
||||
:class="$style.root"
|
||||
:tabindex="isDeleted ? '-1' : '0'"
|
||||
tabindex="0"
|
||||
>
|
||||
<div v-if="appearNote.reply && appearNote.reply.replyId">
|
||||
<div v-if="!conversationLoaded" style="padding: 16px">
|
||||
|
@ -217,7 +216,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="_panel" :class="$style.muted" @click="muted = false">
|
||||
<div v-else-if="muted" class="_panel" :class="$style.muted" @click="muted = false">
|
||||
<I18n :src="i18n.ts.userSaysSomething" tag="small">
|
||||
<template #name>
|
||||
<MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)">
|
||||
|
@ -274,6 +273,7 @@ import { getAppearNote } from '@/utility/get-appear-note.js';
|
|||
import { prefer } from '@/preferences.js';
|
||||
import { getPluginHandlers } from '@/plugin.js';
|
||||
import { DI } from '@/di.js';
|
||||
import { globalEvents, useGlobalEvent } from '@/events.js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
note: Misskey.entities.Note;
|
||||
|
@ -294,10 +294,6 @@ if (noteViewInterruptors.length > 0) {
|
|||
for (const interruptor of noteViewInterruptors) {
|
||||
try {
|
||||
result = await interruptor.handler(result!) as Misskey.entities.Note | null;
|
||||
if (result === null) {
|
||||
isDeleted.value = true;
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
@ -336,6 +332,12 @@ const conversation = ref<Misskey.entities.Note[]>([]);
|
|||
const replies = ref<Misskey.entities.Note[]>([]);
|
||||
const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || appearNote.userId === $i?.id);
|
||||
|
||||
useGlobalEvent('noteDeleted', (noteId) => {
|
||||
if (noteId === note.id || noteId === appearNote.id) {
|
||||
isDeleted.value = true;
|
||||
}
|
||||
});
|
||||
|
||||
const pleaseLoginContext = computed<OpenOnRemoteOptions>(() => ({
|
||||
type: 'lookup',
|
||||
url: `https://${host}/notes/${appearNote.id}`,
|
||||
|
@ -544,18 +546,18 @@ function onContextmenu(ev: MouseEvent): void {
|
|||
ev.preventDefault();
|
||||
react();
|
||||
} else {
|
||||
const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, isDeleted });
|
||||
const { menu, cleanup } = getNoteMenu({ note: note, translating, translation });
|
||||
os.contextMenu(menu, ev).then(focus).finally(cleanup);
|
||||
}
|
||||
}
|
||||
|
||||
function showMenu(): void {
|
||||
const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, isDeleted });
|
||||
const { menu, cleanup } = getNoteMenu({ note: note, translating, translation });
|
||||
os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup);
|
||||
}
|
||||
|
||||
async function clip(): Promise<void> {
|
||||
os.popupMenu(await getNoteClipMenu({ note: note, isDeleted }), clipButton.value).then(focus);
|
||||
os.popupMenu(await getNoteClipMenu({ note: note }), clipButton.value).then(focus);
|
||||
}
|
||||
|
||||
function showRenoteMenu(): void {
|
||||
|
@ -568,8 +570,9 @@ function showRenoteMenu(): void {
|
|||
action: () => {
|
||||
misskeyApi('notes/delete', {
|
||||
noteId: note.id,
|
||||
}).then(() => {
|
||||
globalEvents.emit('noteDeleted', note.id);
|
||||
});
|
||||
isDeleted.value = true;
|
||||
},
|
||||
}], renoteTime.value);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import MkNote from '@/components/MkNote.vue';
|
|||
import MkPagination from '@/components/MkPagination.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { infoImageUrl } from '@/instance.js';
|
||||
import { globalEvents, useGlobalEvent } from '@/events.js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
pagination: PagingCtx;
|
||||
|
@ -47,6 +48,10 @@ const props = withDefaults(defineProps<{
|
|||
|
||||
const pagingComponent = useTemplateRef('pagingComponent');
|
||||
|
||||
useGlobalEvent('noteDeleted', (noteId) => {
|
||||
pagingComponent.value?.paginator.removeItem(noteId);
|
||||
});
|
||||
|
||||
function reload() {
|
||||
return pagingComponent.value?.paginator.reload();
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ import MkNote from '@/components/MkNote.vue';
|
|||
import MkButton from '@/components/MkButton.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { infoImageUrl } from '@/instance.js';
|
||||
import { globalEvents } from '@/events.js';
|
||||
import { globalEvents, useGlobalEvent } from '@/events.js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
src: BasicTimelineType | 'mentions' | 'directs' | 'list' | 'antenna' | 'channel' | 'role';
|
||||
|
@ -156,13 +156,17 @@ if (!store.s.realtimeMode) {
|
|||
afterMounted: true,
|
||||
});
|
||||
|
||||
globalEvents.on('notePosted', (note: Misskey.entities.Note) => {
|
||||
useGlobalEvent('notePosted', (note) => {
|
||||
paginator.fetchNewer({
|
||||
toQueue: !isTop(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
useGlobalEvent('noteDeleted', (noteId) => {
|
||||
paginator.removeItem(noteId);
|
||||
});
|
||||
|
||||
function releaseQueue() {
|
||||
paginator.releaseQueue();
|
||||
scrollToTop(rootEl.value);
|
||||
|
|
|
@ -5,10 +5,24 @@
|
|||
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { onBeforeUnmount } from 'vue';
|
||||
|
||||
export const globalEvents = new EventEmitter<{
|
||||
type Events = {
|
||||
themeChanging: () => void;
|
||||
themeChanged: () => void;
|
||||
clientNotification: (notification: Misskey.entities.Notification) => void;
|
||||
notePosted: (note: Misskey.entities.Note) => void;
|
||||
}>();
|
||||
noteDeleted: (noteId: Misskey.entities.Note['id']) => void;
|
||||
};
|
||||
|
||||
export const globalEvents = new EventEmitter<Events>();
|
||||
|
||||
export function useGlobalEvent<T extends keyof Events>(
|
||||
event: T,
|
||||
callback: Events[T],
|
||||
): void {
|
||||
globalEvents.on(event, callback);
|
||||
onBeforeUnmount(() => {
|
||||
globalEvents.off(event, callback);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -12,12 +12,12 @@ import { $i } from '@/i.js';
|
|||
import { store } from '@/store.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { globalEvents } from '@/events.js';
|
||||
|
||||
export const noteEvents = new EventEmitter<{
|
||||
[ev: `reacted:${string}`]: (ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; }; }) => void;
|
||||
[ev: `unreacted:${string}`]: (ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; }; }) => void;
|
||||
[ev: `pollVoted:${string}`]: (ctx: { userId: Misskey.entities.User['id']; choice: string; }) => void;
|
||||
[ev: `deleted:${string}`]: () => void;
|
||||
}>();
|
||||
|
||||
const fetchEvent = new EventEmitter<{
|
||||
|
@ -113,7 +113,6 @@ function pollingSubscribe(props: {
|
|||
|
||||
function realtimeSubscribe(props: {
|
||||
note: Pick<Misskey.entities.Note, 'id' | 'createdAt'>;
|
||||
isDeletedRef: Ref<boolean>;
|
||||
}): void {
|
||||
const note = props.note;
|
||||
const connection = useStream();
|
||||
|
@ -151,7 +150,7 @@ function realtimeSubscribe(props: {
|
|||
}
|
||||
|
||||
case 'deleted': {
|
||||
noteEvents.emit(`deleted:${id}`);
|
||||
globalEvents.emit('noteDeleted', id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -198,7 +197,6 @@ export function useNoteCapture(props: {
|
|||
noteEvents.on(`reacted:${note.id}`, onReacted);
|
||||
noteEvents.on(`unreacted:${note.id}`, onUnreacted);
|
||||
noteEvents.on(`pollVoted:${note.id}`, onPollVoted);
|
||||
noteEvents.on(`deleted:${note.id}`, onDeleted);
|
||||
|
||||
let latestReactedKey: string | null = null;
|
||||
let latestUnreactedKey: string | null = null;
|
||||
|
@ -256,15 +254,10 @@ export function useNoteCapture(props: {
|
|||
$note.pollChoices = choices;
|
||||
}
|
||||
|
||||
function onDeleted(): void {
|
||||
$note.isDeleted = true;
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
noteEvents.off(`reacted:${note.id}`, onReacted);
|
||||
noteEvents.off(`unreacted:${note.id}`, onUnreacted);
|
||||
noteEvents.off(`pollVoted:${note.id}`, onPollVoted);
|
||||
noteEvents.off(`deleted:${note.id}`, onDeleted);
|
||||
});
|
||||
|
||||
// 投稿からある程度経過している(=タイムラインを遡って表示した)ノートは、イベントが発生する可能性が低いためそもそも購読しない
|
||||
|
|
|
@ -197,6 +197,8 @@ export function usePagination<T extends MisskeyEntity>(props: {
|
|||
}
|
||||
|
||||
function removeItem(id: string) {
|
||||
// TODO: queueからも消す
|
||||
|
||||
const index = items.value.findIndex(x => x.id === id);
|
||||
if (index !== -1) {
|
||||
items.value.splice(index, 1);
|
||||
|
@ -205,6 +207,8 @@ export function usePagination<T extends MisskeyEntity>(props: {
|
|||
}
|
||||
|
||||
function updateItem(id: string, updator: (item: T) => T) {
|
||||
// TODO: queueのも更新
|
||||
|
||||
const index = items.value.findIndex(x => x.id === id);
|
||||
if (index !== -1) {
|
||||
const item = items.value[index]!;
|
||||
|
|
|
@ -29,7 +29,6 @@ import { globalEvents } from '@/events.js';
|
|||
|
||||
export async function getNoteClipMenu(props: {
|
||||
note: Misskey.entities.Note;
|
||||
isDeleted: Ref<boolean>;
|
||||
currentClip?: Misskey.entities.Clip;
|
||||
}) {
|
||||
function getClipName(clip: Misskey.entities.Clip) {
|
||||
|
@ -69,7 +68,6 @@ export async function getNoteClipMenu(props: {
|
|||
}
|
||||
}));
|
||||
});
|
||||
if (props.currentClip?.id === clip.id) props.isDeleted.value = true;
|
||||
}
|
||||
} else if (err.id === 'f0dba960-ff73-4615-8df4-d6ac5d9dc118') {
|
||||
os.alert({
|
||||
|
@ -179,7 +177,6 @@ export function getNoteMenu(props: {
|
|||
note: Misskey.entities.Note;
|
||||
translation: Ref<Misskey.entities.NotesTranslateResponse | null>;
|
||||
translating: Ref<boolean>;
|
||||
isDeleted: Ref<boolean>;
|
||||
currentClip?: Misskey.entities.Clip;
|
||||
}) {
|
||||
const appearNote = getAppearNote(props.note);
|
||||
|
@ -195,6 +192,8 @@ export function getNoteMenu(props: {
|
|||
|
||||
misskeyApi('notes/delete', {
|
||||
noteId: appearNote.id,
|
||||
}).then(() => {
|
||||
globalEvents.emit('noteDeleted', appearNote.id);
|
||||
});
|
||||
|
||||
if (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 60 && appearNote.userId === $i.id) {
|
||||
|
@ -212,6 +211,8 @@ export function getNoteMenu(props: {
|
|||
|
||||
misskeyApi('notes/delete', {
|
||||
noteId: appearNote.id,
|
||||
}).then(() => {
|
||||
globalEvents.emit('noteDeleted', appearNote.id);
|
||||
});
|
||||
|
||||
os.post({ initialNote: appearNote, renote: appearNote.renote, reply: appearNote.reply, channel: appearNote.channel });
|
||||
|
@ -252,7 +253,6 @@ export function getNoteMenu(props: {
|
|||
async function unclip(): Promise<void> {
|
||||
if (!props.currentClip) return;
|
||||
os.apiWithDialog('clips/remove-note', { clipId: props.currentClip.id, noteId: appearNote.id });
|
||||
props.isDeleted.value = true;
|
||||
}
|
||||
|
||||
async function promote(): Promise<void> {
|
||||
|
|
Loading…
Reference in New Issue