Compare commits

..

7 Commits

Author SHA1 Message Date
syuilo 7b8f455758 wip 2025-05-13 21:09:51 +09:00
syuilo 0126523bc4 wip 2025-05-13 21:04:22 +09:00
syuilo cfd95ce9ac wop 2025-05-13 20:43:50 +09:00
syuilo 41f149a35e wip 2025-05-13 20:42:27 +09:00
syuilo 052df8fb1a wip 2025-05-13 20:37:10 +09:00
syuilo 7725107bc3 Update drag-and-drop.ts 2025-05-13 20:32:27 +09:00
syuilo 6573b2ba8e Update MkDrive.vue 2025-05-13 20:18:33 +09:00
10 changed files with 118 additions and 32 deletions

View File

@ -147,7 +147,11 @@ function onDrop(ev: DragEvent) {
fileIds: droppedData.map(f => f.id),
folderId: props.folder.id,
}).then(() => {
globalEvents.emit('driveFilesMoved', droppedData, props.folder);
globalEvents.emit('driveFilesUpdated', droppedData.map(x => ({
...x,
folderId: props.folder.id,
folder: props.folder,
})));
});
}
}
@ -166,7 +170,11 @@ function onDrop(ev: DragEvent) {
folderId: droppedFolder.id,
parentId: props.folder.id,
}).then(() => {
// noop
globalEvents.emit('driveFoldersUpdated', [droppedFolder].map(x => ({
...x,
parentId: props.folder.id,
parent: props.folder,
})));
}).catch(err => {
switch (err.code) {
case 'RECURSIVE_NESTING':
@ -216,6 +224,11 @@ function rename() {
misskeyApi('drive/folders/update', {
folderId: props.folder.id,
name: name,
}).then(() => {
globalEvents.emit('driveFoldersUpdated', [{
...props.folder,
name: name,
}]);
});
});
}
@ -227,6 +240,12 @@ function move() {
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,
}]);
});
});
}
@ -238,6 +257,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':

View File

@ -97,7 +97,11 @@ function onDrop(ev: DragEvent) {
fileIds: droppedData.map(f => f.id),
folderId: props.folder ? props.folder.id : null,
}).then(() => {
globalEvents.emit('driveFilesMoved', droppedData, props.folder ?? null);
globalEvents.emit('driveFilesUpdated', droppedData.map(x => ({
...x,
folderId: props.folder ? props.folder.id : null,
folder: props.folder ?? null,
})));
});
}
}
@ -113,6 +117,12 @@ function onDrop(ev: DragEvent) {
misskeyApi('drive/folders/update', {
folderId: droppedFolder.id,
parentId: props.folder ? props.folder.id : null,
}).then(() => {
globalEvents.emit('driveFoldersUpdated', [droppedFolder].map(x => ({
...x,
parentId: props.folder ? props.folder.id : null,
parent: props.folder ?? null,
})));
});
}
}

View File

@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkStickyContainer>
<MkStickyContainer style="background: var(--MI_THEME-bg);">
<template #header>
<nav :class="$style.nav">
<div :class="$style.navPath" @contextmenu.prevent.stop="() => {}">
@ -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 { chooseFileFromPcAndUpload } from '@/utility/drive.js';
import { chooseFileFromPcAndUpload, selectDriveFolder } from '@/utility/drive.js';
import { store } from '@/store.js';
import { isSeparatorNeeded, getSeparatorInfo, makeDateGroupedTimelineComputedRef } from '@/utility/timeline-date-separate.js';
import { usePagination } from '@/composables/use-pagination.js';
@ -168,9 +168,7 @@ const props = withDefaults(defineProps<{
const emit = defineEmits<{
(ev: 'changeSelectedFiles', v: Misskey.entities.DriveFile[]): void;
(ev: 'changeSelectedFolders', v: (Misskey.entities.DriveFolder | null)[]): void;
(ev: 'move-root'): void;
(ev: 'cd', v: Misskey.entities.DriveFolder | null): void;
(ev: 'open-folder', v: Misskey.entities.DriveFolder): void;
}>();
const folder = ref<Misskey.entities.DriveFolder | null>(null);
@ -330,7 +328,11 @@ function onDrop(ev: DragEvent) {
fileIds: droppedData.map(f => f.id),
folderId: folder.value ? folder.value.id : null,
}).then(() => {
globalEvents.emit('driveFilesMoved', droppedData, folder.value);
globalEvents.emit('driveFilesUpdated', droppedData.map(x => ({
...x,
folderId: folder.value ? folder.value.id : null,
folder: folder.value,
})));
});
}
}
@ -348,7 +350,11 @@ function onDrop(ev: DragEvent) {
folderId: droppedFolder.id,
parentId: folder.value ? folder.value.id : null,
}).then(() => {
// noop
globalEvents.emit('driveFoldersUpdated', [droppedFolder].map(x => ({
...x,
parentId: folder.value ? folder.value.id : null,
parent: folder.value,
})));
}).catch(err => {
switch (err.code) {
case 'RECURSIVE_NESTING':
@ -418,8 +424,7 @@ async function renameFolder(folderToRename: Misskey.entities.DriveFolder) {
name: name,
});
// FIXME:
cd(updatedFolder);
globalEvents.emit('driveFoldersUpdated', [updatedFolder]);
}
function deleteFolder(folderToDelete: Misskey.entities.DriveFolder) {
@ -428,6 +433,7 @@ function deleteFolder(folderToDelete: Misskey.entities.DriveFolder) {
}).then(() => {
//
cd(folderToDelete.parentId);
globalEvents.emit('driveFoldersDeleted', [folderToDelete]);
}).catch(err => {
switch (err.id) {
case 'b0fc8a17-963c-405d-bfbc-859a487295e1':
@ -517,7 +523,6 @@ function cd(target?: Misskey.entities.DriveFolder | Misskey.entities.DriveFolder
if (folderToMove.parent) dive(folderToMove.parent);
emit('open-folder', folderToMove);
initialize();
});
}
@ -525,14 +530,18 @@ function cd(target?: Misskey.entities.DriveFolder | Misskey.entities.DriveFolder
async function moveFilesBulk() {
if (selectedFiles.value.length === 0) return;
const toFolder = await os.selectDriveFolder(folder.value ? folder.value.id : null);
const toFolder = await selectDriveFolder(folder.value ? folder.value.id : null);
await os.apiWithDialog('drive/files/move-bulk', {
fileIds: selectedFiles.value.map(f => f.id),
folderId: toFolder[0] ? toFolder[0].id : null,
});
globalEvents.emit('driveFilesMoved', selectedFiles.value, toFolder[0]);
globalEvents.emit('driveFilesUpdated', selectedFiles.value.map(x => ({
...x,
folderId: toFolder[0] ? toFolder[0].id : null,
folder: toFolder[0] ?? null,
})));
}
function goRoot() {
@ -541,7 +550,6 @@ function goRoot() {
folder.value = null;
hierarchyFolders.value = [];
emit('move-root');
initialize();
}
@ -650,12 +658,47 @@ useGlobalEvent('driveFileCreated', (file) => {
}
});
useGlobalEvent('driveFilesMoved', (files, to) => {
useGlobalEvent('driveFilesUpdated', (files) => {
for (const f of files) {
if (filesPaginator.items.value.some(x => x.id === f.id)) {
if (f.folderId === (folder.value?.id ?? null)) {
filesPaginator.updateItem(f.id, () => f);
} else {
filesPaginator.removeItem(f.id);
}
} else {
if (f.folderId === (folder.value?.id ?? null)) {
filesPaginator.prepend(f);
}
}
}
});
useGlobalEvent('driveFilesDeleted', (files) => {
for (const f of files) {
filesPaginator.removeItem(f.id);
}
if ((to?.id ?? null) === (folder.value?.id ?? null)) {
filesPaginator.unshiftItems(files);
});
useGlobalEvent('driveFoldersUpdated', (folders) => {
for (const f of folders) {
if (foldersPaginator.items.value.some(x => x.id === f.id)) {
if (f.parentId === (folder.value?.id ?? null)) {
foldersPaginator.updateItem(f.id, () => f);
} else {
foldersPaginator.removeItem(f.id);
}
} else {
if (f.parentId === (folder.value?.id ?? null)) {
foldersPaginator.prepend(f);
}
}
}
});
useGlobalEvent('driveFoldersDeleted', (folders) => {
for (const f of folders) {
foldersPaginator.removeItem(f.id);
}
});

View File

@ -14,19 +14,19 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #header>
{{ i18n.ts.drive }}
</template>
<XDrive :initialFolder="initialFolder"/>
<MkDrive :initialFolder="initialFolder"/>
</MkWindow>
</template>
<script lang="ts" setup>
import { } from 'vue';
import * as Misskey from 'misskey-js';
import XDrive from '@/components/MkDrive.vue';
import MkDrive from '@/components/MkDrive.vue';
import MkWindow from '@/components/MkWindow.vue';
import { i18n } from '@/i18n.js';
defineProps<{
initialFolder?: Misskey.entities.DriveFolder;
initialFolder?: Misskey.entities.DriveFolder | null;
}>();
const emit = defineEmits<{

View File

@ -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));
@ -81,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) {

View File

@ -11,6 +11,8 @@ type DragDataMap = {
deckColumn: string;
};
// NOTE: dataTransfer の format は大文字小文字区別されないっぽいので toLowerCase が必要
export function setDragData<T extends keyof DragDataMap>(
event: DragEvent,
type: T,
@ -18,7 +20,7 @@ export function setDragData<T extends keyof DragDataMap>(
) {
if (event.dataTransfer == null) return;
event.dataTransfer.setData(`misskey/${type}`, JSON.stringify(data));
event.dataTransfer.setData(`misskey/${type}`.toLowerCase(), JSON.stringify(data));
}
export function getDragData<T extends keyof DragDataMap>(
@ -27,7 +29,7 @@ export function getDragData<T extends keyof DragDataMap>(
): DragDataMap[T] | null {
if (event.dataTransfer == null) return null;
const data = event.dataTransfer.getData(`misskey/${type}`);
const data = event.dataTransfer.getData(`misskey/${type}`.toLowerCase());
if (data == null || data === '') return null;
return JSON.parse(data);
@ -42,5 +44,5 @@ export function checkDragDataType(
const dataType = event.dataTransfer.types[0];
if (dataType == null || dataType === '') return false;
return types.some((type) => dataType === `misskey/${type}`);
return types.some((type) => `misskey/${type}`.toLowerCase() === dataType.toLowerCase());
}

View File

@ -14,7 +14,10 @@ type Events = {
notePosted: (note: Misskey.entities.Note) => void;
noteDeleted: (noteId: Misskey.entities.Note['id']) => void;
driveFileCreated: (file: Misskey.entities.DriveFile) => void;
driveFilesMoved: (files: Misskey.entities.DriveFile[], to: Misskey.entities.DriveFolder | null) => void;
driveFilesUpdated: (files: Misskey.entities.DriveFile[]) => void;
driveFilesDeleted: (files: Misskey.entities.DriveFile[]) => void;
driveFoldersUpdated: (folders: Misskey.entities.DriveFolder[]) => void;
driveFoldersDeleted: (folders: Misskey.entities.DriveFolder[]) => void;
};
export const globalEvents = new EventEmitter<Events>();

View File

@ -81,6 +81,7 @@ import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { useRouter } from '@/router.js';
import { selectDriveFolder } from '@/utility/drive.js';
import { globalEvents } from '@/events.js';
const router = useRouter();
@ -199,12 +200,14 @@ async function deleteFile() {
type: 'warning',
text: i18n.tsx.driveFileDeleteConfirm({ name: file.value.name }),
});
if (canceled) return;
await os.apiWithDialog('drive/files/delete', {
fileId: file.value.id,
});
globalEvents.emit('driveFilesDeleted', [file.value]);
router.push('/my/drive');
}

View File

@ -5,14 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div>
<XDrive @cd="x => folder = x"/>
<MkDrive @cd="x => folder = x"/>
</div>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
import * as Misskey from 'misskey-js';
import XDrive from '@/components/MkDrive.vue';
import MkDrive from '@/components/MkDrive.vue';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';

View File

@ -12,6 +12,7 @@ import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { prefer } from '@/preferences.js';
import { globalEvents } from '@/events.js';
function rename(file: Misskey.entities.DriveFile) {
os.inputText({
@ -78,11 +79,13 @@ async function deleteFile(file: Misskey.entities.DriveFile) {
type: 'warning',
text: i18n.tsx.driveFileDeleteConfirm({ name: file.name }),
});
if (canceled) return;
misskeyApi('drive/files/delete', {
await os.apiWithDialog('drive/files/delete', {
fileId: file.id,
});
globalEvents.emit('driveFilesDeleted', [file]);
}
export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Misskey.entities.DriveFolder | null): MenuItem[] {