firefish/packages/client/src/scripts/get-note-menu.ts

545 lines
12 KiB
TypeScript
Raw Normal View History

2023-11-27 18:17:53 +09:00
import { url } from "@/config";
2023-07-20 04:17:05 +09:00
import { i18n } from "@/i18n";
import { instance } from "@/instance";
import * as os from "@/os";
2023-11-27 18:17:53 +09:00
import { $i } from "@/reactiveAccount";
2023-07-20 04:17:05 +09:00
import copyToClipboard from "@/scripts/copy-to-clipboard";
import { getUserMenu } from "@/scripts/get-user-menu";
2023-10-17 02:12:35 +09:00
import icon from "@/scripts/icon";
2023-11-27 18:17:53 +09:00
import { shareAvailable } from "@/scripts/share-available";
import { noteActions } from "@/store";
import type * as firefish from "firefish-js";
import type { Ref } from "vue";
import { defineAsyncComponent } from "vue";
2023-07-20 04:17:05 +09:00
export function getNoteMenu(props: {
2023-10-10 23:28:06 +09:00
note: firefish.entities.Note;
2023-07-20 04:17:05 +09:00
menuButton: Ref<HTMLElement | undefined>;
translation: Ref<any>;
translating: Ref<boolean>;
isDeleted: Ref<boolean>;
2023-10-10 23:28:06 +09:00
currentClipPage?: Ref<firefish.entities.Clip>;
2023-07-20 04:17:05 +09:00
}) {
const isRenote =
props.note.renote != null &&
props.note.text == null &&
props.note.fileIds.length === 0 &&
props.note.poll == null;
const appearNote = isRenote
2023-10-10 23:28:06 +09:00
? (props.note.renote as firefish.entities.Note)
2023-07-20 04:17:05 +09:00
: props.note;
function del(): void {
os.confirm({
type: "warning",
text: i18n.ts.noteDeleteConfirm,
}).then(({ canceled }) => {
if (canceled) return;
os.api("notes/delete", {
noteId: appearNote.id,
});
});
}
function delEdit(): void {
os.confirm({
type: "warning",
text: i18n.ts.deleteAndEditConfirm,
}).then(({ canceled }) => {
if (canceled) return;
os.api("notes/delete", {
noteId: appearNote.id,
});
os.post({
initialNote: appearNote,
renote: appearNote.renote,
reply: appearNote.reply,
channel: appearNote.channel,
});
});
}
function edit(): void {
os.post({
initialNote: appearNote,
renote: appearNote.renote,
reply: appearNote.reply,
channel: appearNote.channel,
editId: appearNote.id,
});
}
function makePrivate(): void {
os.confirm({
type: "warning",
text: i18n.ts.makePrivateConfirm,
}).then(async ({ canceled }) => {
if (canceled) return;
await os.api("notes/make-private", {
noteId: appearNote.id,
});
});
}
2023-07-20 04:17:05 +09:00
function toggleFavorite(favorite: boolean): void {
os.apiWithDialog(
favorite ? "notes/favorites/create" : "notes/favorites/delete",
{
noteId: appearNote.id,
},
);
}
function toggleWatch(watch: boolean): void {
os.apiWithDialog(
watch ? "notes/watching/create" : "notes/watching/delete",
{
noteId: appearNote.id,
},
);
}
function toggleThreadMute(mute: boolean): void {
os.apiWithDialog(
mute ? "notes/thread-muting/create" : "notes/thread-muting/delete",
{
noteId: appearNote.id,
},
);
}
function copyContent(): void {
copyToClipboard(appearNote.text);
os.success();
}
function copyLink(): void {
copyToClipboard(`${url}/notes/${appearNote.id}`);
os.success();
}
function copyOriginal(): void {
copyToClipboard(appearNote.url ?? appearNote.uri);
os.success();
}
function togglePin(pin: boolean): void {
os.apiWithDialog(
pin ? "i/pin" : "i/unpin",
{
noteId: appearNote.id,
},
undefined,
).catch((res) => {
if (res.id === "72dab508-c64d-498f-8740-a8eec1ba385a") {
os.alert({
type: "error",
text: i18n.ts.pinLimitExceeded,
});
}
});
}
async function clip(): Promise<void> {
const clips = await os.api("clips/list");
os.popupMenu(
[
{
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-plus")}`,
2023-07-20 04:17:05 +09: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);
os.apiWithDialog("clips/add-note", {
clipId: clip.id,
noteId: appearNote.id,
});
},
},
null,
...clips.map((clip) => ({
text: clip.name,
action: () => {
os.promiseDialog(
os.api("clips/add-note", {
clipId: clip.id,
noteId: appearNote.id,
}),
null,
async (err) => {
if (err.id === "734806c4-542c-463a-9311-15c512803965") {
const confirm = await os.confirm({
type: "warning",
text: i18n.t("confirmToUnclipAlreadyClippedNote", {
name: clip.name,
}),
});
if (!confirm.canceled) {
os.apiWithDialog("clips/remove-note", {
clipId: clip.id,
noteId: appearNote.id,
});
if (props.currentClipPage?.value.id === clip.id)
props.isDeleted.value = true;
}
} else {
os.alert({
type: "error",
2023-11-27 18:17:53 +09:00
text: `${err.message}\n${err.id}`,
2023-07-20 04:17:05 +09:00
});
}
},
);
},
})),
],
props.menuButton.value,
{},
).then(focus);
}
async function unclip(): Promise<void> {
os.apiWithDialog("clips/remove-note", {
clipId: props.currentClipPage.value.id,
noteId: appearNote.id,
});
props.isDeleted.value = true;
}
async function promote(): Promise<void> {
const { canceled, result: days } = await os.inputNumber({
title: i18n.ts.numberOfDays,
});
if (canceled) return;
os.apiWithDialog("admin/promo/create", {
noteId: appearNote.id,
expiresAt: Date.now() + 86400000 * days,
});
}
function share(): void {
navigator.share({
title: i18n.t("noteOf", { user: appearNote.user.name }),
text: appearNote.text,
url: `${url}/notes/${appearNote.id}`,
});
}
2023-09-04 17:47:24 +09:00
async function translate_(noteId: number, targetLang: string) {
return await os.api("notes/translate", {
2023-09-21 07:53:48 +09:00
noteId,
targetLang,
});
2023-09-04 17:47:24 +09:00
}
async function translate(): Promise<void> {
const translateLang = localStorage.getItem("translateLang");
const lang = localStorage.getItem("lang");
2023-07-20 04:17:05 +09:00
if (props.translation.value != null) return;
props.translating.value = true;
props.translation.value = await translate_(
appearNote.id,
2023-09-04 17:47:24 +09:00
translateLang || lang || navigator.language,
);
// use UI language as the second translation target
if (
2023-09-04 17:47:24 +09:00
translateLang != null &&
lang != null &&
translateLang !== lang &&
(!props.translation.value ||
props.translation.value.sourceLang.toLowerCase() ===
2023-09-04 17:47:24 +09:00
translateLang.slice(0, 2))
)
2023-09-04 17:47:24 +09:00
props.translation.value = await translate_(appearNote.id, lang);
2023-07-20 04:17:05 +09:00
props.translating.value = false;
2023-09-04 17:47:24 +09:00
}
2023-07-20 04:17:05 +09:00
let menu;
if ($i) {
const statePromise = os.api("notes/state", {
noteId: appearNote.id,
});
const isAppearAuthor = appearNote.userId === $i.id;
const isModerator = $i.isAdmin || $i.isModerator;
menu = [
...(props.currentClipPage?.value.userId === $i.id
? [
{
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-minus-circle")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.unclip,
danger: true,
action: unclip,
},
null,
]
: []),
statePromise.then((state) =>
state?.isFavorited
? {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-bookmark-simple")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.unfavorite,
action: () => toggleFavorite(false),
}
: {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-bookmark-simple")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.favorite,
action: () => toggleFavorite(true),
},
),
{
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-paperclip")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.clip,
action: () => clip(),
},
!isAppearAuthor
? statePromise.then((state) =>
state.isWatching
? {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-eye-slash")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.unwatch,
action: () => toggleWatch(false),
}
: {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-eye")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.watch,
action: () => toggleWatch(true),
},
)
: undefined,
statePromise.then((state) =>
state.isMutedThread
? {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-speaker-x")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.unmuteThread,
action: () => toggleThreadMute(false),
}
: {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-speaker-x")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.muteThread,
action: () => toggleThreadMute(true),
},
),
isAppearAuthor
? ($i.pinnedNoteIds || []).includes(appearNote.id)
? {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-push-pin")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.unpin,
action: () => togglePin(false),
}
: {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-push-pin")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.pin,
action: () => togglePin(true),
}
: undefined,
instance.translatorAvailable
? {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-translate")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.translate,
action: translate,
}
: undefined,
appearNote.url || appearNote.uri
? {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-arrow-square-out")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.showOnRemote,
action: () => {
window.open(appearNote.url || appearNote.uri, "_blank");
},
}
: undefined,
{
type: "parent",
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-share-network")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.share,
children: [
{
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-clipboard-text")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.copyContent,
action: copyContent,
},
{
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-link-simple")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.copyLink,
action: copyLink,
},
appearNote.url || appearNote.uri
? {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-link-simple")}`,
2023-07-31 19:16:47 +09:00
text: `${i18n.ts.copyLink} (${i18n.ts.origin})`,
2023-07-20 04:17:05 +09:00
action: copyOriginal,
}
: undefined,
shareAvailable()
? {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-share-network")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.share,
action: share,
}
: undefined,
],
},
/*
...($i.isModerator || $i.isAdmin ? [
null,
{
2023-10-17 02:12:35 +09:00
icon: `${icon('ph-megaphone-simple')}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.promote,
action: promote
}]
: []
2023-09-04 17:47:24 +09:00
), */
2023-07-20 04:17:05 +09:00
null,
!isAppearAuthor
? {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-warning-circle")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.reportAbuse,
action: () => {
const u =
appearNote.url ||
appearNote.uri ||
`${url}/notes/${appearNote.id}`;
os.popup(
defineAsyncComponent(
() => import("@/components/MkAbuseReportWindow.vue"),
),
{
user: appearNote.user,
initialComment: `Note: ${u}\n-----\n`,
},
{},
"closed",
);
},
}
: undefined,
isAppearAuthor
? {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-pencil-line")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.edit,
accent: true,
action: edit,
}
: undefined,
isAppearAuthor &&
!(
appearNote.visibility === "specified" &&
appearNote.visibleUserIds.length === 0
)
? {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-eye-slash")}`,
text: i18n.ts.makePrivate,
danger: true,
action: makePrivate,
}
: undefined,
2023-07-20 04:17:05 +09:00
isAppearAuthor
? {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-eraser")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.deleteAndEdit,
2023-07-31 19:16:47 +09:00
danger: true,
2023-07-20 04:17:05 +09:00
action: delEdit,
}
: undefined,
isAppearAuthor || isModerator
? {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-trash")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.delete,
danger: true,
action: del,
}
: undefined,
!isAppearAuthor ? null : undefined,
!isAppearAuthor
? {
type: "parent",
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-user")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.user,
children: getUserMenu(appearNote.user),
}
: undefined,
].filter((x) => x !== undefined);
} else {
menu = [
appearNote.url || appearNote.uri
? {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-arrow-square-out")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.showOnRemote,
action: () => {
window.open(appearNote.url || appearNote.uri, "_blank");
},
}
: undefined,
{
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-clipboard-text")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.copyContent,
action: copyContent,
},
{
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-link-simple")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.copyLink,
action: copyLink,
},
appearNote.url || appearNote.uri
? {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-link-simple")}`,
2023-07-31 19:16:47 +09:00
text: `${i18n.ts.copyLink} (${i18n.ts.origin})`,
2023-07-20 04:17:05 +09:00
action: copyOriginal,
}
: undefined,
shareAvailable()
? {
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-share-network")}`,
2023-07-20 04:17:05 +09:00
text: i18n.ts.share,
action: share,
}
: undefined,
].filter((x) => x !== undefined);
}
if (noteActions.length > 0) {
menu = menu.concat([
null,
...noteActions.map((action) => ({
2023-10-17 02:12:35 +09:00
icon: `${icon("ph-plug")}`,
2023-07-20 04:17:05 +09:00
text: action.title,
action: () => {
action.handler(appearNote);
},
})),
]);
}
return menu;
}