aboutsummaryrefslogtreecommitdiffstats
path: root/apps/comments
diff options
context:
space:
mode:
authorLucas Azevedo <lhs_azevedo@hotmail.com>2023-09-14 00:56:59 -0300
committerJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2023-09-16 14:08:47 +0200
commit03ece129bf930f8e16be69a28a7a1faa369d8e1c (patch)
tree22e542d5bb1fb5ff731d0928ff627e704147bc86 /apps/comments
parentf8ccaf8d580c60fa0bb44a81326023d5131a2df2 (diff)
downloadnextcloud-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.ts193
-rw-r--r--apps/comments/src/actions/inlineUnreadCommentsAction.ts61
-rw-r--r--apps/comments/src/comments.js2
-rw-r--r--apps/comments/src/filesplugin.js141
-rw-r--r--apps/comments/src/logger.js28
-rw-r--r--apps/comments/src/services/CommentsInstance.js9
-rw-r--r--apps/comments/tests/js/filespluginSpec.js117
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();
- });
- });
-});