diff --git a/locales/index.d.ts b/locales/index.d.ts index af30a68f8b..e254f2e9f2 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -79,6 +79,9 @@ export interface Locale { "files": string; "download": string; "driveFileDeleteConfirm": string; + "driveFilesDeleteConfirm": string; + "driveFilesSensitiveonConfirm": string; + "driveFilesSensitiveoffConfirm": string; "driveFolderDeleteConfirm": string; "unfollowConfirm": string; "exportRequested": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 9f51fa5505..c332fd9bf6 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -76,6 +76,9 @@ export: "エクスポート" files: "ファイル" download: "ダウンロード" driveFileDeleteConfirm: "ファイル「{name}」を削除しますか?このファイルを使用した一部のコンテンツも削除されます。" +driveFilesDeleteConfirm: "{name}つのファイルを削除しますか?このファイルを使用した一部のコンテンツも削除されます。" +driveFilesSensitiveonConfirm: "{name}つのファイルをセンシティブにしますか?" +driveFilesSensitiveoffConfirm: "{name}つのファイルのセンシティブを解除しますか?" driveFolderDeleteConfirm: "フォルダ「{name}」を削除しますか?このフォルダの中に存在するファイルを使用した一部のコンテンツも削除されます。" unfollowConfirm: "{name}のフォローを解除しますか?" exportRequested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、「ドライブ」に追加されます。" diff --git a/package.json b/package.json index af11e8e66e..0a7efa9f5e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.3-prismisskey.2", + "version": "2023.9.3-prismisskey.3", "codename": "nasubi", "repository": { "type": "git", @@ -18,6 +18,7 @@ "build-assets": "node ./scripts/build-assets.mjs", "build": "pnpm build-pre && pnpm -r build && pnpm build-assets", "build-storybook": "pnpm --filter frontend build-storybook", + "build-and-start": "pnpm build && pnpm start", "start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js", "start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js", "init": "pnpm migrate", diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue index 07fa06fb0e..f13cdb61a8 100644 --- a/packages/frontend/src/components/MkDrive.folder.vue +++ b/packages/frontend/src/components/MkDrive.folder.vue @@ -18,7 +18,6 @@ SPDX-License-Identifier: AGPL-3.0-only @drop.prevent.stop="onDrop" @dragstart="onDragstart" @dragend="onDragend" - >

@@ -45,7 +44,7 @@ const props = withDefaults(defineProps<{ folder: Misskey.entities.DriveFolder; isSelected?: boolean; selectMode?: boolean; - multipleselect?; + selectedFiles?: string[]; }>(), { isSelected: false, selectMode: false, @@ -145,8 +144,8 @@ function onDrop(ev: DragEvent) { if (driveFile != null && driveFile !== '') { const file = JSON.parse(driveFile); emit('removeFile', file.id); - if (props.multipleselect.length > 0) { - props.multipleselect.forEach((e)=>{ + if (props.selectedFiles.length > 0) { + props.selectedFiles.forEach((e)=>{ os.api('drive/files/update', { fileId: e.id, folderId: props.folder.id, @@ -158,9 +157,7 @@ function onDrop(ev: DragEvent) { folderId: props.folder.id, }); } - - - } + } //#endregion //#region ドライブのフォルダ @@ -234,120 +231,120 @@ function rename() { } function deleteFolder() { - os.api('drive/folders/show', { + os.api('drive/folders/show', { + folderId: props.folder.id, + }).then(async (r) => { + + if (r.foldersCount > 0) { + await os.alert({ + type: 'error', + title: i18n.ts.unableToDelete, + text: 'フォルダ内にフォルダが存在するため、削除できません。 \n フォルダ内のフォルダを削除してから試してみてください。', + }); + } + + if (r.filesCount > 0) { + + const {canceled} = await os.confirm({ + type: 'warning', + text: i18n.t('driveFolderDeleteConfirm', {name: props.folder.name}), + }); + + if (canceled) return; + + let allResults = []; + let Result = await os.api('drive/files', {folderId: props.folder.id, limit: 31}); + allResults = allResults.concat(Result) + while (Result.length >= 31) { + const untilId = Result[Result.length - 1].id; + Result = await os.api('drive/files', { folderId: props.folder.id, limit: 31, untilId }); + allResults = allResults.concat(Result); // pushをconcatに変更 + } + allResults.forEach((r,i)=>{ + os.api('drive/files/delete',{fileId: r.id}) + }) + + + os.api('drive/folders/show', { folderId: props.folder.id, - }).then(async (r) => { + }).then(async (r) =>{ + if (r.filesCount > 0) { - if (r.foldersCount > 0) { - await os.alert({ - type: 'error', - title: i18n.ts.unableToDelete, - text: 'フォルダ内にフォルダが存在するため、削除できません。 \n フォルダ内のフォルダを削除してから試してみてください。', - }); - } + let allResults = []; + let Result = await os.api('drive/files', {folderId: props.folder.id, limit: 31}); + allResults = allResults.concat(Result) + while (Result.length >= 31) { + const untilId = Result[Result.length - 1].id; + Result = await os.api('drive/files', { folderId: props.folder.id, limit: 31, untilId }); + allResults = allResults.concat(Result); + } + allResults.forEach((r,i)=>{ + os.api('drive/files/delete',{fileId: r.id}) + }) - if (r.filesCount > 0) { - - const {canceled} = await os.confirm({ - type: 'warning', - text: i18n.t('driveFolderDeleteConfirm', {name: props.folder.name}), - }); - - if (canceled) return; - - let allResults = []; - let Result = await os.api('drive/files', {folderId: props.folder.id, limit: 31}); - allResults = allResults.concat(Result) - while (Result.length >= 31) { - const untilId = Result[Result.length - 1].id; - Result = await os.api('drive/files', { folderId: props.folder.id, limit: 31, untilId }); - allResults = allResults.concat(Result); // pushをconcatに変更 - } - allResults.forEach((r,i)=>{ - os.api('drive/files/delete',{fileId: r.id}) - }) - - - os.api('drive/folders/show', { - folderId: props.folder.id, - }).then(async (r) =>{ - if (r.filesCount > 0) { - - let allResults = []; - let Result = await os.api('drive/files', {folderId: props.folder.id, limit: 31}); - allResults = allResults.concat(Result) - while (Result.length >= 31) { - const untilId = Result[Result.length - 1].id; - Result = await os.api('drive/files', { folderId: props.folder.id, limit: 31, untilId }); - allResults = allResults.concat(Result); // pushをconcatに変更 + os.api('drive/folders/delete', { + folderId: props.folder.id, + }).then(() => { + if (defaultStore.state.uploadFolder === props.folder.id) { + defaultStore.set('uploadFolder', null); } - allResults.forEach((r,i)=>{ - console.log(r) - os.api('drive/files/delete',{fileId: r.id}) - }) + }).catch(err => { + switch (err.id) { + case 'b0fc8a17-963c-405d-bfbc-859a487295e1': + os.alert({ + type: 'error', + title: i18n.ts.unableToDelete, + text: i18n.ts.hasChildFilesOrFolders, + }); + break; + default: + os.alert({ + type: 'error', + text: i18n.ts.unableToDelete, + }); + } + }); - os.api('drive/folders/delete', { - folderId: props.folder.id, - }).then(() => { - if (defaultStore.state.uploadFolder === props.folder.id) { - defaultStore.set('uploadFolder', null); - } - }).catch(err => { - switch (err.id) { - case 'b0fc8a17-963c-405d-bfbc-859a487295e1': - os.alert({ - type: 'error', - title: i18n.ts.unableToDelete, - text: i18n.ts.hasChildFilesOrFolders, - }); - break; - default: - os.alert({ - type: 'error', - text: i18n.ts.unableToDelete, - }); - } + os.api('drive/folders/delete', { + folderId: props.folder.id, + }) + }else{ + os.api('drive/folders/delete', { + folderId: props.folder.id, + }) + } + }) + + } else { + + await os.api('drive/folders/delete', { + folderId: props.folder.id, + }).then(() => { + if (defaultStore.state.uploadFolder === props.folder.id) { + defaultStore.set('uploadFolder', null); + } + }).catch(err => { + switch (err.id) { + case 'b0fc8a17-963c-405d-bfbc-859a487295e1': + os.alert({ + type: 'error', + title: i18n.ts.unableToDelete, + text: i18n.ts.hasChildFilesOrFolders, }); - - os.api('drive/folders/delete', { - folderId: props.folder.id, - }) - }else{ - os.api('drive/folders/delete', { - folderId: props.folder.id, - }) - } - }) - - } else { - - await os.api('drive/folders/delete', { - folderId: props.folder.id, - }).then(() => { - if (defaultStore.state.uploadFolder === props.folder.id) { - defaultStore.set('uploadFolder', null); - } - }).catch(err => { - switch (err.id) { - case 'b0fc8a17-963c-405d-bfbc-859a487295e1': - os.alert({ - type: 'error', - title: i18n.ts.unableToDelete, - text: i18n.ts.hasChildFilesOrFolders, - }); - break; - default: - os.alert({ - type: 'error', - text: i18n.ts.unableToDelete, - }); - } - }); - } - }) + break; + default: + os.alert({ + type: 'error', + text: i18n.ts.unableToDelete, + }); + } + }); + } + }) } + function setAsUploadFolder() { defaultStore.set('uploadFolder', props.folder.id); } diff --git a/packages/frontend/src/components/MkDrive.navFolder.vue b/packages/frontend/src/components/MkDrive.navFolder.vue index d960d86209..f3c4374997 100644 --- a/packages/frontend/src/components/MkDrive.navFolder.vue +++ b/packages/frontend/src/components/MkDrive.navFolder.vue @@ -26,7 +26,7 @@ import { i18n } from '@/i18n.js'; const props = defineProps<{ folder?: Misskey.entities.DriveFolder; parentFolder: Misskey.entities.DriveFolder | null; - multipleselect?; + selectedFiles: string[]; }>(); const emit = defineEmits<{ @@ -113,22 +113,19 @@ function onDrop(ev: DragEvent) { if (driveFile != null && driveFile !== '') { const file = JSON.parse(driveFile); emit('removeFile', file.id); - - if (props.multipleselect.length > 0) { - - props.multipleselect.forEach((e)=>{ - os.api('drive/files/update', { - fileId: e.id, - folderId: props.folder ? props.folder.id : null, - }); - }) - - }else{ - os.api('drive/files/update', { - fileId: file.id, - folderId: props.folder ? props.folder.id : null, - }); - } + if (props.selectedFiles.length > 0) { + props.selectedFiles.forEach((e) => { + os.api('drive/files/update', { + fileId: e.id, + folderId: props.folder ? props.folder.id : null, + }); + }); + } else { + os.api('drive/files/update', { + fileId: file.id, + folderId: props.folder ? props.folder.id : null, + }); + } } //#endregion diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index 55f76a968f..6d15142d3d 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -4,12 +4,13 @@ SPDX-License-Identifier: AGPL-3.0-only --> - + {{ folder.name }} - + + + ({{ number(selectedFiles.length) }}) + + + + +

@@ -89,14 +99,21 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.t('empty-draghover') }}
-
{{ i18n.ts.emptyDrive }}
{{ i18n.t('empty-drive-description') }}
+
+ {{ + i18n.ts.emptyDrive + }}
{{ i18n.t('empty-drive-description') }} +
{{ i18n.ts.emptyFolder }}
- + @@ -113,23 +130,24 @@ import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import { uploadFile, uploads } from '@/scripts/upload.js'; import { claimAchievement } from '@/scripts/achievements.js'; -import { isTouchUsing } from '@/scripts/touch.js'; +import number from "@/filters/number.js"; + const props = withDefaults(defineProps<{ - initialFolder?: Misskey.entities.DriveFolder; - type?: string; - multiple?: boolean; - select?: 'file' | 'folder' | null; + initialFolder?: Misskey.entities.DriveFolder; + type?: string; + multiple?: boolean; + select?: 'file' | 'folder' | null; }>(), { multiple: false, select: null, }); const emit = defineEmits<{ - (ev: 'selected', v: Misskey.entities.DriveFile | Misskey.entities.DriveFolder): void; - (ev: 'change-selection', v: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]): void; - (ev: 'move-root'): void; - (ev: 'cd', v: Misskey.entities.DriveFolder | null): void; - (ev: 'open-folder', v: Misskey.entities.DriveFolder): void; + (ev: 'selected', v: Misskey.entities.DriveFile | Misskey.entities.DriveFolder): void; + (ev: 'change-selection', v: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]): void; + (ev: 'move-root'): void; + (ev: 'cd', v: Misskey.entities.DriveFolder | null): void; + (ev: 'open-folder', v: Misskey.entities.DriveFolder): void; }>(); const loadMoreFiles = shallowRef>(); @@ -146,10 +164,8 @@ const selectedFolders = ref([]); const uploadings = uploads; const connection = useStream().useChannel('drive'); const keepOriginal = ref(defaultStore.state.keepOriginalUploading); // 外部渡しが多いので$refは使わないほうがよい -const SelectFiles = ref([]); -const isLongPressing = ref(false); -let longPressTimeout = null; -const isScrolling = ref(false); // スクロール中でない状態で初期化 +const multiple = ref(props.multiple || false); +const select = ref(props.select || null); // ドロップされようとしているか const draghover = ref(false); @@ -169,47 +185,6 @@ watch(folder, () => emit('cd', folder.value)); function onStreamDriveFileCreated(file: Misskey.entities.DriveFile) { addFile(file, true); } -let timeout; -const debounceEvent = (event) => { - if (isTouchUsing && SelectFiles.value.length === 0) { - clearTimeout(timeout); - timeout = setTimeout(() => { - isLongPressing.value = false; - }, 900); - }else{ - clearTimeout(timeout); - timeout = setTimeout(() => { - isLongPressing.value = false; - }, 80); - } -}; - -function startLongPress(file) { - console.log(SelectFiles.value.length) - if (isTouchUsing && SelectFiles.value.length === 0) { - isLongPressing.value = true; - longPressTimeout = setTimeout(() => { - if (isLongPressing.value) { - multipleSelect(file); - } - }, 1000); - }else{ - isLongPressing.value = true; - longPressTimeout = setTimeout(() => { - if (isLongPressing.value) { - multipleSelect(file); - } - }, 100); - } -} - - -function endLongPress() { - if (isTouchUsing) { - isLongPressing.value = false; - clearTimeout(longPressTimeout); - } -} function onStreamDriveFileUpdated(file: Misskey.entities.DriveFile) { const current = folder.value ? folder.value.id : null; @@ -227,14 +202,7 @@ function onStreamDriveFileDeleted(fileId: string) { function onStreamDriveFolderCreated(createdFolder: Misskey.entities.DriveFolder) { addFolder(createdFolder, true); } -function multipleSelect(file) { - const index = SelectFiles.value.findIndex(item => item.id === file.id); - if (index !== -1) { - SelectFiles.value.splice(index, 1); - } else { - SelectFiles.value.push(file); - } -} + function onStreamDriveFolderUpdated(updatedFolder: Misskey.entities.DriveFolder) { const current = folder.value ? folder.value.id : null; if (current !== updatedFolder.parentId) { @@ -448,7 +416,7 @@ function upload(file: File, folderToUpload?: Misskey.entities.DriveFolder | null function chooseFile(file: Misskey.entities.DriveFile) { const isAlreadySelected = selectedFiles.value.some(f => f.id === file.id); - if (props.multiple) { + if (multiple.value) { if (isAlreadySelected) { selectedFiles.value = selectedFiles.value.filter(f => f.id !== file.id); } else { @@ -467,7 +435,7 @@ function chooseFile(file: Misskey.entities.DriveFile) { function chooseFolder(folderToChoose: Misskey.entities.DriveFolder) { const isAlreadySelected = selectedFolders.value.some(f => f.id === folderToChoose.id); - if (props.multiple) { + if (multiple.value) { if (isAlreadySelected) { selectedFolders.value = selectedFolders.value.filter(f => f.id !== folderToChoose.id); } else { @@ -554,6 +522,7 @@ function removeFolder(folderToRemove: Misskey.entities.DriveFolder | string) { function removeFile(file: Misskey.entities.DriveFile | string) { const fileId = typeof file === 'object' ? file.id : file; files.value = files.value.filter(f => f.id !== fileId); + selectedFiles.value = selectedFiles.value.filter(f => f.id !== fileId); } function appendFile(file: Misskey.entities.DriveFile) { @@ -563,6 +532,7 @@ function appendFile(file: Misskey.entities.DriveFile) { function appendFolder(folderToAppend: Misskey.entities.DriveFolder) { addFolder(folderToAppend); } + /* function prependFile(file: Misskey.entities.DriveFile) { addFile(file, true); @@ -679,33 +649,130 @@ function getMenu() { }, { text: i18n.ts.upload, icon: 'ti ti-upload', - action: () => { selectLocalFile(); }, + action: () => { + selectLocalFile(); + }, }, { text: i18n.ts.fromUrl, icon: 'ti ti-link', - action: () => { urlUpload(); }, + action: () => { + urlUpload(); + }, }, null, { text: folder.value ? folder.value.name : i18n.ts.drive, type: 'label', }, folder.value ? { text: i18n.ts.renameFolder, icon: 'ti ti-forms', - action: () => { renameFolder(folder.value); }, + action: () => { + renameFolder(folder.value); + }, } : undefined, folder.value ? { text: i18n.ts.deleteFolder, icon: 'ti ti-trash', - action: () => { deleteFolder(folder.value as Misskey.entities.DriveFolder); }, + action: () => { + deleteFolder(folder.value as Misskey.entities.DriveFolder); + }, } : undefined, { text: i18n.ts.createFolder, icon: 'ti ti-folder-plus', - action: () => { createFolder(); }, + action: () => { + createFolder(); + }, }]; } -function showMenu(ev: MouseEvent) { - os.popupMenu(getMenu(), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); +async function isSensitive(Files, isSensitive: boolean) { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.t(isSensitive ? 'driveFilesSensitiveonConfirm' : 'driveFilesSensitiveoffConfirm', { name: Files.length }), + }); + + if (canceled) return; + Files.forEach((file) => { + os.api('drive/files/update', { + fileId: file.id, + isSensitive, + }); + }); } -const contents = ref(null); + +async function fileDelete(Files) { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.t('driveFilesDeleteConfirm', { name: Files.length }), + }); + + if (canceled) return; + Files.forEach((file) => { + os.api('drive/files/delete', { + fileId: file.id, + }); + }); +} + +function getFilesMenu(Files) { + return [{ + text: i18n.ts.createNoteFromTheFile, + icon: 'ti ti-pencil', + action: () => { + if (Files.length >= 16) { + os.confirm({ + type: 'warning', + text: '16ファイル以上添付しようとしています', + }); + return; + } else { + os.post({ + initialFiles: [...Files], + }); + } + }, + }, { + text: i18n.ts.unmarkAsSensitive, + icon: 'ti ti-eye', + action: () => { + isSensitive(Files, false); + }, + }, { + text: i18n.ts.markAsSensitive, + icon: 'ti ti-eye-exclamation', + action: () => { + isSensitive(Files, true); + }, + }, { + text: i18n.ts.delete, + icon: 'ti ti-trash', + danger: true, + action: () => { + fileDelete(Files); + }, + }]; +} + +function filesSelect() { + multiple.value = !multiple.value; + select.value = (select.value === null) ? 'file' : null; + selectedFiles.value = []; +} + +function filesAllSelect() { + if (selectedFiles.value.length === 0) { + selectedFiles.value = files.value; + } else { + selectedFiles.value = []; + } +} + +function showMenu(ev: MouseEvent) { + console.log(selectedFiles.value.length); + if (selectedFiles.value.length === 0) { + os.popupMenu(getMenu(), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); + } else { + os.popupMenu(getFilesMenu(selectedFiles.value), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); + } +} + function onContextmenu(ev: MouseEvent) { os.contextMenu(getMenu(), ev); } @@ -743,119 +810,121 @@ onBeforeUnmount(() => { connection.dispose(); ilFilesObserver.disconnect(); }); -