From a9422d6ed6feb7d2cac578f4d39e3530f5026cf6 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 11 May 2025 13:47:19 +0900 Subject: [PATCH] wip --- .../frontend/src/components/MkDrive.file.vue | 4 +- .../src/components/MkDrive.folder.vue | 29 +++++------- .../src/components/MkDrive.navFolder.vue | 27 +++++------ packages/frontend/src/components/MkDrive.vue | 25 ++++------ .../frontend/src/components/MkPostForm.vue | 11 ++--- packages/frontend/src/consts.ts | 8 ---- packages/frontend/src/drag-and-drop.ts | 46 +++++++++++++++++++ .../frontend/src/pages/chat/room.form.vue | 11 ++--- packages/frontend/src/ui/deck/column.vue | 10 ++-- 9 files changed, 96 insertions(+), 75 deletions(-) delete mode 100644 packages/frontend/src/consts.ts create mode 100644 packages/frontend/src/drag-and-drop.ts diff --git a/packages/frontend/src/components/MkDrive.file.vue b/packages/frontend/src/components/MkDrive.file.vue index b84badf71f..b482f651dc 100644 --- a/packages/frontend/src/components/MkDrive.file.vue +++ b/packages/frontend/src/components/MkDrive.file.vue @@ -48,7 +48,7 @@ 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'; -import { DATA_TRANSFER_DRIVE_FILES } from '@/consts.js'; +import { setDragData } from '@/drag-and-drop.js'; const router = useRouter(); @@ -91,7 +91,7 @@ function onContextmenu(ev: MouseEvent) { function onDragstart(ev: DragEvent) { if (ev.dataTransfer) { ev.dataTransfer.effectAllowed = 'move'; - ev.dataTransfer.setData(DATA_TRANSFER_DRIVE_FILES, JSON.stringify([props.file])); + setDragData(ev, 'driveFiles', [props.file]); } isDragging.value = true; diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue index 61de77ade3..3782a847a9 100644 --- a/packages/frontend/src/components/MkDrive.folder.vue +++ b/packages/frontend/src/components/MkDrive.folder.vue @@ -42,8 +42,8 @@ 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 { DATA_TRANSFER_DRIVE_FILES, DATA_TRANSFER_DRIVE_FOLDERS } from '@/consts.js'; import { globalEvents } from '@/events.js'; +import { checkDragDataType, getDragData, setDragData } from '@/drag-and-drop.js'; const props = withDefaults(defineProps<{ folder: Misskey.entities.DriveFolder; @@ -95,10 +95,7 @@ function onDragover(ev: DragEvent) { } const isFile = ev.dataTransfer.items[0].kind === 'file'; - const isDriveFiles = ev.dataTransfer.types[0] === DATA_TRANSFER_DRIVE_FILES; - const isDriveFolders = ev.dataTransfer.types[0] === DATA_TRANSFER_DRIVE_FOLDERS; - - if (isFile || isDriveFiles || isDriveFolders) { + if (isFile || checkDragDataType(ev, ['driveFiles', 'driveFolders'])) { switch (ev.dataTransfer.effectAllowed) { case 'all': case 'uninitialized': @@ -143,31 +140,29 @@ function onDrop(ev: DragEvent) { //#region ドライブのファイル { - const driveFiles = ev.dataTransfer.getData(DATA_TRANSFER_DRIVE_FILES); - if (driveFiles != null && driveFiles !== '') { - const files = JSON.parse(driveFiles); + const droppedData = getDragData(ev, 'driveFiles'); + if (droppedData != null) { misskeyApi('drive/files/move-bulk', { - fileIds: files.map(f => f.id), + fileIds: droppedData.map(f => f.id), folderId: props.folder.id, }).then(() => { - globalEvents.emit('driveFilesMoved', files, props.folder); + globalEvents.emit('driveFilesMoved', droppedData, props.folder); }); } } //#endregion //#region ドライブのフォルダ - // TODO { - const driveFolder = ev.dataTransfer.getData(DATA_TRANSFER_DRIVE_FOLDERS); - if (driveFolder != null && driveFolder !== '') { - const folder = JSON.parse(driveFolder); + const droppedData = getDragData(ev, 'driveFolders'); + if (droppedData != null) { + const droppedFolder = droppedData[0]; // 移動先が自分自身ならreject - if (folder.id === props.folder.id) return; + if (droppedFolder.id === props.folder.id) return; misskeyApi('drive/folders/update', { - folderId: folder.id, + folderId: droppedFolder.id, parentId: props.folder.id, }).then(() => { // noop @@ -197,7 +192,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; // 親ブラウザに対して、ドラッグが開始されたフラグを立てる diff --git a/packages/frontend/src/components/MkDrive.navFolder.vue b/packages/frontend/src/components/MkDrive.navFolder.vue index dd7031f111..7be031f8ac 100644 --- a/packages/frontend/src/components/MkDrive.navFolder.vue +++ b/packages/frontend/src/components/MkDrive.navFolder.vue @@ -21,8 +21,8 @@ import { ref } from 'vue'; import * as Misskey from 'misskey-js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { i18n } from '@/i18n.js'; -import { DATA_TRANSFER_DRIVE_FILES, DATA_TRANSFER_DRIVE_FOLDERS } from '@/consts.js'; import { globalEvents } from '@/events.js'; +import { checkDragDataType, getDragData } from '@/drag-and-drop.js'; const props = defineProps<{ folder?: Misskey.entities.DriveFolder; @@ -44,10 +44,7 @@ function onDragover(ev: DragEvent) { } const isFile = ev.dataTransfer.items[0].kind === 'file'; - const isDriveFiles = ev.dataTransfer.types[0] === DATA_TRANSFER_DRIVE_FILES; - const isDriveFolders = ev.dataTransfer.types[0] === DATA_TRANSFER_DRIVE_FOLDERS; - - if (isFile || isDriveFiles || isDriveFolders) { + if (isFile || checkDragDataType(ev, ['driveFiles', 'driveFolders'])) { switch (ev.dataTransfer.effectAllowed) { case 'all': case 'uninitialized': @@ -94,29 +91,27 @@ function onDrop(ev: DragEvent) { //#region ドライブのファイル { - const driveFiles = ev.dataTransfer.getData(DATA_TRANSFER_DRIVE_FILES); - if (driveFiles != null && driveFiles !== '') { - const files = JSON.parse(driveFiles); + const droppedData = getDragData(ev, 'driveFiles'); + if (droppedData != null) { misskeyApi('drive/files/move-bulk', { - fileIds: files.map(f => f.id), + fileIds: droppedData.map(f => f.id), folderId: props.folder ? props.folder.id : null, }).then(() => { - globalEvents.emit('driveFilesMoved', files, props.folder ?? null); + globalEvents.emit('driveFilesMoved', droppedData, props.folder ?? null); }); } } //#endregion //#region ドライブのフォルダ - // TODO { - const driveFolder = ev.dataTransfer.getData(DATA_TRANSFER_DRIVE_FOLDER); - if (driveFolder != null && driveFolder !== '') { - const folder = JSON.parse(driveFolder); + const droppedData = getDragData(ev, 'driveFolders'); + if (droppedData != null) { + const droppedFolder = droppedData[0]; // 移動先が自分自身ならreject - if (props.folder && folder.id === props.folder.id) return; + if (props.folder && droppedFolder.id === props.folder.id) return; misskeyApi('drive/folders/update', { - folderId: folder.id, + folderId: droppedFolder.id, parentId: props.folder ? props.folder.id : null, }); } diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index a0896d4b4f..3a587bc720 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -154,7 +154,7 @@ import { store } from '@/store.js'; import { isSeparatorNeeded, getSeparatorInfo, makeDateGroupedTimelineComputedRef } from '@/utility/timeline-date-separate.js'; import { usePagination } from '@/composables/use-pagination.js'; import { globalEvents, useGlobalEvent } from '@/events.js'; -import { DATA_TRANSFER_DRIVE_FILES, DATA_TRANSFER_DRIVE_FOLDERS } from '@/consts.js'; +import { checkDragDataType, getDragData, setDragData } from '@/drag-and-drop.js'; const props = withDefaults(defineProps<{ initialFolder?: Misskey.entities.DriveFolder['id'] | null; @@ -261,7 +261,7 @@ function onFileDragstart(file: Misskey.entities.DriveFile, ev: DragEvent) { if (ev.dataTransfer) { ev.dataTransfer.effectAllowed = 'move'; - ev.dataTransfer.setData(DATA_TRANSFER_DRIVE_FILES, JSON.stringify(selectedFiles.value)); + setDragData(ev, 'driveFiles', selectedFiles.value); } } @@ -279,9 +279,7 @@ function onDragover(ev: DragEvent) { } const isFile = ev.dataTransfer.items[0].kind === 'file'; - const isDriveFiles = ev.dataTransfer.types[0] === DATA_TRANSFER_DRIVE_FILES; - const isDriveFolders = ev.dataTransfer.types[0] === DATA_TRANSFER_DRIVE_FOLDERS; - if (isFile || isDriveFiles || isDriveFolders) { + if (isFile || checkDragDataType(ev, ['driveFiles', 'driveFolders'])) { switch (ev.dataTransfer.effectAllowed) { case 'all': case 'uninitialized': @@ -328,26 +326,23 @@ function onDrop(ev: DragEvent) { //#region ドライブのファイル { - const driveFiles = ev.dataTransfer.getData(DATA_TRANSFER_DRIVE_FILES); - if (driveFiles != null && driveFiles !== '') { - const files = JSON.parse(driveFiles); + const droppedData = getDragData(ev, 'driveFiles'); + if (droppedData != null) { misskeyApi('drive/files/move-bulk', { - fileIds: files.map(f => f.id), + fileIds: droppedData.map(f => f.id), folderId: folder.value ? folder.value.id : null, }).then(() => { - globalEvents.emit('driveFilesMoved', files, folder.value); + globalEvents.emit('driveFilesMoved', droppedData, folder.value); }); } } //#endregion //#region ドライブのフォルダ - // TODO { - const driveFolder = ev.dataTransfer.getData(DATA_TRANSFER_DRIVE_FOLDER); - if (driveFolder != null && driveFolder !== '') { - const droppedFolder = JSON.parse(driveFolder); - + const droppedData = getDragData(ev, 'driveFolders'); + if (droppedData != null) { + const droppedFolder = droppedData[0]; // 移動先が自分自身ならreject if (folder.value && droppedFolder.id === folder.value.id) return false; if (foldersPaginator.items.value.some(f => f.id === droppedFolder.id)) return false; diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 379aee0d6e..5d19f32ea5 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -138,7 +138,7 @@ import { prefer } from '@/preferences.js'; import { getPluginHandlers } from '@/plugin.js'; import { DI } from '@/di.js'; import { globalEvents } from '@/events.js'; -import { DATA_TRANSFER_DRIVE_FILES } from '@/consts.js'; +import { checkDragDataType, getDragData } from '@/drag-and-drop.js'; const $i = ensureSignin(); @@ -702,8 +702,7 @@ async function onPaste(ev: ClipboardEvent) { function onDragover(ev) { if (!ev.dataTransfer.items[0]) return; const isFile = ev.dataTransfer.items[0].kind === 'file'; - const isDriveFiles = ev.dataTransfer.types[0] === DATA_TRANSFER_DRIVE_FILES; - if (isFile || isDriveFiles) { + if (isFile || checkDragDataType(ev, ['driveFiles'])) { ev.preventDefault(); draghover.value = true; switch (ev.dataTransfer.effectAllowed) { @@ -745,9 +744,9 @@ function onDrop(ev: DragEvent): void { //#region ドライブのファイル { - const driveFiles = ev.dataTransfer?.getData(DATA_TRANSFER_DRIVE_FILES); - if (driveFiles != null && driveFiles !== '') { - files.value.push(...JSON.parse(driveFiles)); + const droppedData = getDragData(ev, 'driveFiles'); + if (droppedData != null) { + files.value.push(...droppedData); ev.preventDefault(); } } diff --git a/packages/frontend/src/consts.ts b/packages/frontend/src/consts.ts deleted file mode 100644 index bdd2cae0d4..0000000000 --- a/packages/frontend/src/consts.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -export const DATA_TRANSFER_DRIVE_FILES = 'misskey-drive-files'; -export const DATA_TRANSFER_DRIVE_FOLDERS = 'misskey-drive-folders'; -export const DATA_TRANSFER_DECK_COLUMN = 'misskey-deck-column'; diff --git a/packages/frontend/src/drag-and-drop.ts b/packages/frontend/src/drag-and-drop.ts new file mode 100644 index 0000000000..3d7910eece --- /dev/null +++ b/packages/frontend/src/drag-and-drop.ts @@ -0,0 +1,46 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as Misskey from 'misskey-js'; + +type DragDataMap = { + driveFiles: Misskey.entities.DriveFile[]; + driveFolders: Misskey.entities.DriveFolder[]; + deckColumn: string; +}; + +export function setDragData( + event: DragEvent, + type: T, + data: DragDataMap[T], +) { + if (event.dataTransfer == null) return; + + event.dataTransfer.setData(`misskey/${type}`, JSON.stringify(data)); +} + +export function getDragData( + event: DragEvent, + type: T, +): DragDataMap[T] | null { + if (event.dataTransfer == null) return null; + + const data = event.dataTransfer.getData(`misskey/${type}`); + if (data == null || data === '') return null; + + return JSON.parse(data); +} + +export function checkDragDataType( + event: DragEvent, + types: (keyof DragDataMap)[], +): boolean { + if (event.dataTransfer == null) return false; + + const dataType = event.dataTransfer.types[0]; + if (dataType == null || dataType === '') return false; + + return types.some((type) => dataType === `misskey/${type}`); +} diff --git a/packages/frontend/src/pages/chat/room.form.vue b/packages/frontend/src/pages/chat/room.form.vue index e5fba04165..5e84ade08d 100644 --- a/packages/frontend/src/pages/chat/room.form.vue +++ b/packages/frontend/src/pages/chat/room.form.vue @@ -47,7 +47,7 @@ import { misskeyApi } from '@/utility/misskey-api.js'; import { prefer } from '@/preferences.js'; import { Autocomplete } from '@/utility/autocomplete.js'; import { emojiPicker } from '@/utility/emoji-picker.js'; -import { DATA_TRANSFER_DRIVE_FILES } from '@/consts.js'; +import { checkDragDataType, getDragData } from '@/drag-and-drop.js'; const props = defineProps<{ user?: Misskey.entities.UserDetailed | null; @@ -102,8 +102,7 @@ function onDragover(ev: DragEvent) { if (!ev.dataTransfer) return; const isFile = ev.dataTransfer.items[0].kind === 'file'; - const isDriveFiles = ev.dataTransfer.types[0] === DATA_TRANSFER_DRIVE_FILES; - if (isFile || isDriveFiles) { + if (isFile || checkDragDataType(ev, ['driveFiles'])) { ev.preventDefault(); switch (ev.dataTransfer.effectAllowed) { case 'all': @@ -143,9 +142,9 @@ function onDrop(ev: DragEvent): void { //#region ドライブのファイル { - const driveFiles = ev.dataTransfer.getData(DATA_TRANSFER_DRIVE_FILES); - if (driveFiles != null && driveFiles !== '') { - file.value = JSON.parse(driveFiles)[0]; + const droppedData = getDragData(ev, 'driveFiles'); + if (droppedData != null) { + file.value = droppedData[0]; ev.preventDefault(); } } diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue index d86d1f3d08..4e79b301e3 100644 --- a/packages/frontend/src/ui/deck/column.vue +++ b/packages/frontend/src/ui/deck/column.vue @@ -51,7 +51,7 @@ import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { prefer } from '@/preferences.js'; import { DI } from '@/di.js'; -import { DATA_TRANSFER_DECK_COLUMN } from '@/consts.js'; +import { checkDragDataType, getDragData, setDragData } from '@/drag-and-drop.js'; provide('shouldHeaderThin', true); provide('shouldOmitHeaderTitle', true); @@ -263,7 +263,7 @@ function goTop() { function onDragstart(ev) { ev.dataTransfer.effectAllowed = 'move'; - ev.dataTransfer.setData(DATA_TRANSFER_DECK_COLUMN, props.column.id); + setDragData(ev, 'deckColumn', props.column.id); // Chromeのバグで、Dragstartハンドラ内ですぐにDOMを変更する(=リアクティブなプロパティを変更する)とDragが終了してしまう // SEE: https://stackoverflow.com/questions/19639969/html5-dragend-event-firing-immediately @@ -282,7 +282,7 @@ function onDragover(ev) { // 自分自身にはドロップさせない ev.dataTransfer.dropEffect = 'none'; } else { - const isDeckColumn = ev.dataTransfer.types[0] === DATA_TRANSFER_DECK_COLUMN; + const isDeckColumn = checkDragDataType(ev, ['deckColumn']); ev.dataTransfer.dropEffect = isDeckColumn ? 'move' : 'none'; @@ -298,8 +298,8 @@ function onDrop(ev) { draghover.value = false; os.deckGlobalEvents.emit('column.dragEnd'); - const id = ev.dataTransfer.getData(DATA_TRANSFER_DECK_COLUMN); - if (id != null && id !== '') { + const id = getDragData(ev, 'deckColumn'); + if (id != null) { swapColumn(props.column.id, id); } }