From 616e461d54131b97aa6afd5fa3aa430874a529d5 Mon Sep 17 00:00:00 2001 From: dorosty Date: Tue, 23 Jan 2024 14:21:19 +0330 Subject: [PATCH 01/11] handle folders and multiple files --- index.js | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index b10c181..120ebd0 100644 --- a/index.js +++ b/index.js @@ -284,7 +284,7 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { }; const writeRelative = f => { - const d = dialog('progress', f); + const d = dialog('progress', f.name); return vfs.writefile({ path: pathJoin(state.currentPath.path, f.name) @@ -300,10 +300,76 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { }); }; - const uploadBrowserFiles = (files) => { - Promise.all(files.map(writeRelative)) - .then(() => refresh(files[0].name)) // FIXME: Select all ? - .catch(error => dialog('error', error, __('MSG_UPLOAD_ERROR'))); + const uploadBrowserFiles = async items => { + const uploadList = []; + let totalSize = 0; + let filename; + try { + const checkFile = (file, dirPath) => { + uploadList.push({ dirPath, file }); + totalSize += file.size; + if (filename == null) { + filename = file.name; + } else if (filename) { + filename = ''; + } + }; + const checkDirectory = async (directory, dirPath) => { + uploadList.push({ dirPath }); + const reader = directory.createReader(); + await new Promise(resolve => { + reader.readEntries(async entries => { + for (let entry of entries) { + if (entry.isFile) { + await new Promise((resolve, reject) => { + entry.file(file => { + checkFile(file, dirPath); + resolve(); + }, reject); + }); + } else if (entry.isDirectory) { + await checkDirectory(entry, dirPath + '/' + entry.name); + } + } + resolve(); + }); + }); + } + for (let item of items) { + const entry = item.webkitGetAsEntry(); + if (entry.isFile) { + checkFile(item.getAsFile(), ''); + } else if (entry.isDirectory) { + await checkDirectory(entry, entry.name); + } + }; + } catch (error) { + console.warn(error); + } + + const d = dialog('progress', filename || 'multiple files'); + try { + let uploaded = 0; + for (let { dirPath, file } of uploadList) { + if (file) { + await vfs.writefile({ + path: pathJoin(state.currentPath.path, dirPath, file.name) + }, file, { + pid: proc.pid, + onProgress: (ev, progress) => { + d.setProgress(Math.round((uploaded + progress * file.size / 100) * 100 / totalSize)); + } + }); + uploaded += file.size; + } else { + await vfs.mkdir({path: pathJoin(state.currentPath.path, dirPath)}, {pid: proc.pid}); + } + } + refresh(items[0].name); // FIXME: Select all ? + } catch (error) { + dialog('error', error, __('MSG_UPLOAD_ERROR')); + } + d.destroy(); }; const uploadVirtualFile = (data) => { @@ -449,8 +515,8 @@ const dialogFactory = (core, proc, win) => { action(() => vfs.unlink(file, {pid: proc.pid}), true, __('MSG_DELETE_ERROR')); })); - const progressDialog = (file) => dialog('progress', { - message: __('DIALOG_PROGRESS_MESSAGE', file.name), + const progressDialog = name => dialog('progress', { + message: __('DIALOG_PROGRESS_MESSAGE', name), buttons: [] }, () => {}, false); From 5a8ce94b38fa5a7260cedf1ae45b368a918fdd70 Mon Sep 17 00:00:00 2001 From: Ali Dorosty Date: Wed, 24 Jan 2024 10:51:48 +0330 Subject: [PATCH 02/11] make use of droppableDataTransferProperty --- index.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 120ebd0..6a3eada 100644 --- a/index.js +++ b/index.js @@ -66,7 +66,8 @@ const createWindowOptions = (core, proc, title) => ({ attributes: { mediaQueries: { small: 'screen and (max-width: 400px)' - } + }, + droppableDataTransferProperty: 'items', }, dimension: Object.assign({ width: 400, @@ -306,16 +307,16 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { let filename; try { const checkFile = (file, dirPath) => { - uploadList.push({ dirPath, file }); + uploadList.push({dirPath, file}); totalSize += file.size; - if (filename == null) { + if (filename === undefined) { filename = file.name; } else if (filename) { filename = ''; } }; const checkDirectory = async (directory, dirPath) => { - uploadList.push({ dirPath }); + uploadList.push({dirPath}); const reader = directory.createReader(); await new Promise(resolve => { reader.readEntries(async entries => { @@ -334,7 +335,7 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { resolve(); }); }); - } + }; for (let item of items) { const entry = item.webkitGetAsEntry(); if (entry.isFile) { @@ -342,7 +343,7 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { } else if (entry.isDirectory) { await checkDirectory(entry, entry.name); } - }; + } } catch (error) { console.warn(error); } @@ -350,7 +351,7 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { const d = dialog('progress', filename || 'multiple files'); try { let uploaded = 0; - for (let { dirPath, file } of uploadList) { + for (let {dirPath, file} of uploadList) { if (file) { await vfs.writefile({ path: pathJoin(state.currentPath.path, dirPath, file.name) From b832445b7f1a10229064d8d8038d2ae45d9f18ce Mon Sep 17 00:00:00 2001 From: Ali Dorosty Date: Thu, 25 Jan 2024 14:00:35 +0330 Subject: [PATCH 03/11] make use of new droppable config --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 6a3eada..b7736fa 100644 --- a/index.js +++ b/index.js @@ -67,7 +67,7 @@ const createWindowOptions = (core, proc, title) => ({ mediaQueries: { small: 'screen and (max-width: 400px)' }, - droppableDataTransferProperty: 'items', + droppable: {dataTransferProperty: 'items'}, }, dimension: Object.assign({ width: 400, From 3fcf4275575fc55bd1a5751db0ed2d1d4b1ee4e0 Mon Sep 17 00:00:00 2001 From: dorosty Date: Tue, 30 Jan 2024 10:19:02 +0330 Subject: [PATCH 04/11] fix: check for browser support first --- index.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index b7736fa..86f0cfa 100644 --- a/index.js +++ b/index.js @@ -48,6 +48,12 @@ import { listView } from '@osjs/gui'; + +/** + * flag indicating whether uploading folders is supported + */ +const supportsUploadingFolders = !!(window.DataTransferItem && DataTransferItem.prototype.webkitGetAsEntry); + /** * Creates default settings */ @@ -67,7 +73,7 @@ const createWindowOptions = (core, proc, title) => ({ mediaQueries: { small: 'screen and (max-width: 400px)' }, - droppable: {dataTransferProperty: 'items'}, + droppable: {dataTransferProperty: supportsUploadingFolders ? 'items' : 'files'}, }, dimension: Object.assign({ width: 400, @@ -302,6 +308,16 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { }; const uploadBrowserFiles = async items => { + if (!supportsUploadingFolders) { + try { + const files = items; + await Promise.all(files.map(writeRelative)); + refresh(files[0].name); // FIXME: Select all ? + } catch(error) { + dialog('error', error, __('MSG_UPLOAD_ERROR')); + } + return; + } const uploadList = []; let totalSize = 0; let filename; From 975c57a5edca618002ccc3852584aab8fc5da18c Mon Sep 17 00:00:00 2001 From: Ali Dorosty Date: Sat, 17 Feb 2024 00:11:25 +0330 Subject: [PATCH 05/11] feat(upload): add menu to upload a folder --- index.js | 99 +++++++++++++++++++++++++++++++++--------------------- locales.js | 1 + 2 files changed, 62 insertions(+), 38 deletions(-) diff --git a/index.js b/index.js index 86f0cfa..42ebef2 100644 --- a/index.js +++ b/index.js @@ -104,9 +104,11 @@ const usingPositiveButton = cb => (btn, value) => { /** * Triggers a browser upload */ -const triggerBrowserUpload = (cb) => { +const triggerBrowserUpload = (cb, dir = false) => { const field = document.createElement('input'); field.type = 'file'; + field.multiple = true; + field.webkitdirectory = dir; field.onchange = () => { if (field.files.length > 0) { cb(field.files); @@ -307,6 +309,33 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { }); }; + const uploadFileAndFolderList = async list => { + const files = list.filter(({file}) => file); + const totalSize = files.reduce((sum, {size}) => sum + size); + const d = dialog('progress', files.length === 1 ? files[0].name : 'multiple files'); + try { + let uploaded = 0; + for (let {dirPath, file} of list) { + if (file) { + await vfs.writefile({ + path: pathJoin(state.currentPath.path, dirPath, file.name) + }, file, { + pid: proc.pid, + onProgress: (ev, progress) => { + d.setProgress(Math.round((uploaded + progress * file.size / 100) * 100 / totalSize)); + } + }); + uploaded += file.size; + } else { + await vfs.mkdir({path: pathJoin(state.currentPath.path, dirPath)}, {pid: proc.pid}); + } + } + } catch (error) { + dialog('error', error, __('MSG_UPLOAD_ERROR')); + } + d.destroy(); + } + const uploadBrowserFiles = async items => { if (!supportsUploadingFolders) { try { @@ -318,19 +347,9 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { } return; } + const uploadList = []; - let totalSize = 0; - let filename; try { - const checkFile = (file, dirPath) => { - uploadList.push({dirPath, file}); - totalSize += file.size; - if (filename === undefined) { - filename = file.name; - } else if (filename) { - filename = ''; - } - }; const checkDirectory = async (directory, dirPath) => { uploadList.push({dirPath}); const reader = directory.createReader(); @@ -340,7 +359,7 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { if (entry.isFile) { await new Promise((resolve, reject) => { entry.file(file => { - checkFile(file, dirPath); + uploadList.push({dirPath, file}); resolve(); }, reject); }); @@ -355,7 +374,8 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { for (let item of items) { const entry = item.webkitGetAsEntry(); if (entry.isFile) { - checkFile(item.getAsFile(), ''); + const file = item.getAsFile(); + uploadList.push({dirPath: '', file}); } else if (entry.isDirectory) { await checkDirectory(entry, entry.name); } @@ -363,30 +383,8 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { } catch (error) { console.warn(error); } - - const d = dialog('progress', filename || 'multiple files'); - try { - let uploaded = 0; - for (let {dirPath, file} of uploadList) { - if (file) { - await vfs.writefile({ - path: pathJoin(state.currentPath.path, dirPath, file.name) - }, file, { - pid: proc.pid, - onProgress: (ev, progress) => { - d.setProgress(Math.round((uploaded + progress * file.size / 100) * 100 / totalSize)); - } - }); - uploaded += file.size; - } else { - await vfs.mkdir({path: pathJoin(state.currentPath.path, dirPath)}, {pid: proc.pid}); - } - } - refresh(items[0].name); // FIXME: Select all ? - } catch (error) { - dialog('error', error, __('MSG_UPLOAD_ERROR')); - } - d.destroy(); + await uploadFileAndFolderList(uploadList); + refresh(items[0].name); // FIXME: Select all ? }; const uploadVirtualFile = (data) => { @@ -441,6 +439,27 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { .catch(error => dialog('error', error, __('MSG_UPLOAD_ERROR'))); }); + const uploadDir = () => triggerBrowserUpload(async files => { + const filesArray = []; + const foldersSet = new Set(); + for (let file of files) { + const path = file.webkitRelativePath; + const folders = path.split('/').slice(0, -1); + let dirPath = ''; + folders.forEach(folder => { + if (dirPath) { + dirPath += '/'; + } + dirPath += folder; + foldersSet.add(dirPath); + }); + filesArray.push({file, dirPath}); + } + const allFilesAndFolders = Array.from(foldersSet).map(dirPath => ({dirPath})).concat(filesArray); + await uploadFileAndFolderList(allFilesAndFolders); + refresh(files[0].name); // FIXME: Select all ? + }, true); + const paste = (move, currentPath) => ({item, callback}) => { const dest = {path: pathJoin(currentPath.path, item.filename)}; @@ -462,6 +481,7 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { return { download: file => vfs.download(file), upload, + uploadDir, refresh, action, drop, @@ -591,6 +611,7 @@ const menuFactory = (core, proc, win) => { const createFileMenu = () => ([ {label: _('LBL_UPLOAD'), onclick: () => win.emit('filemanager:menu:upload')}, + {label: __('LBL_UPLOAD_DIR'), onclick: () => win.emit('filemanager:menu:uploaddir')}, {label: _('LBL_MKDIR'), onclick: () => win.emit('filemanager:menu:mkdir')}, {label: _('LBL_QUIT'), onclick: () => win.emit('filemanager:menu:quit')} ]); @@ -906,6 +927,7 @@ const createWindow = (core, proc) => { const onHistoryClear = () => wired.history.clear(); const onMenu = (props, args) => createMenu(props, args || state.currentFile); const onMenuUpload = (...args) => vfs.upload(...args); + const onMenuUploadDir = (...args) => vfs.uploadDir(...args); const onMenuMkdir = () => dialog('mkdir', vfs.action, state.currentPath); const onMenuQuit = () => proc.destroy(); const onMenuRefresh = () => vfs.refresh(); @@ -940,6 +962,7 @@ const createWindow = (core, proc) => { .on('filemanager:historyPush', onHistoryPush) .on('filemanager:historyClear', onHistoryClear) .on('filemanager:menu:upload', onMenuUpload) + .on('filemanager:menu:uploaddir', onMenuUploadDir) .on('filemanager:menu:mkdir', onMenuMkdir) .on('filemanager:menu:quit', onMenuQuit) .on('filemanager:menu:refresh', onMenuRefresh) diff --git a/locales.js b/locales.js index 5a92aa1..f0da76a 100644 --- a/locales.js +++ b/locales.js @@ -1,6 +1,7 @@ export const en_EN = { LBL_SHOW_HIDDEN_FILES: 'Show hidden files', LBL_MINIMALISTIC: 'Minimalistic', + LBL_UPLOAD_DIR: 'Upload Directory', LBL_OPEN_WITH: 'Open with...', LBL_SHOW_DATE: 'Show date column', LBL_STATUS: '{0} directories, {1} files, {2} bytes total', From fd0efc96cb187d866c19914855ca08a5f7dfcf39 Mon Sep 17 00:00:00 2001 From: Ali Dorosty Date: Sat, 17 Feb 2024 07:18:40 +0330 Subject: [PATCH 06/11] style: break into smaller functions --- index.js | 122 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 77 insertions(+), 45 deletions(-) diff --git a/index.js b/index.js index 86f0cfa..8291753 100644 --- a/index.js +++ b/index.js @@ -307,55 +307,75 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { }); }; - const uploadBrowserFiles = async items => { - if (!supportsUploadingFolders) { - try { - const files = items; - await Promise.all(files.map(writeRelative)); - refresh(files[0].name); // FIXME: Select all ? - } catch(error) { - dialog('error', error, __('MSG_UPLOAD_ERROR')); - } - return; - } + const legacyUploadBrowserFiles = (files) => { + Promise.all(files.map(writeRelative)) + .then(() => refresh(files[0].name)) // FIXME: Select all ? + .catch(error => dialog('error', error, __('MSG_UPLOAD_ERROR'))); + }; + + const getUploadData = async (items) => { + /* + type: [{dirPath: string, file?: any}] + Directories do not have the `file` property. + They only have their relative path stored in `dirPath`. + Files store their containing folders path as their `dirPath`. + */ const uploadList = []; + let totalSize = 0; - let filename; - try { - const checkFile = (file, dirPath) => { - uploadList.push({dirPath, file}); - totalSize += file.size; - if (filename === undefined) { - filename = file.name; - } else if (filename) { - filename = ''; - } - }; - const checkDirectory = async (directory, dirPath) => { - uploadList.push({dirPath}); - const reader = directory.createReader(); - await new Promise(resolve => { - reader.readEntries(async entries => { - for (let entry of entries) { - if (entry.isFile) { - await new Promise((resolve, reject) => { - entry.file(file => { - checkFile(file, dirPath); - resolve(); - }, reject); - }); - } else if (entry.isDirectory) { - await checkDirectory(entry, dirPath + '/' + entry.name); - } - } - resolve(); - }); + let title; + + const checkFile = (file, dirPath) => { + uploadList.push({dirPath, file}); + totalSize += file.size; + if (title === undefined) { + title = file.name; + } else if (title) { + // we've got multiple titles; reset the value + title = ''; + } + }; + + const getDirectoryEntries = (directory) => { + const reader = directory.createReader(); + return new Promise(resolve => { + reader.readEntries(async (entries) => { + resolve(entries); }); - }; + }); + }; + + const getFileFromEntry = (entry) => { + return new Promise((resolve, reject) => { + entry.file((file) => { + resolve(file); + }, (error) => { + reject(error); + }); + }); + }; + + const checkDirectory = async (directory, dirPath) => { + uploadList.push({dirPath}); + const entries = await getDirectoryEntries(directory); + for (let entry of entries) { + if (entry.isFile) { + const file = await getFileFromEntry(entry); + checkFile(file, dirPath); + } else if (entry.isDirectory) { + const subDirPath = dirPath + '/' + entry.name; + await checkDirectory(entry, subDirPath); + } + } + }; + + try { for (let item of items) { const entry = item.webkitGetAsEntry(); if (entry.isFile) { - checkFile(item.getAsFile(), ''); + const file = item.getAsFile(); + const dirPath = ''; + checkFile(file, dirPath); } else if (entry.isDirectory) { await checkDirectory(entry, entry.name); } @@ -364,11 +384,22 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { console.warn(error); } - const d = dialog('progress', filename || 'multiple files'); + return { uploadList, totalSize, title }; + } + + const uploadBrowserFiles = async (items) => { + if (!supportsUploadingFolders) { + return legacyUploadBrowserFiles(items); + } + + const { uploadList, totalSize, title } = await getUploadData(items); + + const d = dialog('progress', title || 'multiple files'); try { let uploaded = 0; for (let {dirPath, file} of uploadList) { if (file) { + // upload file await vfs.writefile({ path: pathJoin(state.currentPath.path, dirPath, file.name) }, file, { @@ -379,6 +410,7 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { }); uploaded += file.size; } else { + // create folder await vfs.mkdir({path: pathJoin(state.currentPath.path, dirPath)}, {pid: proc.pid}); } } @@ -532,7 +564,7 @@ const dialogFactory = (core, proc, win) => { action(() => vfs.unlink(file, {pid: proc.pid}), true, __('MSG_DELETE_ERROR')); })); - const progressDialog = name => dialog('progress', { + const progressDialog = (name) => dialog('progress', { message: __('DIALOG_PROGRESS_MESSAGE', name), buttons: [] }, () => {}, false); From 8cd353a5a29cec878a2b4c86f809b2c316333177 Mon Sep 17 00:00:00 2001 From: Ali Dorosty Date: Sat, 17 Feb 2024 07:53:53 +0330 Subject: [PATCH 07/11] style: further improve --- index.js | 47 +++++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/index.js b/index.js index 8291753..d32ad67 100644 --- a/index.js +++ b/index.js @@ -313,7 +313,7 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { .catch(error => dialog('error', error, __('MSG_UPLOAD_ERROR'))); }; - const getUploadData = async (items) => { + const getUploadList = async (items) => { /* type: [{dirPath: string, file?: any}] Directories do not have the `file` property. @@ -322,20 +322,6 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { */ const uploadList = []; - let totalSize = 0; - let title; - - const checkFile = (file, dirPath) => { - uploadList.push({dirPath, file}); - totalSize += file.size; - if (title === undefined) { - title = file.name; - } else if (title) { - // we've got multiple titles; reset the value - title = ''; - } - }; - const getDirectoryEntries = (directory) => { const reader = directory.createReader(); return new Promise(resolve => { @@ -361,7 +347,7 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { for (let entry of entries) { if (entry.isFile) { const file = await getFileFromEntry(entry); - checkFile(file, dirPath); + uploadList.push({dirPath, file}); } else if (entry.isDirectory) { const subDirPath = dirPath + '/' + entry.name; await checkDirectory(entry, subDirPath); @@ -375,7 +361,7 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { if (entry.isFile) { const file = item.getAsFile(); const dirPath = ''; - checkFile(file, dirPath); + uploadList.push({dirPath, file}); } else if (entry.isDirectory) { await checkDirectory(entry, entry.name); } @@ -384,20 +370,16 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { console.warn(error); } - return { uploadList, totalSize, title }; + return uploadList; } - const uploadBrowserFiles = async (items) => { - if (!supportsUploadingFolders) { - return legacyUploadBrowserFiles(items); - } - - const { uploadList, totalSize, title } = await getUploadData(items); - - const d = dialog('progress', title || 'multiple files'); + const uploadFileAndFolderList = async (list) => { + const files = list.map(({file}) => file).filter((file) => file); + const totalSize = files.reduce((sum, {size}) => sum + size, 0); + const d = dialog('progress', files.length === 1 ? files[0].name : 'multiple files'); try { let uploaded = 0; - for (let {dirPath, file} of uploadList) { + for (let {dirPath, file} of list) { if (file) { // upload file await vfs.writefile({ @@ -414,11 +396,20 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { await vfs.mkdir({path: pathJoin(state.currentPath.path, dirPath)}, {pid: proc.pid}); } } - refresh(items[0].name); // FIXME: Select all ? } catch (error) { dialog('error', error, __('MSG_UPLOAD_ERROR')); } d.destroy(); + } + + const uploadBrowserFiles = async (items) => { + if (!supportsUploadingFolders) { + return legacyUploadBrowserFiles(items); + } + + const uploadList = await getUploadList(items); + await uploadFileAndFolderList(uploadList); + refresh(items[0].name); // FIXME: Select all ? }; const uploadVirtualFile = (data) => { From 70e9cc62ca07fba2c30db9153f0785109cfaa42c Mon Sep 17 00:00:00 2001 From: Ali Dorosty Date: Mon, 18 Mar 2024 22:13:01 +0330 Subject: [PATCH 08/11] add semicolons --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index d32ad67..75ba6fc 100644 --- a/index.js +++ b/index.js @@ -371,7 +371,7 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { } return uploadList; - } + }; const uploadFileAndFolderList = async (list) => { const files = list.map(({file}) => file).filter((file) => file); @@ -400,7 +400,7 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { dialog('error', error, __('MSG_UPLOAD_ERROR')); } d.destroy(); - } + }; const uploadBrowserFiles = async (items) => { if (!supportsUploadingFolders) { From 4a102e15222c758f986ade8f552cc59abae02bc0 Mon Sep 17 00:00:00 2001 From: Ali Dorosty Date: Mon, 1 Apr 2024 04:37:58 +0330 Subject: [PATCH 09/11] feat: support cancelation --- index.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 75ba6fc..70704dd 100644 --- a/index.js +++ b/index.js @@ -376,15 +376,26 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { const uploadFileAndFolderList = async (list) => { const files = list.map(({file}) => file).filter((file) => file); const totalSize = files.reduce((sum, {size}) => sum + size, 0); - const d = dialog('progress', files.length === 1 ? files[0].name : 'multiple files'); + const dialogTitle = files.length === 1 ? files[0].name : 'multiple files'; + const abortController = new AbortController(); + let isCancelled = false; + const onCancel = () => { + isCancelled = false; + abortController.abort(); + }; + const d = dialog('progress', dialogTitle, onCancel); try { let uploaded = 0; for (let {dirPath, file} of list) { + if (isCancelled) { + return; + } if (file) { // upload file await vfs.writefile({ path: pathJoin(state.currentPath.path, dirPath, file.name) }, file, { + signal: abortController.signal, pid: proc.pid, onProgress: (ev, progress) => { d.setProgress(Math.round((uploaded + progress * file.size / 100) * 100 / totalSize)); @@ -555,10 +566,10 @@ const dialogFactory = (core, proc, win) => { action(() => vfs.unlink(file, {pid: proc.pid}), true, __('MSG_DELETE_ERROR')); })); - const progressDialog = (name) => dialog('progress', { + const progressDialog = (name, cb = (() => {})) => dialog('progress', { message: __('DIALOG_PROGRESS_MESSAGE', name), buttons: [] - }, () => {}, false); + }, cb, false); const errorDialog = (error, message) => dialog('alert', { type: 'error', From 298757caa9b1db03d43c3a72d4d58539f4921f36 Mon Sep 17 00:00:00 2001 From: Ali Dorosty Date: Wed, 3 Apr 2024 14:07:39 +0330 Subject: [PATCH 10/11] add button --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 70704dd..760e811 100644 --- a/index.js +++ b/index.js @@ -568,7 +568,7 @@ const dialogFactory = (core, proc, win) => { const progressDialog = (name, cb = (() => {})) => dialog('progress', { message: __('DIALOG_PROGRESS_MESSAGE', name), - buttons: [] + buttons: ['Cancel'] }, cb, false); const errorDialog = (error, message) => dialog('alert', { From 1aa3305d539be3bc06caae1cde14ff4bc6429e3a Mon Sep 17 00:00:00 2001 From: Ali Dorosty Date: Sun, 5 May 2024 09:41:14 +0330 Subject: [PATCH 11/11] add `filemanager.disableMultiUpload` config --- index.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 760e811..688eedc 100644 --- a/index.js +++ b/index.js @@ -52,7 +52,9 @@ import { /** * flag indicating whether uploading folders is supported */ -const supportsUploadingFolders = !!(window.DataTransferItem && DataTransferItem.prototype.webkitGetAsEntry); +const supportsUploadingFolders = core => + core.config('filemanager.disableMultiUpload', false) !== true + && !!(window.DataTransferItem && DataTransferItem.prototype.webkitGetAsEntry); /** * Creates default settings @@ -73,7 +75,7 @@ const createWindowOptions = (core, proc, title) => ({ mediaQueries: { small: 'screen and (max-width: 400px)' }, - droppable: {dataTransferProperty: supportsUploadingFolders ? 'items' : 'files'}, + droppable: {dataTransferProperty: supportsUploadingFolders(core) ? 'items' : 'files'}, }, dimension: Object.assign({ width: 400, @@ -414,7 +416,7 @@ const vfsActionFactory = (core, proc, win, dialog, state) => { }; const uploadBrowserFiles = async (items) => { - if (!supportsUploadingFolders) { + if (!supportsUploadingFolders(core)) { return legacyUploadBrowserFiles(items); }