Add "Favorite" action to the file actions menu

The new FileAction for the menu is essentially the same as the old
inline FileAction, except for the rendering; in this case the FileAction
is shown in the menu in a standard way, so there is no need to provide a
custom renderer (although the menu entry text and icon change depending
on whether the file is currently a favorite or not, but that can be done
just with displayName and iconClass functions).

Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
This commit is contained in:
Daniel Calviño Sánchez 2017-09-27 08:07:20 +02:00
parent a8f1902b02
commit 420d1b91ab
3 changed files with 131 additions and 2 deletions

View file

@ -502,7 +502,7 @@ table td.filename .uploadtext {
display: inline-block;
float: left;
}
#fileList tr td.filename .action-favorite {
#fileList tr td.filename .action-favorite:not(.menuitem) {
display: block;
float: left;
width: 30px;

View file

@ -86,7 +86,7 @@
var self = this;
// register "star" action
fileActions.registerAction({
name: 'Favorite',
name: 'FavoriteInline',
displayName: t('files', 'Favorite'),
mime: 'all',
permissions: OC.PERMISSION_READ,
@ -141,6 +141,79 @@
});
}
});
fileActions.registerAction({
name: 'Favorite',
displayName: function(context) {
var $file = context.$file;
var isFavorite = $file.data('favorite') === true;
if (isFavorite) {
return t('files', 'Remove from favorites');
}
// As it is currently not possible to provide a context for
// the i18n strings "Add to favorites" was used instead of
// "Favorite" to remove the ambiguity between verb and noun
// when it is translated.
return t('files', 'Add to favorites');
},
mime: 'all',
order: -23,
permissions: OC.PERMISSION_READ,
iconClass: function(fileName, context) {
var $file = context.$file;
var isFavorite = $file.data('favorite') === true;
if (isFavorite) {
return 'icon-starred';
}
return 'icon-star';
},
actionHandler: function(fileName, context) {
var $actionEl = context.$file.find('.action-favorite');
var $file = context.$file;
var fileInfo = context.fileList.files[$file.index()];
var dir = context.dir || context.fileList.getCurrentDirectory();
var tags = $file.attr('data-tags');
if (_.isUndefined(tags)) {
tags = '';
}
tags = tags.split('|');
tags = _.without(tags, '');
var isFavorite = tags.indexOf(OC.TAG_FAVORITE) >= 0;
if (isFavorite) {
// remove tag from list
tags = _.without(tags, OC.TAG_FAVORITE);
} else {
tags.push(OC.TAG_FAVORITE);
}
// pre-toggle the star
toggleStar($actionEl, !isFavorite);
context.fileInfoModel.trigger('busy', context.fileInfoModel, true);
self.applyFileTags(
dir + '/' + fileName,
tags,
$actionEl,
isFavorite
).then(function(result) {
context.fileInfoModel.trigger('busy', context.fileInfoModel, false);
// response from server should contain updated tags
var newTags = result.tags;
if (_.isUndefined(newTags)) {
newTags = tags;
}
context.fileInfoModel.set({
'tags': newTags,
'favorite': !isFavorite
});
});
}
});
},
_extendFileList: function(fileList) {

View file

@ -117,6 +117,62 @@ describe('OCA.Files.TagsPlugin tests', function() {
$tr = fileList.findFileEl('One.txt');
$action = $tr.find('.action-favorite');
expect($tr.attr('data-favorite')).toBeFalsy();
expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2', 'tag3']);
expect(fileList.files[0].tags).toEqual(['tag1', 'tag2', 'tag3']);
expect($action.find('.icon').hasClass('icon-star')).toEqual(true);
expect($action.find('.icon').hasClass('icon-starred')).toEqual(false);
});
it('through FileActionsMenu sends request to server and updates icon', function() {
var request;
fileList.setFiles(testFiles);
var $tr = fileList.findFileEl('One.txt');
var $action = $tr.find('.action-favorite');
var $showMenuAction = $tr.find('.action-menu');
$showMenuAction.click();
var $favoriteActionInMenu = $tr.find('.fileActionsMenu .action-favorite');
$favoriteActionInMenu.click();
expect(fakeServer.requests.length).toEqual(1);
request = fakeServer.requests[0];
expect(JSON.parse(request.requestBody)).toEqual({
tags: ['tag1', 'tag2', OC.TAG_FAVORITE]
});
request.respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
tags: ['tag1', 'tag2', 'tag3', OC.TAG_FAVORITE]
}));
// re-read the element as it was re-inserted
$tr = fileList.findFileEl('One.txt');
$action = $tr.find('.action-favorite');
$showMenuAction = $tr.find('.action-menu');
expect($tr.attr('data-favorite')).toEqual('true');
expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2', 'tag3', OC.TAG_FAVORITE]);
expect(fileList.files[0].tags).toEqual(['tag1', 'tag2', 'tag3', OC.TAG_FAVORITE]);
expect($action.find('.icon').hasClass('icon-star')).toEqual(false);
expect($action.find('.icon').hasClass('icon-starred')).toEqual(true);
// show again the menu and get the new action, as the menu was
// closed and removed (and with it, the previous action) when that
// action was clicked
$showMenuAction.click();
$favoriteActionInMenu = $tr.find('.fileActionsMenu .action-favorite');
$favoriteActionInMenu.click();
expect(fakeServer.requests.length).toEqual(2);
request = fakeServer.requests[1];
expect(JSON.parse(request.requestBody)).toEqual({
tags: ['tag1', 'tag2', 'tag3']
});
request.respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
tags: ['tag1', 'tag2', 'tag3']
}));
// re-read the element as it was re-inserted
$tr = fileList.findFileEl('One.txt');
$action = $tr.find('.action-favorite');
expect($tr.attr('data-favorite')).toBeFalsy();
expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2', 'tag3']);
expect(fileList.files[0].tags).toEqual(['tag1', 'tag2', 'tag3']);