enhance(frontend): ドライブのファイル・フォルダをドラッグしなくても移動できるように (#14318)

* feat(drive): ファイルをフォルダに移動するメニューを実装

(cherry picked from commit b89c2af6945c6a9f9f10e83f54d2bcf0f240b0b4)

* tweak ui

* Update Changelog

* ファイル詳細からも移動できるように

* feat(drive) フォルダのネストを移動するメニューを実装

(cherry picked from commit 8a7d710c6acb83f50c83f050bd1423c764d60a99)

* Update Changelog

* Update Changelog

* lint

* tweak ui

---------

Co-authored-by: nafu-at <satsuki@nafusoft.dev>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
This commit is contained in:
かっこかり 2024-07-30 14:42:46 +09:00 committed by GitHub
parent 738b3ea43b
commit 45f909ef33
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 117 additions and 17 deletions

View File

@ -25,6 +25,8 @@
- Enhance: AiScriptを0.19.0にアップデート - Enhance: AiScriptを0.19.0にアップデート
- Enhance: Allow negative delay for MFM animation elements (`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`) - Enhance: Allow negative delay for MFM animation elements (`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`)
- Enhance: センシティブなメディアを開く際に確認ダイアログを出せるように - Enhance: センシティブなメディアを開く際に確認ダイアログを出せるように
- Enhance: ドライブのファイル・フォルダをドラッグしなくても移動できるように
(Cherry-picked from https://github.com/nafu-at/misskey/commit/b89c2af6945c6a9f9f10e83f54d2bcf0f240b0b4, https://github.com/nafu-at/misskey/commit/8a7d710c6acb83f50c83f050bd1423c764d60a99)
- Enhance: デッキのアンテナ・リスト選択画面からそれぞれを新規作成できるように - Enhance: デッキのアンテナ・リスト選択画面からそれぞれを新規作成できるように
- Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正 - Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
- Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968) - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)

View File

@ -27,7 +27,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<p v-if="defaultStore.state.uploadFolder == folder.id" :class="$style.upload"> <p v-if="defaultStore.state.uploadFolder == folder.id" :class="$style.upload">
{{ i18n.ts.uploadFolder }} {{ i18n.ts.uploadFolder }}
</p> </p>
<button v-if="selectMode" class="_button" :class="[$style.checkbox, { [$style.checked]: isSelected }]" @click.prevent.stop="checkboxClicked"></button> <button v-if="selectMode" class="_button" :class="$style.checkboxWrapper" @click.prevent.stop="checkboxClicked">
<div :class="[$style.checkbox, { [$style.checked]: isSelected }]"></div>
</button>
</div> </div>
</template> </template>
@ -53,6 +55,7 @@ const props = withDefaults(defineProps<{
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'chosen', v: Misskey.entities.DriveFolder): void; (ev: 'chosen', v: Misskey.entities.DriveFolder): void;
(ev: 'unchose', v: Misskey.entities.DriveFolder): void;
(ev: 'move', v: Misskey.entities.DriveFolder): void; (ev: 'move', v: Misskey.entities.DriveFolder): void;
(ev: 'upload', file: File, folder: Misskey.entities.DriveFolder); (ev: 'upload', file: File, folder: Misskey.entities.DriveFolder);
(ev: 'removeFile', v: Misskey.entities.DriveFile['id']): void; (ev: 'removeFile', v: Misskey.entities.DriveFile['id']): void;
@ -68,7 +71,11 @@ const isDragging = ref(false);
const title = computed(() => props.folder.name); const title = computed(() => props.folder.name);
function checkboxClicked() { function checkboxClicked() {
emit('chosen', props.folder); if (props.isSelected) {
emit('unchose', props.folder);
} else {
emit('chosen', props.folder);
}
} }
function onClick() { function onClick() {
@ -222,6 +229,17 @@ function rename() {
}); });
} }
function move() {
os.selectDriveFolder(false).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,
});
});
}
function deleteFolder() { function deleteFolder() {
misskeyApi('drive/folders/delete', { misskeyApi('drive/folders/delete', {
folderId: props.folder.id, folderId: props.folder.id,
@ -267,6 +285,10 @@ function onContextmenu(ev: MouseEvent) {
text: i18n.ts.rename, text: i18n.ts.rename,
icon: 'ti ti-forms', icon: 'ti ti-forms',
action: rename, action: rename,
}, {
text: i18n.ts.move,
icon: 'ti ti ti-folder-symlink',
action: move,
}, { type: 'divider' }, { }, { type: 'divider' }, {
text: i18n.ts.delete, text: i18n.ts.delete,
icon: 'ti ti-trash', icon: 'ti ti-trash',
@ -310,17 +332,43 @@ function onContextmenu(ev: MouseEvent) {
} }
} }
.checkbox { .checkboxWrapper {
position: absolute; position: absolute;
bottom: 8px; border-radius: 50%;
right: 8px; bottom: 2px;
width: 16px; right: 2px;
height: 16px; padding: 8px;
background: #fff; box-sizing: border-box;
border: solid 1px #000;
&.checked { > .checkbox {
background: var(--accent); position: relative;
width: 18px;
height: 18px;
background: #fff;
border: solid 2px var(--divider);
border-radius: 4px;
box-sizing: border-box;
&.checked {
border-color: var(--accent);
background: var(--accent);
&::after {
content: "\ea5e";
font-family: 'tabler-icons';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #fff;
font-size: 12px;
line-height: 22px;
}
}
}
&:hover {
background: var(--accentedBg);
} }
} }

View File

@ -52,6 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:selectMode="select === 'folder'" :selectMode="select === 'folder'"
:isSelected="selectedFolders.some(x => x.id === f.id)" :isSelected="selectedFolders.some(x => x.id === f.id)"
@chosen="chooseFolder" @chosen="chooseFolder"
@unchose="unchoseFolder"
@move="move" @move="move"
@upload="upload" @upload="upload"
@removeFile="removeFile" @removeFile="removeFile"
@ -428,6 +429,11 @@ function chooseFolder(folderToChoose: Misskey.entities.DriveFolder) {
} }
} }
function unchoseFolder(folderToUnchose: Misskey.entities.DriveFolder) {
selectedFolders.value = selectedFolders.value.filter(f => f.id !== folderToUnchose.id);
emit('change-selection', selectedFolders.value);
}
function move(target?: Misskey.entities.DriveFolder | Misskey.entities.DriveFolder['id' | 'parentId']) { function move(target?: Misskey.entities.DriveFolder | Misskey.entities.DriveFolder['id' | 'parentId']) {
if (!target) { if (!target) {
goRoot(); goRoot();

View File

@ -37,11 +37,17 @@ SPDX-License-Identifier: AGPL-3.0-only
</button> </button>
</div> </div>
</div> </div>
<div> <div class="_gaps_s">
<button class="_button" :class="$style.fileAltEditBtn" @click="describe()"> <button class="_button" :class="$style.kvEditBtn" @click="move()">
<MkKeyValue>
<template #key>{{ i18n.ts.folder }}</template>
<template #value>{{ folderHierarchy.join(' > ') }}<i class="ti ti-pencil" :class="$style.kvEditIcon"></i></template>
</MkKeyValue>
</button>
<button class="_button" :class="$style.kvEditBtn" @click="describe()">
<MkKeyValue> <MkKeyValue>
<template #key>{{ i18n.ts.description }}</template> <template #key>{{ i18n.ts.description }}</template>
<template #value>{{ file.comment ? file.comment : `(${i18n.ts.none})` }}<i class="ti ti-pencil" :class="$style.fileAltEditIcon"></i></template> <template #value>{{ file.comment ? file.comment : `(${i18n.ts.none})` }}<i class="ti ti-pencil" :class="$style.kvEditIcon"></i></template>
</MkKeyValue> </MkKeyValue>
</button> </button>
<MkKeyValue :class="$style.fileMetaDataChildren"> <MkKeyValue :class="$style.fileMetaDataChildren">
@ -90,6 +96,18 @@ const props = defineProps<{
const fetching = ref(true); const fetching = ref(true);
const file = ref<Misskey.entities.DriveFile>(); const file = ref<Misskey.entities.DriveFile>();
const folderHierarchy = computed(() => {
if (!file.value) return [i18n.ts.drive];
const folderNames = [i18n.ts.drive];
function get(folder: Misskey.entities.DriveFolder) {
if (folder.parent) get(folder.parent);
folderNames.push(folder.name);
}
if (file.value.folder) get(file.value.folder);
return folderNames;
});
const isImage = computed(() => file.value?.type.startsWith('image/')); const isImage = computed(() => file.value?.type.startsWith('image/'));
async function fetch() { async function fetch() {
@ -122,6 +140,19 @@ function crop() {
}); });
} }
function move() {
if (!file.value) return;
os.selectDriveFolder(false).then(folder => {
misskeyApi('drive/files/update', {
fileId: file.value.id,
folderId: folder[0] ? folder[0].id : null,
}).then(async () => {
await fetch();
});
});
}
function toggleSensitive() { function toggleSensitive() {
if (!file.value) return; if (!file.value) return;
@ -282,14 +313,14 @@ onMounted(async () => {
padding: .5rem 1rem; padding: .5rem 1rem;
} }
.fileAltEditBtn { .kvEditBtn {
text-align: start; text-align: start;
display: block; display: block;
width: 100%; width: 100%;
padding: .5rem 1rem; padding: .5rem 1rem;
border-radius: var(--radius); border-radius: var(--radius);
.fileAltEditIcon { .kvEditIcon {
display: inline-block; display: inline-block;
color: transparent; color: transparent;
visibility: hidden; visibility: hidden;
@ -300,7 +331,7 @@ onMounted(async () => {
color: var(--accent); color: var(--accent);
background-color: var(--accentedBg); background-color: var(--accentedBg);
.fileAltEditIcon { .kvEditIcon {
color: var(--accent); color: var(--accent);
visibility: visible; visibility: visible;
} }

View File

@ -41,6 +41,15 @@ function describe(file: Misskey.entities.DriveFile) {
}); });
} }
function move(file: Misskey.entities.DriveFile) {
os.selectDriveFolder(false).then(folder => {
misskeyApi('drive/files/update', {
fileId: file.id,
folderId: folder[0] ? folder[0].id : null,
});
});
}
function toggleSensitive(file: Misskey.entities.DriveFile) { function toggleSensitive(file: Misskey.entities.DriveFile) {
misskeyApi('drive/files/update', { misskeyApi('drive/files/update', {
fileId: file.id, fileId: file.id,
@ -88,6 +97,10 @@ export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Miss
text: i18n.ts.rename, text: i18n.ts.rename,
icon: 'ti ti-forms', icon: 'ti ti-forms',
action: () => rename(file), action: () => rename(file),
}, {
text: i18n.ts.move,
icon: 'ti ti-folder-symlink',
action: () => move(file),
}, { }, {
text: file.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive, text: file.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive,
icon: file.isSensitive ? 'ti ti-eye' : 'ti ti-eye-exclamation', icon: file.isSensitive ? 'ti ti-eye' : 'ti ti-eye-exclamation',