diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 0b5794d1e3..6f6dbfc8cc 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -116,7 +116,7 @@ import { formatTimeString } from '@/scripts/format-time-string.js'; import { Autocomplete } from '@/scripts/autocomplete.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { selectFiles } from '@/scripts/select-file.js'; +import { selectFile } from '@/scripts/select-file.js'; import { defaultStore, notePostInterruptors, postFormActions } from '@/store.js'; import MkInfo from '@/components/MkInfo.vue'; import { i18n } from '@/i18n.js'; @@ -413,7 +413,10 @@ function focus() { function chooseFileFrom(ev) { if (props.mock) return; - selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => { + selectFile(ev.currentTarget ?? ev.target, { + label: i18n.ts.attachFile, + multiple: true, + }).then(files_ => { for (const file of files_) { files.value.push(file); } diff --git a/packages/frontend/src/components/MkUserSetupDialog.Profile.vue b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue index 7cb48f6afb..ac7d0a6a83 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.Profile.vue +++ b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue @@ -68,7 +68,9 @@ watch(description, () => { }); function setAvatar(ev) { - chooseFileFromPc(false).then(async (files) => { + chooseFileFromPc({ + multiple: false, + }).then(async (files) => { const file = files[0]; let originalOrCropped = file; diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue index 6d8274a55c..67fa904015 100644 --- a/packages/frontend/src/pages/channel-editor.vue +++ b/packages/frontend/src/pages/channel-editor.vue @@ -189,7 +189,7 @@ async function archive() { } function setBannerImage(evt) { - selectFile(evt.currentTarget ?? evt.target, null).then(file => { + selectFile(evt.currentTarget ?? evt.target).then(file => { bannerId.value = file.id; }); } diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue index 3765319b25..eea726dbe6 100644 --- a/packages/frontend/src/pages/emoji-edit-dialog.vue +++ b/packages/frontend/src/pages/emoji-edit-dialog.vue @@ -121,7 +121,7 @@ watch(roleIdsThatCanBeUsedThisEmojiAsReaction, async () => { const imgUrl = computed(() => file.value ? file.value.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null); async function changeImage(ev: Event) { - file.value = await selectFile(ev.currentTarget ?? ev.target, null); + file.value = await selectFile(ev.currentTarget ?? ev.target); const candidate = file.value.name.replace(/\.(.+)$/, ''); if (candidate.match(/^[a-z0-9_]+$/)) { name.value = candidate; diff --git a/packages/frontend/src/pages/gallery/edit.vue b/packages/frontend/src/pages/gallery/edit.vue index 70f8b2c31d..8c5be813e4 100644 --- a/packages/frontend/src/pages/gallery/edit.vue +++ b/packages/frontend/src/pages/gallery/edit.vue @@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ file.name }}
- {{ i18n.ts.attachFile }} + {{ i18n.ts.attachFile }} {{ i18n.ts.markAsSensitive }} @@ -45,7 +45,7 @@ import MkInput from '@/components/MkInput.vue'; import MkTextarea from '@/components/MkTextarea.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import FormSuspense from '@/components/form/suspense.vue'; -import { selectFiles } from '@/scripts/select-file.js'; +import { selectFile } from '@/scripts/select-file.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -64,8 +64,8 @@ const description = ref(null); const title = ref(null); const isSensitive = ref(false); -function selectFile(evt) { - selectFiles(evt.currentTarget ?? evt.target, null).then(selected => { +function chooseFile(evt) { + selectFile(evt.currentTarget ?? evt.target, { multiple: true }).then(selected => { files.value = files.value.concat(selected); }); } diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue index ddb808390c..134849925d 100644 --- a/packages/frontend/src/pages/page-editor/page-editor.vue +++ b/packages/frontend/src/pages/page-editor/page-editor.vue @@ -217,7 +217,7 @@ async function add() { } function setEyeCatchingImage(img) { - selectFile(img.currentTarget ?? img.target, null).then(file => { + selectFile(img.currentTarget ?? img.target).then(file => { eyeCatchingImageId.value = file.id; }); } diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index 561894d2b7..aaecea1aa3 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -223,7 +223,7 @@ function save() { } function changeAvatar(ev) { - selectFile(ev.currentTarget ?? ev.target, i18n.ts.avatar).then(async (file) => { + selectFile(ev.currentTarget ?? ev.target, { label: i18n.ts.avatar }).then(async (file) => { let originalOrCropped = file; const { canceled } = await os.confirm({ @@ -250,7 +250,7 @@ function changeAvatar(ev) { } function changeBanner(ev) { - selectFile(ev.currentTarget ?? ev.target, i18n.ts.banner).then(async (file) => { + selectFile(ev.currentTarget ?? ev.target, { label: i18n.ts.banner }).then(async (file) => { let originalOrCropped = file; const { canceled } = await os.confirm({ diff --git a/packages/frontend/src/pages/settings/sounds.sound.vue b/packages/frontend/src/pages/settings/sounds.sound.vue index 56f65e2309..be68afed98 100644 --- a/packages/frontend/src/pages/settings/sounds.sound.vue +++ b/packages/frontend/src/pages/settings/sounds.sound.vue @@ -94,7 +94,9 @@ const friendlyFileName = computed(() => { }); function selectSound(ev) { - selectFile(ev.currentTarget ?? ev.target, i18n.ts._soundSettings.driveFile).then(async (file) => { + selectFile(ev.currentTarget ?? ev.target, { + label: i18n.ts._soundSettings.driveFile, + }).then(async (file) => { if (!file.type.startsWith('audio')) { os.alert({ type: 'warning', diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue index f1ec231588..cbd128d69f 100644 --- a/packages/frontend/src/pages/settings/theme.vue +++ b/packages/frontend/src/pages/settings/theme.vue @@ -158,7 +158,7 @@ fetchThemes().then(() => { }); function setWallpaper(event) { - selectFile(event.currentTarget ?? event.target, null).then(file => { + selectFile(event.currentTarget ?? event.target).then(file => { wallpaper.value = file.url; }); } diff --git a/packages/frontend/src/scripts/select-file.ts b/packages/frontend/src/scripts/select-file.ts index 667226ed68..747ce3600e 100644 --- a/packages/frontend/src/scripts/select-file.ts +++ b/packages/frontend/src/scripts/select-file.ts @@ -9,19 +9,22 @@ import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { useStream } from '@/stream.js'; import { i18n } from '@/i18n.js'; -import { $i } from '@/account.js'; import { defaultStore } from '@/store.js'; import { uploadFile } from '@/scripts/upload.js'; import type { MenuItem } from '@/types/menu.js'; -export function chooseFileFromPc(multiple: boolean, keepOriginal = false, useWatermark = false): Promise { +export function chooseFileFromPc(opts?: { + multiple?: boolean; + keepOriginal?: boolean; + useWatermark?: boolean; +}): Promise { return new Promise((res, rej) => { const input = document.createElement('input'); input.type = 'file'; - input.multiple = multiple; + input.multiple = opts?.multiple ?? false; input.onchange = () => { if (!input.files) return res([]); - const promises = Array.from(input.files, file => uploadFile(file, defaultStore.state.uploadFolder, undefined, keepOriginal, useWatermark)); + const promises = Array.from(input.files, file => uploadFile(file, defaultStore.state.uploadFolder, undefined, opts?.keepOriginal, opts?.useWatermark)); Promise.all(promises).then(driveFiles => { res(driveFiles); @@ -41,9 +44,11 @@ export function chooseFileFromPc(multiple: boolean, keepOriginal = false, useWat }); } -export function chooseFileFromDrive(multiple: boolean): Promise { +export function chooseFileFromDrive(opts?: { + multiple?: boolean; +}): Promise { return new Promise((res, rej) => { - os.selectDriveFile(multiple).then(files => { + os.selectDriveFile(opts?.multiple ?? false).then(files => { res(files); }); }); @@ -82,42 +87,76 @@ export function chooseFileFromUrl(): Promise { }); } -function select(src: HTMLElement | EventTarget | null, label: string | null, multiple: boolean): Promise { +function select(src: HTMLElement | EventTarget | null, opts?: { + label?: string; + multiple?: boolean; + dontUseWatermark?: boolean; +}): Promise { return new Promise((res, rej) => { const keepOriginal = ref(defaultStore.state.keepOriginalUploading); - const useWatermark = ref(defaultStore.state.useWatermark); + const useWatermark = ref(opts?.dontUseWatermark ? false : defaultStore.state.useWatermark); - os.popupMenu([label ? { - text: label, - type: 'label', - } : undefined, { + const menu: MenuItem[] = []; + + if (opts?.label) { + menu.push({ + text: opts.label, + type: 'label', + }); + } + + menu.push({ type: 'switch', text: i18n.ts.keepOriginalUploading, ref: keepOriginal, - }, { - type: 'switch', - text: i18n.ts.useWatermark, - ref: useWatermark, - }, { + }); + + if (!opts?.dontUseWatermark) { + menu.push({ + type: 'switch', + text: i18n.ts.useWatermark, + ref: useWatermark, + }); + } + + menu.push({ text: i18n.ts.upload, icon: 'ti ti-upload', - action: () => chooseFileFromPc(multiple, keepOriginal.value, useWatermark.value).then(files => res(files)), + action: () => chooseFileFromPc({ + multiple: opts?.multiple, + keepOriginal: keepOriginal.value, + useWatermark: useWatermark.value, + }).then(files => res(files)), }, { text: i18n.ts.fromDrive, icon: 'ti ti-cloud', - action: () => chooseFileFromDrive(multiple).then(files => res(files)), + action: () => chooseFileFromDrive({ multiple: opts?.multiple }).then(files => res(files)), }, { text: i18n.ts.fromUrl, icon: 'ti ti-link', action: () => chooseFileFromUrl().then(file => res([file])), - }], src); + }); + + os.popupMenu(menu, src); }); } -export function selectFile(src: HTMLElement | EventTarget | null, label: string | null = null): Promise { - return select(src, label, false).then(files => files[0]); -} - -export function selectFiles(src: HTMLElement | EventTarget | null, label: string | null = null): Promise { - return select(src, label, true); +export function selectFile(src: HTMLElement | EventTarget | null, opts: { + label?: string; + multiple: true; + dontUseWatermark?: boolean; +}): Promise; +export function selectFile(src: HTMLElement | EventTarget | null, opts?: { + label?: string; + multiple?: false; + dontUseWatermark?: boolean; +}): Promise; +export function selectFile(src: HTMLElement | EventTarget | null, opts?: { + label?: string; + multiple?: boolean; + dontUseWatermark?: boolean; +}): Promise { + return select(src, opts).then(files => { + return opts?.multiple ? files : files[0]; + }); }