]> source.dussan.org Git - nextcloud-server.git/commitdiff
Sync file list with file actions
authorVincent Petry <pvince81@owncloud.com>
Fri, 27 Jun 2014 11:36:18 +0000 (13:36 +0200)
committerVincent Petry <pvince81@owncloud.com>
Fri, 27 Jun 2014 11:41:01 +0000 (13:41 +0200)
Whenever file actions are registered later, now the file lists are
automatically notified.

Added FileActions.addUpdateListener() to be able to receive such
notifications.

This removes the need for apps to manually call FileActions.display()
after registering new actions.

This fixes issues with race conditions when file actions are
registered after the file list was already rendered.

apps/files/js/app.js
apps/files/js/fileactions.js
apps/files/js/filelist.js
apps/files/tests/js/fileactionsSpec.js
apps/files/tests/js/filelistSpec.js
apps/files_external/tests/appSpec.js
apps/files_external/tests/js/mountsfilelistSpec.js
apps/files_sharing/tests/js/appSpec.js
apps/files_sharing/tests/js/shareSpec.js
apps/files_sharing/tests/js/sharedfilelistSpec.js
apps/files_trashbin/tests/js/filelistSpec.js

index 71802948a5ca70d9eaf9fb1aca8bcd07acc15830..45b6b6a0e1666b7e1ad5df4a640032e183f25dd2 100644 (file)
                        // regular actions
                        fileActions.merge(OCA.Files.fileActions);
 
+                       // in case apps would decide to register file actions later,
+                       // replace the global object with this one
+                       OCA.Files.fileActions = fileActions;
+
                        this.files = OCA.Files.Files;
 
                        // TODO: ideally these should be in a separate class / app (the embedded "all files" app)
index 47a6ab2f04b3cecd0e955a639da11cb3ece480f4..fc7c9ccacef655ee25a7f0f487ff3de0be4db536 100644 (file)
                defaults: {},
                icons: {},
                currentFile: null,
+
+               /**
+                * List of handlers to be notified whenever a register() or
+                * setDefault() was called.
+                */
+               _updateListeners: [],
+
                initialize: function() {
                        this.clear();
                },
+
+               /**
+                * Adds an update listener to be notified whenever register()
+                * or setDefault() has been called.
+                *
+                * @param Function callback
+                */
+               addUpdateListener: function(callback) {
+                       if (!_.isFunction(callback)) {
+                               throw 'Argument passed to FileActions.addUpdateListener must be a function';
+                       }
+                       this._updateListeners.push(callback);
+               },
+
+               /**
+                * Removes an update listener.
+                *
+                * @param Function callback
+                */
+               removeUpdateListener: function(callback) {
+                       if (!_.isFunction(callback)) {
+                               throw 'Argument passed to FileActions.removeUpdateListener must be a function';
+                       }
+                       this._updateListeners = _.without(this._updateListeners, callback);
+               },
+
+               /**
+                * Notifies the registered update listeners
+                */
+               _notifyUpdateListeners: function() {
+                       for (var i = 0; i < this._updateListeners.length; i++) {
+                               this._updateListeners[i](this);
+                       }
+               },
+
                /**
                 * Merges the actions from the given fileActions into
                 * this instance.
                        this.actions[mime][name]['permissions'] = permissions;
                        this.actions[mime][name]['displayName'] = displayName;
                        this.icons[name] = icon;
+                       this._notifyUpdateListeners();
                },
                clear: function() {
                        this.actions = {};
                        this.defaults = {};
                        this.icons = {};
                        this.currentFile = null;
+                       this._updateListeners = [];
                },
                setDefault: function (mime, name) {
                        this.defaults[mime] = name;
+                       this._notifyUpdateListeners();
                },
                get: function (mime, type, permissions) {
                        var actions = this.getActions(mime, type, permissions);
                display: function (parent, triggerEvent, fileList) {
                        if (!fileList) {
                                console.warn('FileActions.display() MUST be called with a OCA.Files.FileList instance');
-                               // using default list instead, which could be wrong
-                               fileList = OCA.Files.App.fileList;
+                               return;
                        }
                        this.currentFile = parent;
                        var self = this;
                                window.FileActions, mime, name, permissions, icon, action, displayName
                );
        };
-       window.FileActions.setDefault = function (mime, name) {
-               console.warn('FileActions.setDefault() is deprecated, please use OCA.Files.fileActions.setDefault() instead', mime, name);
-               OCA.Files.FileActions.prototype.setDefault.call(window.FileActions, mime, name);
+       window.FileActions.display = function (parent, triggerEvent, fileList) {
+               fileList = fileList || OCA.Files.App.fileList;
+               console.warn('FileActions.display() is deprecated, please use OCA.Files.fileActions.register() which automatically redisplays actions', mime, name);
+               OCA.Files.FileActions.prototype.display.call(window.FileActions, parent, triggerEvent, fileList);
        };
 })();
 
index 55afedb2065829e38cbf5b30ca06264a54e58802..82aa29670dfc4921aed19664d01921c980b61cbc 100644 (file)
                        this.$container.on('scroll', _.bind(this._onScroll, this));
                },
 
+               /**
+                * Destroy / uninitialize this instance.
+                */
+               destroy: function() {
+                       // TODO: also unregister other event handlers
+                       this.fileActions.removeUpdateListener(this._onFileActionsUpdated);
+               },
+
                _initFileActions: function(fileActions) {
                        this.fileActions = fileActions;
                        if (!this.fileActions) {
                                this.fileActions = new OCA.Files.FileActions();
                                this.fileActions.registerDefaultActions();
                        }
+                       this._onFileActionsUpdated = _.debounce(_.bind(this._onFileActionsUpdated, this), 100);
+                       this.fileActions.addUpdateListener(this._onFileActionsUpdated);
                },
 
                /**
                        }
                },
 
+               /**
+                * Event handler for when file actions were updated.
+                * This will refresh the file actions on the list.
+                */
+               _onFileActionsUpdated: function() {
+                       console.log('onFileActionsUpdated');
+                       var self = this;
+                       this.$fileList.find('tr td.filename').each(function() {
+                               self.fileActions.display($(this), true, self);
+                       });
+               },
+
                /**
                 * Sets the files to be displayed in the list.
                 * This operation will re-render the list and update the summary.
index 355761afa0115023bdcf3b53257fe791e793c496..f464800730a0d9d080af88c9017a8f2a63a3acda 100644 (file)
@@ -21,7 +21,7 @@
 
 describe('OCA.Files.FileActions tests', function() {
        var $filesTable, fileList;
-       var FileActions; 
+       var FileActions;
 
        beforeEach(function() {
                // init horrible parameters
@@ -36,6 +36,7 @@ describe('OCA.Files.FileActions tests', function() {
        });
        afterEach(function() {
                FileActions = null;
+               fileList.destroy();
                fileList = undefined;
                $('#dir, #permissions, #filestable').remove();
        });
@@ -192,4 +193,54 @@ describe('OCA.Files.FileActions tests', function() {
                context = actionStub.getCall(0).args[1];
                expect(context.dir).toEqual('/somepath');
        });
+       describe('events', function() {
+               var clock;
+               beforeEach(function() {
+                       clock = sinon.useFakeTimers();
+               });
+               afterEach(function() {
+                       clock.restore();
+               });
+               it('notifies update event handlers once after multiple changes', function() {
+                       var actionStub = sinon.stub();
+                       var handler = sinon.stub();
+                       FileActions.addUpdateListener(handler);
+                       FileActions.register(
+                                       'all',
+                                       'Test',
+                                       OC.PERMISSION_READ,
+                                       OC.imagePath('core', 'actions/test'),
+                                       actionStub
+                       );
+                       FileActions.register(
+                                       'all',
+                                       'Test2',
+                                       OC.PERMISSION_READ,
+                                       OC.imagePath('core', 'actions/test'),
+                                       actionStub
+                       );
+                       expect(handler.calledTwice).toEqual(true);
+               });
+               it('does not notifies update event handlers after unregistering', function() {
+                       var actionStub = sinon.stub();
+                       var handler = sinon.stub();
+                       FileActions.addUpdateListener(handler);
+                       FileActions.removeUpdateListener(handler);
+                       FileActions.register(
+                                       'all',
+                                       'Test',
+                                       OC.PERMISSION_READ,
+                                       OC.imagePath('core', 'actions/test'),
+                                       actionStub
+                       );
+                       FileActions.register(
+                                       'all',
+                                       'Test2',
+                                       OC.PERMISSION_READ,
+                                       OC.imagePath('core', 'actions/test'),
+                                       actionStub
+                       );
+                       expect(handler.notCalled).toEqual(true);
+               });
+       });
 });
index dea7c48e05eaa8633df81f3a54cf3b7744f38b48..7100a2667c9e6089c70ff38f641e0ccdb81432fd 100644 (file)
@@ -1623,6 +1623,38 @@ describe('OCA.Files.FileList tests', function() {
                        expect(context.fileActions).toBeDefined();
                        expect(context.dir).toEqual('/subdir');
                });
+               it('redisplays actions when new actions have been registered', function() {
+                       var actionStub = sinon.stub();
+                       var clock = sinon.useFakeTimers();
+                       var debounceStub = sinon.stub(_, 'debounce', function(callback) {
+                               return function() {
+                                       // defer instead of debounce, to make it work with clock
+                                       _.defer(callback);
+                               };
+                       });
+                       // need to reinit the list to make the debounce call
+                       fileList.destroy();
+                       fileList = new OCA.Files.FileList($('#app-content-files'));
+
+                       fileList.setFiles(testFiles);
+                       fileList.fileActions.register(
+                               'text/plain',
+                               'Test',
+                               OC.PERMISSION_ALL,
+                               function() {
+                                       // Specify icon for hitory button
+                                       return OC.imagePath('core','actions/history');
+                               },
+                               actionStub
+                       );
+                       var $tr = fileList.findFileEl('One.txt');
+                       expect($tr.find('.action-test').length).toEqual(0);
+                       // update is delayed
+                       clock.tick(100);
+                       expect($tr.find('.action-test').length).toEqual(1);
+                       clock.restore();
+                       debounceStub.restore();
+               });
        });
        describe('Sorting files', function() {
                it('Sorts by name by default', function() {
index b00442384dae6aac42685bd1b1c325860f85fcfe..43902d1c1d0009d7807417ce886be1258d03019e 100644 (file)
@@ -42,6 +42,7 @@ describe('OCA.External.App tests', function() {
        });
        afterEach(function() {
                App.fileList = null;
+               fileList.destroy();
                fileList = null;
        });
 
index 96a6b622a4e55028646861e5de83d5ee2913331a..b599df77aacf0f4ddaaf03f83a1ae691c89827f3 100644 (file)
@@ -54,6 +54,7 @@ describe('OCA.External.FileList tests', function() {
        afterEach(function() {
                OCA.Files.FileList.prototype = oldFileListPrototype;
                testFiles = undefined;
+               fileList.destroy();
                fileList = undefined;
                fileActions = undefined;
 
index 9c46b7caf1b6bf74f3a07450448249c41e7d399e..d0480ad1aa436ad1a27b23d61187887fa0429df4 100644 (file)
@@ -47,6 +47,8 @@ describe('OCA.Sharing.App tests', function() {
        afterEach(function() {
                App._inFileList = null;
                App._outFileList = null;
+               fileListIn.destroy();
+               fileListOut.destroy();
                fileListIn = null;
                fileListOut = null;
        });
index 455addaccc0d9587476a17f39ef1ef5385ae6d4b..3d4fc75482131260d060e1acdcb4834f157d1cb2 100644 (file)
@@ -70,6 +70,8 @@ describe('OCA.Sharing.Util tests', function() {
                OCA.Files.FileList.prototype = oldFileListPrototype;
                delete OCA.Sharing.sharesLoaded;
                delete OC.Share.droppedDown;
+               fileList.destroy();
+               fileList = null;
                OC.Share.statuses = {};
                OC.Share.currentShares = {};
        });
index 0f6d0a0ba49a2b6d23c131ca145484c6f24e5c4e..4e130885500b770cdb7bc3ffa8d3d6a93e8029a8 100644 (file)
@@ -55,6 +55,7 @@ describe('OCA.Sharing.FileList tests', function() {
        afterEach(function() {
                OCA.Files.FileList.prototype = oldFileListPrototype;
                testFiles = undefined;
+               fileList.destroy();
                fileList = undefined;
                fileActions = undefined;
 
index 11eeff68df81caf353410ba04a5bce9330e15839..fd479234b30d6501e15110f91718fd2577947430 100644 (file)
@@ -96,6 +96,7 @@ describe('OCA.Trashbin.FileList tests', function() {
        });
        afterEach(function() {
                testFiles = undefined;
+               fileList.destroy();
                fileList = undefined;
 
                $('#dir').remove();