-
-
-
-
-
-
-
![]()
+
+
+
+
+
+
+
-
+
@@ -35,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 props = defineProps<{
+ imageFile: File | Blob;
+ aspectRatio: number | null;
+ uploadFolder?: string | null;
+}>();
const emit = defineEmits<{
- (ev: 'ok', cropped: Misskey.entities.DriveFile): void;
+ (ev: 'ok', cropped: File | Blob): void;
(ev: 'cancel'): void;
(ev: 'closed'): void;
}>();
-const props = defineProps<{
- file: Misskey.entities.DriveFile;
- aspectRatio: number;
- uploadFolder?: string | null;
-}>();
-
-const imgUrl = getProxiedImageUrl(props.file.url, undefined, true);
+const imgUrl = URL.createObjectURL(props.imageFile);
const dialogEl = useTemplateRef('dialogEl');
const imgEl = useTemplateRef('imgEl');
let cropper: Cropper | null = null;
@@ -73,31 +67,10 @@ 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);
});
});
- os.promiseDialog(promise);
-
const f = await promise;
emit('ok', f);
@@ -126,8 +99,8 @@ onMounted(() => {
const selection = cropper.getCropperSelection()!;
selection.themeColor = tinycolor(computedStyle.getPropertyValue('--MI_THEME-accent')).toHexString();
- selection.aspectRatio = props.aspectRatio;
- selection.initialAspectRatio = props.aspectRatio;
+ if (props.aspectRatio != null) selection.aspectRatio = props.aspectRatio;
+ selection.initialAspectRatio = props.aspectRatio ?? 1;
selection.outlined = true;
window.setTimeout(() => {
diff --git a/packages/frontend/src/components/MkDrive.file.vue b/packages/frontend/src/components/MkDrive.file.vue
index 70ab60cfae..bbb6a7ea2a 100644
--- a/packages/frontend/src/components/MkDrive.file.vue
+++ b/packages/frontend/src/components/MkDrive.file.vue
@@ -8,7 +8,6 @@ SPDX-License-Identifier: AGPL-3.0-only
:class="[$style.root, { [$style.isSelected]: isSelected }]"
draggable="true"
:title="title"
- @click="onClick"
@contextmenu.stop="onContextmenu"
@dragstart="onDragstart"
@dragend="onDragend"
@@ -46,24 +45,18 @@ import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { $i } from '@/i.js';
import { getDriveFileMenu } from '@/utility/get-drive-file-menu.js';
-import { deviceKind } from '@/utility/device-kind.js';
-import { useRouter } from '@/router.js';
-
-const router = useRouter();
+import { setDragData } from '@/drag-and-drop.js';
const props = withDefaults(defineProps<{
file: Misskey.entities.DriveFile;
folder: Misskey.entities.DriveFolder | null;
isSelected?: boolean;
- selectMode?: boolean;
}>(), {
isSelected: false,
- selectMode: false,
});
const emit = defineEmits<{
- (ev: 'chosen', r: Misskey.entities.DriveFile): void;
- (ev: 'dragstart'): void;
+ (ev: 'dragstart', dragEvent: DragEvent): void;
(ev: 'dragend'): void;
}>();
@@ -71,18 +64,6 @@ const isDragging = ref(false);
const title = computed(() => `${props.file.name}\n${props.file.type} ${bytes(props.file.size)}`);
-function onClick(ev: MouseEvent) {
- if (props.selectMode) {
- emit('chosen', props.file);
- } else {
- if (deviceKind === 'desktop') {
- router.push(`/my/drive/file/${props.file.id}`);
- } else {
- os.popupMenu(getDriveFileMenu(props.file, props.folder), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined);
- }
- }
-}
-
function onContextmenu(ev: MouseEvent) {
os.contextMenu(getDriveFileMenu(props.file, props.folder), ev);
}
@@ -90,11 +71,11 @@ function onContextmenu(ev: MouseEvent) {
function onDragstart(ev: DragEvent) {
if (ev.dataTransfer) {
ev.dataTransfer.effectAllowed = 'move';
- ev.dataTransfer.setData(_DATA_TRANSFER_DRIVE_FILE_, JSON.stringify(props.file));
+ setDragData(ev, 'driveFiles', [props.file]);
}
isDragging.value = true;
- emit('dragstart');
+ emit('dragstart', ev);
}
function onDragend() {
@@ -114,7 +95,7 @@ function onDragend() {
&:hover {
background: rgba(#000, 0.05);
- > .label {
+ .label {
&::before,
&::after {
background: #0b65a5;
@@ -132,7 +113,7 @@ function onDragend() {
&:active {
background: rgba(#000, 0.1);
- > .label {
+ .label {
&::before,
&::after {
background: #0b588c;
@@ -158,19 +139,19 @@ function onDragend() {
background: hsl(from var(--MI_THEME-accent) h s calc(l - 10));
}
- > .label {
+ .label {
&::before,
&::after {
display: none;
}
}
- > .name {
- color: #fff;
+ .name {
+ color: var(--MI_THEME-fgOnAccent);
}
- > .thumbnail {
- color: #fff;
+ .thumbnail {
+ color: var(--MI_THEME-fgOnAccent);
}
}
}
@@ -240,8 +221,8 @@ function onDragend() {
.name {
display: block;
- margin: 4px 0 0 0;
- font-size: 0.8em;
+ margin: 8px 0 0 0;
+ font-size: 82%;
text-align: center;
word-break: break-all;
color: var(--MI_THEME-fg);
diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue
index 9c72691d21..83472eec3d 100644
--- a/packages/frontend/src/components/MkDrive.folder.vue
+++ b/packages/frontend/src/components/MkDrive.folder.vue
@@ -8,7 +8,6 @@ SPDX-License-Identifier: AGPL-3.0-only
:class="[$style.root, { [$style.draghover]: draghover }]"
draggable="true"
:title="title"
- @click="onClick"
@contextmenu.stop="onContextmenu"
@mouseover="onMouseover"
@mouseout="onMouseout"
@@ -19,14 +18,13 @@ SPDX-License-Identifier: AGPL-3.0-only
@dragstart="onDragstart"
@dragend="onDragend"
>
-
-
-
- {{ folder.name }}
-
-
+
+
{{ folder.name }}
+
{{ i18n.ts.uploadFolder }}
-
+
@@ -43,6 +41,9 @@ import { i18n } from '@/i18n.js';
import { claimAchievement } from '@/utility/achievements.js';
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
import { prefer } from '@/preferences.js';
+import { globalEvents } from '@/events.js';
+import { checkDragDataType, getDragData, setDragData } from '@/drag-and-drop.js';
+import { selectDriveFolder } from '@/utility/drive.js';
const props = withDefaults(defineProps<{
folder: Misskey.entities.DriveFolder;
@@ -56,10 +57,7 @@ const props = withDefaults(defineProps<{
const emit = defineEmits<{
(ev: 'chosen', v: Misskey.entities.DriveFolder): void;
(ev: 'unchose', v: Misskey.entities.DriveFolder): void;
- (ev: 'move', v: Misskey.entities.DriveFolder): void;
- (ev: 'upload', file: File, folder: Misskey.entities.DriveFolder);
- (ev: 'removeFile', v: Misskey.entities.DriveFile['id']): void;
- (ev: 'removeFolder', v: Misskey.entities.DriveFolder['id']): void;
+ (ev: 'upload', files: File[], folder: Misskey.entities.DriveFolder);
(ev: 'dragstart'): void;
(ev: 'dragend'): void;
}>();
@@ -78,10 +76,6 @@ function checkboxClicked() {
}
}
-function onClick() {
- emit('move', props.folder);
-}
-
function onMouseover() {
hover.value = true;
}
@@ -101,10 +95,7 @@ function onDragover(ev: DragEvent) {
}
const isFile = ev.dataTransfer.items[0].kind === 'file';
- const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
- const isDriveFolder = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FOLDER_;
-
- if (isFile || isDriveFile || isDriveFolder) {
+ if (isFile || checkDragDataType(ev, ['driveFiles', 'driveFolders'])) {
switch (ev.dataTransfer.effectAllowed) {
case 'all':
case 'uninitialized':
@@ -141,55 +132,64 @@ function onDrop(ev: DragEvent) {
// ファイルだったら
if (ev.dataTransfer.files.length > 0) {
- for (const file of Array.from(ev.dataTransfer.files)) {
- emit('upload', file, props.folder);
- }
+ emit('upload', Array.from(ev.dataTransfer.files), props.folder);
return;
}
//#region ドライブのファイル
- const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
- if (driveFile != null && driveFile !== '') {
- const file = JSON.parse(driveFile);
- emit('removeFile', file.id);
- misskeyApi('drive/files/update', {
- fileId: file.id,
- folderId: props.folder.id,
- });
+ {
+ const droppedData = getDragData(ev, 'driveFiles');
+ if (droppedData != null) {
+ misskeyApi('drive/files/move-bulk', {
+ fileIds: droppedData.map(f => f.id),
+ folderId: props.folder.id,
+ }).then(() => {
+ globalEvents.emit('driveFilesUpdated', droppedData.map(x => ({
+ ...x,
+ folderId: props.folder.id,
+ folder: props.folder,
+ })));
+ });
+ }
}
//#endregion
//#region ドライブのフォルダ
- const driveFolder = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FOLDER_);
- if (driveFolder != null && driveFolder !== '') {
- const folder = JSON.parse(driveFolder);
-
- // 移動先が自分自身ならreject
- if (folder.id === props.folder.id) return;
-
- emit('removeFolder', folder.id);
- misskeyApi('drive/folders/update', {
- folderId: folder.id,
- parentId: props.folder.id,
- }).then(() => {
- // noop
- }).catch(err => {
- switch (err.code) {
- case 'RECURSIVE_NESTING':
- claimAchievement('driveFolderCircularReference');
- os.alert({
- type: 'error',
- title: i18n.ts.unableToProcess,
- text: i18n.ts.circularReferenceFolder,
- });
- break;
- default:
- os.alert({
- type: 'error',
- text: i18n.ts.somethingHappened,
- });
- }
- });
+ {
+ const droppedData = getDragData(ev, 'driveFolders');
+ if (droppedData != null) {
+ const droppedFolder = droppedData[0];
+
+ // 移動先が自分自身ならreject
+ if (droppedFolder.id === props.folder.id) return;
+
+ misskeyApi('drive/folders/update', {
+ folderId: droppedFolder.id,
+ parentId: props.folder.id,
+ }).then(() => {
+ globalEvents.emit('driveFoldersUpdated', [droppedFolder].map(x => ({
+ ...x,
+ parentId: props.folder.id,
+ parent: props.folder,
+ })));
+ }).catch(err => {
+ switch (err.code) {
+ case 'RECURSIVE_NESTING':
+ claimAchievement('driveFolderCircularReference');
+ os.alert({
+ type: 'error',
+ title: i18n.ts.unableToProcess,
+ text: i18n.ts.circularReferenceFolder,
+ });
+ break;
+ default:
+ os.alert({
+ type: 'error',
+ text: i18n.ts.somethingHappened,
+ });
+ }
+ });
+ }
}
//#endregion
}
@@ -198,7 +198,7 @@ function onDragstart(ev: DragEvent) {
if (!ev.dataTransfer) return;
ev.dataTransfer.effectAllowed = 'move';
- ev.dataTransfer.setData(_DATA_TRANSFER_DRIVE_FOLDER_, JSON.stringify(props.folder));
+ setDragData(ev, 'driveFolders', [props.folder]);
isDragging.value = true;
// 親ブラウザに対して、ドラッグが開始されたフラグを立てる
@@ -211,10 +211,6 @@ function onDragend() {
emit('dragend');
}
-function go() {
- emit('move', props.folder);
-}
-
function rename() {
os.inputText({
title: i18n.ts.renameFolder,
@@ -225,17 +221,28 @@ function rename() {
misskeyApi('drive/folders/update', {
folderId: props.folder.id,
name: name,
+ }).then(() => {
+ globalEvents.emit('driveFoldersUpdated', [{
+ ...props.folder,
+ name: name,
+ }]);
});
});
}
function move() {
- os.selectDriveFolder(false).then(folder => {
+ selectDriveFolder(null).then(folder => {
if (folder[0] && folder[0].id === props.folder.id) return;
misskeyApi('drive/folders/update', {
folderId: props.folder.id,
parentId: folder[0] ? folder[0].id : null,
+ }).then(() => {
+ globalEvents.emit('driveFoldersUpdated', [{
+ ...props.folder,
+ parentId: folder[0] ? folder[0].id : null,
+ parent: folder[0] ?? null,
+ }]);
});
});
}
@@ -247,6 +254,7 @@ function deleteFolder() {
if (prefer.s.uploadFolder === props.folder.id) {
prefer.commit('uploadFolder', null);
}
+ globalEvents.emit('driveFoldersDeleted', [props.folder]);
}).catch(err => {
switch (err.id) {
case 'b0fc8a17-963c-405d-bfbc-859a487295e1':
@@ -311,10 +319,9 @@ function onContextmenu(ev: MouseEvent) {
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 5114e98494..d07ee2a978 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -71,7 +71,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ maxTextLength - textLength }}
-
+
@@ -120,14 +120,13 @@ import { formatTimeString } from '@/utility/format-time-string.js';
import { Autocomplete } from '@/utility/autocomplete.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
-import { selectFiles } from '@/utility/select-file.js';
+import { selectFiles } from '@/utility/drive.js';
import { store } from '@/store.js';
import MkInfo from '@/components/MkInfo.vue';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import { ensureSignin, notesCount, incNotesCount } from '@/i.js';
import { getAccounts, openAccountMenu as openAccountMenu_ } from '@/accounts.js';
-import { uploadFile } from '@/utility/upload.js';
import { deepClone } from '@/utility/clone.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
import { miLocalStorage } from '@/local-storage.js';
@@ -138,6 +137,7 @@ import { prefer } from '@/preferences.js';
import { getPluginHandlers } from '@/plugin.js';
import { DI } from '@/di.js';
import { globalEvents } from '@/events.js';
+import { checkDragDataType, getDragData } from '@/drag-and-drop.js';
const $i = ensureSignin();
@@ -459,18 +459,6 @@ function updateFileName(file, name) {
files.value[files.value.findIndex(x => x.id === file.id)].name = name;
}
-function replaceFile(file: Misskey.entities.DriveFile, newFile: Misskey.entities.DriveFile): void {
- files.value[files.value.findIndex(x => x.id === file.id)] = newFile;
-}
-
-function upload(file: File, name?: string): void {
- if (props.mock) return;
-
- uploadFile(file, prefer.s.uploadFolder, name).then(res => {
- files.value.push(res);
- });
-}
-
function setVisibility() {
if (props.channel) {
visibility.value = 'public';
@@ -651,16 +639,25 @@ async function onPaste(ev: ClipboardEvent) {
if (props.mock) return;
if (!ev.clipboardData) return;
+ let pastedFiles: File[] = [];
for (const { item, i } of Array.from(ev.clipboardData.items, (data, x) => ({ item: data, i: x }))) {
if (item.kind === 'file') {
const file = item.getAsFile();
if (!file) continue;
const lio = file.name.lastIndexOf('.');
const ext = lio >= 0 ? file.name.slice(lio) : '';
- const formatted = `${formatTimeString(new Date(file.lastModified), pastedFileName).replace(/{{number}}/g, `${i + 1}`)}${ext}`;
- upload(file, formatted);
+ const formattedName = `${formatTimeString(new Date(file.lastModified), pastedFileName).replace(/{{number}}/g, `${i + 1}`)}${ext}`;
+ const renamedFile = new File([file], formattedName, { type: file.type });
+ pastedFiles.push(renamedFile);
}
}
+ if (pastedFiles.length > 0) {
+ ev.preventDefault();
+ os.launchUploader(pastedFiles, {}).then(driveFiles => {
+ files.value.push(...driveFiles);
+ });
+ return;
+ }
const paste = ev.clipboardData.getData('text');
@@ -693,7 +690,9 @@ async function onPaste(ev: ClipboardEvent) {
const fileName = formatTimeString(new Date(), pastedFileName).replace(/{{number}}/g, '0');
const file = new File([paste], `${fileName}.txt`, { type: 'text/plain' });
- upload(file, `${fileName}.txt`);
+ os.launchUploader([file], {}).then(driveFiles => {
+ files.value.push(...driveFiles);
+ });
});
}
}
@@ -701,8 +700,7 @@ async function onPaste(ev: ClipboardEvent) {
function onDragover(ev) {
if (!ev.dataTransfer.items[0]) return;
const isFile = ev.dataTransfer.items[0].kind === 'file';
- const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
- if (isFile || isDriveFile) {
+ if (isFile || checkDragDataType(ev, ['driveFiles'])) {
ev.preventDefault();
draghover.value = true;
switch (ev.dataTransfer.effectAllowed) {
@@ -738,16 +736,19 @@ function onDrop(ev: DragEvent): void {
// ファイルだったら
if (ev.dataTransfer && ev.dataTransfer.files.length > 0) {
ev.preventDefault();
- for (const x of Array.from(ev.dataTransfer.files)) upload(x);
+ os.launchUploader(Array.from(ev.dataTransfer.files), {}).then(driveFiles => {
+ files.value.push(...driveFiles);
+ });
return;
}
//#region ドライブのファイル
- const driveFile = ev.dataTransfer?.getData(_DATA_TRANSFER_DRIVE_FILE_);
- if (driveFile != null && driveFile !== '') {
- const file = JSON.parse(driveFile);
- files.value.push(file);
- ev.preventDefault();
+ {
+ const droppedData = getDragData(ev, 'driveFiles');
+ if (droppedData != null) {
+ files.value.push(...droppedData);
+ ev.preventDefault();
+ }
}
//#endregion
}
diff --git a/packages/frontend/src/components/MkPostFormAttaches.vue b/packages/frontend/src/components/MkPostFormAttaches.vue
index e8404cbd4f..dd594ef7f1 100644
--- a/packages/frontend/src/components/MkPostFormAttaches.vue
+++ b/packages/frontend/src/components/MkPostFormAttaches.vue
@@ -43,6 +43,7 @@ import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
import { DI } from '@/di.js';
+import { globalEvents } from '@/events.js';
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
@@ -58,7 +59,6 @@ const emit = defineEmits<{
(ev: 'detach', id: string): void;
(ev: 'changeSensitive', file: Misskey.entities.DriveFile, isSensitive: boolean): void;
(ev: 'changeName', file: Misskey.entities.DriveFile, newName: string): void;
- (ev: 'replaceFile', file: Misskey.entities.DriveFile, newFile: Misskey.entities.DriveFile): void;
}>();
let menuShowing = false;
@@ -82,12 +82,13 @@ async function detachAndDeleteMedia(file: Misskey.entities.DriveFile) {
type: 'warning',
text: i18n.tsx.driveFileDeleteConfirm({ name: file.name }),
});
-
if (canceled) return;
- os.apiWithDialog('drive/files/delete', {
+ await os.apiWithDialog('drive/files/delete', {
fileId: file.id,
});
+
+ globalEvents.emit('driveFilesDeleted', [file]);
}
function toggleSensitive(file) {
@@ -142,13 +143,6 @@ async function describe(file: Misskey.entities.DriveFile) {
});
}
-async function crop(file: Misskey.entities.DriveFile): Promise {
- if (mock) return;
-
- const newFile = await os.cropImage(file, { aspectRatio: NaN });
- emit('replaceFile', file, newFile);
-}
-
function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent | KeyboardEvent): void {
if (menuShowing) return;
@@ -172,10 +166,6 @@ function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent | Keyboar
if (isImage) {
menuItems.push({
- text: i18n.ts.cropImage,
- icon: 'ti ti-crop',
- action: () : void => { crop(file); },
- }, {
text: i18n.ts.preview,
icon: 'ti ti-photo-search',
action: () => {
diff --git a/packages/frontend/src/components/MkPreview.vue b/packages/frontend/src/components/MkPreview.vue
index d8dfbd1655..6c7bf6be6b 100644
--- a/packages/frontend/src/components/MkPreview.vue
+++ b/packages/frontend/src/components/MkPreview.vue
@@ -18,8 +18,8 @@ SPDX-License-Identifier: AGPL-3.0-only
Pleroma
- This is
- the button
+ This is
+ the button
@@ -36,14 +36,15 @@ SPDX-License-Identifier: AGPL-3.0-only
+
+
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Profile.vue b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
index 30925b854c..4e96eff82e 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
@@ -37,7 +37,6 @@ 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 * as os from '@/os.js';
import { ensureSignin } from '@/i.js';
@@ -49,7 +48,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,36 +61,37 @@ 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) => {
- const file = files[0];
+async function setAvatar(ev) {
+ const files = await os.chooseFileFromPc({ multiple: false });
+ const file = files[0];
- let originalOrCropped = file;
+ let originalOrCropped = file;
- const { canceled } = await os.confirm({
- type: 'question',
- text: i18n.ts.cropImageAsk,
- okText: i18n.ts.cropYes,
- cancelText: i18n.ts.cropNo,
+ const { canceled } = await os.confirm({
+ type: 'question',
+ text: i18n.ts.cropImageAsk,
+ okText: i18n.ts.cropYes,
+ cancelText: i18n.ts.cropNo,
+ });
+
+ if (!canceled) {
+ originalOrCropped = await os.cropImageFile(file, {
+ aspectRatio: 1,
});
+ }
- if (!canceled) {
- originalOrCropped = await os.cropImage(file, {
- aspectRatio: 1,
- });
- }
+ const driveFile = (await os.launchUploader([originalOrCropped], { multiple: false }))[0];
- const i = await os.apiWithDialog('i/update', {
- avatarId: originalOrCropped.id,
- });
- $i.avatarId = i.avatarId;
- $i.avatarUrl = i.avatarUrl;
+ const i = await os.apiWithDialog('i/update', {
+ avatarId: driveFile.id,
});
+ $i.avatarId = i.avatarId;
+ $i.avatarUrl = i.avatarUrl;
}
diff --git a/packages/frontend/src/components/global/MkSystemIcon.vue b/packages/frontend/src/components/global/MkSystemIcon.vue
index 3454cdc9f2..d2ef0fb2d8 100644
--- a/packages/frontend/src/components/global/MkSystemIcon.vue
+++ b/packages/frontend/src/components/global/MkSystemIcon.vue
@@ -28,13 +28,17 @@ SPDX-License-Identifier: AGPL-3.0-only
+
@@ -62,6 +66,10 @@ const props = defineProps<{
&.error {
color: var(--MI_THEME-error);
}
+
+ &.waiting {
+ color: var(--MI_THEME-accent);
+ }
}
.line {
@@ -87,6 +95,13 @@ const props = defineProps<{
transform: rotate(-90deg);
}
+.animCircleWaiting {
+ stroke-dasharray: var(--l);
+ stroke-dashoffset: calc(var(--l) / 1.5);
+ animation: waiting 0.75s linear infinite;
+ transform-origin: center;
+}
+
.animFade {
opacity: 0;
animation: fade-in var(--duration, 0.5s) cubic-bezier(0,0,.25,1) 1 forwards;
@@ -104,6 +119,15 @@ const props = defineProps<{
}
}
+@keyframes waiting {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
@keyframes fade-in {
0% {
opacity: 0;
--
cgit v1.2.3-freya