Commit a762a135 authored by Sheetal Umesh Kumar's avatar Sheetal Umesh Kumar Committed by GitHub

Implement moderation UI for group reader for flagging an annotation. (#296)

https://github.com/hypothesis/product-backlog/issues/181
parent 3030bf40
......@@ -60,6 +60,7 @@ function analytics($analytics, $window, settings) {
events: {
ANNOTATION_CREATED: 'annotationCreated',
ANNOTATION_DELETED: 'annotationDeleted',
ANNOTATION_FLAGGED: 'annotationFlagged',
ANNOTATION_UPDATED: 'annotationUpdated',
HIGHLIGHT_CREATED: 'highlightCreated',
HIGHLIGHT_UPDATED: 'highlightUpdated',
......
......@@ -54,11 +54,21 @@ function annotationMapper($rootScope, annotationUI, store) {
});
}
function flagAnnotation(annot) {
return store.flag.create(null,{
annotation: annot.id,
}).then(function () {
$rootScope.$broadcast(events.ANNOTATION_FLAGGED, annot);
return annot;
});
}
return {
loadAnnotations: loadAnnotations,
unloadAnnotations: unloadAnnotations,
createAnnotation: createAnnotation,
deleteAnnotation: deleteAnnotation,
flagAnnotation: flagAnnotation,
};
}
......
......@@ -220,6 +220,22 @@ function AnnotationController(
return permissions.permits(action, vm.annotation, session.state.userid);
};
/**
* @ngdoc method
* @name annotation.AnnotationController#flag
* @description Flag the annotation.
*/
vm.flag = function() {
var onRejected = function(reason) {
flash.error(
errorMessage(reason), 'Flagging annotation failed');
};
annotationMapper.flagAnnotation(vm.annotation).then(function(){
analytics.track(analytics.events.ANNOTATION_FLAGGED);
vm.isFlagged = true;
}, onRejected);
};
/**
* @ngdoc method
* @name annotation.AnnotationController#delete
......
......@@ -138,6 +138,7 @@ describe('annotation', function() {
},
}),
deleteAnnotation: sandbox.stub(),
flagAnnotation: sandbox.stub(),
};
var fakeAnnotationUI = {};
......@@ -689,6 +690,47 @@ describe('annotation', function() {
});
});
describe('#flag()', function() {
beforeEach(function() {
fakeAnnotationMapper.flagAnnotation = sandbox.stub();
});
it(
'calls annotationMapper.flag() when an annotation is flagged',
function(done) {
var parts = createDirective();
fakeAnnotationMapper.flagAnnotation.returns($q.resolve());
parts.controller.flag();
assert.calledWith(fakeAnnotationMapper.flagAnnotation,
parts.annotation);
done();
}
);
it('flashes an error if the flag fails', function(done) {
var controller = createDirective().controller;
fakeAnnotationMapper.flagAnnotation.returns(Promise.reject({
status: 500,
statusText: 'Server error',
}));
controller.flag();
setTimeout(function () {
assert.calledWith(fakeFlash.error, '500 Server error', 'Flagging annotation failed');
done();
}, 0);
});
it('doesn\'t flash an error if the flag succeeds', function(done) {
var controller = createDirective().controller;
fakeAnnotationMapper.flagAnnotation.returns($q.resolve());
controller.flag();
setTimeout(function () {
assert.notCalled(fakeFlash.error);
done();
}, 0);
});
});
describe('#documentMeta()', function () {
it('returns the domain, title link and text for the annotation', function () {
var annot = fixtures.defaultAnnotation();
......
......@@ -31,6 +31,9 @@ module.exports = {
/** An annotation was either deleted or unloaded. */
ANNOTATION_DELETED: 'annotationDeleted',
/** An annotation was flagged. */
ANNOTATION_FLAGGED: 'annotationFlagged',
/** An annotation has been updated. */
ANNOTATION_UPDATED: 'annotationUpdated',
......
......@@ -143,6 +143,9 @@ function store($http, $q, auth, settings) {
get: apiCall('annotation.read'),
update: apiCall('annotation.update'),
},
flag: {
create: apiCall('flag.create'),
},
profile: {
read: apiCall('profile.read'),
update: apiCall('profile.update'),
......
......@@ -185,6 +185,23 @@
on-close="vm.showShareDialog = false">
</annotation-share-dialog>
</span>
<span ng-if="vm.isThirdPartyUser()">
<button class="btn btn-clean annotation-action-btn"
ng-if="!vm.isFlagged"
ng-click="vm.flag()"
ng-disabled="vm.isDeleted()"
aria-label="Flag"
h-tooltip>
<i class="h-icon-annotation-flag btn-icon"></i>
</button>
<button class="btn btn-clean annotation-action-btn"
ng-if="vm.isFlagged"
ng-disabled="vm.isDeleted()"
aria-label="Annotation has been flagged"
h-tooltip>
<i class="h-icon-annotation-flag annotation--flagged btn-icon"></i>
</button>
</span>
</div>
</footer>
</div>
......@@ -17,6 +17,9 @@ describe('annotationMapper', function() {
annotation: {
delete: sinon.stub().returns(Promise.resolve({})),
},
flag: {
create: sinon.stub().returns(Promise.resolve({})),
},
};
angular.module('app', [])
.service('annotationMapper', require('../annotation-mapper'))
......@@ -124,6 +127,24 @@ describe('annotationMapper', function() {
});
});
describe('#flagAnnotation()', function () {
it('flags an annotation', function () {
var ann = {id: 'test-id'};
annotationMapper.flagAnnotation(ann);
assert.calledOnce(fakeStore.flag.create);
assert.calledWith(fakeStore.flag.create, null, {annotation: ann.id});
});
it('emits the "annotationFlagged" event', function (done) {
sandbox.stub($rootScope, '$broadcast');
var ann = {id: 'test-id'};
annotationMapper.flagAnnotation(ann).then(function () {
assert.calledWith($rootScope.$broadcast,
events.ANNOTATION_FLAGGED, ann);
}).then(done, done);
});
});
describe('#createAnnotation()', function () {
it('creates a new annotation resource', function () {
var ann = {};
......
......@@ -67,6 +67,12 @@ describe('store', function () {
url: 'http://example.com/api/annotations/:id',
},
},
flag: {
create: {
method: 'POST',
url: 'http://example.com/api/flags',
},
},
search: {
method: 'GET',
url: 'http://example.com/api/search',
......@@ -123,6 +129,18 @@ describe('store', function () {
$httpBackend.flush();
});
it('flags an annotation', function (done) {
store.flag.create(null, {annotation: 'an-id'}).then(function () {
done();
});
$httpBackend.expectPOST('http://example.com/api/flags')
.respond(function () {
return [204, {}, {}];
});
$httpBackend.flush();
});
it('removes internal properties before sending data to the server', function (done) {
var annotation = {
$highlight: true,
......
......@@ -261,3 +261,8 @@
margin-bottom: $layout-h-margin - 3px;
}
}
.annotation--flagged {
color: $brand-color;
cursor: default;
}
{
"IcoMoonType": "selection",
"icons": [
{
"icon": {
"paths": [
"M472.615 78.769c159.111 0 195.411-78.769 315.077-78.769s236.308 78.769 236.308 78.769v551.385c0 0-80.282-78.769-236.308-78.769s-159.050 70.968-315.077 78.769c-156.027 7.801-315.077-47.262-315.077-47.262v362.246c0 43.554-34.963 78.861-78.769 78.861v0c-43.503 0-78.769-35.248-78.769-78.651v-866.697c0-43.438 34.884-70.222 76.689-60.58 0 0 236.815 60.698 395.926 60.698z"
],
"attrs": [
{
"fill": "rgb(166, 166, 166)"
}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"annotation-flag"
],
"colorPermutations": {
"12212212211661661661": [
{
"f": 1
}
]
}
},
"attrs": [
{
"fill": "rgb(166, 166, 166)"
}
],
"properties": {
"order": 71,
"id": 39,
"name": "annotation-flag",
"prevSize": 24,
"code": 59661
},
"setIdx": 0,
"setId": 1,
"iconIdx": 1
},
{
"icon": {
"paths": [
......@@ -18,7 +58,9 @@
],
"colorPermutations": {
"12212212211661661661": [
1
{
"f": 1
}
]
}
},
......@@ -36,7 +78,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 0
"iconIdx": 2
},
{
"icon": {
......@@ -56,7 +98,9 @@
],
"colorPermutations": {
"12212212211661661661": [
1
{
"f": 1
}
]
}
},
......@@ -74,7 +118,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 1
"iconIdx": 3
},
{
"icon": {
......@@ -93,7 +137,9 @@
"grid": 16,
"colorPermutations": {
"12212212211661661661": [
1
{
"f": 1
}
]
},
"width": 896
......@@ -112,7 +158,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 2
"iconIdx": 4
},
{
"icon": {
......@@ -132,7 +178,9 @@
"grid": 16,
"colorPermutations": {
"12212212211661661661": [
1
{
"f": 1
}
]
},
"width": 864
......@@ -151,7 +199,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 3
"iconIdx": 5
},
{
"icon": {
......@@ -176,8 +224,12 @@
"grid": 16,
"colorPermutations": {
"12212212211661661661": [
1,
1
{
"f": 1
},
{
"f": 1
}
]
}
},
......@@ -198,7 +250,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 4
"iconIdx": 6
},
{
"icon": {
......@@ -217,7 +269,9 @@
"grid": 16,
"colorPermutations": {
"12212212211661661661": [
1
{
"f": 1
}
]
},
"width": 768
......@@ -236,7 +290,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 5
"iconIdx": 7
},
{
"icon": {
......@@ -262,8 +316,12 @@
"grid": 16,
"colorPermutations": {
"12212212211661661661": [
1,
1
{
"f": 1
},
{
"f": 1
}
]
}
},
......@@ -284,7 +342,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 6
"iconIdx": 8
},
{
"icon": {
......@@ -299,7 +357,9 @@
"isMulticolor": false,
"colorPermutations": {
"12212212211661661661": [
0
{
"f": 0
}
]
},
"tags": [
......@@ -321,7 +381,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 7
"iconIdx": 9
},
{
"icon": {
......@@ -344,9 +404,15 @@
"isMulticolor": false,
"colorPermutations": {
"12212212211661661661": [
0,
0,
0
{
"f": 0
},
{
"f": 0
},
{
"f": 0
}
]
},
"tags": [
......@@ -374,7 +440,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 8
"iconIdx": 10
},
{
"icon": {
......@@ -389,7 +455,9 @@
"isMulticolor": false,
"colorPermutations": {
"12212212211661661661": [
0
{
"f": 0
}
]
},
"tags": [
......@@ -411,7 +479,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 9
"iconIdx": 11
},
{
"icon": {
......@@ -431,7 +499,9 @@
"grid": 16,
"colorPermutations": {
"12212212211661661661": [
1
{
"f": 1
}
]
}
},
......@@ -449,7 +519,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 10
"iconIdx": 12
},
{
"icon": {
......@@ -472,8 +542,12 @@
"grid": 16,
"colorPermutations": {
"12212212211661661661": [
1,
1
{
"f": 1
},
{
"f": 1
}
]
}
},
......@@ -494,7 +568,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 11
"iconIdx": 13
},
{
"icon": {
......@@ -517,8 +591,12 @@
"grid": 16,
"colorPermutations": {
"12212212211661661661": [
1,
1
{
"f": 1
},
{
"f": 1
}
]
}
},
......@@ -539,7 +617,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 12
"iconIdx": 14
},
{
"icon": {
......@@ -570,7 +648,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 13
"iconIdx": 15
},
{
"icon": {
......@@ -601,7 +679,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 14
"iconIdx": 16
},
{
"icon": {
......@@ -623,7 +701,9 @@
"grid": 16,
"colorPermutations": {
"12212212211661661661": [
1
{
"f": 1
}
]
}
},
......@@ -642,7 +722,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 15
"iconIdx": 17
},
{
"icon": {
......@@ -674,7 +754,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 16
"iconIdx": 18
},
{
"icon": {
......@@ -705,7 +785,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 17
"iconIdx": 19
},
{
"icon": {
......@@ -735,7 +815,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 18
"iconIdx": 20
},
{
"icon": {
......@@ -768,7 +848,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 19
"iconIdx": 21
},
{
"icon": {
......@@ -796,9 +876,15 @@
"grid": 16,
"colorPermutations": {
"12212212211661661661": [
1,
1,
1
{
"f": 1
},
{
"f": 1
},
{
"f": 1
}
]
}
},
......@@ -822,7 +908,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 20
"iconIdx": 22
},
{
"icon": {
......@@ -862,7 +948,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 21
"iconIdx": 23
},
{
"icon": {
......@@ -875,7 +961,9 @@
"isMulticolor": false,
"colorPermutations": {
"6868681": [
0
{
"f": 0
}
]
},
"tags": [
......@@ -896,7 +984,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 22
"iconIdx": 24
},
{
"icon": {
......@@ -923,7 +1011,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 23
"iconIdx": 25
},
{
"icon": {
......@@ -951,7 +1039,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 24
"iconIdx": 26
},
{
"icon": {
......@@ -979,7 +1067,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 25
"iconIdx": 27
},
{
"icon": {
......@@ -1007,7 +1095,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 26
"iconIdx": 28
},
{
"icon": {
......@@ -1035,7 +1123,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 27
"iconIdx": 29
},
{
"icon": {
......@@ -1063,7 +1151,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 28
"iconIdx": 30
},
{
"icon": {
......@@ -1091,7 +1179,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 29
"iconIdx": 31
},
{
"icon": {
......@@ -1119,7 +1207,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 30
"iconIdx": 32
},
{
"icon": {
......@@ -1147,7 +1235,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 31
"iconIdx": 33
},
{
"icon": {
......@@ -1175,7 +1263,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 32
"iconIdx": 34
},
{
"icon": {
......@@ -1203,7 +1291,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 33
"iconIdx": 35
},
{
"icon": {
......@@ -1232,7 +1320,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 34
"iconIdx": 36
},
{
"icon": {
......@@ -1260,7 +1348,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 35
"iconIdx": 37
},
{
"icon": {
......@@ -1288,7 +1376,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 36
"iconIdx": 38
},
{
"icon": {
......@@ -1316,7 +1404,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 37
"iconIdx": 39
},
{
"icon": {
......@@ -1344,7 +1432,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 38
"iconIdx": 40
},
{
"icon": {
......@@ -1372,7 +1460,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 39
"iconIdx": 41
},
{
"icon": {
......@@ -1400,7 +1488,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 40
"iconIdx": 42
},
{
"icon": {
......@@ -1428,7 +1516,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 41
"iconIdx": 43
},
{
"icon": {
......@@ -1456,7 +1544,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 42
"iconIdx": 44
},
{
"icon": {
......@@ -1485,7 +1573,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 43
"iconIdx": 45
},
{
"icon": {
......@@ -1513,7 +1601,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 44
"iconIdx": 46
},
{
"icon": {
......@@ -1541,7 +1629,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 45
"iconIdx": 47
},
{
"icon": {
......@@ -1569,7 +1657,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 46
"iconIdx": 48
},
{
"icon": {
......@@ -1597,7 +1685,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 47
"iconIdx": 49
},
{
"icon": {
......@@ -1625,7 +1713,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 48
"iconIdx": 50
},
{
"icon": {
......@@ -1653,7 +1741,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 49
"iconIdx": 51
},
{
"icon": {
......@@ -1681,7 +1769,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 50
"iconIdx": 52
},
{
"icon": {
......@@ -1709,7 +1797,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 51
"iconIdx": 53
},
{
"icon": {
......@@ -1737,7 +1825,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 52
"iconIdx": 54
},
{
"icon": {
......@@ -1765,7 +1853,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 53
"iconIdx": 55
},
{
"icon": {
......@@ -1793,7 +1881,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 54
"iconIdx": 56
},
{
"icon": {
......@@ -1821,7 +1909,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 55
"iconIdx": 57
},
{
"icon": {
......@@ -1849,7 +1937,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 56
"iconIdx": 58
},
{
"icon": {
......@@ -1877,7 +1965,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 57
"iconIdx": 59
},
{
"icon": {
......@@ -1905,7 +1993,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 58
"iconIdx": 60
},
{
"icon": {
......@@ -1933,7 +2021,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 59
"iconIdx": 61
},
{
"icon": {
......@@ -1962,7 +2050,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 60
"iconIdx": 62
}
],
"height": 1024,
......
......@@ -20,6 +20,9 @@
-moz-osx-font-smoothing: grayscale;
}
.h-icon-annotation-flag:before {
content: "\e90d";
}
.h-icon-clipboard:before {
content: "\e90c";
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment