@@ -7757,6 +7757,7 @@ function rcube_webmail() {
77577757 id_encode : this . html_identifier_encode ,
77587758 id_decode : this . html_identifier_decode ,
77597759 searchbox : '#foldersearch' ,
7760+ sortable : true ,
77607761 } ) ;
77617762
77627763 this . subscription_list
@@ -7773,59 +7774,74 @@ function rcube_webmail() {
77737774 if ( p . query ) {
77747775 ref . subscription_select ( ) ;
77757776 }
7776- } )
7777- . draggable ( { cancel : 'li.mailbox.root,input,div.treetoggle,.custom-control' } )
7778- . droppable ( {
7779- // @todo : find better way, accept callback is executed for every folder
7780- // on the list when dragging starts (and stops), this is slow, but
7781- // I didn't find a method to check droptarget on over event
7782- accept : function ( node ) {
7783- // Break on .is-being-sorted to enable sorting.
7784- if ( ! node . is ( '.mailbox' ) || node . is ( '.is-being-sorted' ) ) {
7785- return false ;
7786- }
7787-
7788- var source_folder = ref . folder_id2name ( node . attr ( 'id' ) ) ,
7789- dest_folder = ref . folder_id2name ( this . id ) ,
7790- source = ref . env . subscriptionrows [ source_folder ] ,
7791- dest = ref . env . subscriptionrows [ dest_folder ] ;
7792-
7793- return source && ! source [ 2 ]
7794- && dest_folder != source_folder . replace ( ref . last_sub_rx , '' )
7795- && ! dest_folder . startsWith ( source_folder + ref . env . delimiter ) ;
7796- } ,
7797- drop : function ( e , ui ) {
7798- var source = ref . folder_id2name ( ui . draggable . attr ( 'id' ) ) ,
7799- dest = ref . folder_id2name ( this . id ) ;
7800-
7801- ref . subscription_move_folder ( source , dest ) ;
7802- ref . make_folder_lists_sortable ( ) ;
7803- } ,
78047777 } ) ;
78057778
78067779 this . make_folder_lists_sortable ( ) ;
78077780 } ;
78087781
7782+ // TODO: In the receive callback, can we wait for the confirmation dialog without introducing async/await and Promises?
7783+ // TODO: figure out if the item was moved between protected folders, which is not allowed.
7784+ // TODO: save only if the list is different than at start
7785+ // TODO: Fix the styling (padding)
78097786 this . make_folder_lists_sortable = ( ) => {
7810- var sortableHandle = $ ( '<div>' ) . addClass ( 'sortable-handle' ) ;
7811- // Destroy and re-create all sortable lists because this gets called in scenarios, in which new lists might
7812- // exist (e.g. if a folder was moved into another one that previously didn't have child-folders).
7813- $ ( 'ul.ui-sortable' ) . sortable ( 'destroy' ) ;
7814- // (Re-)Add handle icons to every sortable folder.
7815- $ ( '.sortable-handle' ) . remove ( ) ;
7816- $ ( 'li.mailbox:not(.protected) > .custom-switch' , this . subscription_list . container ) . prepend ( sortableHandle ) ;
7817- // (Re-)create the sorting.
7818- $ ( this . subscription_list . container ) . parent ( ) . find ( 'ul' ) . each ( ( _i , el ) => {
7819- $ ( el ) . sortable ( {
7820- axis : 'y' ,
7821- containment : 'parent' ,
7822- items : '> li.mailbox:not(.protected)' ,
7823- handle : '.sortable-handle' ,
7824- update : ( ) => this . save_reordered_folder_list ( ) ,
7825- // Add/remove a marker to disable dropping on other elements during sorting (see accept() of droppable).
7826- start : ( _ev , ui ) => ui . item . addClass ( 'is-being-sorted' ) ,
7827- stop : ( _ev , ui ) => ui . item . removeClass ( 'is-being-sorted' ) ,
7828- } ) ;
7787+ const mainFolderList = this . gui_objects . subscriptionlist ;
7788+ $folderLists = $ ( 'ul' , mainFolderList . parentElement ) ;
7789+ $folderLists . sortable ( {
7790+ axis : 'y' ,
7791+ // We can't use `li.mailbox.protected` here because that would disable moving elements out of protected
7792+ // folders. jQuery UI uses `closest()` with this selector, which makes it impossible to keep main list items
7793+ // and sub-list items apart. We disable sorting protected items via a `mousedown` event in treelist.js.
7794+ cancel : 'input, div.treetoggle, .custom-control' ,
7795+ helper : 'clone' , // a clone doesn't have the borders, which looks nicer.
7796+ items : '> li.mailbox' , // handle only the directly descending items, not those of sub-lists (they get they own instance of $.sortable()
7797+ connectWith : `#${ mainFolderList . id } , #${ mainFolderList . id } ul` ,
7798+ forcePlaceholderSize : true , // Make the placeholder displace the neighboring elements.
7799+ placeholder : 'placeholder' , // Class name for the placeholder
7800+ over : ( event , ui ) => {
7801+ // Highlight the list that the dragged element is hovering over.
7802+ $ ( '.hover' , $folderLists ) . removeClass ( 'hover' ) ;
7803+ if ( event . target !== mainFolderList ) {
7804+ $ ( event . target ) . closest ( 'li' ) . addClass ( 'hover' ) ;
7805+ }
7806+ } ,
7807+ receive : async ( event , ui ) => {
7808+ $ ( '.hover' , $folderLists ) . removeClass ( 'hover' ) ;
7809+ const folderId = ui . item . attr ( 'id' ) ;
7810+ const folderName = ref . folder_id2name ( folderId ) ;
7811+ const folderAttribs = ref . env . subscriptionrows [ folderName ] ;
7812+
7813+ let destName ;
7814+ if ( event . target === mainFolderList ) {
7815+ destName = '*' ;
7816+ } else {
7817+ const destId = event . target . parentElement . id ;
7818+ destName = ref . folder_id2name ( destId ) ;
7819+ }
7820+
7821+ if ( ! (
7822+ folderAttribs && ! folderAttribs [ 2 ]
7823+ && destName != folderName . replace ( ref . last_sub_rx , '' )
7824+ && ! destName . startsWith ( folderName + ref . env . delimiter )
7825+ ) ) {
7826+ ui . sender . sortable ( 'cancel' ) ;
7827+ }
7828+
7829+ const result = await ref . subscription_move_folder ( folderName , destName ) ;
7830+ if ( ! result ) {
7831+ ui . sender . sortable ( 'cancel' ) ;
7832+ }
7833+ } ,
7834+ stop : ( event , ui ) => {
7835+ $ ( '.hover' , $folderLists ) . removeClass ( 'hover' ) ;
7836+ // Save the order if the item was moved only within its list. In case it was moved into a (different)
7837+ // sub-list, the order-saving function gets called from the server's response after the relevant folder
7838+ // rows have been re-rendered, and we can save one HTTP request. We don't skip the other function call
7839+ // because in this moment here we don't know yet if the confirmation dialog about moving the folder will
7840+ // be confirmed or cancelled.
7841+ if ( ui . item [ 0 ] . parentElement === event . target ) {
7842+ ref . save_reordered_folder_list ( ) ;
7843+ }
7844+ } ,
78297845 } ) ;
78307846 } ;
78317847
0 commit comments