From e0791aa2b0f53dd31f2ddebd23284609cbbc5204 Mon Sep 17 00:00:00 2001
From: Aaron Meese <ajmeese7@gmail.com>
Date: Tue, 9 Aug 2022 20:02:26 -0400
Subject: [PATCH 1/4] Multiselect support (#22)

---
 index.js | 167 +++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 107 insertions(+), 60 deletions(-)

diff --git a/index.js b/index.js
index b10c181..32f4398 100644
--- a/index.js
+++ b/index.js
@@ -125,23 +125,26 @@ const createInitialPaths = (core, proc) => {
   return {homePath, initialPath};
 };
 
-/**
- * Formats file status message
- */
-const formatFileMessage = file => `${file.filename} (${file.size} bytes)`;
+const getDirectoryCount = files =>
+	files.filter(file => file.isDirectory).length;
+const getFileCount = files =>
+	files.filter(file => !file.isDirectory).length;
+const getTotalSize = files =>
+	files.reduce((total, file) => total + (file.size || 0), 0);
 
 /**
  * Formats directory status message
  */
-const formatStatusMessage = (core) => {
+const formatStatusMessage = (core, files) => {
   const {translatable} = core.make('osjs/locale');
   const __ = translatable(translations);
 
   return (path, files) => {
-    const directoryCount = files.filter(f => f.isDirectory).length;
-    const fileCount = files.filter(f => !f.isDirectory).length;
-    const totalSize = files.reduce((t, f) => t + (f.size || 0), 0);
+    const directoryCount = getDirectoryCount(files);
+	  const fileCount = getFileCount(files);
+	  const totalSize = getTotalSize(files);
 
+    // TODO: Copy over new label messages with translation
     return __('LBL_STATUS', directoryCount, fileCount, totalSize);
   };
 };
@@ -318,6 +321,8 @@ const vfsActionFactory = (core, proc, win, dialog, state) => {
   const readdir = async (dir, history, selectFile) => {
     if (win.getState('loading')) {
       return;
+    } else if (Array.isArray(dir)) {
+      dir = dir[0];
     }
 
     try {
@@ -347,7 +352,7 @@ const vfsActionFactory = (core, proc, win, dialog, state) => {
     } catch (error) {
       dialog('error', error, __('MSG_READDIR_ERROR', dir.path));
     } finally {
-      state.currentFile = undefined;
+      state.currentFile = [];
       win.setState('loading', false);
     }
   };
@@ -359,25 +364,32 @@ const vfsActionFactory = (core, proc, win, dialog, state) => {
   });
 
   const paste = (move, currentPath) => ({item, callback}) => {
-    const dest = {path: pathJoin(currentPath.path, item.filename)};
+    const promises = items.map(item => {
+      const dest = {
+        path: pathJoin(currentPath.path, item.filename)
+      };
 
-    const fn = move
-      ? vfs.move(item, dest, {pid: proc.pid})
-      : vfs.copy(item, dest, {pid: proc.pid});
+      return move
+        ? vfs.move(item, dest, {pid: proc.pid})
+        : vfs.copy(item, dest, {pid: proc.pid});
+    });
 
-    return fn
-      .then(() => {
+    return Promise
+      .all(promises)
+      .then(results => {
         refresh(true);
 
         if (typeof callback === 'function') {
           callback();
         }
+
+        return results;
       })
       .catch(error => dialog('error', error, __('MSG_PASTE_ERROR')));
   };
 
   return {
-    download: file => vfs.download(file),
+    download: files => files.forEach(file => vfs.download(file)),
     upload,
     refresh,
     action,
@@ -430,23 +442,34 @@ const dialogFactory = (core, proc, win) => {
     value: __('DIALOG_MKDIR_PLACEHOLDER')
   }, usingPositiveButton(value => {
     const newPath = pathJoin(currentPath.path, value);
-    action(() => vfs.mkdir({path: newPath}, {pid: proc.pid}), value, __('MSG_MKDIR_ERROR'));
+    action(
+      () => vfs.mkdir({path: newPath}, {pid: proc.pid}),
+      value,
+      __('MSG_MKDIR_ERROR')
+    );
   }));
 
-  const renameDialog = (action, file) => dialog('prompt', {
-    message: __('DIALOG_RENAME_MESSAGE', file.filename),
-    value: file.filename
-  }, usingPositiveButton(value => {
-    const idx = file.path.lastIndexOf(file.filename);
-    const newPath = file.path.substr(0, idx) + value;
+  const renameDialog = (action, files) => files.forEach(file =>
+    dialog('prompt', {
+      message: __('DIALOG_RENAME_MESSAGE', file.filename),
+      value: file.filename
+    }, usingPositiveButton(value => {
+      const idx = file.path.lastIndexOf(file.filename);
+      const newPath = file.path.substr(0, idx) + value;
 
-    action(() => vfs.rename(file, {path: newPath}), value, __('MSG_RENAME_ERROR'));
-  }));
+      action(() => vfs.rename(file, {path: newPath}), value, __('MSG_RENAME_ERROR'));
+    })));
 
-  const deleteDialog = (action, file) => dialog('confirm', {
+  const deleteDialog = (action, files) => dialog('confirm', {
     message: __('DIALOG_DELETE_MESSAGE', file.filename),
   }, usingPositiveButton(() => {
-    action(() => vfs.unlink(file, {pid: proc.pid}), true, __('MSG_DELETE_ERROR'));
+    action(
+      () => Promise.all(
+        files.map(file => vfs.unlink(file, {pid: proc.pid}))
+      ),
+      true,
+      __('MSG_DELETE_ERROR')
+    );
   }));
 
   const progressDialog = (file) => dialog('progress', {
@@ -512,40 +535,44 @@ const menuFactory = (core, proc, win) => {
     {label: _('LBL_QUIT'), onclick: () => win.emit('filemanager:menu:quit')}
   ]);
 
-  const createEditMenu = async (item, isContextMenu) => {
-    const emitter = name => win.emit(name, item);
+  const createEditMenu = async (items, isContextMenu) => {
+    const emitter = name => win.emit(name, items);
+    const item = items[items.length - 1];
 
-    if (item && isSpecialFile(item.filename)) {
-      return [{
+    if (items.length === 1 && item && isSpecialFile(item.filename)) {
+			return [{
         label: _('LBL_GO'),
-        onclick: () => emitter('filemanager:navigate')
-      }];
-    }
+        onclick: () => emitter('filemanager:navigate'),
+			}];
+		}
 
-    const isValidFile = item && !isSpecialFile(item.filename);
-    const isDirectory = item && item.isDirectory;
+    const canDownload = items.some(
+			item => !item.isDirectory && !isSpecialFile(item.filename)
+		);
+		const hasValidFile = items.some(item => !isSpecialFile(item.filename));
+		const isDirectory = items.length === 1 && item.isDirectory;
 
     const openMenu = isDirectory ? [{
       label: _('LBL_GO'),
-      disabled: !item,
+      disabled: !items.length,
       onclick: () => emitter('filemanager:navigate')
     }] : [{
       label: _('LBL_OPEN'),
-      disabled: !item,
+      disabled: !items.length,
       onclick: () => emitter('filemanager:open')
     }, {
       label: __('LBL_OPEN_WITH'),
-      disabled: !item,
+      disabled: !items.length,
       onclick: () => emitter('filemanager:openWith')
     }];
 
     const clipboardMenu = [{
       label: _('LBL_COPY'),
-      disabled: !isValidFile,
+      disabled: !hasValidFile,
       onclick: () => emitter('filemanager:menu:copy')
     }, {
       label: _('LBL_CUT'),
-      disabled: !isValidFile,
+      disabled: !hasValidFile,
       onclick: () => emitter('filemanager:menu:cut')
     }];
 
@@ -563,7 +590,7 @@ const menuFactory = (core, proc, win) => {
     if (core.config('filemanager.disableDownload', false) !== true) {
       configuredItems.push({
         label: _('LBL_DOWNLOAD'),
-        disabled: !item || isDirectory || !isValidFile,
+        disabled: !canDownload,
         onclick: () => emitter('filemanager:menu:download')
       });
     }
@@ -572,12 +599,12 @@ const menuFactory = (core, proc, win) => {
       ...openMenu,
       {
         label: _('LBL_RENAME'),
-        disabled: !isValidFile,
+        disabled: !hasValidFile,
         onclick: () => emitter('filemanager:menu:rename')
       },
       {
         label: _('LBL_DELETE'),
-        disabled: !isValidFile,
+        disabled: !hasValidFile,
         onclick: () => emitter('filemanager:menu:delete')
       },
       ...clipboardMenu,
@@ -685,7 +712,6 @@ const createApplication = (core, proc) => {
   const createRows = listViewRowFactory(core, proc);
   const createMounts = mountViewRowsFactory(core);
   const {draggable} = core.make('osjs/dnd');
-  const statusMessage = formatStatusMessage(core);
 
   const initialState = {
     path: '',
@@ -705,7 +731,9 @@ const createApplication = (core, proc) => {
     }),
 
     fileview: listView.state({
-      columns: []
+      columns: [],
+      multiselect: true,
+			previousSelectedIndex: 0
     })
   };
 
@@ -742,18 +770,18 @@ const createApplication = (core, proc) => {
     setStatus: status => ({status}),
     setMinimalistic: minimalistic => ({minimalistic}),
     setList: ({list, path, selectFile}) => ({fileview, mountview}) => {
-      let selectedIndex;
+      let selectedIndex = [];
 
       if (selectFile) {
         const foundIndex = list.findIndex(file => file.filename === selectFile);
         if (foundIndex !== -1) {
-          selectedIndex = foundIndex;
+          selectedIndex = [foundIndex];
         }
       }
 
       return {
         path,
-        status: statusMessage(path, list),
+        status: formatStatusMessage(list),
         mountview: Object.assign({}, mountview, {
           rows: createMounts()
         }),
@@ -771,7 +799,10 @@ const createApplication = (core, proc) => {
 
     fileview: listView.actions({
       select: ({data}) => win.emit('filemanager:select', data),
-      activate: ({data}) => win.emit(`filemanager:${data.isFile ? 'open' : 'navigate'}`, data),
+      activate: ({data}) =>
+				data.forEach(item =>
+					win.emit(`filemanager:${item.isFile ? 'open' : 'navigate'}`, item)
+				),
       contextmenu: args => win.emit('filemanager:contextmenu', args),
       created: ({el, data}) => {
         if (data.isFile) {
@@ -793,7 +824,7 @@ const createApplication = (core, proc) => {
  */
 const createWindow = (core, proc) => {
   let wired;
-  const state = {currentFile: undefined, currentPath: undefined};
+  const state = {currentFile: [], currentPath: undefined};
   const {homePath, initialPath} = createInitialPaths(core, proc);
 
   const title = core.make('osjs/locale').translatableFlat(proc.metadata.title);
@@ -812,13 +843,29 @@ const createWindow = (core, proc) => {
   const onDrop = (...args) => vfs.drop(...args);
   const onHome = () => vfs.readdir(homePath, 'clear');
   const onNavigate = (...args) => vfs.readdir(...args);
-  const onSelectItem = file => (state.currentFile = file);
-  const onSelectStatus = file => win.emit('filemanager:status', formatFileMessage(file));
+  const onSelectItem = files => (state.currentFile = files);
+  const onSelectStatus = files => win.emit('filemanager:status', formatStatusMessage(files));
   const onContextMenu = ({ev, data}) => createMenu({ev, name: 'edit'}, data, true);
   const onReaddirRender = args => wired.setList(args);
   const onRefresh = (...args) => vfs.refresh(...args);
-  const onOpen = file => core.open(file, {useDefault: true});
-  const onOpenWith = file => core.open(file, {useDefault: true, forceDialog: true});
+  const onOpen = files => {
+		if (!Array.isArray(files)) {
+      files = [files];
+    }
+
+		return files.forEach(
+			file => core.open(file, {useDefault: true})
+		);
+	};
+	const onOpenWith = files => {
+		if (!Array.isArray(files)) {
+      files = [files];
+    }
+
+		return files.forEach(
+			file => core.open(file, {useDefault: true, forceDialog: true})
+		);
+	};
   const onHistoryPush = file => wired.history.push(file);
   const onHistoryClear = () => wired.history.clear();
   const onMenu = (props, args) => createMenu(props, args || state.currentFile);
@@ -829,11 +876,11 @@ const createWindow = (core, proc) => {
   const onMenuToggleMinimalistic = () => wired.toggleMinimalistic();
   const onMenuShowDate = () => setSetting('showDate', !proc.settings.showDate);
   const onMenuShowHidden = () => setSetting('showHiddenFiles', !proc.settings.showHiddenFiles);
-  const onMenuRename = file => dialog('rename', vfs.action, file);
-  const onMenuDelete = file => dialog('delete', vfs.action, file);
-  const onMenuDownload = (...args) => vfs.download(...args);
-  const onMenuCopy = item => clipboard.set(item);
-  const onMenuCut = item => clipboard.cut(item);
+  const onMenuRename = files => dialog('rename', vfs.action, files);
+  const onMenuDelete = files => dialog('delete', vfs.action, files);
+  const onMenuDownload = (files) => vfs.download(files);
+  const onMenuCopy = items => clipboard.set(items);
+  const onMenuCut = items => clipboard.cut(items);
   const onMenuPaste = () => clipboard.paste();
 
   return win

From 4b677b97ecf49ee5f6d181a4bf4be8591d82a9e0 Mon Sep 17 00:00:00 2001
From: Aaron Meese <ajmeese7@gmail.com>
Date: Wed, 10 Aug 2022 20:07:54 -0400
Subject: [PATCH 2/4] More accurate selection status messages

---
 index.js   | 17 ++++++++++++-----
 locales.js | 49 ++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 54 insertions(+), 12 deletions(-)

diff --git a/index.js b/index.js
index 32f4398..435a92f 100644
--- a/index.js
+++ b/index.js
@@ -141,11 +141,18 @@ const formatStatusMessage = (core, files) => {
 
   return (path, files) => {
     const directoryCount = getDirectoryCount(files);
-	  const fileCount = getFileCount(files);
-	  const totalSize = getTotalSize(files);
-
-    // TODO: Copy over new label messages with translation
-    return __('LBL_STATUS', directoryCount, fileCount, totalSize);
+    const fileCount = getFileCount(files);
+    const totalSize = getTotalSize(files);
+    const directoryCountMessage = `${directoryCount} ${__(directoryCount === 1 ? 'SINGLE_DIR' : 'MULTI_DIR')}`;
+    const fileCountMessage = `${fileCount} ${__(fileCount === 1 ? 'SINGLE_FILE' : 'MULTI_FILE')}`;
+
+    if (directoryCount > 0 && fileCount > 0) {
+      return __('LBL_DIR_AND_FILE_STATUS', directoryCountMessage, fileCountMessage, totalSize);
+    } else if (directoryCount > 0) {
+      return __('LBL_DIR_OR_FILE_STATUS', directoryCountMessage, totalSize);
+    } else {
+      return __('LBL_DIR_OR_FILE_STATUS', fileCountMessage, totalSize);
+    }
   };
 };
 
diff --git a/locales.js b/locales.js
index 5a92aa1..741cae7 100644
--- a/locales.js
+++ b/locales.js
@@ -3,7 +3,12 @@ export const en_EN = {
   LBL_MINIMALISTIC: 'Minimalistic',
   LBL_OPEN_WITH: 'Open with...',
   LBL_SHOW_DATE: 'Show date column',
-  LBL_STATUS: '{0} directories, {1} files, {2} bytes total',
+  LBL_DIR_AND_FILE_STATUS: '{0}, {1}, {2} bytes total',
+  LBL_DIR_OR_FILE_STATUS: '{0}, {1} bytes total',
+  SINGLE_DIR: 'directory',
+  MULTI_DIR: 'directories',
+  SINGLE_FILE: 'file',
+  MULTI_FILE: 'files',
   LBL_DATE: 'Date', // FIXME: Move to client
   LBL_LOADING: 'Loading {0}',
   DIALOG_MKDIR_MESSAGE: 'Create new directory',
@@ -25,7 +30,12 @@ export const sv_SE = {
   LBL_MINIMALISTIC: 'Minimalistic',
   LBL_OPEN_WITH: 'Öppna med...',
   LBL_SHOW_DATE: 'Visa datumkolumn',
-  LBL_STATUS: '{0} kataloger, {1} filer, {2} byte totalt',
+  LBL_DIR_AND_FILE_STATUS: '{0}, {1}, {2} byte totalt',
+  LBL_DIR_OR_FILE_STATUS: '{0}, {1} byte totalt',
+  SINGLE_DIR: 'katalog',
+  MULTI_DIR: 'kataloger',
+  SINGLE_FILE: 'fil',
+  MULTI_FILE: 'filer',
   LBL_DATE: 'Datum', // FIXME: Move to client
   LBL_LOADING: 'Laddar {0}',
   DIALOG_MKDIR_MESSAGE: 'Skapa ny katalog',
@@ -46,7 +56,12 @@ export const nb_NO = {
   LBL_MINIMALISTIC: 'Minimalistisk',
   LBL_OPEN_WITH: 'Åpne med...',
   LBL_SHOW_DATE: 'Vis dato kolonne',
-  LBL_STATUS: '{0} mapper, {1} filer, {2} bytes totalt',
+  LBL_DIR_AND_FILE_STATUS: '{0}, {1}, {2} bytes totalt',
+  LBL_DIR_OR_FILE_STATUS: '{0}, {1} bytes totalt',
+  SINGLE_DIR: 'mappe',
+  MULTI_DIR: 'mapper',
+  SINGLE_FILE: 'fil',
+  MULTI_FILE: 'filer',
   LBL_DATE: 'Dato',
   LBL_LOADING: 'Laster {0}',
   DIALOG_MKDIR_MESSAGE: 'Lag ny mappe',
@@ -67,7 +82,12 @@ export const vi_VN = {
   LBL_MINIMALISTIC: 'Tối giản',
   LBL_OPEN_WITH: 'Mở bằng...',
   LBL_SHOW_DATE: 'Hiện cột thời gian',
-  LBL_STATUS: '{0} thư mục, {1} tập tin, tổng dung lượng {2} bytes',
+  LBL_DIR_AND_FILE_STATUS: '{0}, {1}, tổng dung lượng {2} bytes',
+  LBL_DIR_OR_FILE_STATUS: '{0}, tổng dung lượng {1} bytes',
+  SINGLE_DIR: 'thư mục',
+  MULTI_DIR: 'thư mục',
+  SINGLE_FILE: 'tập tin',
+  MULTI_FILE: 'các tập tin',
   LBL_DATE: 'Thời gian',
   LBL_LOADING: 'Đang tải {0}',
   DIALOG_MKDIR_MESSAGE: 'Tạo thư mục mới',
@@ -88,7 +108,12 @@ export const pt_BR = {
   LBL_MINIMALISTIC: 'Minimalista',
   LBL_OPEN_WITH: 'Abrir com...',
   LBL_SHOW_DATE: 'Mostrar coluna Data',
-  LBL_STATUS: '{0} diretórios, {1} arquivos, {2} bytes no total',
+  LBL_DIR_AND_FILE_STATUS: '{0}, {1}, {2} bytes no total',
+  LBL_DIR_OR_FILE_STATUS: '{0}, {1} bytes no total',
+  SINGLE_DIR: 'diretório',
+  MULTI_DIR: 'diretórios',
+  SINGLE_FILE: 'arquivo',
+  MULTI_FILE: 'arquivos',
   LBL_DATE: 'Data', // FIXME: Move to client
   LBL_LOADING: 'Carregando {0}',
   DIALOG_MKDIR_MESSAGE: 'Criar novo diretório',
@@ -109,7 +134,12 @@ export const fr_FR = {
   LBL_MINIMALISTIC: 'Minimaliste',
   LBL_OPEN_WITH: 'Ouvrir avec...',
   LBL_SHOW_DATE: 'Affichier la colonne date',
-  LBL_STATUS: '{0} dossiers, {1} fichiers, {2} bytes au total',
+  LBL_DIR_AND_FILE_STATUS: '{0}, {1}, {2} bytes au total',
+  LBL_DIR_OR_FILE_STATUS: '{0}, {1} bytes au total',
+  SINGLE_DIR: 'dossier',
+  MULTI_DIR: 'dossiers',
+  SINGLE_FILE: 'dossier',
+  MULTI_FILE: 'des dossiers',
   LBL_DATE: 'Date', // FIXME: Move to client
   LBL_LOADING: 'Chargement en cours {0}',
   DIALOG_MKDIR_MESSAGE: 'Créer nouveau dossier',
@@ -130,7 +160,12 @@ export const tr_TR = {
   LBL_MINIMALISTIC: 'Minimalist',
   LBL_OPEN_WITH: 'Şununla aç:',
   LBL_SHOW_DATE: 'Tarih sütununu göster',
-  LBL_STATUS: 'toplamda {0} dizin, {1} dosya, {2} byte var',
+  LBL_DIR_AND_FILE_STATUS: 'toplamda {0}, {1}, {2} byte var',
+  LBL_DIR_OR_FILE_STATUS: 'toplamda {0}, {1} byte var',
+  SINGLE_DIR: 'dizin',
+  MULTI_DIR: 'dizinler',
+  SINGLE_FILE: 'dosya',
+  MULTI_FILE: 'dosyalar',
   LBL_DATE: 'Tarih', // FIXME: Move to client
   LBL_LOADING: 'Yükleniyor {0}',
   DIALOG_MKDIR_MESSAGE: 'Yeni dizin oluştur',

From 7b2b4dfba510e67da1fc9296e753f4932496c524 Mon Sep 17 00:00:00 2001
From: Aaron Meese <ajmeese7@gmail.com>
Date: Wed, 10 Aug 2022 20:10:33 -0400
Subject: [PATCH 3/4] Resolved eslint errors

---
 index.js | 50 +++++++++++++++++++++++++-------------------------
 1 file changed, 25 insertions(+), 25 deletions(-)

diff --git a/index.js b/index.js
index 435a92f..091df5f 100644
--- a/index.js
+++ b/index.js
@@ -126,11 +126,11 @@ const createInitialPaths = (core, proc) => {
 };
 
 const getDirectoryCount = files =>
-	files.filter(file => file.isDirectory).length;
+  files.filter(file => file.isDirectory).length;
 const getFileCount = files =>
-	files.filter(file => !file.isDirectory).length;
+  files.filter(file => !file.isDirectory).length;
 const getTotalSize = files =>
-	files.reduce((total, file) => total + (file.size || 0), 0);
+  files.reduce((total, file) => total + (file.size || 0), 0);
 
 /**
  * Formats directory status message
@@ -547,17 +547,17 @@ const menuFactory = (core, proc, win) => {
     const item = items[items.length - 1];
 
     if (items.length === 1 && item && isSpecialFile(item.filename)) {
-			return [{
+      return [{
         label: _('LBL_GO'),
         onclick: () => emitter('filemanager:navigate'),
-			}];
-		}
+      }];
+    }
 
     const canDownload = items.some(
-			item => !item.isDirectory && !isSpecialFile(item.filename)
-		);
-		const hasValidFile = items.some(item => !isSpecialFile(item.filename));
-		const isDirectory = items.length === 1 && item.isDirectory;
+      item => !item.isDirectory && !isSpecialFile(item.filename)
+    );
+    const hasValidFile = items.some(item => !isSpecialFile(item.filename));
+    const isDirectory = items.length === 1 && item.isDirectory;
 
     const openMenu = isDirectory ? [{
       label: _('LBL_GO'),
@@ -740,7 +740,7 @@ const createApplication = (core, proc) => {
     fileview: listView.state({
       columns: [],
       multiselect: true,
-			previousSelectedIndex: 0
+      previousSelectedIndex: 0
     })
   };
 
@@ -807,9 +807,9 @@ const createApplication = (core, proc) => {
     fileview: listView.actions({
       select: ({data}) => win.emit('filemanager:select', data),
       activate: ({data}) =>
-				data.forEach(item =>
-					win.emit(`filemanager:${item.isFile ? 'open' : 'navigate'}`, item)
-				),
+        data.forEach(item =>
+          win.emit(`filemanager:${item.isFile ? 'open' : 'navigate'}`, item)
+        ),
       contextmenu: args => win.emit('filemanager:contextmenu', args),
       created: ({el, data}) => {
         if (data.isFile) {
@@ -856,23 +856,23 @@ const createWindow = (core, proc) => {
   const onReaddirRender = args => wired.setList(args);
   const onRefresh = (...args) => vfs.refresh(...args);
   const onOpen = files => {
-		if (!Array.isArray(files)) {
+    if (!Array.isArray(files)) {
       files = [files];
     }
 
-		return files.forEach(
-			file => core.open(file, {useDefault: true})
-		);
-	};
-	const onOpenWith = files => {
-		if (!Array.isArray(files)) {
+    return files.forEach(
+      file => core.open(file, {useDefault: true})
+    );
+  };
+  const onOpenWith = files => {
+    if (!Array.isArray(files)) {
       files = [files];
     }
 
-		return files.forEach(
-			file => core.open(file, {useDefault: true, forceDialog: true})
-		);
-	};
+    return files.forEach(
+      file => core.open(file, {useDefault: true, forceDialog: true})
+    );
+  };
   const onHistoryPush = file => wired.history.push(file);
   const onHistoryClear = () => wired.history.clear();
   const onMenu = (props, args) => createMenu(props, args || state.currentFile);

From 8df52d07ddc4c35328c7f9ef46d712b9a130085f Mon Sep 17 00:00:00 2001
From: Aaron Meese <ajmeese7@gmail.com>
Date: Wed, 10 Aug 2022 20:16:49 -0400
Subject: [PATCH 4/4] Improved dialog messages

---
 index.js   |  4 ++--
 locales.js | 26 +++++++++++++-------------
 2 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/index.js b/index.js
index 091df5f..760662d 100644
--- a/index.js
+++ b/index.js
@@ -370,7 +370,7 @@ const vfsActionFactory = (core, proc, win, dialog, state) => {
       .catch(error => dialog('error', error, __('MSG_UPLOAD_ERROR')));
   });
 
-  const paste = (move, currentPath) => ({item, callback}) => {
+  const paste = (move, currentPath) => ({items, callback}) => {
     const promises = items.map(item => {
       const dest = {
         path: pathJoin(currentPath.path, item.filename)
@@ -468,7 +468,7 @@ const dialogFactory = (core, proc, win) => {
     })));
 
   const deleteDialog = (action, files) => dialog('confirm', {
-    message: __('DIALOG_DELETE_MESSAGE', file.filename),
+    message: __('DIALOG_DELETE_MESSAGE', files.length),
   }, usingPositiveButton(() => {
     action(
       () => Promise.all(
diff --git a/locales.js b/locales.js
index 741cae7..7070914 100644
--- a/locales.js
+++ b/locales.js
@@ -13,8 +13,8 @@ export const en_EN = {
   LBL_LOADING: 'Loading {0}',
   DIALOG_MKDIR_MESSAGE: 'Create new directory',
   DIALOG_MKDIR_PLACEHOLDER: 'New directory',
-  DIALOG_RENAME_MESSAGE: 'Rename {0} ?',
-  DIALOG_DELETE_MESSAGE: 'Delete {0} ?',
+  DIALOG_RENAME_MESSAGE: 'Rename {0}?',
+  DIALOG_DELETE_MESSAGE: 'Delete {0} file(s)?',
   DIALOG_PROGRESS_MESSAGE: 'Uploading {0}...',
   MSG_ERROR: 'An error occurred',
   MSG_UPLOAD_ERROR: 'Failed to upload file(s)',
@@ -40,8 +40,8 @@ export const sv_SE = {
   LBL_LOADING: 'Laddar {0}',
   DIALOG_MKDIR_MESSAGE: 'Skapa ny katalog',
   DIALOG_MKDIR_PLACEHOLDER: 'Ny katalog',
-  DIALOG_RENAME_MESSAGE: 'Döp om {0} ?',
-  DIALOG_DELETE_MESSAGE: 'Radera {0} ?',
+  DIALOG_RENAME_MESSAGE: 'Döp om {0}?',
+  DIALOG_DELETE_MESSAGE: 'Radera {0} fil(er)?',
   MSG_ERROR: 'Ett fel uppstod',
   MSG_UPLOAD_ERROR: 'Det gick inte att ladda upp filer(na)',
   MSG_READDIR_ERROR: 'Ett fel uppstod när katalogen lästes: {0}',
@@ -66,8 +66,8 @@ export const nb_NO = {
   LBL_LOADING: 'Laster {0}',
   DIALOG_MKDIR_MESSAGE: 'Lag ny mappe',
   DIALOG_MKDIR_PLACEHOLDER: 'Ny mappe',
-  DIALOG_RENAME_MESSAGE: 'Omdøpe {0} ?',
-  DIALOG_DELETE_MESSAGE: 'Slette {0} ?',
+  DIALOG_RENAME_MESSAGE: 'Omdøpe {0}?',
+  DIALOG_DELETE_MESSAGE: 'Slette {0} fil(er)?',
   MSG_ERROR: 'En feil oppstod',
   MSG_UPLOAD_ERROR: 'Feil under opplasting av fil(er)',
   MSG_READDIR_ERROR: 'En feil oppstod under lesing av mappe: {0}',
@@ -92,8 +92,8 @@ export const vi_VN = {
   LBL_LOADING: 'Đang tải {0}',
   DIALOG_MKDIR_MESSAGE: 'Tạo thư mục mới',
   DIALOG_MKDIR_PLACEHOLDER: 'Thư mục mới',
-  DIALOG_RENAME_MESSAGE: 'Đổi tên {0} ?',
-  DIALOG_DELETE_MESSAGE: 'Xoá {0} ?',
+  DIALOG_RENAME_MESSAGE: 'Đổi tên {0}?',
+  DIALOG_DELETE_MESSAGE: 'Xoá {0} (các) tập tin?',
   MSG_ERROR: 'Đã xảy ra lỗi',
   MSG_UPLOAD_ERROR: 'Không thể tải lên tập tin',
   MSG_READDIR_ERROR: 'Đã xảy ra lỗi trong khi đọc thư mục: {0}',
@@ -118,8 +118,8 @@ export const pt_BR = {
   LBL_LOADING: 'Carregando {0}',
   DIALOG_MKDIR_MESSAGE: 'Criar novo diretório',
   DIALOG_MKDIR_PLACEHOLDER: 'Novo diretório',
-  DIALOG_RENAME_MESSAGE: 'Renomear {0} ?',
-  DIALOG_DELETE_MESSAGE: 'Deletar {0} ?',
+  DIALOG_RENAME_MESSAGE: 'Renomear {0}?',
+  DIALOG_DELETE_MESSAGE: 'Deletar {0} arquivo(s)?',
   MSG_ERROR: 'Um erro ocorreu',
   MSG_UPLOAD_ERROR: 'Falha ao fazer upload do(s) arquivo(s)',
   MSG_READDIR_ERROR: 'Um erro ocorreu ao ler o diretório: {0}',
@@ -144,8 +144,8 @@ export const fr_FR = {
   LBL_LOADING: 'Chargement en cours {0}',
   DIALOG_MKDIR_MESSAGE: 'Créer nouveau dossier',
   DIALOG_MKDIR_PLACEHOLDER: 'Nouveau dossier',
-  DIALOG_RENAME_MESSAGE: 'Renommer {0} ?',
-  DIALOG_DELETE_MESSAGE: 'Supprimer {0} ?',
+  DIALOG_RENAME_MESSAGE: 'Renommer {0}?',
+  DIALOG_DELETE_MESSAGE: 'Supprimer {0} (des) dossiers?',
   MSG_ERROR: 'Une erreur est survenue',
   MSG_UPLOAD_ERROR: 'Echec du chargement du(des) fichier(s)',
   MSG_READDIR_ERROR: 'Une erreur est survenue lors de la lecture du répertoire : {0}',
@@ -171,7 +171,7 @@ export const tr_TR = {
   DIALOG_MKDIR_MESSAGE: 'Yeni dizin oluştur',
   DIALOG_MKDIR_PLACEHOLDER: 'Yeni dizin',
   DIALOG_RENAME_MESSAGE: '{0} ismi değişsin mi?',
-  DIALOG_DELETE_MESSAGE: '{0} silinsin mi?',
+  DIALOG_DELETE_MESSAGE: '{0} dosya(lar) silinsin mi?',
   MSG_ERROR: 'Bir hata oldu',
   MSG_UPLOAD_ERROR: 'Dosya(lar)ın yüklenmesi işlemi başarısız',
   MSG_READDIR_ERROR: 'Belirtilen dizin okunurken bir hata oluştu: {0}',