mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-03-21 03:30:42 +00:00
refactor(frontend): refactor uploader image editing features and menu
Replaces separate 'effect' and 'crop' features with a unified 'imageEditing' feature in the uploader. Groups crop and effect actions under a new parent 'editImage' menu item, adds localization for 'editImage', and updates supported types accordingly.
This commit is contained in:
4
locales/index.d.ts
vendored
4
locales/index.d.ts
vendored
@@ -11991,6 +11991,10 @@ export interface Locale extends ILocale {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
"_uploader": {
|
"_uploader": {
|
||||||
|
/**
|
||||||
|
* 画像の編集
|
||||||
|
*/
|
||||||
|
"editImage": string;
|
||||||
/**
|
/**
|
||||||
* {x}に圧縮
|
* {x}に圧縮
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -3208,6 +3208,7 @@ _serverSetupWizard:
|
|||||||
text3: "支援者向け特典もあります!"
|
text3: "支援者向け特典もあります!"
|
||||||
|
|
||||||
_uploader:
|
_uploader:
|
||||||
|
editImage: "画像の編集"
|
||||||
compressedToX: "{x}に圧縮"
|
compressedToX: "{x}に圧縮"
|
||||||
savedXPercent: "{x}%節約"
|
savedXPercent: "{x}%節約"
|
||||||
abortConfirm: "アップロードされていないファイルがありますが、中止しますか?"
|
abortConfirm: "アップロードされていないファイルがありますが、中止しますか?"
|
||||||
|
|||||||
@@ -19,9 +19,8 @@ import { ensureSignin } from '@/i.js';
|
|||||||
import { WatermarkRenderer } from '@/utility/watermark.js';
|
import { WatermarkRenderer } from '@/utility/watermark.js';
|
||||||
|
|
||||||
export type UploaderFeatures = {
|
export type UploaderFeatures = {
|
||||||
effect?: boolean;
|
imageEditing?: boolean;
|
||||||
watermark?: boolean;
|
watermark?: boolean;
|
||||||
crop?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const THUMBNAIL_SUPPORTED_TYPES = [
|
const THUMBNAIL_SUPPORTED_TYPES = [
|
||||||
@@ -38,12 +37,6 @@ const IMAGE_COMPRESSION_SUPPORTED_TYPES = [
|
|||||||
'image/svg+xml',
|
'image/svg+xml',
|
||||||
];
|
];
|
||||||
|
|
||||||
const CROPPING_SUPPORTED_TYPES = [
|
|
||||||
'image/jpeg',
|
|
||||||
'image/png',
|
|
||||||
'image/webp',
|
|
||||||
];
|
|
||||||
|
|
||||||
const IMAGE_EDITING_SUPPORTED_TYPES = [
|
const IMAGE_EDITING_SUPPORTED_TYPES = [
|
||||||
'image/jpeg',
|
'image/jpeg',
|
||||||
'image/png',
|
'image/png',
|
||||||
@@ -55,7 +48,6 @@ const WATERMARK_SUPPORTED_TYPES = IMAGE_EDITING_SUPPORTED_TYPES;
|
|||||||
const IMAGE_PREPROCESS_NEEDED_TYPES = [
|
const IMAGE_PREPROCESS_NEEDED_TYPES = [
|
||||||
...WATERMARK_SUPPORTED_TYPES,
|
...WATERMARK_SUPPORTED_TYPES,
|
||||||
...IMAGE_COMPRESSION_SUPPORTED_TYPES,
|
...IMAGE_COMPRESSION_SUPPORTED_TYPES,
|
||||||
...CROPPING_SUPPORTED_TYPES,
|
|
||||||
...IMAGE_EDITING_SUPPORTED_TYPES,
|
...IMAGE_EDITING_SUPPORTED_TYPES,
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -112,17 +104,14 @@ export function useUploader(options: {
|
|||||||
multiple?: boolean;
|
multiple?: boolean;
|
||||||
features?: UploaderFeatures;
|
features?: UploaderFeatures;
|
||||||
} = {}) {
|
} = {}) {
|
||||||
const $i = ensureSignin();
|
|
||||||
|
|
||||||
const events = new EventEmitter<{
|
const events = new EventEmitter<{
|
||||||
'itemUploaded': (ctx: { item: UploaderItem; }) => void;
|
'itemUploaded': (ctx: { item: UploaderItem; }) => void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const uploaderFeatures = computed<Required<UploaderFeatures>>(() => {
|
const uploaderFeatures = computed<Required<UploaderFeatures>>(() => {
|
||||||
return {
|
return {
|
||||||
effect: options.features?.effect ?? true,
|
imageEditing: options.features?.imageEditing ?? true,
|
||||||
watermark: options.features?.watermark ?? true,
|
watermark: options.features?.watermark ?? true,
|
||||||
crop: options.features?.crop ?? true,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -215,60 +204,61 @@ export function useUploader(options: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
uploaderFeatures.value.crop &&
|
uploaderFeatures.value.imageEditing &&
|
||||||
CROPPING_SUPPORTED_TYPES.includes(item.file.type) &&
|
|
||||||
!item.preprocessing &&
|
|
||||||
!item.uploading &&
|
|
||||||
!item.uploaded
|
|
||||||
) {
|
|
||||||
menu.push({
|
|
||||||
icon: 'ti ti-crop',
|
|
||||||
text: i18n.ts.cropImage,
|
|
||||||
action: async () => {
|
|
||||||
const cropped = await os.cropImageFile(item.file, { aspectRatio: null });
|
|
||||||
if (item.thumbnail != null) URL.revokeObjectURL(item.thumbnail);
|
|
||||||
items.value.splice(items.value.indexOf(item), 1, {
|
|
||||||
...item,
|
|
||||||
file: markRaw(cropped),
|
|
||||||
thumbnail: window.URL.createObjectURL(cropped),
|
|
||||||
});
|
|
||||||
const reactiveItem = items.value.find(x => x.id === item.id)!;
|
|
||||||
preprocess(reactiveItem).then(() => {
|
|
||||||
triggerRef(items);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
uploaderFeatures.value.effect &&
|
|
||||||
IMAGE_EDITING_SUPPORTED_TYPES.includes(item.file.type) &&
|
IMAGE_EDITING_SUPPORTED_TYPES.includes(item.file.type) &&
|
||||||
!item.preprocessing &&
|
!item.preprocessing &&
|
||||||
!item.uploading &&
|
!item.uploading &&
|
||||||
!item.uploaded
|
!item.uploaded
|
||||||
) {
|
) {
|
||||||
menu.push({
|
menu.push({
|
||||||
icon: 'ti ti-sparkles',
|
type: 'parent',
|
||||||
text: i18n.ts._imageEffector.title + ' (BETA)',
|
icon: 'ti ti-photo-edit',
|
||||||
action: async () => {
|
text: i18n.ts._uploader.editImage,
|
||||||
const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkImageEffectorDialog.vue').then(x => x.default), {
|
children: [{
|
||||||
image: item.file,
|
icon: 'ti ti-crop',
|
||||||
}, {
|
text: i18n.ts.cropImage,
|
||||||
ok: (file) => {
|
action: async () => {
|
||||||
if (item.thumbnail != null) URL.revokeObjectURL(item.thumbnail);
|
const cropped = await os.cropImageFile(item.file, { aspectRatio: null });
|
||||||
items.value.splice(items.value.indexOf(item), 1, {
|
if (item.thumbnail != null) URL.revokeObjectURL(item.thumbnail);
|
||||||
...item,
|
items.value.splice(items.value.indexOf(item), 1, {
|
||||||
file: markRaw(file),
|
...item,
|
||||||
thumbnail: window.URL.createObjectURL(file),
|
file: markRaw(cropped),
|
||||||
});
|
thumbnail: window.URL.createObjectURL(cropped),
|
||||||
const reactiveItem = items.value.find(x => x.id === item.id)!;
|
});
|
||||||
preprocess(reactiveItem).then(() => {
|
const reactiveItem = items.value.find(x => x.id === item.id)!;
|
||||||
triggerRef(items);
|
preprocess(reactiveItem).then(() => {
|
||||||
});
|
triggerRef(items);
|
||||||
},
|
});
|
||||||
closed: () => dispose(),
|
},
|
||||||
});
|
}, /*{
|
||||||
},
|
icon: 'ti ti-resize',
|
||||||
|
text: i18n.ts.resize,
|
||||||
|
action: async () => {
|
||||||
|
// TODO
|
||||||
|
},
|
||||||
|
},*/ {
|
||||||
|
icon: 'ti ti-sparkles',
|
||||||
|
text: i18n.ts._imageEffector.title + ' (BETA)',
|
||||||
|
action: async () => {
|
||||||
|
const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkImageEffectorDialog.vue').then(x => x.default), {
|
||||||
|
image: item.file,
|
||||||
|
}, {
|
||||||
|
ok: (file) => {
|
||||||
|
if (item.thumbnail != null) URL.revokeObjectURL(item.thumbnail);
|
||||||
|
items.value.splice(items.value.indexOf(item), 1, {
|
||||||
|
...item,
|
||||||
|
file: markRaw(file),
|
||||||
|
thumbnail: window.URL.createObjectURL(file),
|
||||||
|
});
|
||||||
|
const reactiveItem = items.value.find(x => x.id === item.id)!;
|
||||||
|
preprocess(reactiveItem).then(() => {
|
||||||
|
triggerRef(items);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
closed: () => dispose(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user