2023-07-27 07:31:52 +02:00
|
|
|
/*
|
|
|
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
|
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
*/
|
|
|
|
|
2024-01-30 11:53:53 +01:00
|
|
|
import { defineAsyncComponent, Ref, ShallowRef } from 'vue';
|
2023-09-04 06:33:38 +02:00
|
|
|
import * as Misskey from 'misskey-js';
|
2023-09-20 09:44:12 +02:00
|
|
|
import { claimAchievement } from './achievements.js';
|
2023-09-19 09:37:43 +02:00
|
|
|
import { $i } from '@/account.js';
|
|
|
|
import { i18n } from '@/i18n.js';
|
|
|
|
import { instance } from '@/instance.js';
|
|
|
|
import * as os from '@/os.js';
|
2024-01-04 10:32:46 +01:00
|
|
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
2023-09-19 09:37:43 +02:00
|
|
|
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
|
|
|
|
import { url } from '@/config.js';
|
|
|
|
import { defaultStore, noteActions } from '@/store.js';
|
|
|
|
import { miLocalStorage } from '@/local-storage.js';
|
|
|
|
import { getUserMenu } from '@/scripts/get-user-menu.js';
|
2023-09-20 09:44:12 +02:00
|
|
|
import { clipsCache } from '@/cache.js';
|
|
|
|
import { MenuItem } from '@/types/menu.js';
|
2023-11-03 09:34:23 +01:00
|
|
|
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
2023-11-30 00:15:13 +01:00
|
|
|
import { isSupportShare } from '@/scripts/navigator.js';
|
2023-03-24 08:54:37 +01:00
|
|
|
|
|
|
|
export async function getNoteClipMenu(props: {
|
2023-09-04 06:33:38 +02:00
|
|
|
note: Misskey.entities.Note;
|
2023-03-24 08:54:37 +01:00
|
|
|
isDeleted: Ref<boolean>;
|
2023-09-04 06:33:38 +02:00
|
|
|
currentClip?: Misskey.entities.Clip;
|
2023-03-24 08:54:37 +01:00
|
|
|
}) {
|
|
|
|
const isRenote = (
|
|
|
|
props.note.renote != null &&
|
|
|
|
props.note.text == null &&
|
|
|
|
props.note.fileIds.length === 0 &&
|
|
|
|
props.note.poll == null
|
|
|
|
);
|
|
|
|
|
2023-09-04 06:33:38 +02:00
|
|
|
const appearNote = isRenote ? props.note.renote as Misskey.entities.Note : props.note;
|
2023-03-24 08:54:37 +01:00
|
|
|
|
2023-09-11 07:55:18 +02:00
|
|
|
const clips = await clipsCache.fetch();
|
2024-01-30 11:53:53 +01:00
|
|
|
const menu: MenuItem[] = [...clips.map(clip => ({
|
2023-03-24 08:54:37 +01:00
|
|
|
text: clip.name,
|
|
|
|
action: () => {
|
|
|
|
claimAchievement('noteClipped1');
|
|
|
|
os.promiseDialog(
|
2024-01-04 10:32:46 +01:00
|
|
|
misskeyApi('clips/add-note', { clipId: clip.id, noteId: appearNote.id }),
|
2023-03-24 08:54:37 +01:00
|
|
|
null,
|
|
|
|
async (err) => {
|
|
|
|
if (err.id === '734806c4-542c-463a-9311-15c512803965') {
|
|
|
|
const confirm = await os.confirm({
|
|
|
|
type: 'warning',
|
2024-01-20 00:11:59 +01:00
|
|
|
text: i18n.tsx.confirmToUnclipAlreadyClippedNote({ name: clip.name }),
|
2023-03-24 08:54:37 +01:00
|
|
|
});
|
|
|
|
if (!confirm.canceled) {
|
|
|
|
os.apiWithDialog('clips/remove-note', { clipId: clip.id, noteId: appearNote.id });
|
2023-03-31 08:01:56 +02:00
|
|
|
if (props.currentClip?.id === clip.id) props.isDeleted.value = true;
|
2023-03-24 08:54:37 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
os.alert({
|
|
|
|
type: 'error',
|
|
|
|
text: err.message + '\n' + err.id,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
|
|
|
},
|
2023-12-12 02:26:37 +01:00
|
|
|
})), { type: 'divider' }, {
|
2023-09-30 21:53:52 +02:00
|
|
|
icon: 'ph-plus ph-bold ph-lg',
|
2023-03-24 08:54:37 +01:00
|
|
|
text: i18n.ts.createNew,
|
|
|
|
action: async () => {
|
|
|
|
const { canceled, result } = await os.form(i18n.ts.createNewClip, {
|
|
|
|
name: {
|
|
|
|
type: 'string',
|
|
|
|
label: i18n.ts.name,
|
|
|
|
},
|
|
|
|
description: {
|
|
|
|
type: 'string',
|
|
|
|
required: false,
|
|
|
|
multiline: true,
|
|
|
|
label: i18n.ts.description,
|
|
|
|
},
|
|
|
|
isPublic: {
|
|
|
|
type: 'boolean',
|
|
|
|
label: i18n.ts.public,
|
|
|
|
default: false,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
if (canceled) return;
|
|
|
|
|
|
|
|
const clip = await os.apiWithDialog('clips/create', result);
|
|
|
|
|
|
|
|
clipsCache.delete();
|
|
|
|
|
|
|
|
claimAchievement('noteClipped1');
|
|
|
|
os.apiWithDialog('clips/add-note', { clipId: clip.id, noteId: appearNote.id });
|
|
|
|
},
|
|
|
|
}];
|
2024-01-30 11:53:53 +01:00
|
|
|
|
|
|
|
return menu;
|
2023-03-24 08:54:37 +01:00
|
|
|
}
|
2022-01-14 02:25:51 +01:00
|
|
|
|
2023-12-12 02:26:37 +01:00
|
|
|
export function getAbuseNoteMenu(note: Misskey.entities.Note, text: string): MenuItem {
|
2023-09-05 10:25:08 +02:00
|
|
|
return {
|
2023-10-01 00:46:42 +02:00
|
|
|
icon: 'ph-warning-circle ph-bold ph-lg',
|
2023-09-05 10:25:08 +02:00
|
|
|
text,
|
|
|
|
action: (): void => {
|
|
|
|
const u = note.url ?? note.uri ?? `${url}/notes/${note.id}`;
|
|
|
|
os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
|
|
|
|
user: note.user,
|
|
|
|
initialComment: `Note: ${u}\n-----\n`,
|
|
|
|
}, {}, 'closed');
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-12-12 02:26:37 +01:00
|
|
|
export function getCopyNoteLinkMenu(note: Misskey.entities.Note, text: string): MenuItem {
|
2023-09-05 10:25:08 +02:00
|
|
|
return {
|
2023-09-30 21:53:52 +02:00
|
|
|
icon: 'ph-link ph-bold ph-lg',
|
2023-09-05 10:25:08 +02:00
|
|
|
text,
|
|
|
|
action: (): void => {
|
|
|
|
copyToClipboard(`${url}/notes/${note.id}`);
|
|
|
|
os.success();
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-10-02 00:55:29 +02:00
|
|
|
export function getCopyNoteOriginLinkMenu(note: misskey.entities.Note, text: string): MenuItem {
|
|
|
|
return {
|
|
|
|
icon: 'ph-link ph-bold ph-lg',
|
|
|
|
text,
|
|
|
|
action: (): void => {
|
|
|
|
copyToClipboard(note.url ?? note.uri);
|
|
|
|
os.success();
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-01-14 02:25:51 +01:00
|
|
|
export function getNoteMenu(props: {
|
2023-09-04 06:33:38 +02:00
|
|
|
note: Misskey.entities.Note;
|
2023-12-26 06:19:35 +01:00
|
|
|
translation: Ref<Misskey.entities.NotesTranslateResponse | null>;
|
2022-01-14 02:25:51 +01:00
|
|
|
translating: Ref<boolean>;
|
2022-06-18 11:27:09 +02:00
|
|
|
isDeleted: Ref<boolean>;
|
2023-09-04 06:33:38 +02:00
|
|
|
currentClip?: Misskey.entities.Clip;
|
2022-01-14 02:25:51 +01:00
|
|
|
}) {
|
|
|
|
const isRenote = (
|
|
|
|
props.note.renote != null &&
|
|
|
|
props.note.text == null &&
|
|
|
|
props.note.fileIds.length === 0 &&
|
|
|
|
props.note.poll == null
|
|
|
|
);
|
|
|
|
|
2023-09-04 06:33:38 +02:00
|
|
|
const appearNote = isRenote ? props.note.renote as Misskey.entities.Note : props.note;
|
2022-01-14 02:25:51 +01:00
|
|
|
|
2023-08-01 08:32:03 +02:00
|
|
|
const cleanups = [] as (() => void)[];
|
|
|
|
|
2022-01-14 02:25:51 +01:00
|
|
|
function del(): void {
|
|
|
|
os.confirm({
|
|
|
|
type: 'warning',
|
2022-01-28 03:39:49 +01:00
|
|
|
text: i18n.ts.noteDeleteConfirm,
|
2022-01-14 02:25:51 +01:00
|
|
|
}).then(({ canceled }) => {
|
|
|
|
if (canceled) return;
|
|
|
|
|
2024-01-04 10:32:46 +01:00
|
|
|
misskeyApi('notes/delete', {
|
2022-06-30 03:53:40 +02:00
|
|
|
noteId: appearNote.id,
|
2022-01-14 02:25:51 +01:00
|
|
|
});
|
2023-01-21 05:14:55 +01:00
|
|
|
|
|
|
|
if (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 60) {
|
|
|
|
claimAchievement('noteDeletedWithin1min');
|
|
|
|
}
|
2022-01-14 02:25:51 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function delEdit(): void {
|
|
|
|
os.confirm({
|
|
|
|
type: 'warning',
|
2022-01-28 03:39:49 +01:00
|
|
|
text: i18n.ts.deleteAndEditConfirm,
|
2022-01-14 02:25:51 +01:00
|
|
|
}).then(({ canceled }) => {
|
|
|
|
if (canceled) return;
|
|
|
|
|
2024-01-04 10:32:46 +01:00
|
|
|
misskeyApi('notes/delete', {
|
2022-06-30 03:53:40 +02:00
|
|
|
noteId: appearNote.id,
|
2022-01-14 02:25:51 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
os.post({ initialNote: appearNote, renote: appearNote.renote, reply: appearNote.reply, channel: appearNote.channel });
|
2023-01-21 05:14:55 +01:00
|
|
|
|
|
|
|
if (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 60) {
|
|
|
|
claimAchievement('noteDeletedWithin1min');
|
|
|
|
}
|
2022-01-14 02:25:51 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-09-22 22:33:18 +02:00
|
|
|
function edit(): void {
|
|
|
|
os.post({
|
|
|
|
initialNote: appearNote,
|
|
|
|
renote: appearNote.renote,
|
|
|
|
reply: appearNote.reply,
|
|
|
|
channel: appearNote.channel,
|
|
|
|
editId: appearNote.id,
|
2023-11-01 01:22:08 +01:00
|
|
|
initialFiles: appearNote.files,
|
2023-09-22 22:33:18 +02:00
|
|
|
});
|
2023-09-28 10:21:16 +02:00
|
|
|
}
|
2022-01-14 02:25:51 +01:00
|
|
|
|
|
|
|
function toggleFavorite(favorite: boolean): void {
|
2023-01-21 05:14:55 +01:00
|
|
|
claimAchievement('noteFavorited1');
|
2022-01-14 02:25:51 +01:00
|
|
|
os.apiWithDialog(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', {
|
2022-06-30 03:53:40 +02:00
|
|
|
noteId: appearNote.id,
|
2022-01-14 02:25:51 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function toggleThreadMute(mute: boolean): void {
|
|
|
|
os.apiWithDialog(mute ? 'notes/thread-muting/create' : 'notes/thread-muting/delete', {
|
2022-06-30 03:53:40 +02:00
|
|
|
noteId: appearNote.id,
|
2022-01-14 02:25:51 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function copyContent(): void {
|
|
|
|
copyToClipboard(appearNote.text);
|
|
|
|
os.success();
|
|
|
|
}
|
|
|
|
|
|
|
|
function copyLink(): void {
|
|
|
|
copyToClipboard(`${url}/notes/${appearNote.id}`);
|
|
|
|
os.success();
|
|
|
|
}
|
|
|
|
|
|
|
|
function togglePin(pin: boolean): void {
|
|
|
|
os.apiWithDialog(pin ? 'i/pin' : 'i/unpin', {
|
2022-06-30 03:53:40 +02:00
|
|
|
noteId: appearNote.id,
|
2022-05-07 07:19:15 +02:00
|
|
|
}, undefined, null, res => {
|
|
|
|
if (res.id === '72dab508-c64d-498f-8740-a8eec1ba385a') {
|
2022-01-14 02:25:51 +01:00
|
|
|
os.alert({
|
|
|
|
type: 'error',
|
2022-06-30 03:53:40 +02:00
|
|
|
text: i18n.ts.pinLimitExceeded,
|
2022-01-14 02:25:51 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-06-18 11:27:09 +02:00
|
|
|
async function unclip(): Promise<void> {
|
2023-03-31 08:01:56 +02:00
|
|
|
os.apiWithDialog('clips/remove-note', { clipId: props.currentClip.id, noteId: appearNote.id });
|
2022-06-18 11:27:09 +02:00
|
|
|
props.isDeleted.value = true;
|
|
|
|
}
|
|
|
|
|
2022-01-14 02:25:51 +01:00
|
|
|
async function promote(): Promise<void> {
|
|
|
|
const { canceled, result: days } = await os.inputNumber({
|
2022-01-28 03:39:49 +01:00
|
|
|
title: i18n.ts.numberOfDays,
|
2022-01-14 02:25:51 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
if (canceled) return;
|
|
|
|
|
|
|
|
os.apiWithDialog('admin/promo/create', {
|
|
|
|
noteId: appearNote.id,
|
|
|
|
expiresAt: Date.now() + (86400000 * days),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function share(): void {
|
|
|
|
navigator.share({
|
2024-01-20 00:11:59 +01:00
|
|
|
title: i18n.tsx.noteOf({ user: appearNote.user.name }),
|
2022-01-14 02:25:51 +01:00
|
|
|
text: appearNote.text,
|
|
|
|
url: `${url}/notes/${appearNote.id}`,
|
|
|
|
});
|
|
|
|
}
|
2023-01-19 02:29:30 +01:00
|
|
|
|
2023-01-21 08:59:58 +01:00
|
|
|
function openDetail(): void {
|
2022-11-12 23:54:05 +01:00
|
|
|
os.pageWindow(`/notes/${appearNote.id}`);
|
|
|
|
}
|
2023-01-19 02:29:30 +01:00
|
|
|
|
2022-01-14 02:25:51 +01:00
|
|
|
async function translate(): Promise<void> {
|
|
|
|
if (props.translation.value != null) return;
|
|
|
|
props.translating.value = true;
|
2024-01-04 10:32:46 +01:00
|
|
|
const res = await misskeyApi('notes/translate', {
|
2022-01-14 02:25:51 +01:00
|
|
|
noteId: appearNote.id,
|
2023-02-22 07:36:12 +01:00
|
|
|
targetLang: miLocalStorage.getItem('lang') ?? navigator.language,
|
2022-01-14 02:25:51 +01:00
|
|
|
});
|
|
|
|
props.translating.value = false;
|
|
|
|
props.translation.value = res;
|
|
|
|
}
|
|
|
|
|
2023-08-01 08:32:03 +02:00
|
|
|
let menu: MenuItem[];
|
2022-01-14 02:25:51 +01:00
|
|
|
if ($i) {
|
2024-01-04 10:32:46 +01:00
|
|
|
const statePromise = misskeyApi('notes/state', {
|
2022-06-30 03:53:40 +02:00
|
|
|
noteId: appearNote.id,
|
2022-01-14 02:25:51 +01:00
|
|
|
});
|
|
|
|
|
2022-06-18 11:27:09 +02:00
|
|
|
menu = [
|
2022-06-30 03:53:40 +02:00
|
|
|
...(
|
2023-03-31 08:01:56 +02:00
|
|
|
props.currentClip?.userId === $i.id ? [{
|
2023-10-01 00:46:42 +02:00
|
|
|
icon: 'ph-backspace ph-bold ph-lg',
|
2022-06-30 03:53:40 +02:00
|
|
|
text: i18n.ts.unclip,
|
|
|
|
danger: true,
|
|
|
|
action: unclip,
|
2023-12-12 02:26:37 +01:00
|
|
|
}, { type: 'divider' }] : []
|
2022-11-12 23:54:05 +01:00
|
|
|
), {
|
2023-09-30 21:53:52 +02:00
|
|
|
icon: 'ph-info ph-bold ph-lg',
|
2022-11-12 23:54:05 +01:00
|
|
|
text: i18n.ts.details,
|
2023-01-21 08:59:58 +01:00
|
|
|
action: openDetail,
|
2022-11-12 23:54:05 +01:00
|
|
|
}, {
|
2023-09-30 21:53:52 +02:00
|
|
|
icon: 'ph-copy ph-bold ph-lg',
|
2022-06-30 03:53:40 +02:00
|
|
|
text: i18n.ts.copyContent,
|
|
|
|
action: copyContent,
|
2023-09-05 10:25:08 +02:00
|
|
|
}, getCopyNoteLinkMenu(appearNote, i18n.ts.copyLink)
|
2024-02-05 12:39:15 +01:00
|
|
|
, (appearNote.url || appearNote.uri) ?
|
2023-10-02 00:55:29 +02:00
|
|
|
getCopyNoteOriginLinkMenu(appearNote, 'Copy link (Origin)')
|
|
|
|
: undefined,
|
|
|
|
(appearNote.url || appearNote.uri) ? {
|
2023-09-30 21:53:52 +02:00
|
|
|
icon: 'ph-arrow-square-out ph-bold ph-lg',
|
2022-06-30 03:53:40 +02:00
|
|
|
text: i18n.ts.showOnRemote,
|
|
|
|
action: () => {
|
2023-12-08 09:48:18 +01:00
|
|
|
window.open(appearNote.url ?? appearNote.uri, '_blank', 'noopener');
|
2022-06-30 03:53:40 +02:00
|
|
|
},
|
|
|
|
} : undefined,
|
2023-11-30 00:15:13 +01:00
|
|
|
...(isSupportShare() ? [{
|
2023-11-03 23:20:53 +01:00
|
|
|
icon: 'ph-share-network ph-bold ph-lg',
|
2022-06-30 03:53:40 +02:00
|
|
|
text: i18n.ts.share,
|
|
|
|
action: share,
|
2023-11-30 00:15:13 +01:00
|
|
|
}] : []),
|
2023-09-30 00:54:11 +02:00
|
|
|
$i && $i.policies.canUseTranslator && instance.translatorAvailable ? {
|
2023-10-01 00:46:42 +02:00
|
|
|
icon: 'ph-translate ph-bold ph-lg',
|
2022-06-30 03:53:40 +02:00
|
|
|
text: i18n.ts.translate,
|
|
|
|
action: translate,
|
|
|
|
} : undefined,
|
2023-12-12 02:26:37 +01:00
|
|
|
{ type: 'divider' },
|
2022-06-30 03:53:40 +02:00
|
|
|
statePromise.then(state => state.isFavorited ? {
|
2023-11-03 23:20:53 +01:00
|
|
|
icon: 'ph-star-half ph-bold ph-lg',
|
2022-06-30 03:53:40 +02:00
|
|
|
text: i18n.ts.unfavorite,
|
|
|
|
action: () => toggleFavorite(false),
|
|
|
|
} : {
|
2023-11-03 23:20:53 +01:00
|
|
|
icon: 'ph-star ph-bold ph-lg',
|
2022-06-30 03:53:40 +02:00
|
|
|
text: i18n.ts.favorite,
|
|
|
|
action: () => toggleFavorite(true),
|
|
|
|
}),
|
|
|
|
{
|
2023-08-01 08:32:03 +02:00
|
|
|
type: 'parent' as const,
|
2023-09-30 21:53:52 +02:00
|
|
|
icon: 'ph-paperclip ph-bold ph-lg',
|
2022-06-30 03:53:40 +02:00
|
|
|
text: i18n.ts.clip,
|
2023-03-24 08:54:37 +01:00
|
|
|
children: () => getNoteClipMenu(props),
|
2022-06-30 03:53:40 +02:00
|
|
|
},
|
|
|
|
statePromise.then(state => state.isMutedThread ? {
|
2023-10-01 00:46:42 +02:00
|
|
|
icon: 'ph-bell-slash ph-bold ph-lg',
|
2022-06-30 03:53:40 +02:00
|
|
|
text: i18n.ts.unmuteThread,
|
|
|
|
action: () => toggleThreadMute(false),
|
|
|
|
} : {
|
2023-10-01 00:46:42 +02:00
|
|
|
icon: 'ph-bell-slash ph-bold ph-lg',
|
2022-06-30 03:53:40 +02:00
|
|
|
text: i18n.ts.muteThread,
|
|
|
|
action: () => toggleThreadMute(true),
|
|
|
|
}),
|
2023-03-31 08:01:56 +02:00
|
|
|
appearNote.userId === $i.id ? ($i.pinnedNoteIds ?? []).includes(appearNote.id) ? {
|
2023-09-30 21:53:52 +02:00
|
|
|
icon: 'ph-push-pin ph-bold ph-lgned-off',
|
2022-06-30 03:53:40 +02:00
|
|
|
text: i18n.ts.unpin,
|
|
|
|
action: () => togglePin(false),
|
|
|
|
} : {
|
2023-09-30 21:53:52 +02:00
|
|
|
icon: 'ph-push-pin ph-bold ph-lg',
|
2022-06-30 03:53:40 +02:00
|
|
|
text: i18n.ts.pin,
|
|
|
|
action: () => togglePin(true),
|
|
|
|
} : undefined,
|
2023-08-01 08:32:03 +02:00
|
|
|
{
|
|
|
|
type: 'parent' as const,
|
2023-09-30 21:53:52 +02:00
|
|
|
icon: 'ph-user ph-bold ph-lg',
|
2023-02-25 01:18:36 +01:00
|
|
|
text: i18n.ts.user,
|
|
|
|
children: async () => {
|
2024-01-04 10:32:46 +01:00
|
|
|
const user = appearNote.userId === $i?.id ? $i : await misskeyApi('users/show', { userId: appearNote.userId });
|
2023-08-01 08:32:03 +02:00
|
|
|
const { menu, cleanup } = getUserMenu(user);
|
|
|
|
cleanups.push(cleanup);
|
|
|
|
return menu;
|
2023-02-25 01:18:36 +01:00
|
|
|
},
|
2023-08-01 08:32:03 +02:00
|
|
|
},
|
2022-06-30 03:53:40 +02:00
|
|
|
/*
|
2022-01-14 02:25:51 +01:00
|
|
|
...($i.isModerator || $i.isAdmin ? [
|
2023-12-12 02:26:37 +01:00
|
|
|
{ type: 'divider' },
|
2022-01-14 02:25:51 +01:00
|
|
|
{
|
2023-09-30 21:53:52 +02:00
|
|
|
icon: 'ph-megaphone ph-bold ph-lg',
|
2022-01-28 03:39:49 +01:00
|
|
|
text: i18n.ts.promote,
|
2022-01-14 02:25:51 +01:00
|
|
|
action: promote
|
|
|
|
}]
|
|
|
|
: []
|
|
|
|
),*/
|
2022-06-30 03:53:40 +02:00
|
|
|
...(appearNote.userId !== $i.id ? [
|
2023-12-12 02:26:37 +01:00
|
|
|
{ type: 'divider' },
|
2023-09-05 10:25:08 +02:00
|
|
|
appearNote.userId !== $i.id ? getAbuseNoteMenu(appearNote, i18n.ts.reportAbuse) : undefined,
|
|
|
|
]
|
2022-01-14 02:25:51 +01:00
|
|
|
: []
|
2022-06-30 03:53:40 +02:00
|
|
|
),
|
2024-01-03 05:35:40 +01:00
|
|
|
...(appearNote.channel && (appearNote.channel.userId === $i.id || $i.isModerator || $i.isAdmin) ? [
|
|
|
|
{ type: 'divider' },
|
|
|
|
{
|
|
|
|
type: 'parent' as const,
|
|
|
|
icon: 'ti ti-device-tv',
|
|
|
|
text: i18n.ts.channel,
|
|
|
|
children: async () => {
|
|
|
|
const channelChildMenu = [] as MenuItem[];
|
|
|
|
|
2024-01-04 10:32:46 +01:00
|
|
|
const channel = await misskeyApi('channels/show', { channelId: appearNote.channel!.id });
|
2024-01-03 05:35:40 +01:00
|
|
|
|
|
|
|
if (channel.pinnedNoteIds.includes(appearNote.id)) {
|
|
|
|
channelChildMenu.push({
|
|
|
|
icon: 'ti ti-pinned-off',
|
|
|
|
text: i18n.ts.unpin,
|
|
|
|
action: () => os.apiWithDialog('channels/update', {
|
|
|
|
channelId: appearNote.channel!.id,
|
|
|
|
pinnedNoteIds: channel.pinnedNoteIds.filter(id => id !== appearNote.id),
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
channelChildMenu.push({
|
|
|
|
icon: 'ti ti-pin',
|
|
|
|
text: i18n.ts.pin,
|
|
|
|
action: () => os.apiWithDialog('channels/update', {
|
|
|
|
channelId: appearNote.channel!.id,
|
|
|
|
pinnedNoteIds: [...channel.pinnedNoteIds, appearNote.id],
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return channelChildMenu;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]
|
|
|
|
: []
|
|
|
|
),
|
2022-06-30 03:53:40 +02:00
|
|
|
...(appearNote.userId === $i.id || $i.isModerator || $i.isAdmin ? [
|
2023-12-12 02:26:37 +01:00
|
|
|
{ type: 'divider' },
|
2022-06-30 03:53:40 +02:00
|
|
|
appearNote.userId === $i.id ? {
|
2024-02-05 12:39:15 +01:00
|
|
|
icon: 'ph-pencil-simple ph-bold ph-lg',
|
2023-09-22 22:33:18 +02:00
|
|
|
text: i18n.ts.edit,
|
|
|
|
action: edit,
|
2023-09-22 23:03:39 +02:00
|
|
|
} : undefined,
|
2023-09-22 22:33:18 +02:00
|
|
|
{
|
2024-02-05 12:39:15 +01:00
|
|
|
icon: 'ph-pencil-simple-line ph-bold ph-lg',
|
2022-06-30 03:53:40 +02:00
|
|
|
text: i18n.ts.deleteAndEdit,
|
2023-09-22 22:33:18 +02:00
|
|
|
danger: true,
|
2022-06-30 03:53:40 +02:00
|
|
|
action: delEdit,
|
2024-02-05 12:39:15 +01:00
|
|
|
},
|
2022-06-30 03:53:40 +02:00
|
|
|
{
|
2023-09-30 21:53:52 +02:00
|
|
|
icon: 'ph-trash ph-bold ph-lg',
|
2022-06-30 03:53:40 +02:00
|
|
|
text: i18n.ts.delete,
|
|
|
|
danger: true,
|
|
|
|
action: del,
|
|
|
|
}]
|
2022-01-14 02:25:51 +01:00
|
|
|
: []
|
2022-06-30 03:53:40 +02:00
|
|
|
)]
|
2022-09-24 03:39:17 +02:00
|
|
|
.filter(x => x !== undefined);
|
2022-01-14 02:25:51 +01:00
|
|
|
} else {
|
|
|
|
menu = [{
|
2023-09-30 21:53:52 +02:00
|
|
|
icon: 'ph-info ph-bold ph-lg',
|
2023-09-25 03:30:00 +02:00
|
|
|
text: i18n.ts.details,
|
2022-11-12 23:54:05 +01:00
|
|
|
action: openDetail,
|
|
|
|
}, {
|
2023-09-30 21:53:52 +02:00
|
|
|
icon: 'ph-copy ph-bold ph-lg',
|
2022-01-28 03:39:49 +01:00
|
|
|
text: i18n.ts.copyContent,
|
2022-06-30 03:53:40 +02:00
|
|
|
action: copyContent,
|
2023-09-05 10:25:08 +02:00
|
|
|
}, getCopyNoteLinkMenu(appearNote, i18n.ts.copyLink)
|
2024-02-05 12:39:15 +01:00
|
|
|
, (appearNote.url || appearNote.uri) ?
|
2023-10-02 00:55:29 +02:00
|
|
|
getCopyNoteOriginLinkMenu(appearNote, 'Copy link (Origin)')
|
|
|
|
: undefined,
|
|
|
|
(appearNote.url || appearNote.uri) ? {
|
2023-09-30 21:53:52 +02:00
|
|
|
icon: 'ph-arrow-square-out ph-bold ph-lg',
|
2022-01-28 03:39:49 +01:00
|
|
|
text: i18n.ts.showOnRemote,
|
2022-01-14 02:25:51 +01:00
|
|
|
action: () => {
|
2023-12-08 09:48:18 +01:00
|
|
|
window.open(appearNote.url ?? appearNote.uri, '_blank', 'noopener');
|
2022-06-30 03:53:40 +02:00
|
|
|
},
|
2022-01-14 02:25:51 +01:00
|
|
|
} : undefined]
|
2022-09-24 03:39:17 +02:00
|
|
|
.filter(x => x !== undefined);
|
2022-01-14 02:25:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (noteActions.length > 0) {
|
2023-12-14 08:16:21 +01:00
|
|
|
menu = menu.concat([{ type: "divider" }, ...noteActions.map(action => ({
|
2023-10-01 00:46:42 +02:00
|
|
|
icon: 'ph-plug ph-bold ph-lg',
|
2022-01-14 02:25:51 +01:00
|
|
|
text: action.title,
|
|
|
|
action: () => {
|
|
|
|
action.handler(appearNote);
|
2022-06-30 03:53:40 +02:00
|
|
|
},
|
2022-01-14 02:25:51 +01:00
|
|
|
}))]);
|
|
|
|
}
|
|
|
|
|
2023-05-14 03:30:46 +02:00
|
|
|
if (defaultStore.state.devMode) {
|
2023-12-14 08:16:21 +01:00
|
|
|
menu = menu.concat([{ type: "divider" }, {
|
2023-09-30 21:53:52 +02:00
|
|
|
icon: 'ph-identification-card ph-bold ph-lg',
|
2023-05-14 03:30:46 +02:00
|
|
|
text: i18n.ts.copyNoteId,
|
|
|
|
action: () => {
|
|
|
|
copyToClipboard(appearNote.id);
|
|
|
|
},
|
|
|
|
}]);
|
|
|
|
}
|
|
|
|
|
2023-08-01 08:32:03 +02:00
|
|
|
const cleanup = () => {
|
|
|
|
if (_DEV_) console.log('note menu cleanup', cleanups);
|
2023-08-09 02:08:47 +02:00
|
|
|
for (const cl of cleanups) {
|
|
|
|
cl();
|
|
|
|
}
|
2023-08-01 08:32:03 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
return {
|
|
|
|
menu,
|
|
|
|
cleanup,
|
|
|
|
};
|
2022-01-14 02:25:51 +01:00
|
|
|
}
|
2023-11-03 09:34:23 +01:00
|
|
|
|
|
|
|
type Visibility = 'public' | 'home' | 'followers' | 'specified';
|
|
|
|
|
|
|
|
// defaultStore.state.visibilityがstringなためstringも受け付けている
|
|
|
|
function smallerVisibility(a: Visibility | string, b: Visibility | string): Visibility {
|
|
|
|
if (a === 'specified' || b === 'specified') return 'specified';
|
|
|
|
if (a === 'followers' || b === 'followers') return 'followers';
|
|
|
|
if (a === 'home' || b === 'home') return 'home';
|
|
|
|
// if (a === 'public' || b === 'public')
|
|
|
|
return 'public';
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getRenoteMenu(props: {
|
|
|
|
note: Misskey.entities.Note;
|
2024-01-30 11:53:53 +01:00
|
|
|
renoteButton: ShallowRef<HTMLElement | undefined>;
|
2023-11-03 09:34:23 +01:00
|
|
|
mock?: boolean;
|
|
|
|
}) {
|
|
|
|
const isRenote = (
|
|
|
|
props.note.renote != null &&
|
|
|
|
props.note.text == null &&
|
|
|
|
props.note.fileIds.length === 0 &&
|
|
|
|
props.note.poll == null
|
|
|
|
);
|
|
|
|
|
|
|
|
const appearNote = isRenote ? props.note.renote as Misskey.entities.Note : props.note;
|
|
|
|
|
|
|
|
const channelRenoteItems: MenuItem[] = [];
|
|
|
|
const normalRenoteItems: MenuItem[] = [];
|
|
|
|
|
|
|
|
if (appearNote.channel) {
|
|
|
|
channelRenoteItems.push(...[{
|
|
|
|
text: i18n.ts.inChannelRenote,
|
|
|
|
icon: 'ti ti-repeat',
|
|
|
|
action: () => {
|
2024-01-30 11:53:53 +01:00
|
|
|
const el = props.renoteButton.value;
|
2023-11-03 09:34:23 +01:00
|
|
|
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');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!props.mock) {
|
2024-01-04 10:32:46 +01:00
|
|
|
misskeyApi('notes/create', {
|
2023-11-03 09:34:23 +01:00
|
|
|
renoteId: appearNote.id,
|
|
|
|
channelId: appearNote.channelId,
|
|
|
|
}).then(() => {
|
|
|
|
os.toast(i18n.ts.renoted);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}, {
|
|
|
|
text: i18n.ts.inChannelQuote,
|
|
|
|
icon: 'ti ti-quote',
|
|
|
|
action: () => {
|
|
|
|
if (!props.mock) {
|
|
|
|
os.post({
|
|
|
|
renote: appearNote,
|
|
|
|
channel: appearNote.channel,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}]);
|
|
|
|
}
|
|
|
|
|
2023-11-30 00:15:13 +01:00
|
|
|
if (!appearNote.channel || appearNote.channel.allowRenoteToExternal) {
|
2023-11-03 09:34:23 +01:00
|
|
|
normalRenoteItems.push(...[{
|
|
|
|
text: i18n.ts.renote,
|
|
|
|
icon: 'ti ti-repeat',
|
|
|
|
action: () => {
|
2024-01-30 11:53:53 +01:00
|
|
|
const el = props.renoteButton.value;
|
2023-11-03 09:34:23 +01:00
|
|
|
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');
|
|
|
|
}
|
|
|
|
|
|
|
|
const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility;
|
|
|
|
const localOnly = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly;
|
|
|
|
|
|
|
|
let visibility = appearNote.visibility;
|
|
|
|
visibility = smallerVisibility(visibility, configuredVisibility);
|
|
|
|
if (appearNote.channel?.isSensitive) {
|
|
|
|
visibility = smallerVisibility(visibility, 'home');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!props.mock) {
|
2024-01-04 10:32:46 +01:00
|
|
|
misskeyApi('notes/create', {
|
2023-11-03 09:34:23 +01:00
|
|
|
localOnly,
|
|
|
|
visibility,
|
|
|
|
renoteId: appearNote.id,
|
|
|
|
}).then(() => {
|
|
|
|
os.toast(i18n.ts.renoted);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}, (props.mock) ? undefined : {
|
|
|
|
text: i18n.ts.quote,
|
|
|
|
icon: 'ti ti-quote',
|
|
|
|
action: () => {
|
|
|
|
os.post({
|
|
|
|
renote: appearNote,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
}]);
|
|
|
|
}
|
|
|
|
|
|
|
|
const renoteItems = [
|
|
|
|
...normalRenoteItems,
|
2024-01-30 11:53:53 +01:00
|
|
|
...(channelRenoteItems.length > 0 && normalRenoteItems.length > 0) ? [{ type: 'divider' }] as MenuItem[] : [],
|
2023-11-03 09:34:23 +01:00
|
|
|
...channelRenoteItems,
|
|
|
|
];
|
|
|
|
|
|
|
|
return {
|
|
|
|
menu: renoteItems,
|
|
|
|
};
|
|
|
|
}
|