Commit d9da9fb5 authored by Nick Stenning's avatar Nick Stenning Committed by GitHub

Merge pull request #3543 from hypothesis/match-annot-by-id-and-tag

Fix #2965 (3/N) - Match annotations by ID or tag in drafts service and when removing annotations
parents 40ab48f7 de6fa295
...@@ -81,15 +81,25 @@ var types = { ...@@ -81,15 +81,25 @@ var types = {
SELECT_TAB: 'SELECT_TAB', SELECT_TAB: 'SELECT_TAB',
}; };
/**
* Return a copy of `current` with all matching annotations in `annotations`
* removed.
*/
function excludeAnnotations(current, annotations) { function excludeAnnotations(current, annotations) {
var idsAndTags = annotations.reduce(function (map, annot) { var ids = {};
var id = annot.id || annot.$$tag; var tags = {};
map[id] = true; annotations.forEach(function (annot) {
return map; if (annot.id) {
}, {}); ids[annot.id] = true;
}
if (annot.$$tag) {
tags[annot.$$tag] = true;
}
});
return current.filter(function (annot) { return current.filter(function (annot) {
var id = annot.id || annot.$$tag; var shouldRemove = (annot.id && (annot.id in ids)) ||
return !idsAndTags[id]; (annot.$$tag && (annot.$$tag in tags));
return !shouldRemove;
}); });
} }
......
...@@ -23,11 +23,10 @@ function DraftStore() { ...@@ -23,11 +23,10 @@ function DraftStore() {
* Returns true if 'draft' is a draft for a given * Returns true if 'draft' is a draft for a given
* annotation. * annotation.
* *
* Annotations are matched by ID and annotation instance (for unsaved * Annotations are matched by ID or local tag.
* annotations which have no ID)
*/ */
function match(draft, model) { function match(draft, model) {
return draft.model === model || return (draft.model.$$tag && model.$$tag === draft.model.$$tag) ||
(draft.model.id && model.id === draft.model.id); (draft.model.id && model.id === draft.model.id);
} }
......
'use strict'; 'use strict';
var annotationUIFactory = require('../annotation-ui'); var immutable = require('seamless-immutable');
var annotationUIFactory = require('../annotation-ui');
var annotationFixtures = require('./annotation-fixtures'); var annotationFixtures = require('./annotation-fixtures');
var unroll = require('./util').unroll; var unroll = require('./util').unroll;
var defaultAnnotation = annotationFixtures.defaultAnnotation;
var fixtures = immutable({
pair: [
Object.assign(defaultAnnotation(), {id: 1, $$tag: 't1'}),
Object.assign(defaultAnnotation(), {id: 2, $$tag: 't2'}),
],
});
describe('annotationUI', function () { describe('annotationUI', function () {
var annotationUI; var annotationUI;
...@@ -36,7 +45,7 @@ describe('annotationUI', function () { ...@@ -36,7 +45,7 @@ describe('annotationUI', function () {
describe('#addAnnotations()', function () { describe('#addAnnotations()', function () {
it('adds annotations to the current state', function () { it('adds annotations to the current state', function () {
var annot = annotationFixtures.defaultAnnotation(); var annot = defaultAnnotation();
annotationUI.addAnnotations([annot]); annotationUI.addAnnotations([annot]);
assert.deepEqual(annotationUI.getState().annotations, [annot]); assert.deepEqual(annotationUI.getState().annotations, [annot]);
}); });
...@@ -44,16 +53,28 @@ describe('annotationUI', function () { ...@@ -44,16 +53,28 @@ describe('annotationUI', function () {
describe('#removeAnnotations()', function () { describe('#removeAnnotations()', function () {
it('removes annotations from the current state', function () { it('removes annotations from the current state', function () {
var annot = annotationFixtures.defaultAnnotation(); var annot = defaultAnnotation();
annotationUI.addAnnotations([annot]); annotationUI.addAnnotations([annot]);
annotationUI.removeAnnotations([annot]); annotationUI.removeAnnotations([annot]);
assert.deepEqual(annotationUI.getState().annotations, []); assert.deepEqual(annotationUI.getState().annotations, []);
}); });
it('matches annotations to remove by ID', function () {
annotationUI.addAnnotations(fixtures.pair);
annotationUI.removeAnnotations([{id: fixtures.pair[0].id}]);
assert.deepEqual(annotationUI.getState().annotations, [fixtures.pair[1]]);
});
it('matches annotations to remove by tag', function () {
annotationUI.addAnnotations(fixtures.pair);
annotationUI.removeAnnotations([{$$tag: fixtures.pair[0].$$tag}]);
assert.deepEqual(annotationUI.getState().annotations, [fixtures.pair[1]]);
});
}); });
describe('#clearAnnotations()', function () { describe('#clearAnnotations()', function () {
it('removes all annotations', function () { it('removes all annotations', function () {
var annot = annotationFixtures.defaultAnnotation(); var annot = defaultAnnotation();
annotationUI.addAnnotations([annot]); annotationUI.addAnnotations([annot]);
annotationUI.clearAnnotations(); annotationUI.clearAnnotations();
assert.deepEqual(annotationUI.getState().annotations, []); assert.deepEqual(annotationUI.getState().annotations, []);
......
'use strict';
var draftsService = require('../drafts'); var draftsService = require('../drafts');
describe('drafts', function () { describe('drafts', function () {
...@@ -7,7 +9,7 @@ describe('drafts', function () { ...@@ -7,7 +9,7 @@ describe('drafts', function () {
drafts = draftsService(); drafts = draftsService();
}); });
describe('.update', function () { describe('#update', function () {
it('should save changes', function () { it('should save changes', function () {
var model = {id: 'foo'}; var model = {id: 'foo'};
assert.notOk(drafts.get(model)); assert.notOk(drafts.get(model));
...@@ -31,9 +33,17 @@ describe('drafts', function () { ...@@ -31,9 +33,17 @@ describe('drafts', function () {
drafts.update(modelB, {isPrivate:true, tags:['foo'], text:'bar'}); drafts.update(modelB, {isPrivate:true, tags:['foo'], text:'bar'});
assert.equal(drafts.get(modelA).text, 'bar'); assert.equal(drafts.get(modelA).text, 'bar');
}); });
it('should replace drafts with the same tag', function () {
var modelA = {$$tag: 'foo'};
var modelB = {$$tag: 'foo'};
drafts.update(modelA, {isPrivate:true, tags:['foo'], text:'foo'});
drafts.update(modelB, {isPrivate:true, tags:['foo'], text:'bar'});
assert.equal(drafts.get(modelA).text, 'bar');
});
}); });
describe('.remove', function () { describe('#remove', function () {
it('should remove drafts', function () { it('should remove drafts', function () {
var model = {id: 'foo'}; var model = {id: 'foo'};
drafts.update(model, {text: 'bar'}); drafts.update(model, {text: 'bar'});
...@@ -42,7 +52,7 @@ describe('drafts', function () { ...@@ -42,7 +52,7 @@ describe('drafts', function () {
}); });
}); });
describe('.unsaved', function () { describe('#unsaved', function () {
it('should return drafts for unsaved annotations', function () { it('should return drafts for unsaved annotations', function () {
var model = {}; var model = {};
drafts.update(model, {text: 'bar'}); drafts.update(model, {text: 'bar'});
......
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