@@ -125,23 +125,26 @@ const createInitialPaths = (core, proc) => {
125
125
return { homePath, initialPath} ;
126
126
} ;
127
127
128
- /**
129
- * Formats file status message
130
- */
131
- const formatFileMessage = file => `${ file . filename } (${ file . size } bytes)` ;
128
+ const getDirectoryCount = files =>
129
+ files . filter ( file => file . isDirectory ) . length ;
130
+ const getFileCount = files =>
131
+ files . filter ( file => ! file . isDirectory ) . length ;
132
+ const getTotalSize = files =>
133
+ files . reduce ( ( total , file ) => total + ( file . size || 0 ) , 0 ) ;
132
134
133
135
/**
134
136
* Formats directory status message
135
137
*/
136
- const formatStatusMessage = ( core ) => {
138
+ const formatStatusMessage = ( core , files ) => {
137
139
const { translatable} = core . make ( 'osjs/locale' ) ;
138
140
const __ = translatable ( translations ) ;
139
141
140
142
return ( path , files ) => {
141
- const directoryCount = files . filter ( f => f . isDirectory ) . length ;
142
- const fileCount = files . filter ( f => ! f . isDirectory ) . length ;
143
- const totalSize = files . reduce ( ( t , f ) => t + ( f . size || 0 ) , 0 ) ;
143
+ const directoryCount = getDirectoryCount ( files ) ;
144
+ const fileCount = getFileCount ( files ) ;
145
+ const totalSize = getTotalSize ( files ) ;
144
146
147
+ // TODO: Copy over new label messages with translation
145
148
return __ ( 'LBL_STATUS' , directoryCount , fileCount , totalSize ) ;
146
149
} ;
147
150
} ;
@@ -318,6 +321,8 @@ const vfsActionFactory = (core, proc, win, dialog, state) => {
318
321
const readdir = async ( dir , history , selectFile ) => {
319
322
if ( win . getState ( 'loading' ) ) {
320
323
return ;
324
+ } else if ( Array . isArray ( dir ) ) {
325
+ dir = dir [ 0 ] ;
321
326
}
322
327
323
328
try {
@@ -347,7 +352,7 @@ const vfsActionFactory = (core, proc, win, dialog, state) => {
347
352
} catch ( error ) {
348
353
dialog ( 'error' , error , __ ( 'MSG_READDIR_ERROR' , dir . path ) ) ;
349
354
} finally {
350
- state . currentFile = undefined ;
355
+ state . currentFile = [ ] ;
351
356
win . setState ( 'loading' , false ) ;
352
357
}
353
358
} ;
@@ -359,25 +364,32 @@ const vfsActionFactory = (core, proc, win, dialog, state) => {
359
364
} ) ;
360
365
361
366
const paste = ( move , currentPath ) => ( { item, callback} ) => {
362
- const dest = { path : pathJoin ( currentPath . path , item . filename ) } ;
367
+ const promises = items . map ( item => {
368
+ const dest = {
369
+ path : pathJoin ( currentPath . path , item . filename )
370
+ } ;
363
371
364
- const fn = move
365
- ? vfs . move ( item , dest , { pid : proc . pid } )
366
- : vfs . copy ( item , dest , { pid : proc . pid } ) ;
372
+ return move
373
+ ? vfs . move ( item , dest , { pid : proc . pid } )
374
+ : vfs . copy ( item , dest , { pid : proc . pid } ) ;
375
+ } ) ;
367
376
368
- return fn
369
- . then ( ( ) => {
377
+ return Promise
378
+ . all ( promises )
379
+ . then ( results => {
370
380
refresh ( true ) ;
371
381
372
382
if ( typeof callback === 'function' ) {
373
383
callback ( ) ;
374
384
}
385
+
386
+ return results ;
375
387
} )
376
388
. catch ( error => dialog ( 'error' , error , __ ( 'MSG_PASTE_ERROR' ) ) ) ;
377
389
} ;
378
390
379
391
return {
380
- download : file => vfs . download ( file ) ,
392
+ download : files => files . forEach ( file => vfs . download ( file ) ) ,
381
393
upload,
382
394
refresh,
383
395
action,
@@ -430,23 +442,34 @@ const dialogFactory = (core, proc, win) => {
430
442
value : __ ( 'DIALOG_MKDIR_PLACEHOLDER' )
431
443
} , usingPositiveButton ( value => {
432
444
const newPath = pathJoin ( currentPath . path , value ) ;
433
- action ( ( ) => vfs . mkdir ( { path : newPath } , { pid : proc . pid } ) , value , __ ( 'MSG_MKDIR_ERROR' ) ) ;
445
+ action (
446
+ ( ) => vfs . mkdir ( { path : newPath } , { pid : proc . pid } ) ,
447
+ value ,
448
+ __ ( 'MSG_MKDIR_ERROR' )
449
+ ) ;
434
450
} ) ) ;
435
451
436
- const renameDialog = ( action , file ) => dialog ( 'prompt' , {
437
- message : __ ( 'DIALOG_RENAME_MESSAGE' , file . filename ) ,
438
- value : file . filename
439
- } , usingPositiveButton ( value => {
440
- const idx = file . path . lastIndexOf ( file . filename ) ;
441
- const newPath = file . path . substr ( 0 , idx ) + value ;
452
+ const renameDialog = ( action , files ) => files . forEach ( file =>
453
+ dialog ( 'prompt' , {
454
+ message : __ ( 'DIALOG_RENAME_MESSAGE' , file . filename ) ,
455
+ value : file . filename
456
+ } , usingPositiveButton ( value => {
457
+ const idx = file . path . lastIndexOf ( file . filename ) ;
458
+ const newPath = file . path . substr ( 0 , idx ) + value ;
442
459
443
- action ( ( ) => vfs . rename ( file , { path : newPath } ) , value , __ ( 'MSG_RENAME_ERROR' ) ) ;
444
- } ) ) ;
460
+ action ( ( ) => vfs . rename ( file , { path : newPath } ) , value , __ ( 'MSG_RENAME_ERROR' ) ) ;
461
+ } ) ) ) ;
445
462
446
- const deleteDialog = ( action , file ) => dialog ( 'confirm' , {
463
+ const deleteDialog = ( action , files ) => dialog ( 'confirm' , {
447
464
message : __ ( 'DIALOG_DELETE_MESSAGE' , file . filename ) ,
448
465
} , usingPositiveButton ( ( ) => {
449
- action ( ( ) => vfs . unlink ( file , { pid : proc . pid } ) , true , __ ( 'MSG_DELETE_ERROR' ) ) ;
466
+ action (
467
+ ( ) => Promise . all (
468
+ files . map ( file => vfs . unlink ( file , { pid : proc . pid } ) )
469
+ ) ,
470
+ true ,
471
+ __ ( 'MSG_DELETE_ERROR' )
472
+ ) ;
450
473
} ) ) ;
451
474
452
475
const progressDialog = ( file ) => dialog ( 'progress' , {
@@ -512,40 +535,44 @@ const menuFactory = (core, proc, win) => {
512
535
{ label : _ ( 'LBL_QUIT' ) , onclick : ( ) => win . emit ( 'filemanager:menu:quit' ) }
513
536
] ) ;
514
537
515
- const createEditMenu = async ( item , isContextMenu ) => {
516
- const emitter = name => win . emit ( name , item ) ;
538
+ const createEditMenu = async ( items , isContextMenu ) => {
539
+ const emitter = name => win . emit ( name , items ) ;
540
+ const item = items [ items . length - 1 ] ;
517
541
518
- if ( item && isSpecialFile ( item . filename ) ) {
519
- return [ {
542
+ if ( items . length === 1 && item && isSpecialFile ( item . filename ) ) {
543
+ return [ {
520
544
label : _ ( 'LBL_GO' ) ,
521
- onclick : ( ) => emitter ( 'filemanager:navigate' )
522
- } ] ;
523
- }
545
+ onclick : ( ) => emitter ( 'filemanager:navigate' ) ,
546
+ } ] ;
547
+ }
524
548
525
- const isValidFile = item && ! isSpecialFile ( item . filename ) ;
526
- const isDirectory = item && item . isDirectory ;
549
+ const canDownload = items . some (
550
+ item => ! item . isDirectory && ! isSpecialFile ( item . filename )
551
+ ) ;
552
+ const hasValidFile = items . some ( item => ! isSpecialFile ( item . filename ) ) ;
553
+ const isDirectory = items . length === 1 && item . isDirectory ;
527
554
528
555
const openMenu = isDirectory ? [ {
529
556
label : _ ( 'LBL_GO' ) ,
530
- disabled : ! item ,
557
+ disabled : ! items . length ,
531
558
onclick : ( ) => emitter ( 'filemanager:navigate' )
532
559
} ] : [ {
533
560
label : _ ( 'LBL_OPEN' ) ,
534
- disabled : ! item ,
561
+ disabled : ! items . length ,
535
562
onclick : ( ) => emitter ( 'filemanager:open' )
536
563
} , {
537
564
label : __ ( 'LBL_OPEN_WITH' ) ,
538
- disabled : ! item ,
565
+ disabled : ! items . length ,
539
566
onclick : ( ) => emitter ( 'filemanager:openWith' )
540
567
} ] ;
541
568
542
569
const clipboardMenu = [ {
543
570
label : _ ( 'LBL_COPY' ) ,
544
- disabled : ! isValidFile ,
571
+ disabled : ! hasValidFile ,
545
572
onclick : ( ) => emitter ( 'filemanager:menu:copy' )
546
573
} , {
547
574
label : _ ( 'LBL_CUT' ) ,
548
- disabled : ! isValidFile ,
575
+ disabled : ! hasValidFile ,
549
576
onclick : ( ) => emitter ( 'filemanager:menu:cut' )
550
577
} ] ;
551
578
@@ -563,7 +590,7 @@ const menuFactory = (core, proc, win) => {
563
590
if ( core . config ( 'filemanager.disableDownload' , false ) !== true ) {
564
591
configuredItems . push ( {
565
592
label : _ ( 'LBL_DOWNLOAD' ) ,
566
- disabled : ! item || isDirectory || ! isValidFile ,
593
+ disabled : ! canDownload ,
567
594
onclick : ( ) => emitter ( 'filemanager:menu:download' )
568
595
} ) ;
569
596
}
@@ -572,12 +599,12 @@ const menuFactory = (core, proc, win) => {
572
599
...openMenu ,
573
600
{
574
601
label : _ ( 'LBL_RENAME' ) ,
575
- disabled : ! isValidFile ,
602
+ disabled : ! hasValidFile ,
576
603
onclick : ( ) => emitter ( 'filemanager:menu:rename' )
577
604
} ,
578
605
{
579
606
label : _ ( 'LBL_DELETE' ) ,
580
- disabled : ! isValidFile ,
607
+ disabled : ! hasValidFile ,
581
608
onclick : ( ) => emitter ( 'filemanager:menu:delete' )
582
609
} ,
583
610
...clipboardMenu ,
@@ -685,7 +712,6 @@ const createApplication = (core, proc) => {
685
712
const createRows = listViewRowFactory ( core , proc ) ;
686
713
const createMounts = mountViewRowsFactory ( core ) ;
687
714
const { draggable} = core . make ( 'osjs/dnd' ) ;
688
- const statusMessage = formatStatusMessage ( core ) ;
689
715
690
716
const initialState = {
691
717
path : '' ,
@@ -705,7 +731,9 @@ const createApplication = (core, proc) => {
705
731
} ) ,
706
732
707
733
fileview : listView . state ( {
708
- columns : [ ]
734
+ columns : [ ] ,
735
+ multiselect : true ,
736
+ previousSelectedIndex : 0
709
737
} )
710
738
} ;
711
739
@@ -742,18 +770,18 @@ const createApplication = (core, proc) => {
742
770
setStatus : status => ( { status} ) ,
743
771
setMinimalistic : minimalistic => ( { minimalistic} ) ,
744
772
setList : ( { list, path, selectFile} ) => ( { fileview, mountview} ) => {
745
- let selectedIndex ;
773
+ let selectedIndex = [ ] ;
746
774
747
775
if ( selectFile ) {
748
776
const foundIndex = list . findIndex ( file => file . filename === selectFile ) ;
749
777
if ( foundIndex !== - 1 ) {
750
- selectedIndex = foundIndex ;
778
+ selectedIndex = [ foundIndex ] ;
751
779
}
752
780
}
753
781
754
782
return {
755
783
path,
756
- status : statusMessage ( path , list ) ,
784
+ status : formatStatusMessage ( list ) ,
757
785
mountview : Object . assign ( { } , mountview , {
758
786
rows : createMounts ( )
759
787
} ) ,
@@ -771,7 +799,10 @@ const createApplication = (core, proc) => {
771
799
772
800
fileview : listView . actions ( {
773
801
select : ( { data} ) => win . emit ( 'filemanager:select' , data ) ,
774
- activate : ( { data} ) => win . emit ( `filemanager:${ data . isFile ? 'open' : 'navigate' } ` , data ) ,
802
+ activate : ( { data} ) =>
803
+ data . forEach ( item =>
804
+ win . emit ( `filemanager:${ item . isFile ? 'open' : 'navigate' } ` , item )
805
+ ) ,
775
806
contextmenu : args => win . emit ( 'filemanager:contextmenu' , args ) ,
776
807
created : ( { el, data} ) => {
777
808
if ( data . isFile ) {
@@ -793,7 +824,7 @@ const createApplication = (core, proc) => {
793
824
*/
794
825
const createWindow = ( core , proc ) => {
795
826
let wired ;
796
- const state = { currentFile : undefined , currentPath : undefined } ;
827
+ const state = { currentFile : [ ] , currentPath : undefined } ;
797
828
const { homePath, initialPath} = createInitialPaths ( core , proc ) ;
798
829
799
830
const title = core . make ( 'osjs/locale' ) . translatableFlat ( proc . metadata . title ) ;
@@ -812,13 +843,29 @@ const createWindow = (core, proc) => {
812
843
const onDrop = ( ...args ) => vfs . drop ( ...args ) ;
813
844
const onHome = ( ) => vfs . readdir ( homePath , 'clear' ) ;
814
845
const onNavigate = ( ...args ) => vfs . readdir ( ...args ) ;
815
- const onSelectItem = file => ( state . currentFile = file ) ;
816
- const onSelectStatus = file => win . emit ( 'filemanager:status' , formatFileMessage ( file ) ) ;
846
+ const onSelectItem = files => ( state . currentFile = files ) ;
847
+ const onSelectStatus = files => win . emit ( 'filemanager:status' , formatStatusMessage ( files ) ) ;
817
848
const onContextMenu = ( { ev, data} ) => createMenu ( { ev, name : 'edit' } , data , true ) ;
818
849
const onReaddirRender = args => wired . setList ( args ) ;
819
850
const onRefresh = ( ...args ) => vfs . refresh ( ...args ) ;
820
- const onOpen = file => core . open ( file , { useDefault : true } ) ;
821
- const onOpenWith = file => core . open ( file , { useDefault : true , forceDialog : true } ) ;
851
+ const onOpen = files => {
852
+ if ( ! Array . isArray ( files ) ) {
853
+ files = [ files ] ;
854
+ }
855
+
856
+ return files . forEach (
857
+ file => core . open ( file , { useDefault : true } )
858
+ ) ;
859
+ } ;
860
+ const onOpenWith = files => {
861
+ if ( ! Array . isArray ( files ) ) {
862
+ files = [ files ] ;
863
+ }
864
+
865
+ return files . forEach (
866
+ file => core . open ( file , { useDefault : true , forceDialog : true } )
867
+ ) ;
868
+ } ;
822
869
const onHistoryPush = file => wired . history . push ( file ) ;
823
870
const onHistoryClear = ( ) => wired . history . clear ( ) ;
824
871
const onMenu = ( props , args ) => createMenu ( props , args || state . currentFile ) ;
@@ -829,11 +876,11 @@ const createWindow = (core, proc) => {
829
876
const onMenuToggleMinimalistic = ( ) => wired . toggleMinimalistic ( ) ;
830
877
const onMenuShowDate = ( ) => setSetting ( 'showDate' , ! proc . settings . showDate ) ;
831
878
const onMenuShowHidden = ( ) => setSetting ( 'showHiddenFiles' , ! proc . settings . showHiddenFiles ) ;
832
- const onMenuRename = file => dialog ( 'rename' , vfs . action , file ) ;
833
- const onMenuDelete = file => dialog ( 'delete' , vfs . action , file ) ;
834
- const onMenuDownload = ( ... args ) => vfs . download ( ... args ) ;
835
- const onMenuCopy = item => clipboard . set ( item ) ;
836
- const onMenuCut = item => clipboard . cut ( item ) ;
879
+ const onMenuRename = files => dialog ( 'rename' , vfs . action , files ) ;
880
+ const onMenuDelete = files => dialog ( 'delete' , vfs . action , files ) ;
881
+ const onMenuDownload = ( files ) => vfs . download ( files ) ;
882
+ const onMenuCopy = items => clipboard . set ( items ) ;
883
+ const onMenuCut = items => clipboard . cut ( items ) ;
837
884
const onMenuPaste = ( ) => clipboard . paste ( ) ;
838
885
839
886
return win
0 commit comments