summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/files/js/detailsview.js176
-rw-r--r--apps/files/js/detailtabview.js6
-rw-r--r--apps/files/js/tagsplugin.js3
-rw-r--r--apps/files/templates/test.pngbin0 -> 2302 bytes
-rw-r--r--apps/files/tests/js/detailsviewSpec.js80
-rw-r--r--apps/files_sharing/js/share.js8
-rw-r--r--apps/files_sharing/js/sharetabview.js9
7 files changed, 199 insertions, 83 deletions
diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js
index 4df359e4523..a4ebe90cd64 100644
--- a/apps/files/js/detailsview.js
+++ b/apps/files/js/detailsview.js
@@ -9,23 +9,26 @@
*/
(function() {
-
var TEMPLATE =
'<div>' +
- ' <div class="detailFileInfoContainer">' +
- ' </div>' +
- ' <div>' +
- ' <ul class="tabHeaders">' +
- ' </ul>' +
- ' <div class="tabsContainer">' +
- ' </div>' +
- ' </div>' +
- ' <a class="close icon-close" href="#" alt="{{closeLabel}}"></a>' +
+ ' <div class="detailFileInfoContainer">' +
+ ' </div>' +
+ ' <div>' +
+ ' {{#if tabHeaders}}' +
+ ' <ul class="tabHeaders">' +
+ ' {{#each tabHeaders}}' +
+ ' <li class="tabHeader" data-tabid="{{tabId}}" data-tabindex="{{tabIndex}}">' +
+ ' <a href="#">{{label}}</a>' +
+ ' </li>' +
+ ' {{/each}}' +
+ ' </ul>' +
+ ' {{/if}}' +
+ ' <div class="tabsContainer">' +
+ ' </div>' +
+ ' </div>' +
+ ' <a class="close icon-close" href="#" alt="{{closeLabel}}"></a>' +
'</div>';
- var TEMPLATE_TAB_HEADER =
- '<li class="tabHeader {{#if selected}}selected{{/if}}" data-tabid="{{tabId}}" data-tabindex="{{tabIndex}}"><a href="#">{{label}}</a></li>';
-
/**
* @class OCA.Files.DetailsView
* @classdesc
@@ -39,7 +42,6 @@
className: 'detailsView',
_template: null,
- _templateTabHeader: null,
/**
* List of detail tab views
@@ -62,6 +64,11 @@
*/
_currentTabId: null,
+ /**
+ * Dirty flag, whether the view needs to be rerendered
+ */
+ _dirty: false,
+
events: {
'click a.close': '_onClose',
'click .tabHeaders .tabHeader': '_onClickTab'
@@ -74,8 +81,10 @@
this._tabViews = [];
this._detailFileInfoViews = [];
+ this._dirty = true;
+
// uncomment to add some dummy tabs for testing
- // this._addTestTabs();
+ //this._addTestTabs();
},
_onClose: function(event) {
@@ -85,28 +94,21 @@
_onClickTab: function(e) {
var $target = $(e.target);
+ e.preventDefault();
if (!$target.hasClass('tabHeader')) {
$target = $target.closest('.tabHeader');
}
- var tabIndex = $target.attr('data-tabindex');
- var targetTab;
- if (_.isUndefined(tabIndex)) {
+ var tabId = $target.attr('data-tabid');
+ if (_.isUndefined(tabId)) {
return;
}
- this.$el.find('.tabsContainer .tab').addClass('hidden');
- targetTab = this._tabViews[tabIndex];
- targetTab.$el.removeClass('hidden');
-
- this.$el.find('.tabHeaders li').removeClass('selected');
- $target.addClass('selected');
-
- e.preventDefault();
+ this.selectTab(tabId);
},
_addTestTabs: function() {
for (var j = 0; j < 2; j++) {
- var testView = new OCA.Files.DetailTabView('testtab' + j);
+ var testView = new OCA.Files.DetailTabView({id: 'testtab' + j});
testView.index = j;
testView.getLabel = function() { return 'Test tab ' + this.index; };
testView.render = function() {
@@ -119,26 +121,34 @@
}
},
+ template: function(vars) {
+ if (!this._template) {
+ this._template = Handlebars.compile(TEMPLATE);
+ }
+ return this._template(vars);
+ },
+
/**
* Renders this details view
*/
render: function() {
- var self = this;
-
- if (!this._template) {
- this._template = Handlebars.compile(TEMPLATE);
- }
+ var templateVars = {
+ closeLabel: t('files', 'Close')
+ };
- if (!this._templateTabHeader) {
- this._templateTabHeader = Handlebars.compile(TEMPLATE_TAB_HEADER);
+ if (this._tabViews.length > 1) {
+ // only render headers if there is more than one available
+ templateVars.tabHeaders = _.map(this._tabViews, function(tabView, i) {
+ return {
+ tabId: tabView.id,
+ tabIndex: i,
+ label: tabView.getLabel()
+ };
+ });
}
- this.$el.html(this._template({
- closeLabel: t('files', 'Close')
- }));
+ this.$el.html(this.template(templateVars));
- var $tabsContainer = this.$el.find('.tabsContainer');
- var $tabHeadsContainer = this.$el.find('.tabHeaders');
var $detailsContainer = this.$el.find('.detailFileInfoContainer');
// render details
@@ -146,29 +156,58 @@
$detailsContainer.append(detailView.get$());
});
- if (this._tabViews.length > 0) {
- if (!this._currentTab) {
- this._currentTab = this._tabViews[0].id;
- }
-
- // render tabs
- _.each(this._tabViews, function(tabView, i) {
- // hidden by default
- var $el = tabView.get$();
- var isCurrent = (tabView.id === self._currentTab);
- if (!isCurrent) {
- $el.addClass('hidden');
- }
- $tabsContainer.append($el);
+ if (!this._currentTabId && this._tabViews.length > 0) {
+ this._currentTabId = this._tabViews[0].id;
+ }
- $tabHeadsContainer.append(self._templateTabHeader({
- tabId: tabView.id,
- tabIndex: i,
- label: tabView.getLabel(),
- selected: isCurrent
- }));
- });
+ this.selectTab(this._currentTabId);
+
+ this._dirty = false;
+ },
+
+ /**
+ * Selects the given tab by id
+ *
+ * @param {string} tabId tab id
+ */
+ selectTab: function(tabId) {
+ if (!tabId) {
+ return;
+ }
+
+ var tabView = _.find(this._tabViews, function(tab) {
+ return tab.id === tabId;
+ });
+
+ if (!tabView) {
+ console.warn('Details view tab with id "' + tabId + '" not found');
+ return;
}
+
+ this._currentTabId = tabId;
+
+ var $tabsContainer = this.$el.find('.tabsContainer');
+ var $tabEl = $tabsContainer.find('#' + tabId);
+
+ // hide other tabs
+ $tabsContainer.find('.tab').addClass('hidden');
+
+ // tab already rendered ?
+ if (!$tabEl.length) {
+ // render tab
+ $tabsContainer.append(tabView.$el);
+ $tabEl = tabView.$el;
+ }
+
+ // this should trigger tab rendering
+ tabView.setFileInfo(this.model);
+
+ $tabEl.removeClass('hidden');
+
+ // update tab headers
+ var $tabHeaders = this.$el.find('.tabHeaders li');
+ $tabHeaders.removeClass('selected');
+ $tabHeaders.filterAttr('data-tabid', tabView.id).addClass('selected');
},
/**
@@ -179,12 +218,19 @@
setFileInfo: function(fileInfo) {
this.model = fileInfo;
- this.render();
+ if (this._dirty) {
+ this.render();
+ }
- // notify all panels
- _.each(this._tabViews, function(tabView) {
+ if (this._currentTabId) {
+ // only update current tab, others will be updated on-demand
+ var tabId = this._currentTabId;
+ var tabView = _.find(this._tabViews, function(tab) {
+ return tab.id === tabId;
+ });
tabView.setFileInfo(fileInfo);
- });
+ }
+
_.each(this._detailFileInfoViews, function(detailView) {
detailView.setFileInfo(fileInfo);
});
@@ -206,6 +252,7 @@
*/
addTabView: function(tabView) {
this._tabViews.push(tabView);
+ this._dirty = true;
},
/**
@@ -215,6 +262,7 @@
*/
addDetailView: function(detailView) {
this._detailFileInfoViews.push(detailView);
+ this._dirty = true;
}
});
diff --git a/apps/files/js/detailtabview.js b/apps/files/js/detailtabview.js
index b2e02971fb4..67f8b535abd 100644
--- a/apps/files/js/detailtabview.js
+++ b/apps/files/js/detailtabview.js
@@ -71,8 +71,10 @@
* @param {OCA.Files.FileInfoModel} fileInfo file info to set
*/
setFileInfo: function(fileInfo) {
- this.model = fileInfo;
- this.render();
+ if (this.model !== fileInfo) {
+ this.model = fileInfo;
+ this.render();
+ }
},
/**
diff --git a/apps/files/js/tagsplugin.js b/apps/files/js/tagsplugin.js
index d8552c71e45..ed1105a1706 100644
--- a/apps/files/js/tagsplugin.js
+++ b/apps/files/js/tagsplugin.js
@@ -108,6 +108,8 @@
}
toggleStar($actionEl, !isFavorite);
+ context.fileInfoModel.set('tags', tags);
+
self.applyFileTags(
dir + '/' + fileName,
tags,
@@ -124,7 +126,6 @@
toggleStar($actionEl, (newTags.indexOf(OC.TAG_FAVORITE) >= 0));
$file.attr('data-tags', newTags.join('|'));
$file.attr('data-favorite', !isFavorite);
- context.fileInfoModel.set('tags', newTags);
fileInfo.tags = newTags;
});
}
diff --git a/apps/files/templates/test.png b/apps/files/templates/test.png
new file mode 100644
index 00000000000..2b90216f797
--- /dev/null
+++ b/apps/files/templates/test.png
Binary files differ
diff --git a/apps/files/tests/js/detailsviewSpec.js b/apps/files/tests/js/detailsviewSpec.js
index 4261aa53c94..852f8b04293 100644
--- a/apps/files/tests/js/detailsviewSpec.js
+++ b/apps/files/tests/js/detailsviewSpec.js
@@ -67,32 +67,75 @@ describe('OCA.Files.DetailsView tests', function() {
var testView, testView2;
beforeEach(function() {
- testView = new OCA.Files.DetailTabView('test1');
- testView2 = new OCA.Files.DetailTabView('test2');
+ testView = new OCA.Files.DetailTabView({id: 'test1'});
+ testView2 = new OCA.Files.DetailTabView({id: 'test2'});
detailsView.addTabView(testView);
detailsView.addTabView(testView2);
detailsView.render();
});
- it('renders registered tabs', function() {
- expect(detailsView.$el.find('.tab').length).toEqual(2);
+ it('initially renders only the selected tab', function() {
+ expect(detailsView.$el.find('.tab').length).toEqual(1);
+ expect(detailsView.$el.find('.tab').attr('id')).toEqual('test1');
});
- it('updates registered tabs when fileinfo is updated', function() {
- var tabRenderStub = sinon.stub(OCA.Files.DetailTabView.prototype, 'render');
- var fileInfo = {id: 5, name: 'test.txt'};
- tabRenderStub.reset();
- detailsView.setFileInfo(fileInfo);
+ it('updates tab model and rerenders on-demand as soon as it gets selected', function() {
+ var tab1RenderStub = sinon.stub(testView, 'render');
+ var tab2RenderStub = sinon.stub(testView2, 'render');
+ var fileInfo1 = new OCA.Files.FileInfoModel({id: 5, name: 'test.txt'});
+ var fileInfo2 = new OCA.Files.FileInfoModel({id: 8, name: 'test2.txt'});
- expect(testView.getFileInfo()).toEqual(fileInfo);
- expect(testView2.getFileInfo()).toEqual(fileInfo);
+ detailsView.setFileInfo(fileInfo1);
+
+ // first tab renders, not the second one
+ expect(tab1RenderStub.calledOnce).toEqual(true);
+ expect(tab2RenderStub.notCalled).toEqual(true);
+
+ // info got set only to the first visible tab
+ expect(testView.getFileInfo()).toEqual(fileInfo1);
+ expect(testView2.getFileInfo()).toBeUndefined();
+
+ // select second tab for first render
+ detailsView.$el.find('.tabHeader').eq(1).click();
+
+ // second tab got rendered
+ expect(tab2RenderStub.calledOnce).toEqual(true);
+ expect(testView2.getFileInfo()).toEqual(fileInfo1);
+
+ // select the first tab again
+ detailsView.$el.find('.tabHeader').eq(0).click();
+
+ // no re-render
+ expect(tab1RenderStub.calledOnce).toEqual(true);
+ expect(tab2RenderStub.calledOnce).toEqual(true);
- expect(tabRenderStub.callCount).toEqual(2);
- tabRenderStub.restore();
+ tab1RenderStub.reset();
+ tab2RenderStub.reset();
+
+ // switch to another file
+ detailsView.setFileInfo(fileInfo2);
+
+ // only the visible tab was updated and rerendered
+ expect(tab1RenderStub.calledOnce).toEqual(true);
+ expect(testView.getFileInfo()).toEqual(fileInfo2);
+
+ // second/invisible tab still has old info, not rerendered
+ expect(tab2RenderStub.notCalled).toEqual(true);
+ expect(testView2.getFileInfo()).toEqual(fileInfo1);
+
+ // reselect the second one
+ detailsView.$el.find('.tabHeader').eq(1).click();
+
+ // second tab becomes visible, updated and rendered
+ expect(testView2.getFileInfo()).toEqual(fileInfo2);
+ expect(tab2RenderStub.calledOnce).toEqual(true);
+
+ tab1RenderStub.restore();
+ tab2RenderStub.restore();
});
it('selects the first tab by default', function() {
expect(detailsView.$el.find('.tabHeader').eq(0).hasClass('selected')).toEqual(true);
expect(detailsView.$el.find('.tabHeader').eq(1).hasClass('selected')).toEqual(false);
expect(detailsView.$el.find('.tab').eq(0).hasClass('hidden')).toEqual(false);
- expect(detailsView.$el.find('.tab').eq(1).hasClass('hidden')).toEqual(true);
+ expect(detailsView.$el.find('.tab').eq(1).length).toEqual(0);
});
it('switches the current tab when clicking on tab header', function() {
detailsView.$el.find('.tabHeader').eq(1).click();
@@ -101,5 +144,14 @@ describe('OCA.Files.DetailsView tests', function() {
expect(detailsView.$el.find('.tab').eq(0).hasClass('hidden')).toEqual(true);
expect(detailsView.$el.find('.tab').eq(1).hasClass('hidden')).toEqual(false);
});
+ it('does not render tab headers when only one tab exists', function() {
+ detailsView.remove();
+ detailsView = new OCA.Files.DetailsView();
+ testView = new OCA.Files.DetailTabView({id: 'test1'});
+ detailsView.addTabView(testView);
+ detailsView.render();
+
+ expect(detailsView.$el.find('.tabHeader').length).toEqual(0);
+ });
});
});
diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js
index 04700b84011..c124d390d04 100644
--- a/apps/files_sharing/js/share.js
+++ b/apps/files_sharing/js/share.js
@@ -60,6 +60,14 @@
return tr;
};
+ var oldElementToFile = fileList.elementToFile;
+ fileList.elementToFile = function($el) {
+ var fileInfo = oldElementToFile.apply(this, arguments);
+ fileInfo.sharePermissions = $el.attr('data-share-permissions') || undefined;
+ fileInfo.shareOwner = $el.attr('data-share-owner') || undefined;
+ return fileInfo;
+ };
+
// use delegate to catch the case with multiple file lists
fileList.$el.on('fileActionsReady', function(ev){
var fileList = ev.fileList;
diff --git a/apps/files_sharing/js/sharetabview.js b/apps/files_sharing/js/sharetabview.js
index 5f4a21a4a57..ee572b747ea 100644
--- a/apps/files_sharing/js/sharetabview.js
+++ b/apps/files_sharing/js/sharetabview.js
@@ -10,7 +10,7 @@
(function() {
var TEMPLATE =
- '<div>Owner: {{owner}}';
+ '<div><ul>{{#if owner}}<li>Owner: {{owner}}</li>{{/if}}</ul></div>';
/**
* @memberof OCA.Sharing
@@ -37,8 +37,13 @@
}
if (this.model) {
+ console.log(this.model);
+ var owner = this.model.get('shareOwner');
+ if (owner === OC.currentUser) {
+ owner = null;
+ }
this.$el.append(this._template({
- owner: this.model.get('shareOwner') || OC.currentUser
+ owner: owner
}));
} else {