enhance: refine uploadFile
This commit is contained in:
parent
67512e0b43
commit
ed60942717
|
@ -51,7 +51,10 @@ if (props.fileId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectButton(ev: MouseEvent) {
|
function selectButton(ev: MouseEvent) {
|
||||||
selectFile(ev.currentTarget ?? ev.target).then(async (file) => {
|
selectFile({
|
||||||
|
anchorElement: ev.currentTarget ?? ev.target,
|
||||||
|
multiple: false,
|
||||||
|
}).then(async (file) => {
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
if (props.validate && !await props.validate(file)) return;
|
if (props.validate && !await props.validate(file)) return;
|
||||||
|
|
||||||
|
|
|
@ -120,7 +120,7 @@ import { formatTimeString } from '@/utility/format-time-string.js';
|
||||||
import { Autocomplete } from '@/utility/autocomplete.js';
|
import { Autocomplete } from '@/utility/autocomplete.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
import { selectFiles } from '@/utility/drive.js';
|
import { selectFile } from '@/utility/drive.js';
|
||||||
import { store } from '@/store.js';
|
import { store } from '@/store.js';
|
||||||
import MkInfo from '@/components/MkInfo.vue';
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
@ -437,7 +437,11 @@ function focus() {
|
||||||
function chooseFileFrom(ev) {
|
function chooseFileFrom(ev) {
|
||||||
if (props.mock) return;
|
if (props.mock) return;
|
||||||
|
|
||||||
selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => {
|
selectFile({
|
||||||
|
anchorElement: ev.currentTarget ?? ev.target,
|
||||||
|
multiple: true,
|
||||||
|
label: i18n.ts.attachFile,
|
||||||
|
}).then(files_ => {
|
||||||
for (const file of files_) {
|
for (const file of files_) {
|
||||||
files.value.push(file);
|
files.value.push(file);
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</MkModalWindow>
|
</MkModalWindow>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export type UploaderDialogFeatures = {
|
||||||
|
watermark?: boolean;
|
||||||
|
crop?: boolean;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, markRaw, onMounted, ref, useTemplateRef, watch } from 'vue';
|
import { computed, markRaw, onMounted, ref, useTemplateRef, watch } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
|
@ -107,6 +114,7 @@ import bytes from '@/filters/bytes.js';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
import { isWebpSupported } from '@/utility/isWebpSupported.js';
|
import { isWebpSupported } from '@/utility/isWebpSupported.js';
|
||||||
import { uploadFile, UploadAbortedError } from '@/utility/drive.js';
|
import { uploadFile, UploadAbortedError } from '@/utility/drive.js';
|
||||||
|
import { canApplyWatermark, getWatermarkAppliedImage } from '@/utility/watermark.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { ensureSignin } from '@/i.js';
|
import { ensureSignin } from '@/i.js';
|
||||||
|
|
||||||
|
@ -119,7 +127,7 @@ const COMPRESSION_SUPPORTED_TYPES = [
|
||||||
'image/svg+xml',
|
'image/svg+xml',
|
||||||
];
|
];
|
||||||
|
|
||||||
const CROPPING_SUPPORTED_TYPES = [
|
const IMGEDIT_SUPPORTED_TYPES = [
|
||||||
'image/jpeg',
|
'image/jpeg',
|
||||||
'image/png',
|
'image/png',
|
||||||
'image/webp',
|
'image/webp',
|
||||||
|
@ -135,10 +143,18 @@ const props = withDefaults(defineProps<{
|
||||||
files: File[];
|
files: File[];
|
||||||
folderId?: string | null;
|
folderId?: string | null;
|
||||||
multiple?: boolean;
|
multiple?: boolean;
|
||||||
|
features?: UploaderDialogFeatures;
|
||||||
}>(), {
|
}>(), {
|
||||||
multiple: true,
|
multiple: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const uploaderFeatures = computed<Required<UploaderDialogFeatures>>(() => {
|
||||||
|
return {
|
||||||
|
watermark: props.features?.watermark ?? true,
|
||||||
|
crop: props.features?.crop ?? true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: 'done', driveFiles: Misskey.entities.DriveFile[]): void;
|
(ev: 'done', driveFiles: Misskey.entities.DriveFile[]): void;
|
||||||
(ev: 'canceled'): void;
|
(ev: 'canceled'): void;
|
||||||
|
@ -157,6 +173,7 @@ const items = ref<{
|
||||||
aborted: boolean;
|
aborted: boolean;
|
||||||
compressedSize?: number | null;
|
compressedSize?: number | null;
|
||||||
compressedImage?: Blob | null;
|
compressedImage?: Blob | null;
|
||||||
|
applyWatermark?: boolean | null;
|
||||||
file: File;
|
file: File;
|
||||||
abort?: (() => void) | null;
|
abort?: (() => void) | null;
|
||||||
}[]>([]);
|
}[]>([]);
|
||||||
|
@ -274,19 +291,37 @@ function showMenu(ev: MouseEvent, item: typeof items.value[0]) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (CROPPING_SUPPORTED_TYPES.includes(item.file.type) && !item.waiting && !item.uploading && !item.uploaded) {
|
if (IMGEDIT_SUPPORTED_TYPES.includes(item.file.type) && !item.waiting && !item.uploading && !item.uploaded) {
|
||||||
menu.push({
|
if (uploaderFeatures.value.watermark) {
|
||||||
icon: 'ti ti-crop',
|
const _applyWatermark = computed({
|
||||||
text: i18n.ts.cropImage,
|
get: () => item.applyWatermark ?? prefer.s.useWatermark,
|
||||||
action: async () => {
|
set: (v) => {
|
||||||
const cropped = await os.cropImageFile(item.file, { aspectRatio: null });
|
item.applyWatermark = v;
|
||||||
items.value.splice(items.value.indexOf(item), 1, {
|
},
|
||||||
...item,
|
});
|
||||||
file: markRaw(cropped),
|
|
||||||
thumbnail: window.URL.createObjectURL(cropped),
|
menu.push({
|
||||||
});
|
icon: 'ti ti-ripple',
|
||||||
},
|
text: i18n.ts.useWatermark,
|
||||||
});
|
type: 'switch',
|
||||||
|
ref: _applyWatermark,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uploaderFeatures.value.crop) {
|
||||||
|
menu.push({
|
||||||
|
icon: 'ti ti-crop',
|
||||||
|
text: i18n.ts.cropImage,
|
||||||
|
action: async () => {
|
||||||
|
const cropped = await os.cropImageFile(item.file, { aspectRatio: null });
|
||||||
|
items.value.splice(items.value.indexOf(item), 1, {
|
||||||
|
...item,
|
||||||
|
file: markRaw(cropped),
|
||||||
|
thumbnail: window.URL.createObjectURL(cropped),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!item.waiting && !item.uploading && !item.uploaded) {
|
if (!item.waiting && !item.uploading && !item.uploaded) {
|
||||||
|
@ -333,6 +368,15 @@ async function upload() { // エラーハンドリングなどを考慮してシ
|
||||||
item.waiting = true;
|
item.waiting = true;
|
||||||
item.uploadFailed = false;
|
item.uploadFailed = false;
|
||||||
|
|
||||||
|
if (
|
||||||
|
item.applyWatermark === true &&
|
||||||
|
uploaderFeatures.value.watermark &&
|
||||||
|
IMGEDIT_SUPPORTED_TYPES.includes(item.file.type) &&
|
||||||
|
canApplyWatermark(prefer.s.watermarkConfig)
|
||||||
|
) {
|
||||||
|
item.file = await getWatermarkAppliedImage(item.file, prefer.s.watermarkConfig);
|
||||||
|
}
|
||||||
|
|
||||||
const shouldCompress = item.compressedImage == null && compressionLevel.value !== 0 && compressionSettings.value && COMPRESSION_SUPPORTED_TYPES.includes(item.file.type) && !(await isAnimated(item.file));
|
const shouldCompress = item.compressedImage == null && compressionLevel.value !== 0 && compressionSettings.value && COMPRESSION_SUPPORTED_TYPES.includes(item.file.type) && !(await isAnimated(item.file));
|
||||||
|
|
||||||
if (shouldCompress) {
|
if (shouldCompress) {
|
||||||
|
|
|
@ -251,7 +251,14 @@ const friendlyFileName = computed<string>(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
function chooseFile(ev: MouseEvent) {
|
function chooseFile(ev: MouseEvent) {
|
||||||
selectFile(ev.currentTarget ?? ev.target, i18n.ts.selectFile).then((file) => {
|
selectFile({
|
||||||
|
anchorElement: ev.currentTarget ?? ev.target,
|
||||||
|
multiple: false,
|
||||||
|
label: i18n.ts.selectFile,
|
||||||
|
features: {
|
||||||
|
watermark: false,
|
||||||
|
},
|
||||||
|
}).then((file) => {
|
||||||
if (!file.type.startsWith('image')) {
|
if (!file.type.startsWith('image')) {
|
||||||
os.alert({
|
os.alert({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
|
|
|
@ -13,6 +13,7 @@ import type { ComponentProps as CP } from 'vue-component-type-helpers';
|
||||||
import type { Form, GetFormResultType } from '@/utility/form.js';
|
import type { Form, GetFormResultType } from '@/utility/form.js';
|
||||||
import type { MenuItem } from '@/types/menu.js';
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
import type { PostFormProps } from '@/types/post-form.js';
|
import type { PostFormProps } from '@/types/post-form.js';
|
||||||
|
import type { UploaderDialogFeatures } from '@/components/MkUploaderDialog.vue';
|
||||||
import type MkRoleSelectDialog_TypeReferenceOnly from '@/components/MkRoleSelectDialog.vue';
|
import type MkRoleSelectDialog_TypeReferenceOnly from '@/components/MkRoleSelectDialog.vue';
|
||||||
import type MkEmojiPickerDialog_TypeReferenceOnly from '@/components/MkEmojiPickerDialog.vue';
|
import type MkEmojiPickerDialog_TypeReferenceOnly from '@/components/MkEmojiPickerDialog.vue';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
|
@ -773,6 +774,7 @@ export function launchUploader(
|
||||||
options?: {
|
options?: {
|
||||||
folderId?: string | null;
|
folderId?: string | null;
|
||||||
multiple?: boolean;
|
multiple?: boolean;
|
||||||
|
features?: UploaderDialogFeatures;
|
||||||
},
|
},
|
||||||
): Promise<Misskey.entities.DriveFile[]> {
|
): Promise<Misskey.entities.DriveFile[]> {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
|
@ -781,6 +783,7 @@ export function launchUploader(
|
||||||
files: markRaw(files),
|
files: markRaw(files),
|
||||||
folderId: options?.folderId,
|
folderId: options?.folderId,
|
||||||
multiple: options?.multiple,
|
multiple: options?.multiple,
|
||||||
|
features: options?.features,
|
||||||
}, {
|
}, {
|
||||||
done: driveFiles => {
|
done: driveFiles => {
|
||||||
if (driveFiles.length === 0) return rej();
|
if (driveFiles.length === 0) return rej();
|
||||||
|
|
|
@ -174,7 +174,10 @@ function setupGrid(): GridSetting {
|
||||||
{
|
{
|
||||||
bindTo: 'url', icon: 'ti-icons', type: 'image', editable: true, width: 'auto', validators: [required],
|
bindTo: 'url', icon: 'ti-icons', type: 'image', editable: true, width: 'auto', validators: [required],
|
||||||
async customValueEditor(row, col, value, cellElement) {
|
async customValueEditor(row, col, value, cellElement) {
|
||||||
const file = await selectFile(cellElement);
|
const file = await selectFile({
|
||||||
|
anchorElement: cellElement,
|
||||||
|
multiple: false,
|
||||||
|
});
|
||||||
gridItems.value[row.index].url = file.url;
|
gridItems.value[row.index].url = file.url;
|
||||||
gridItems.value[row.index].fileId = file.id;
|
gridItems.value[row.index].fileId = file.id;
|
||||||
|
|
||||||
|
|
|
@ -188,7 +188,10 @@ async function archive() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setBannerImage(evt) {
|
function setBannerImage(evt) {
|
||||||
selectFile(evt.currentTarget ?? evt.target).then(file => {
|
selectFile({
|
||||||
|
anchorElement: evt.currentTarget ?? evt.target,
|
||||||
|
multiple: false,
|
||||||
|
}).then(file => {
|
||||||
bannerId.value = file.id;
|
bannerId.value = file.id;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,7 +168,11 @@ function onKeydown(ev: KeyboardEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function chooseFile(ev: MouseEvent) {
|
function chooseFile(ev: MouseEvent) {
|
||||||
selectFile(ev.currentTarget ?? ev.target, i18n.ts.selectFile).then(selectedFile => {
|
selectFile({
|
||||||
|
anchorElement: ev.currentTarget ?? ev.target,
|
||||||
|
multiple: false,
|
||||||
|
label: i18n.ts.selectFile,
|
||||||
|
}).then(selectedFile => {
|
||||||
file.value = selectedFile;
|
file.value = selectedFile;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,7 +214,10 @@ const menu = (ev: MouseEvent) => {
|
||||||
icon: 'ti ti-upload',
|
icon: 'ti ti-upload',
|
||||||
text: i18n.ts.import,
|
text: i18n.ts.import,
|
||||||
action: async () => {
|
action: async () => {
|
||||||
const file = await selectFile(ev.currentTarget ?? ev.target);
|
const file = await selectFile({
|
||||||
|
anchorElement: ev.currentTarget ?? ev.target,
|
||||||
|
multiple: false,
|
||||||
|
});
|
||||||
misskeyApi('admin/emoji/import-zip', {
|
misskeyApi('admin/emoji/import-zip', {
|
||||||
fileId: file.id,
|
fileId: file.id,
|
||||||
})
|
})
|
||||||
|
|
|
@ -121,7 +121,10 @@ watch(roleIdsThatCanBeUsedThisEmojiAsReaction, async () => {
|
||||||
const imgUrl = computed(() => file.value ? file.value.url : props.emoji ? props.emoji.url : null);
|
const imgUrl = computed(() => file.value ? file.value.url : props.emoji ? props.emoji.url : null);
|
||||||
|
|
||||||
async function changeImage(ev: Event) {
|
async function changeImage(ev: Event) {
|
||||||
file.value = await selectFile(ev.currentTarget ?? ev.target);
|
file.value = await selectFile({
|
||||||
|
anchorElement: ev.currentTarget ?? ev.target,
|
||||||
|
multiple: false,
|
||||||
|
});
|
||||||
const candidate = file.value.name.replace(/\.(.+)$/, '');
|
const candidate = file.value.name.replace(/\.(.+)$/, '');
|
||||||
if (candidate.match(/^[a-z0-9_]+$/)) {
|
if (candidate.match(/^[a-z0-9_]+$/)) {
|
||||||
name.value = candidate;
|
name.value = candidate;
|
||||||
|
|
|
@ -44,7 +44,7 @@ import MkInput from '@/components/MkInput.vue';
|
||||||
import MkTextarea from '@/components/MkTextarea.vue';
|
import MkTextarea from '@/components/MkTextarea.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import FormSuspense from '@/components/form/suspense.vue';
|
import FormSuspense from '@/components/form/suspense.vue';
|
||||||
import { selectFiles } from '@/utility/drive.js';
|
import { selectFile } from '@/utility/drive.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
import { definePage } from '@/page.js';
|
import { definePage } from '@/page.js';
|
||||||
|
@ -64,7 +64,10 @@ const title = ref<string | null>(null);
|
||||||
const isSensitive = ref(false);
|
const isSensitive = ref(false);
|
||||||
|
|
||||||
function chooseFile(evt) {
|
function chooseFile(evt) {
|
||||||
selectFiles(evt.currentTarget ?? evt.target).then(selected => {
|
selectFile({
|
||||||
|
anchorElement: evt.currentTarget ?? evt.target,
|
||||||
|
multiple: false,
|
||||||
|
}).then(selected => {
|
||||||
files.value = files.value.concat(selected);
|
files.value = files.value.concat(selected);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -205,7 +205,10 @@ async function add() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setEyeCatchingImage(img: Event) {
|
function setEyeCatchingImage(img: Event) {
|
||||||
selectFile(img.currentTarget ?? img.target, null).then(file => {
|
selectFile({
|
||||||
|
anchorElement: img.currentTarget ?? img.target,
|
||||||
|
multiple: false,
|
||||||
|
}).then(file => {
|
||||||
eyeCatchingImageId.value = file.id;
|
eyeCatchingImageId.value = file.id;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,7 +233,10 @@ const exportAntennas = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const importFollowing = async (ev) => {
|
const importFollowing = async (ev) => {
|
||||||
const file = await selectFile(ev.currentTarget ?? ev.target);
|
const file = await selectFile({
|
||||||
|
anchorElement: ev.currentTarget ?? ev.target,
|
||||||
|
multiple: false,
|
||||||
|
});
|
||||||
misskeyApi('i/import-following', {
|
misskeyApi('i/import-following', {
|
||||||
fileId: file.id,
|
fileId: file.id,
|
||||||
withReplies: withReplies.value,
|
withReplies: withReplies.value,
|
||||||
|
@ -241,22 +244,34 @@ const importFollowing = async (ev) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const importUserLists = async (ev) => {
|
const importUserLists = async (ev) => {
|
||||||
const file = await selectFile(ev.currentTarget ?? ev.target);
|
const file = await selectFile({
|
||||||
|
anchorElement: ev.currentTarget ?? ev.target,
|
||||||
|
multiple: false,
|
||||||
|
});
|
||||||
misskeyApi('i/import-user-lists', { fileId: file.id }).then(onImportSuccess).catch(onError);
|
misskeyApi('i/import-user-lists', { fileId: file.id }).then(onImportSuccess).catch(onError);
|
||||||
};
|
};
|
||||||
|
|
||||||
const importMuting = async (ev) => {
|
const importMuting = async (ev) => {
|
||||||
const file = await selectFile(ev.currentTarget ?? ev.target);
|
const file = await selectFile({
|
||||||
|
anchorElement: ev.currentTarget ?? ev.target,
|
||||||
|
multiple: false,
|
||||||
|
});
|
||||||
misskeyApi('i/import-muting', { fileId: file.id }).then(onImportSuccess).catch(onError);
|
misskeyApi('i/import-muting', { fileId: file.id }).then(onImportSuccess).catch(onError);
|
||||||
};
|
};
|
||||||
|
|
||||||
const importBlocking = async (ev) => {
|
const importBlocking = async (ev) => {
|
||||||
const file = await selectFile(ev.currentTarget ?? ev.target);
|
const file = await selectFile({
|
||||||
|
anchorElement: ev.currentTarget ?? ev.target,
|
||||||
|
multiple: false,
|
||||||
|
});
|
||||||
misskeyApi('i/import-blocking', { fileId: file.id }).then(onImportSuccess).catch(onError);
|
misskeyApi('i/import-blocking', { fileId: file.id }).then(onImportSuccess).catch(onError);
|
||||||
};
|
};
|
||||||
|
|
||||||
const importAntennas = async (ev) => {
|
const importAntennas = async (ev) => {
|
||||||
const file = await selectFile(ev.currentTarget ?? ev.target);
|
const file = await selectFile({
|
||||||
|
anchorElement: ev.currentTarget ?? ev.target,
|
||||||
|
multiple: false,
|
||||||
|
});
|
||||||
misskeyApi('i/import-antennas', { fileId: file.id }).then(onImportSuccess).catch(onError);
|
misskeyApi('i/import-antennas', { fileId: file.id }).then(onImportSuccess).catch(onError);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,10 @@ watch(wallpaper, async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
function setWallpaper(ev: MouseEvent) {
|
function setWallpaper(ev: MouseEvent) {
|
||||||
selectFile(ev.currentTarget ?? ev.target, null).then(file => {
|
selectFile({
|
||||||
|
anchorElement: ev.currentTarget ?? ev.target,
|
||||||
|
multiple: false,
|
||||||
|
}).then(file => {
|
||||||
wallpaper.value = file.url;
|
wallpaper.value = file.url;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,9 @@ const friendlyFileName = computed<string>(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
function selectSound(ev) {
|
function selectSound(ev) {
|
||||||
selectFile(ev.currentTarget ?? ev.target, {
|
selectFile({
|
||||||
|
anchorElement: ev.currentTarget ?? ev.target,
|
||||||
|
multiple: false,
|
||||||
label: i18n.ts._soundSettings.driveFile,
|
label: i18n.ts._soundSettings.driveFile,
|
||||||
}).then(async (file) => {
|
}).then(async (file) => {
|
||||||
if (!file.type.startsWith('audio')) {
|
if (!file.type.startsWith('audio')) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { $i } from '@/i.js';
|
||||||
import { instance } from '@/instance.js';
|
import { instance } from '@/instance.js';
|
||||||
import { globalEvents } from '@/events.js';
|
import { globalEvents } from '@/events.js';
|
||||||
import { getProxiedImageUrl } from '@/utility/media-proxy.js';
|
import { getProxiedImageUrl } from '@/utility/media-proxy.js';
|
||||||
|
import type { UploaderDialogFeatures } from '@/components/MkUploaderDialog.vue';
|
||||||
|
|
||||||
type UploadReturnType = {
|
type UploadReturnType = {
|
||||||
filePromise: Promise<Misskey.entities.DriveFile>;
|
filePromise: Promise<Misskey.entities.DriveFile>;
|
||||||
|
@ -154,6 +155,7 @@ export function uploadFile(file: File | Blob, options: {
|
||||||
export function chooseFileFromPcAndUpload(
|
export function chooseFileFromPcAndUpload(
|
||||||
options: {
|
options: {
|
||||||
multiple?: boolean;
|
multiple?: boolean;
|
||||||
|
features?: UploaderDialogFeatures;
|
||||||
folderId?: string | null;
|
folderId?: string | null;
|
||||||
} = {},
|
} = {},
|
||||||
): Promise<Misskey.entities.DriveFile[]> {
|
): Promise<Misskey.entities.DriveFile[]> {
|
||||||
|
@ -162,6 +164,7 @@ export function chooseFileFromPcAndUpload(
|
||||||
if (files.length === 0) return;
|
if (files.length === 0) return;
|
||||||
os.launchUploader(files, {
|
os.launchUploader(files, {
|
||||||
folderId: options.folderId,
|
folderId: options.folderId,
|
||||||
|
features: options.features,
|
||||||
}).then(driveFiles => {
|
}).then(driveFiles => {
|
||||||
res(driveFiles);
|
res(driveFiles);
|
||||||
});
|
});
|
||||||
|
@ -220,7 +223,7 @@ export function chooseFileFromUrl(): Promise<Misskey.entities.DriveFile> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function select(anchorElement: HTMLElement | EventTarget | null, label: string | null, multiple: boolean): Promise<Misskey.entities.DriveFile[]> {
|
function select(anchorElement: HTMLElement | EventTarget | null, label: string | null, multiple: boolean, features?: UploaderDialogFeatures): Promise<Misskey.entities.DriveFile[]> {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
os.popupMenu([label ? {
|
os.popupMenu([label ? {
|
||||||
text: label,
|
text: label,
|
||||||
|
@ -228,7 +231,7 @@ function select(anchorElement: HTMLElement | EventTarget | null, label: string |
|
||||||
} : undefined, {
|
} : undefined, {
|
||||||
text: i18n.ts.upload,
|
text: i18n.ts.upload,
|
||||||
icon: 'ti ti-upload',
|
icon: 'ti ti-upload',
|
||||||
action: () => chooseFileFromPcAndUpload({ multiple }).then(files => res(files)),
|
action: () => chooseFileFromPcAndUpload({ multiple, features }).then(files => res(files)),
|
||||||
}, {
|
}, {
|
||||||
text: i18n.ts.fromDrive,
|
text: i18n.ts.fromDrive,
|
||||||
icon: 'ti ti-cloud',
|
icon: 'ti ti-cloud',
|
||||||
|
@ -241,12 +244,19 @@ function select(anchorElement: HTMLElement | EventTarget | null, label: string |
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function selectFile(anchorElement: HTMLElement | EventTarget | null, label: string | null = null): Promise<Misskey.entities.DriveFile> {
|
type SelectFileOptions<M extends boolean> = {
|
||||||
return select(anchorElement, label, false).then(files => files[0]);
|
anchorElement: HTMLElement | EventTarget | null;
|
||||||
}
|
multiple: M;
|
||||||
|
label?: string | null;
|
||||||
|
features?: UploaderDialogFeatures;
|
||||||
|
};
|
||||||
|
|
||||||
export function selectFiles(anchorElement: HTMLElement | EventTarget | null, label: string | null = null): Promise<Misskey.entities.DriveFile[]> {
|
export async function selectFile<
|
||||||
return select(anchorElement, label, true);
|
M extends boolean,
|
||||||
|
MR extends M extends true ? Misskey.entities.DriveFile[] : Misskey.entities.DriveFile
|
||||||
|
>(opts: SelectFileOptions<M>): Promise<MR> {
|
||||||
|
const files = await select(opts.anchorElement, opts.label ?? null, opts.multiple ?? false, opts.features);
|
||||||
|
return opts.multiple ? (files as MR) : (files[0]! as MR);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createCroppedImageDriveFileFromImageDriveFile(imageDriveFile: Misskey.entities.DriveFile, options: {
|
export async function createCroppedImageDriveFileFromImageDriveFile(imageDriveFile: Misskey.entities.DriveFile, options: {
|
||||||
|
|
|
@ -294,8 +294,14 @@ export function applyWatermark(img: string | Blob, el: HTMLCanvasElement | Offsc
|
||||||
* @param config ウォーターマークの設定
|
* @param config ウォーターマークの設定
|
||||||
* @returns ウォーターマークを適用した画像のBlob
|
* @returns ウォーターマークを適用した画像のBlob
|
||||||
*/
|
*/
|
||||||
export async function getWatermarkAppliedImage(img: Blob, config: WatermarkConfig): Promise<Blob> {
|
export async function getWatermarkAppliedImage<F extends Blob | File>(img: F, config: WatermarkConfig): Promise<F> {
|
||||||
const canvas = window.document.createElement('canvas');
|
const canvas = window.document.createElement('canvas');
|
||||||
await applyWatermark(img, canvas, config);
|
await applyWatermark(img, canvas, config);
|
||||||
return new Promise(resolve => canvas.toBlob(blob => resolve(blob!)));
|
return new Promise(resolve => canvas.toBlob(blob => {
|
||||||
|
if (img instanceof File) {
|
||||||
|
resolve(new File([blob!], img.name) as F);
|
||||||
|
} else {
|
||||||
|
resolve(blob as F);
|
||||||
|
}
|
||||||
|
}, img.type || 'image/png'));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue