diff options
Diffstat (limited to 'core/js')
-rw-r--r-- | core/js/core.json | 1 | ||||
-rw-r--r-- | core/js/jquery.contactsmenu.js | 117 | ||||
-rw-r--r-- | core/js/merged-template-prepend.json | 3 | ||||
-rw-r--r-- | core/js/sharedialogresharerinfoview.js | 5 | ||||
-rw-r--r-- | core/js/sharedialogshareelistview.js | 15 | ||||
-rw-r--r-- | core/js/tests/specs/jquery.contactsmenuSpec.js | 213 |
6 files changed, 350 insertions, 4 deletions
diff --git a/core/js/core.json b/core/js/core.json index aadd66a0558..15e406bf2d2 100644 --- a/core/js/core.json +++ b/core/js/core.json @@ -20,6 +20,7 @@ "libraries": [ "jquery-showpassword.js", "jquery.avatar.js", + "jquery.contactsmenu.js", "placeholder.js" ], "modules": [ diff --git a/core/js/jquery.contactsmenu.js b/core/js/jquery.contactsmenu.js new file mode 100644 index 00000000000..1ea9f732f79 --- /dev/null +++ b/core/js/jquery.contactsmenu.js @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2017 Georg Ehrke <oc.list@georgehrke.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +(function ($) { + var ENTRY = '' + + '<li>' + + ' <a href="{{hyperlink}}">' + + ' {{#if icon}}<img src="{{icon}}">{{/if}}' + + ' <span>{{title}}</span>' + + ' </a>' + + '</li>'; + + var LIST = '' + + '<div class="menu popovermenu bubble hidden contactsmenu-popover">' + + ' <ul>' + + ' <li>' + + ' <a>' + + ' <span class="icon-loading-small"></span>' + + ' </a>' + + ' </li>' + + ' </ul>' + + '</div>'; + + $.fn.contactsMenu = function(shareWith, shareType, appendTo) { + // 0 - user, 4 - email, 6 - remote + var allowedTypes = [0, 4, 6]; + if (allowedTypes.indexOf(shareType) === -1) { + return; + } + + var $div = this; + appendTo.append(LIST); + var $list = appendTo.find('div.contactsmenu-popover'); + + $div.click(function() { + if (!$list.hasClass('hidden')) { + $list.addClass('hidden'); + $list.hide(); + return; + } + + $list.removeClass('hidden'); + $list.show(); + + if ($list.hasClass('loaded')) { + return; + } + + $list.addClass('loaded'); + $.ajax(OC.generateUrl('/contactsmenu/findOne'), { + method: 'POST', + data: { + shareType: shareType, + shareWith: shareWith + } + }).then(function(data) { + $list.find('ul').find('li').addClass('hidden'); + + var actions; + if (!data.topAction) { + actions = [{ + hyperlink: '#', + title: t('core', 'No action available') + }]; + } else { + actions = [data.topAction].concat(data.actions); + } + + actions.forEach(function(action) { + var template = Handlebars.compile(ENTRY); + $list.find('ul').append(template(action)); + }); + + if (actions.length === 0) { + + } + }, function(jqXHR) { + $list.find('ul').find('li').addClass('hidden'); + + var title; + if (jqXHR.status === 404) { + title = t('core', 'No action available'); + } else { + title = t('core', 'Error fetching contact actions'); + } + + var template = Handlebars.compile(ENTRY); + $list.find('ul').append(template({ + hyperlink: '#', + title: title + })); + }); + }); + + $(document).click(function(event) { + var clickedList = ($list.has(event.target).length > 0); + var clickedTarget = ($div.has(event.target).length > 0); + + $div.each(function() { + if ($(this).is(event.target)) { + clickedTarget = true; + } + }); + + if (clickedList || clickedTarget) { + return; + } + + $list.addClass('hidden'); + $list.hide(); + }); + }; +}(jQuery)); diff --git a/core/js/merged-template-prepend.json b/core/js/merged-template-prepend.json index 12b7ca8faa3..0dd6bed5329 100644 --- a/core/js/merged-template-prepend.json +++ b/core/js/merged-template-prepend.json @@ -13,5 +13,6 @@ "mimetypelist.js", "oc-backbone.js", "placeholder.js", - "jquery.avatar.js" + "jquery.avatar.js", + "jquery.contactsmenu.js" ] diff --git a/core/js/sharedialogresharerinfoview.js b/core/js/sharedialogresharerinfoview.js index a82b495bdcc..201484c52a8 100644 --- a/core/js/sharedialogresharerinfoview.js +++ b/core/js/sharedialogresharerinfoview.js @@ -100,6 +100,11 @@ $this.avatar($this.data('username'), 32); }); + this.$el.find('.reshare').contactsMenu( + this.model.getReshareOwner(), + OC.Share.SHARE_TYPE_USER, + this.$el); + return this; }, diff --git a/core/js/sharedialogshareelistview.js b/core/js/sharedialogshareelistview.js index 3a481e53dde..f513eb75848 100644 --- a/core/js/sharedialogshareelistview.js +++ b/core/js/sharedialogshareelistview.js @@ -26,7 +26,7 @@ '{{#each sharees}}' + '<li data-share-id="{{shareId}}" data-share-type="{{shareType}}" data-share-with="{{shareWith}}">' + '<div class="avatar {{#if modSeed}}imageplaceholderseed{{/if}}" data-username="{{shareWith}}" data-displayname="{{shareWithDisplayName}}" {{#if modSeed}}data-seed="{{shareWith}} {{shareType}}"{{/if}}></div>' + - '<span class="has-tooltip username" title="{{shareWithTitle}}">{{shareWithDisplayName}}</span>' + + '<span class="username" title="{{shareWithTitle}}">{{shareWithDisplayName}}</span>' + '<span class="sharingOptionsGroup">' + '{{#if editPermissionPossible}}' + '<span class="shareOption">' + @@ -361,6 +361,15 @@ this.$('.has-tooltip').tooltip({ placement: 'bottom' }); + + this.$('ul.shareWithList > li').each(function() { + var $this = $(this); + + var shareWith = $this.data('share-with'); + var shareType = $this.data('share-type'); + + $this.find('div.avatar, span.username').contactsMenu(shareWith, shareType, $this); + }) } else { var permissionChangeShareId = parseInt(this._renderPermissionChange, 10); var shareWithIndex = this.model.findShareWithIndex(permissionChangeShareId); @@ -399,7 +408,7 @@ var shareId = parseInt(this._menuOpen, 10); if(!_.isNaN(shareId)) { var liSelector = 'li[data-share-id=' + shareId + ']'; - OC.showMenu(null, this.$(liSelector + ' .popovermenu')); + OC.showMenu(null, this.$(liSelector + '.sharingOptionsGroup .popovermenu')); } } @@ -476,7 +485,7 @@ event.stopPropagation(); var $element = $(event.target); var $li = $element.closest('li[data-share-id]'); - var $menu = $li.find('.popovermenu'); + var $menu = $li.find('.sharingOptionsGroup .popovermenu'); OC.showMenu(null, $menu); this._menuOpen = $li.data('share-id'); diff --git a/core/js/tests/specs/jquery.contactsmenuSpec.js b/core/js/tests/specs/jquery.contactsmenuSpec.js new file mode 100644 index 00000000000..7287648f5a2 --- /dev/null +++ b/core/js/tests/specs/jquery.contactsmenuSpec.js @@ -0,0 +1,213 @@ +/** + * Copyright (c) 2017 Georg Ehrke <oc.list@georgehrke.com> + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +describe('jquery.contactsMenu tests', function() { + + var $selector1, $selector2, $appendTo; + + beforeEach(function() { + $('#testArea').append($('<div id="selector1">')); + $('#testArea').append($('<div id="selector2">')); + $('#testArea').append($('<div id="appendTo">')); + $selector1 = $('#selector1'); + $selector2 = $('#selector2'); + $appendTo = $('#appendTo'); + }); + + afterEach(function() { + $selector1.remove(); + $selector2.remove(); + $appendTo.remove(); + }); + + describe('shareType', function() { + it('stops if type not supported', function() { + $selector1.contactsMenu('user', 1, $appendTo); + expect($appendTo.children().length).toEqual(0); + + $selector1.contactsMenu('user', 2, $appendTo); + expect($appendTo.children().length).toEqual(0); + + $selector1.contactsMenu('user', 3, $appendTo); + expect($appendTo.children().length).toEqual(0); + + $selector1.contactsMenu('user', 5, $appendTo); + expect($appendTo.children().length).toEqual(0); + }); + + it('append list if shareType supported', function() { + $selector1.contactsMenu('user', 0, $appendTo); + expect($appendTo.children().length).toEqual(1); + expect($appendTo.html()).toEqual('<div class="menu popovermenu bubble hidden contactsmenu-popover"> <ul> <li> <a> <span class="icon-loading-small"></span> </a> </li> </ul></div>'); + }); + }); + + describe('open on click', function() { + it('with one selector', function() { + $selector1.contactsMenu('user', 0, $appendTo); + expect($appendTo.children().length).toEqual(1); + expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(true); + $selector1.click(); + expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(false); + }); + + it('with multiple selectors - 1', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + + expect($appendTo.children().length).toEqual(1); + expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(true); + $selector1.click(); + expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(false); + }); + + it('with multiple selectors - 2', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + + expect($appendTo.children().length).toEqual(1); + expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(true); + $selector2.click(); + expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(false); + }); + + it ('should close when clicking the selector again - 1', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + + expect($appendTo.children().length).toEqual(1); + expect($appendTo.find('div').hasClass('hidden')).toEqual(true); + $selector1.click(); + expect($appendTo.find('div').hasClass('hidden')).toEqual(false); + $selector1.click(); + expect($appendTo.find('div').hasClass('hidden')).toEqual(true); + }); + + it ('should close when clicking the selector again - 1', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + + expect($appendTo.children().length).toEqual(1); + expect($appendTo.find('div').hasClass('hidden')).toEqual(true); + $selector1.click(); + expect($appendTo.find('div').hasClass('hidden')).toEqual(false); + $selector2.click(); + expect($appendTo.find('div').hasClass('hidden')).toEqual(true); + }); + }); + + describe('send requests to the server and render', function() { + it('load a topaction only', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + $selector1.click(); + + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json; charset=utf-8' }, + JSON.stringify({ + "id": null, + "fullName": "Name 123", + "topAction": { + "title": "bar@baz.wtf", + "icon": "foo.svg", + "hyperlink": "mailto:bar%40baz.wtf"}, + "actions": [] + }) + ); + expect(fakeServer.requests[0].method).toEqual('POST'); + expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne'); + + expect($appendTo.html()).toEqual('<div class="menu popovermenu bubble contactsmenu-popover loaded" style="display: block;"> <ul> <li class="hidden"> <a> <span class="icon-loading-small"></span> </a> </li> <li> <a href="mailto:bar%40baz.wtf"> <img src="foo.svg"> <span>bar@baz.wtf</span> </a></li></ul></div>'); + }); + + it('load topaction and more actions', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + $selector1.click(); + + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json; charset=utf-8' }, + JSON.stringify({ + "id": null, + "fullName": "Name 123", + "topAction": { + "title": "bar@baz.wtf", + "icon": "foo.svg", + "hyperlink": "mailto:bar%40baz.wtf"}, + "actions": [{ + "title": "Details", + "icon": "details.svg", + "hyperlink": "http:\/\/localhost\/index.php\/apps\/contacts" + }] + }) + ); + expect(fakeServer.requests[0].method).toEqual('POST'); + expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne'); + + expect($appendTo.html()).toEqual('<div class="menu popovermenu bubble contactsmenu-popover loaded" style="display: block;"> <ul> <li class="hidden"> <a> <span class="icon-loading-small"></span> </a> </li> <li> <a href="mailto:bar%40baz.wtf"> <img src="foo.svg"> <span>bar@baz.wtf</span> </a></li><li> <a href="http://localhost/index.php/apps/contacts"> <img src="details.svg"> <span>Details</span> </a></li></ul></div>'); + }); + + it('load no actions', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + $selector1.click(); + + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json; charset=utf-8' }, + JSON.stringify({ + "id": null, + "fullName": "Name 123", + "topAction": null, + "actions": [] + }) + ); + expect(fakeServer.requests[0].method).toEqual('POST'); + expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne'); + + expect($appendTo.html()).toEqual('<div class="menu popovermenu bubble contactsmenu-popover loaded" style="display: block;"> <ul> <li class="hidden"> <a> <span class="icon-loading-small"></span> </a> </li> <li> <a href="#"> <span>No action available</span> </a></li></ul></div>'); + }); + + it('should throw an error', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + $selector1.click(); + + fakeServer.requests[0].respond( + 400, + { 'Content-Type': 'application/json; charset=utf-8' }, + JSON.stringify([]) + ); + expect(fakeServer.requests[0].method).toEqual('POST'); + expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne'); + + expect($appendTo.html()).toEqual('<div class="menu popovermenu bubble contactsmenu-popover loaded" style="display: block;"> <ul> <li class="hidden"> <a> <span class="icon-loading-small"></span> </a> </li> <li> <a href="#"> <span>Error fetching contact actions</span> </a></li></ul></div>'); + }); + + it('should handle 404', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + $selector1.click(); + + fakeServer.requests[0].respond( + 404, + { 'Content-Type': 'application/json; charset=utf-8' }, + JSON.stringify([]) + ); + expect(fakeServer.requests[0].method).toEqual('POST'); + expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne'); + + expect($appendTo.html()).toEqual('<div class="menu popovermenu bubble contactsmenu-popover loaded" style="display: block;"> <ul> <li class="hidden"> <a> <span class="icon-loading-small"></span> </a> </li> <li> <a href="#"> <span>No action available</span> </a></li></ul></div>'); + }); + }); + + it('click anywhere else to close the menu', function() { + $('#selector1, #selector2').contactsMenu('user', 0, $appendTo); + + expect($appendTo.find('div').hasClass('hidden')).toEqual(true); + $selector1.click(); + expect($appendTo.find('div').hasClass('hidden')).toEqual(false); + $(document).click(); + expect($appendTo.find('div').hasClass('hidden')).toEqual(true); + }); +}); |