wip
This commit is contained in:
parent
566f729300
commit
37f846af8a
|
@ -33,27 +33,23 @@ import { onMounted, useTemplateRef, ref } from 'vue';
|
|||
import * as Misskey from 'misskey-js';
|
||||
import Cropper from 'cropperjs';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import { apiUrl } from '@@/js/config.js';
|
||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { $i } from '@/i.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { getProxiedImageUrl } from '@/utility/media-proxy.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'ok', cropped: Misskey.entities.DriveFile): void;
|
||||
(ev: 'cancel'): void;
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
file: Misskey.entities.DriveFile;
|
||||
imageFile: File;
|
||||
aspectRatio: number;
|
||||
uploadFolder?: string | null;
|
||||
}>();
|
||||
|
||||
const imgUrl = getProxiedImageUrl(props.file.url, undefined, true);
|
||||
const emit = defineEmits<{
|
||||
(ev: 'ok', cropped: File | Blob): void;
|
||||
(ev: 'cancel'): void;
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
const imgUrl = URL.createObjectURL(props.imageFile);
|
||||
const dialogEl = useTemplateRef('dialogEl');
|
||||
const imgEl = useTemplateRef('imgEl');
|
||||
let cropper: Cropper | null = null;
|
||||
|
@ -71,26 +67,7 @@ const ok = async () => {
|
|||
const croppedCanvas = await croppedSection?.$toCanvas({ width: widthToRender });
|
||||
croppedCanvas?.toBlob(blob => {
|
||||
if (!blob) return;
|
||||
const formData = new FormData();
|
||||
formData.append('file', blob);
|
||||
formData.append('name', `cropped_${props.file.name}`);
|
||||
formData.append('isSensitive', props.file.isSensitive ? 'true' : 'false');
|
||||
if (props.file.comment) { formData.append('comment', props.file.comment);}
|
||||
formData.append('i', $i!.token);
|
||||
if (props.uploadFolder) {
|
||||
formData.append('folderId', props.uploadFolder);
|
||||
} else if (props.uploadFolder !== null && prefer.s.uploadFolder) {
|
||||
formData.append('folderId', prefer.s.uploadFolder);
|
||||
}
|
||||
|
||||
window.fetch(apiUrl + '/drive/files/create', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(f => {
|
||||
res(f);
|
||||
});
|
||||
res(blob);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ import { useStream } from '@/stream.js';
|
|||
import { i18n } from '@/i18n.js';
|
||||
import { claimAchievement } from '@/utility/achievements.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { chooseFileFromPc } from '@/utility/select-file.js';
|
||||
import { chooseFileFromPcAndUpload } from '@/utility/select-file.js';
|
||||
import { store } from '@/store.js';
|
||||
import { isSeparatorNeeded, getSeparatorInfo, makeDateGroupedTimelineComputedRef } from '@/utility/timeline-date-separate.js';
|
||||
import { usePagination } from '@/composables/use-pagination.js';
|
||||
|
@ -555,7 +555,7 @@ function getMenu() {
|
|||
text: i18n.ts.upload,
|
||||
icon: 'ti ti-upload',
|
||||
action: () => {
|
||||
chooseFileFromPc(true, { uploadFolder: folder.value?.id, keepOriginal: true });
|
||||
chooseFileFromPcAndUpload(true, { uploadFolder: folder.value?.id, keepOriginal: true });
|
||||
},
|
||||
}, {
|
||||
text: i18n.ts.fromUrl,
|
||||
|
|
|
@ -145,7 +145,7 @@ async function describe(file: Misskey.entities.DriveFile) {
|
|||
async function crop(file: Misskey.entities.DriveFile): Promise<void> {
|
||||
if (mock) return;
|
||||
|
||||
const newFile = await os.cropImage(file, { aspectRatio: NaN });
|
||||
const newFile = await os.createCroppedImageDriveFileFromImageDriveFile(file, { aspectRatio: NaN });
|
||||
emit('replaceFile', file, newFile);
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ import MkInput from '@/components/MkInput.vue';
|
|||
import MkTextarea from '@/components/MkTextarea.vue';
|
||||
import FormSlot from '@/components/form/slot.vue';
|
||||
import MkInfo from '@/components/MkInfo.vue';
|
||||
import { chooseFileFromPc } from '@/utility/select-file.js';
|
||||
import { chooseFileFromPcAndUpload } from '@/utility/select-file.js';
|
||||
import * as os from '@/os.js';
|
||||
import { ensureSignin } from '@/i.js';
|
||||
|
||||
|
@ -49,7 +49,7 @@ const description = ref($i.description ?? '');
|
|||
watch(name, () => {
|
||||
os.apiWithDialog('i/update', {
|
||||
// 空文字列をnullにしたいので??は使うな
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||
|
||||
name: name.value || null,
|
||||
}, undefined, {
|
||||
'0b3f9f6a-2f4d-4b1f-9fb4-49d3a2fd7191': {
|
||||
|
@ -62,13 +62,13 @@ watch(name, () => {
|
|||
watch(description, () => {
|
||||
os.apiWithDialog('i/update', {
|
||||
// 空文字列をnullにしたいので??は使うな
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||
|
||||
description: description.value || null,
|
||||
});
|
||||
});
|
||||
|
||||
function setAvatar(ev) {
|
||||
chooseFileFromPc(false).then(async (files) => {
|
||||
os.chooseFileFromPc({ multiple: false }).then(async (files) => {
|
||||
const file = files[0];
|
||||
|
||||
let originalOrCropped = file;
|
||||
|
@ -81,13 +81,15 @@ function setAvatar(ev) {
|
|||
});
|
||||
|
||||
if (!canceled) {
|
||||
originalOrCropped = await os.cropImage(file, {
|
||||
originalOrCropped = await os.cropImageFile(file, {
|
||||
aspectRatio: 1,
|
||||
});
|
||||
}
|
||||
|
||||
const driveFile = (await os.launchUploader([originalOrCropped], {}))[0];
|
||||
|
||||
const i = await os.apiWithDialog('i/update', {
|
||||
avatarId: originalOrCropped.id,
|
||||
avatarId: driveFile.id,
|
||||
});
|
||||
$i.avatarId = i.avatarId;
|
||||
$i.avatarUrl = i.avatarUrl;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { markRaw, ref, defineAsyncComponent, nextTick } from 'vue';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { getProxiedImageUrl } from './utility/media-proxy.js';
|
||||
import type { Component, Ref } from 'vue';
|
||||
import type { ComponentProps as CP } from 'vue-component-type-helpers';
|
||||
import type { Form, GetFormResultType } from '@/utility/form.js';
|
||||
|
@ -653,15 +654,13 @@ export async function pickEmoji(src: HTMLElement, opts: ComponentProps<typeof Mk
|
|||
});
|
||||
}
|
||||
|
||||
export async function cropImage(image: Misskey.entities.DriveFile, options: {
|
||||
export async function cropImageFile(imageFile: File, options: {
|
||||
aspectRatio: number;
|
||||
uploadFolder?: string | null;
|
||||
}): Promise<Misskey.entities.DriveFile> {
|
||||
}): Promise<File> {
|
||||
return new Promise(resolve => {
|
||||
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkCropperDialog.vue')), {
|
||||
file: image,
|
||||
imageFile: imageFile,
|
||||
aspectRatio: options.aspectRatio,
|
||||
uploadFolder: options.uploadFolder,
|
||||
}, {
|
||||
ok: x => {
|
||||
resolve(x);
|
||||
|
@ -671,6 +670,29 @@ export async function cropImage(image: Misskey.entities.DriveFile, options: {
|
|||
});
|
||||
}
|
||||
|
||||
export async function createCroppedImageDriveFileFromImageDriveFile(imageDriveFile: Misskey.entities.DriveFile, options: {
|
||||
aspectRatio: number;
|
||||
uploadFolder?: string | null;
|
||||
}): Promise<Misskey.entities.DriveFile> {
|
||||
return new Promise(resolve => {
|
||||
const imgUrl = getProxiedImageUrl(imageDriveFile.url, undefined, true);
|
||||
const image = new Image();
|
||||
image.src = imgUrl;
|
||||
image.onload = () => {
|
||||
const canvas = window.document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d')!;
|
||||
canvas.width = image.width;
|
||||
canvas.height = image.height;
|
||||
ctx.drawImage(image, 0, 0);
|
||||
const imageFile = new File([canvas.toBlob()], imageDriveFile.name, { type: imageDriveFile.type });
|
||||
|
||||
cropImageFile(imageFile).then(croppedImageFile => {
|
||||
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function popupMenu(items: MenuItem[], src?: HTMLElement | EventTarget | null, options?: {
|
||||
align?: string;
|
||||
width?: number;
|
||||
|
@ -774,6 +796,32 @@ export function checkExistence(fileData: ArrayBuffer): Promise<any> {
|
|||
});
|
||||
}*/
|
||||
|
||||
export function chooseFileFromPc(
|
||||
options: {
|
||||
multiple?: boolean;
|
||||
} = {},
|
||||
): Promise<File[]> {
|
||||
return new Promise((res, rej) => {
|
||||
const input = window.document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.multiple = options.multiple ?? false;
|
||||
input.onchange = () => {
|
||||
if (!input.files) return res([]);
|
||||
|
||||
res(Array.from(input.files));
|
||||
|
||||
// 一応廃棄
|
||||
(window as any).__misskey_input_ref__ = null;
|
||||
};
|
||||
|
||||
// https://qiita.com/fukasawah/items/b9dc732d95d99551013d
|
||||
// iOS Safari で正常に動かす為のおまじない
|
||||
(window as any).__misskey_input_ref__ = input;
|
||||
|
||||
input.click();
|
||||
});
|
||||
}
|
||||
|
||||
export function launchUploader(
|
||||
files: File[],
|
||||
options?: {
|
||||
|
@ -781,6 +829,7 @@ export function launchUploader(
|
|||
},
|
||||
): Promise<Misskey.entities.DriveFile[]> {
|
||||
return new Promise((res, rej) => {
|
||||
if (files.length === 0) return;
|
||||
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkUploaderDialog.vue')), {
|
||||
files: markRaw(files),
|
||||
folderId: options?.folderId,
|
||||
|
|
|
@ -94,7 +94,7 @@ import MkFolder from '@/components/MkFolder.vue';
|
|||
import MkButton from '@/components/MkButton.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { validators } from '@/components/grid/cell-validators.js';
|
||||
import { chooseFileFromDrive, chooseFileFromPc } from '@/utility/select-file.js';
|
||||
import { chooseFileFromDrive, chooseFileFromPcAndUpload } from '@/utility/select-file.js';
|
||||
import { extractDroppedItems, flattenDroppedFiles } from '@/utility/file-drop.js';
|
||||
import XRegisterLogs from '@/pages/admin/custom-emojis-manager.logs.vue';
|
||||
import { copyGridDataToClipboard } from '@/components/grid/grid-utils.js';
|
||||
|
@ -364,7 +364,7 @@ async function onDrop(ev: DragEvent) {
|
|||
}
|
||||
|
||||
async function onFileSelectClicked() {
|
||||
const driveFiles = await chooseFileFromPc(
|
||||
const driveFiles = await chooseFileFromPcAndUpload(
|
||||
true,
|
||||
{
|
||||
uploadFolder: selectedFolderId.value,
|
||||
|
|
|
@ -130,7 +130,7 @@ function postThis() {
|
|||
function crop() {
|
||||
if (!file.value) return;
|
||||
|
||||
os.cropImage(file.value, {
|
||||
os.createCroppedImageDriveFileFromImageDriveFile(file.value, {
|
||||
aspectRatio: NaN,
|
||||
uploadFolder: file.value.folderId ?? null,
|
||||
});
|
||||
|
|
|
@ -268,7 +268,7 @@ function changeAvatar(ev) {
|
|||
});
|
||||
|
||||
if (!canceled) {
|
||||
originalOrCropped = await os.cropImage(file, {
|
||||
originalOrCropped = await os.createCroppedImageDriveFileFromImageDriveFile(file, {
|
||||
aspectRatio: 1,
|
||||
});
|
||||
}
|
||||
|
@ -294,7 +294,7 @@ function changeBanner(ev) {
|
|||
});
|
||||
|
||||
if (!canceled) {
|
||||
originalOrCropped = await os.cropImage(file, {
|
||||
originalOrCropped = await os.createCroppedImageDriveFileFromImageDriveFile(file, {
|
||||
aspectRatio: 2,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Miss
|
|||
menuItems.push({
|
||||
text: i18n.ts.cropImage,
|
||||
icon: 'ti ti-crop',
|
||||
action: () => os.cropImage(file, {
|
||||
action: () => os.createCroppedImageDriveFileFromImageDriveFile(file, {
|
||||
aspectRatio: NaN,
|
||||
uploadFolder: folder ? folder.id : folder,
|
||||
}),
|
||||
|
|
|
@ -11,34 +11,21 @@ import { useStream } from '@/stream.js';
|
|||
import { i18n } from '@/i18n.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
|
||||
export function chooseFileFromPc(
|
||||
export function chooseFileFromPcAndUpload(
|
||||
options: {
|
||||
multiple?: boolean;
|
||||
folderId?: string | null;
|
||||
} = {},
|
||||
): Promise<Misskey.entities.DriveFile[]> {
|
||||
return new Promise((res, rej) => {
|
||||
const input = window.document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.multiple = options.multiple ?? false;
|
||||
input.onchange = () => {
|
||||
if (!input.files) return res([]);
|
||||
|
||||
os.launchUploader(Array.from(input.files), {
|
||||
os.chooseFileFromPc({ multiple: options.multiple }).then(files => {
|
||||
if (files.length === 0) return;
|
||||
os.launchUploader(files, {
|
||||
folderId: options.folderId,
|
||||
}).then(driveFiles => {
|
||||
res(driveFiles);
|
||||
});
|
||||
|
||||
// 一応廃棄
|
||||
(window as any).__misskey_input_ref__ = null;
|
||||
};
|
||||
|
||||
// https://qiita.com/fukasawah/items/b9dc732d95d99551013d
|
||||
// iOS Safari で正常に動かす為のおまじない
|
||||
(window as any).__misskey_input_ref__ = input;
|
||||
|
||||
input.click();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -91,7 +78,7 @@ function select(src: HTMLElement | EventTarget | null, label: string | null, mul
|
|||
} : undefined, {
|
||||
text: i18n.ts.upload,
|
||||
icon: 'ti ti-upload',
|
||||
action: () => chooseFileFromPc(multiple, { keepOriginal: true }).then(files => res(files)),
|
||||
action: () => chooseFileFromPcAndUpload({ multiple }).then(files => res(files)),
|
||||
}, {
|
||||
text: i18n.ts.fromDrive,
|
||||
icon: 'ti ti-cloud',
|
||||
|
|
Loading…
Reference in New Issue