mirror of https://github.com/nextcloud/server.git
refactor(f2v): Migrate unread comments action to the new FileAction API
Signed-off-by: Lucas Azevedo <lhs_azevedo@hotmail.com>pull/40409/head
parent
f8ccaf8d58
commit
03ece129bf
@ -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)
|
||||
})
|
||||
})
|
@ -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)
|
@ -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)
|
@ -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()
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue