diff options
author | Lucas Azevedo <lhs_azevedo@hotmail.com> | 2023-09-14 00:56:59 -0300 |
---|---|---|
committer | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2023-09-16 14:08:47 +0200 |
commit | 03ece129bf930f8e16be69a28a7a1faa369d8e1c (patch) | |
tree | 22e542d5bb1fb5ff731d0928ff627e704147bc86 /apps/comments | |
parent | f8ccaf8d580c60fa0bb44a81326023d5131a2df2 (diff) | |
download | nextcloud-server-03ece129bf930f8e16be69a28a7a1faa369d8e1c.tar.gz nextcloud-server-03ece129bf930f8e16be69a28a7a1faa369d8e1c.zip |
refactor(f2v): Migrate unread comments action to the new FileAction API
Signed-off-by: Lucas Azevedo <lhs_azevedo@hotmail.com>
Diffstat (limited to 'apps/comments')
-rw-r--r-- | apps/comments/src/actions/inlineUnreadCommentsAction.spec.ts | 193 | ||||
-rw-r--r-- | apps/comments/src/actions/inlineUnreadCommentsAction.ts | 61 | ||||
-rw-r--r-- | apps/comments/src/comments.js | 2 | ||||
-rw-r--r-- | apps/comments/src/filesplugin.js | 141 | ||||
-rw-r--r-- | apps/comments/src/logger.js | 28 | ||||
-rw-r--r-- | apps/comments/src/services/CommentsInstance.js | 9 | ||||
-rw-r--r-- | apps/comments/tests/js/filespluginSpec.js | 117 |
7 files changed, 285 insertions, 266 deletions
diff --git a/apps/comments/src/actions/inlineUnreadCommentsAction.spec.ts b/apps/comments/src/actions/inlineUnreadCommentsAction.spec.ts new file mode 100644 index 00000000000..9ce192bb477 --- /dev/null +++ b/apps/comments/src/actions/inlineUnreadCommentsAction.spec.ts @@ -0,0 +1,193 @@ +/** + * @copyright Copyright (c) 2023 Lucas Azevedo <lhs_azevedo@hotmail.com> + * + * @author Lucas Azevedo <lhs_azevedo@hotmail.com> + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +import { action } from './inlineUnreadCommentsAction' +import { expect } from '@jest/globals' +import { File, Permission, View, FileAction } from '@nextcloud/files' +import logger from '../logger' + +const view = { + id: 'files', + name: 'Files', +} as View + +describe('Inline unread comments action display name tests', () => { + test('Default values', () => { + const file = new File({ + id: 1, + source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt', + owner: 'admin', + mime: 'text/plain', + permissions: Permission.ALL, + attributes: { + 'comments-unread': 1, + }, + }) + + expect(action).toBeInstanceOf(FileAction) + expect(action.id).toBe('comments-unread') + expect(action.displayName([file], view)).toBe('1 new comment') + expect(action.iconSvgInline([], view)).toBe('<svg>SvgMock</svg>') + expect(action.enabled!([file], view)).toBe(true) + expect(action.inline!(file, view)).toBe(true) + expect(action.default).toBeUndefined() + expect(action.order).toBe(-140) + }) + + test('Display name when file has two new comments', () => { + const file = new File({ + id: 1, + source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt', + owner: 'admin', + mime: 'text/plain', + permissions: Permission.ALL, + attributes: { + 'comments-unread': 2, + }, + }) + + expect(action.displayName([file], view)).toBe('2 new comments') + }) +}) + +describe('Inline unread comments action enabled tests', () => { + test('Action is disabled when comments-unread attribute is missing', () => { + const file = new File({ + id: 1, + source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt', + owner: 'admin', + mime: 'text/plain', + permissions: Permission.ALL, + attributes: { }, + }) + + expect(action.enabled!([file], view)).toBe(false) + }) + + test('Action is disabled when file does not have unread comments', () => { + const file = new File({ + id: 1, + source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt', + owner: 'admin', + mime: 'text/plain', + permissions: Permission.ALL, + attributes: { + 'comments-unread': 0, + }, + }) + + expect(action.enabled!([file], view)).toBe(false) + }) + + test('Action is enabled when file has a single unread comment', () => { + const file = new File({ + id: 1, + source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt', + owner: 'admin', + mime: 'text/plain', + permissions: Permission.ALL, + attributes: { + 'comments-unread': 1, + }, + }) + + expect(action.enabled!([file], view)).toBe(true) + }) + + test('Action is enabled when file has a two unread comments', () => { + const file = new File({ + id: 1, + source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt', + owner: 'admin', + mime: 'text/plain', + permissions: Permission.ALL, + attributes: { + 'comments-unread': 2, + }, + }) + + expect(action.enabled!([file], view)).toBe(true) + }) +}) + +describe('Inline unread comments action execute tests', () => { + test('Action opens sidebar', async () => { + const openMock = jest.fn() + const setActiveTabMock = jest.fn() + window.OCA = { + Files: { + Sidebar: { + open: openMock, + setActiveTab: setActiveTabMock, + }, + }, + } + + const file = new File({ + id: 1, + source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt', + owner: 'admin', + mime: 'text/plain', + permissions: Permission.ALL, + attributes: { + 'comments-unread': 1, + }, + }) + + const result = await action.exec!(file, view, '/') + + expect(result).toBe(null) + expect(setActiveTabMock).toBeCalledWith('comments') + expect(openMock).toBeCalledWith('/foobar.txt') + }) + + test('Action handles sidebar open failure', async () => { + const openMock = jest.fn(() => { throw new Error('Mock error') }) + const setActiveTabMock = jest.fn() + window.OCA = { + Files: { + Sidebar: { + open: openMock, + setActiveTab: setActiveTabMock, + }, + }, + } + jest.spyOn(logger, 'error').mockImplementation(() => jest.fn()) + + const file = new File({ + id: 1, + source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt', + owner: 'admin', + mime: 'text/plain', + permissions: Permission.ALL, + attributes: { + 'comments-unread': 1, + }, + }) + + const result = await action.exec!(file, view, '/') + + expect(result).toBe(false) + expect(setActiveTabMock).toBeCalledWith('comments') + expect(openMock).toBeCalledWith('/foobar.txt') + expect(logger.error).toBeCalledTimes(1) + }) +}) diff --git a/apps/comments/src/actions/inlineUnreadCommentsAction.ts b/apps/comments/src/actions/inlineUnreadCommentsAction.ts new file mode 100644 index 00000000000..e29e7d50b28 --- /dev/null +++ b/apps/comments/src/actions/inlineUnreadCommentsAction.ts @@ -0,0 +1,61 @@ +/** + * @copyright Copyright (c) 2023 Lucas Azevedo <lhs_azevedo@hotmail.com> + * + * @author Lucas Azevedo <lhs_azevedo@hotmail.com> + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +import { FileAction, Node, registerFileAction } from '@nextcloud/files' +import { translate as t, translatePlural as n } from '@nextcloud/l10n' +import commentProcessingSvg from '@mdi/svg/svg/comment-processing.svg?raw' +import logger from '../logger' + +export const action = new FileAction({ + id: 'comments-unread', + + displayName(nodes: Node[]) { + const unread = nodes[0].attributes['comments-unread'] as number + if (unread >= 0) { + return n('comments', '1 new comment', '{unread} new comments', unread, { unread }) + } + return t('comments', 'Comment') + }, + + iconSvgInline: () => commentProcessingSvg, + + enabled(nodes: Node[]) { + const unread = nodes[0].attributes['comments-unread'] as number|undefined + return typeof unread === 'number' && unread > 0 + }, + + async exec(node: Node) { + try { + window.OCA.Files.Sidebar.setActiveTab('comments') + await window.OCA.Files.Sidebar.open(node.path) + return null + } catch (error) { + logger.error('Error while opening sidebar', { error }) + return false + } + }, + + inline: () => true, + + order: -140, +}) + +registerFileAction(action) diff --git a/apps/comments/src/comments.js b/apps/comments/src/comments.js index 076c42894e3..0c0d2b866ee 100644 --- a/apps/comments/src/comments.js +++ b/apps/comments/src/comments.js @@ -23,7 +23,7 @@ import './app.js' import './templates.js' -import './filesplugin.js' import './activitytabviewplugin.js' +import './actions/inlineUnreadCommentsAction.ts' window.OCA.Comments = OCA.Comments diff --git a/apps/comments/src/filesplugin.js b/apps/comments/src/filesplugin.js deleted file mode 100644 index 45d7372dfc7..00000000000 --- a/apps/comments/src/filesplugin.js +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Copyright (c) 2016 Vincent Petry <pvince81@owncloud.com> - * - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Michael Jobst <mjobst+github@tecratech.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -(function() { - - _.extend(OC.Files.Client, { - PROPERTY_COMMENTS_UNREAD: '{' + OC.Files.Client.NS_OWNCLOUD + '}comments-unread', - }) - - OCA.Comments = _.extend({}, OCA.Comments) - if (!OCA.Comments) { - /** - * @namespace - */ - OCA.Comments = {} - } - - /** - * @namespace - */ - OCA.Comments.FilesPlugin = { - ignoreLists: [ - 'trashbin', - 'files.public', - ], - - _formatCommentCount(count) { - return OCA.Comments.Templates.filesplugin({ - count, - countMessage: n('comments', '%n unread comment', '%n unread comments', count), - iconUrl: OC.imagePath('core', 'actions/comment'), - }) - }, - - attach(fileList) { - const self = this - if (this.ignoreLists.indexOf(fileList.id) >= 0) { - return - } - - const oldGetWebdavProperties = fileList._getWebdavProperties - fileList._getWebdavProperties = function() { - const props = oldGetWebdavProperties.apply(this, arguments) - props.push(OC.Files.Client.PROPERTY_COMMENTS_UNREAD) - return props - } - - fileList.filesClient.addFileInfoParser(function(response) { - const data = {} - const props = response.propStat[0].properties - const commentsUnread = props[OC.Files.Client.PROPERTY_COMMENTS_UNREAD] - if (!_.isUndefined(commentsUnread) && commentsUnread !== '') { - data.commentsUnread = parseInt(commentsUnread, 10) - } - return data - }) - - fileList.$el.addClass('has-comments') - const oldCreateRow = fileList._createRow - fileList._createRow = function(fileData) { - const $tr = oldCreateRow.apply(this, arguments) - if (fileData.commentsUnread) { - $tr.attr('data-comments-unread', fileData.commentsUnread) - } - return $tr - } - - // register "comment" action for reading comments - fileList.fileActions.registerAction({ - name: 'Comment', - displayName(context) { - if (context && context.$file) { - const unread = parseInt(context.$file.data('comments-unread'), 10) - if (unread >= 0) { - return n('comments', '1 new comment', '{unread} new comments', unread, { unread }) - } - } - return t('comments', 'Comment') - }, - mime: 'all', - order: -140, - iconClass: 'icon-comment', - permissions: OC.PERMISSION_READ, - type: OCA.Files.FileActions.TYPE_INLINE, - render(actionSpec, isDefault, context) { - const $file = context.$file - const unreadComments = $file.data('comments-unread') - if (unreadComments) { - const $actionLink = $(self._formatCommentCount(unreadComments)) - context.$file.find('a.name>span.fileactions').append($actionLink) - return $actionLink - } - return '' - }, - actionHandler(fileName, context) { - context.$file.find('.action-comment').tooltip('hide') - // open sidebar in comments section - OCA.Files.Sidebar.setActiveTab('comments') - OCA.Files.Sidebar.open(context.dir + '/' + fileName) - }, - }) - - // add attribute to "elementToFile" - const oldElementToFile = fileList.elementToFile - fileList.elementToFile = function($el) { - const fileInfo = oldElementToFile.apply(this, arguments) - const commentsUnread = $el.data('comments-unread') - if (commentsUnread) { - fileInfo.commentsUnread = commentsUnread - } - return fileInfo - } - }, - } - -})() - -OC.Plugins.register('OCA.Files.FileList', OCA.Comments.FilesPlugin) diff --git a/apps/comments/src/logger.js b/apps/comments/src/logger.js new file mode 100644 index 00000000000..d96e85dd92e --- /dev/null +++ b/apps/comments/src/logger.js @@ -0,0 +1,28 @@ +/** + * @copyright Copyright (c) 2023 Lucas Azevedo <lhs_azevedo@hotmail.com> + * + * @author Lucas Azevedo <lhs_azevedo@hotmail.com> + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +import { getLoggerBuilder } from '@nextcloud/logger' + +export default getLoggerBuilder() + .setApp('comments') + .detectUser() + .build() diff --git a/apps/comments/src/services/CommentsInstance.js b/apps/comments/src/services/CommentsInstance.js index 838305a00e9..e283e833c34 100644 --- a/apps/comments/src/services/CommentsInstance.js +++ b/apps/comments/src/services/CommentsInstance.js @@ -20,20 +20,15 @@ * */ -import { getLoggerBuilder } from '@nextcloud/logger' import { translate as t, translatePlural as n } from '@nextcloud/l10n' import { getRequestToken } from '@nextcloud/auth' -import CommentsApp from '../views/Comments.vue' import Vue from 'vue' +import CommentsApp from '../views/Comments.vue' +import logger from '../logger.js' // eslint-disable-next-line camelcase __webpack_nonce__ = btoa(getRequestToken()) -const logger = getLoggerBuilder() - .setApp('comments') - .detectUser() - .build() - // Add translates functions Vue.mixin({ data() { diff --git a/apps/comments/tests/js/filespluginSpec.js b/apps/comments/tests/js/filespluginSpec.js deleted file mode 100644 index 4230a77874d..00000000000 --- a/apps/comments/tests/js/filespluginSpec.js +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright (c) 2016 Vincent Petry <pvince81@owncloud.com> - * - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -describe('OCA.Comments.FilesPlugin tests', function() { - var fileList; - var testFiles; - - beforeEach(function() { - var $content = $('<div id="app-content"></div>'); - $('#testArea').append($content); - // dummy file list - var $div = $( - '<div>' + - '<table class="files-filestable">' + - '<thead></thead>' + - '<tbody class="files-fileList"></tbody>' + - '</table>' + - '</div>'); - $('#app-content').append($div); - - fileList = new OCA.Files.FileList($div); - OCA.Comments.FilesPlugin.attach(fileList); - - testFiles = [{ - id: 1, - type: 'file', - name: 'One.txt', - path: '/subdir', - mimetype: 'text/plain', - size: 12, - permissions: OC.PERMISSION_ALL, - etag: 'abc', - shareOwner: 'User One', - isShareMountPoint: false, - commentsUnread: 3 - }]; - }); - afterEach(function() { - fileList.destroy(); - fileList = null; - }); - - describe('Comment icon', function() { - it('does not render icon when no unread comments available', function() { - testFiles[0].commentsUnread = 0; - fileList.setFiles(testFiles); - var $tr = fileList.findFileEl('One.txt'); - expect($tr.find('.action-comment').length).toEqual(0); - }); - it('renders comment icon and extra data', function() { - var $action, $tr; - fileList.setFiles(testFiles); - $tr = fileList.findFileEl('One.txt'); - $action = $tr.find('.action-comment'); - expect($action.length).toEqual(1); - expect($action.hasClass('permanent')).toEqual(true); - - expect($tr.attr('data-comments-unread')).toEqual('3'); - }); - it('clicking icon opens sidebar', function() { - var sidebarTabStub = sinon.stub(OCA.Files.Sidebar, 'setActiveTab'); - var sidebarStub = sinon.stub(OCA.Files.Sidebar, 'open'); - var $action, $tr; - fileList.setFiles(testFiles); - $tr = fileList.findFileEl('One.txt'); - $action = $tr.find('.action-comment'); - $action.click(); - - expect(sidebarTabStub.calledOnce).toEqual(true); - expect(sidebarTabStub.lastCall.args[0]).toEqual('comments'); - expect(sidebarStub.calledOnce).toEqual(true); - expect(sidebarStub.lastCall.args[0]).toEqual('/subdir/One.txt'); - }); - }); - describe('elementToFile', function() { - it('returns comment count', function() { - fileList.setFiles(testFiles); - var $tr = fileList.findFileEl('One.txt'); - var data = fileList.elementToFile($tr); - expect(data.commentsUnread).toEqual(3); - }); - it('does not set comment count when not set', function() { - delete testFiles[0].commentsUnread; - fileList.setFiles(testFiles); - var $tr = fileList.findFileEl('One.txt'); - var data = fileList.elementToFile($tr); - expect(data.commentsUnread).not.toBeDefined(); - }); - it('does not set comment count when zero', function() { - testFiles[0].commentsUnread = 0; - fileList.setFiles(testFiles); - var $tr = fileList.findFileEl('One.txt'); - var data = fileList.elementToFile($tr); - expect(data.commentsUnread).not.toBeDefined(); - }); - }); -}); |