Commit 34f6950f authored by Robert Knight's avatar Robert Knight

Eliminate extra reference to Annotation domain object

As a step towards making <annotation> stateless or nearly-stateless and
instead just a display of an annotation derived from the current
annotation instance and its draft, remove the `domainModel` copy of
`vm.annotation`.
parent e25710c9
...@@ -38,8 +38,8 @@ function errorMessage(reason) { ...@@ -38,8 +38,8 @@ function errorMessage(reason) {
* If there are no draft changes to this annotation, does nothing. * If there are no draft changes to this annotation, does nothing.
* *
*/ */
function restoreFromDrafts(drafts, domainModel, vm) { function restoreFromDrafts(drafts, vm) {
var draft = drafts.get(domainModel); var draft = drafts.get(vm.annotation);
if (draft) { if (draft) {
vm.isPrivate = draft.isPrivate; vm.isPrivate = draft.isPrivate;
vm.form.tags = draft.tags; vm.form.tags = draft.tags;
...@@ -53,17 +53,17 @@ function restoreFromDrafts(drafts, domainModel, vm) { ...@@ -53,17 +53,17 @@ function restoreFromDrafts(drafts, domainModel, vm) {
* Any existing drafts for this annotation will be overwritten. * Any existing drafts for this annotation will be overwritten.
* *
* @param {object} drafts - The drafts service * @param {object} drafts - The drafts service
* @param {object} domainModel - The full domainModel object of the * @param {object} vm.annotation - The full vm.annotation object of the
* annotation to be saved. This full domainModel model is not retrieved * annotation to be saved. This full vm.annotation model is not retrieved
* again from drafts, it's only used to identify the annotation's draft in * again from drafts, it's only used to identify the annotation's draft in
* order to retrieve the fields below. * order to retrieve the fields below.
* @param {object} vm - The view model object containing the user's unsaved * @param {object} vm - The view model object containing the user's unsaved
* changes to the annotation. * changes to the annotation.
* *
*/ */
function saveToDrafts(drafts, domainModel, vm) { function saveToDrafts(drafts, vm) {
drafts.update( drafts.update(
domainModel, vm.annotation,
{ {
isPrivate: vm.isPrivate, isPrivate: vm.isPrivate,
tags: vm.form.tags, tags: vm.form.tags,
...@@ -71,48 +71,47 @@ function saveToDrafts(drafts, domainModel, vm) { ...@@ -71,48 +71,47 @@ function saveToDrafts(drafts, domainModel, vm) {
}); });
} }
/** Update domainModel from vm. /** Update `annotation` from vm.
* *
* Copy any properties from vm that might have been modified by the user into * Copy any properties from vm that might have been modified by the user into
* domainModel, overwriting any existing properties in domainModel. * `annotation`, overwriting any existing properties in `annotation`.
* *
* @param {object} domainModel The object to copy properties to * @param {object} annotation The object to copy properties to
* @param {object} vm The object to copy properties from * @param {object} vm The object to copy properties from
* *
*/ */
function updateDomainModel(domainModel, vm, permissions) { function updateDomainModel(annotation, vm, permissions) {
domainModel.text = vm.form.text; annotation.text = vm.form.text;
domainModel.tags = vm.form.tags; annotation.tags = vm.form.tags;
if (vm.isPrivate) { if (vm.isPrivate) {
domainModel.permissions = permissions.private(); annotation.permissions = permissions.private();
} else { } else {
domainModel.permissions = permissions.shared(domainModel.group); annotation.permissions = permissions.shared(annotation.group);
} }
} }
/** Update the view model from the domain model changes. */ /** Update the view model from the domain model changes. */
function updateViewModel($scope, domainModel, function updateViewModel($scope, vm, permissions) {
vm, permissions) {
vm.form = { vm.form = {
text: domainModel.text, text: vm.annotation.text,
tags: domainModel.tags, tags: vm.annotation.tags,
}; };
if (domainModel.links) { if (vm.annotation.links) {
vm.linkInContext = domainModel.links.incontext || vm.linkInContext = vm.annotation.links.incontext ||
domainModel.links.html || vm.annotation.links.html ||
''; '';
vm.linkHTML = domainModel.links.html || ''; vm.linkHTML = vm.annotation.links.html || '';
} else { } else {
vm.linkInContext = ''; vm.linkInContext = '';
vm.linkHTML = ''; vm.linkHTML = '';
} }
vm.isPrivate = permissions.isPrivate( vm.isPrivate = permissions.isPrivate(
domainModel.permissions, domainModel.user); vm.annotation.permissions, vm.annotation.user);
vm.documentMeta = annotationMetadata.domainAndTitle(domainModel); vm.documentMeta = annotationMetadata.domainAndTitle(vm.annotation);
} }
/** /**
...@@ -127,7 +126,6 @@ function AnnotationController( ...@@ -127,7 +126,6 @@ function AnnotationController(
settings, store) { settings, store) {
var vm = this; var vm = this;
var domainModel;
var newlyCreatedByHighlightButton; var newlyCreatedByHighlightButton;
/** Save an annotation to the server. */ /** Save an annotation to the server. */
...@@ -197,13 +195,6 @@ function AnnotationController( ...@@ -197,13 +195,6 @@ function AnnotationController(
/** True if the 'Share' dialog for this annotation is currently open. */ /** True if the 'Share' dialog for this annotation is currently open. */
vm.showShareDialog = false; vm.showShareDialog = false;
/** The domain model, contains the currently saved version of the
* annotation from the server (or in the case of new annotations that
* haven't been saved yet - the data that will be saved to the server when
* they are saved).
*/
domainModel = vm.annotation;
/** /**
* `true` if this AnnotationController instance was created as a result of * `true` if this AnnotationController instance was created as a result of
* the highlight button being clicked. * the highlight button being clicked.
...@@ -212,7 +203,7 @@ function AnnotationController( ...@@ -212,7 +203,7 @@ function AnnotationController(
* or annotation that was fetched from the server (as opposed to created * or annotation that was fetched from the server (as opposed to created
* new client-side). * new client-side).
*/ */
newlyCreatedByHighlightButton = domainModel.$highlight || false; newlyCreatedByHighlightButton = vm.annotation.$highlight || false;
// Call `onAnnotationUpdated()` whenever the "annotationUpdated" event is // Call `onAnnotationUpdated()` whenever the "annotationUpdated" event is
// emitted. This event is emitted after changes to the annotation are // emitted. This event is emitted after changes to the annotation are
...@@ -233,14 +224,14 @@ function AnnotationController( ...@@ -233,14 +224,14 @@ function AnnotationController(
// New annotations (just created locally by the client, rather then // New annotations (just created locally by the client, rather then
// received from the server) have some fields missing. Add them. // received from the server) have some fields missing. Add them.
domainModel.user = domainModel.user || session.state.userid; vm.annotation.user = vm.annotation.user || session.state.userid;
domainModel.group = domainModel.group || groups.focused().id; vm.annotation.group = vm.annotation.group || groups.focused().id;
if (!domainModel.permissions) { if (!vm.annotation.permissions) {
domainModel.permissions = permissions['default'](domainModel.group); vm.annotation.permissions = permissions['default'](vm.annotation.group);
} }
domainModel.text = domainModel.text || ''; vm.annotation.text = vm.annotation.text || '';
if (!Array.isArray(domainModel.tags)) { if (!Array.isArray(vm.annotation.tags)) {
domainModel.tags = []; vm.annotation.tags = [];
} }
// Automatically save new highlights to the server when they're created. // Automatically save new highlights to the server when they're created.
...@@ -250,31 +241,31 @@ function AnnotationController( ...@@ -250,31 +241,31 @@ function AnnotationController(
// log in. // log in.
saveNewHighlight(); saveNewHighlight();
updateView(domainModel); updateView();
// If this annotation is not a highlight and if it's new (has just been // If this annotation is not a highlight and if it's new (has just been
// created by the annotate button) or it has edits not yet saved to the // created by the annotate button) or it has edits not yet saved to the
// server - then open the editor on AnnotationController instantiation. // server - then open the editor on AnnotationController instantiation.
if (!newlyCreatedByHighlightButton) { if (!newlyCreatedByHighlightButton) {
if (isNew(domainModel) || drafts.get(domainModel)) { if (isNew(vm.annotation) || drafts.get(vm.annotation)) {
vm.edit(); vm.edit();
} }
} }
} }
function updateView(domainModel) { function updateView() {
updateViewModel($scope, domainModel, vm, permissions); updateViewModel($scope, vm, permissions);
} }
function onAnnotationUpdated(event, updatedDomainModel) { function onAnnotationUpdated(event, updatedDomainModel) {
if (updatedDomainModel.id === domainModel.id) { if (updatedDomainModel.id === vm.annotation.id) {
domainModel = updatedDomainModel; vm.annotation = updatedDomainModel;
updateView(updatedDomainModel); updateView();
} }
} }
function deleteIfNewAndEmpty() { function deleteIfNewAndEmpty() {
if (isNew(domainModel) && !vm.form.text && vm.form.tags.length === 0) { if (isNew(vm.annotation) && !vm.form.text && vm.form.tags.length === 0) {
vm.revert(); vm.revert();
} }
} }
...@@ -287,14 +278,14 @@ function AnnotationController( ...@@ -287,14 +278,14 @@ function AnnotationController(
// The annotation component may be destroyed when switching accounts, // The annotation component may be destroyed when switching accounts,
// when switching groups or when the component is scrolled off-screen. // when switching groups or when the component is scrolled off-screen.
if (vm.editing()) { if (vm.editing()) {
saveToDrafts(drafts, domainModel, vm); saveToDrafts(drafts, vm);
} }
} }
function onGroupFocused() { function onGroupFocused() {
// New annotations move to the new group, when a new group is focused. // New annotations move to the new group, when a new group is focused.
if (isNew(domainModel)) { if (isNew(vm.annotation)) {
domainModel.group = groups.focused().id; vm.annotation.group = groups.focused().id;
} }
} }
...@@ -308,7 +299,7 @@ function AnnotationController( ...@@ -308,7 +299,7 @@ function AnnotationController(
* *
*/ */
function saveNewHighlight() { function saveNewHighlight() {
if (!isNew(domainModel)) { if (!isNew(vm.annotation)) {
// Already saved. // Already saved.
return; return;
} }
...@@ -318,18 +309,18 @@ function AnnotationController( ...@@ -318,18 +309,18 @@ function AnnotationController(
return; return;
} }
if (domainModel.user) { if (vm.annotation.user) {
// User is logged in, save to server. // User is logged in, save to server.
// Highlights are always private. // Highlights are always private.
domainModel.permissions = permissions.private(); vm.annotation.permissions = permissions.private();
save(domainModel).then(function(model) { save(vm.annotation).then(function(model) {
domainModel = model; model.$$tag = vm.annotation.$$tag;
$rootScope.$emit(events.ANNOTATION_CREATED, model); $rootScope.$emit(events.ANNOTATION_CREATED, model);
updateView(domainModel); updateView();
}); });
} else { } else {
// User isn't logged in, save to drafts. // User isn't logged in, save to drafts.
saveToDrafts(drafts, domainModel, vm); saveToDrafts(drafts, vm);
} }
} }
...@@ -355,7 +346,7 @@ function AnnotationController( ...@@ -355,7 +346,7 @@ function AnnotationController(
// performance bottleneck and we would need to get the id token into the // performance bottleneck and we would need to get the id token into the
// session, which we should probably do anyway (and move to opaque bearer // session, which we should probably do anyway (and move to opaque bearer
// tokens for the access token). // tokens for the access token).
return permissions.permits(action, domainModel, session.state.userid); return permissions.permits(action, vm.annotation, session.state.userid);
}; };
/** /**
...@@ -372,7 +363,7 @@ function AnnotationController( ...@@ -372,7 +363,7 @@ function AnnotationController(
errorMessage(reason), 'Deleting annotation failed'); errorMessage(reason), 'Deleting annotation failed');
}; };
$scope.$apply(function() { $scope.$apply(function() {
annotationMapper.deleteAnnotation(domainModel).then( annotationMapper.deleteAnnotation(vm.annotation).then(
null, onRejected); null, onRejected);
}); });
} }
...@@ -385,8 +376,8 @@ function AnnotationController( ...@@ -385,8 +376,8 @@ function AnnotationController(
* @description Switches the view to an editor. * @description Switches the view to an editor.
*/ */
vm.edit = function() { vm.edit = function() {
restoreFromDrafts(drafts, domainModel, vm); restoreFromDrafts(drafts, vm);
vm.action = isNew(domainModel) ? 'create' : 'edit'; vm.action = isNew(vm.annotation) ? 'create' : 'edit';
}; };
/** /**
...@@ -409,7 +400,7 @@ function AnnotationController( ...@@ -409,7 +400,7 @@ function AnnotationController(
* @returns {Object} The full group object associated with the annotation. * @returns {Object} The full group object associated with the annotation.
*/ */
vm.group = function() { vm.group = function() {
return groups.get(domainModel.group); return groups.get(vm.annotation.group);
}; };
/** /**
...@@ -426,7 +417,7 @@ function AnnotationController( ...@@ -426,7 +417,7 @@ function AnnotationController(
* @returns {boolean} True if this annotation has quotes * @returns {boolean} True if this annotation has quotes
*/ */
vm.hasQuotes = function() { vm.hasQuotes = function() {
return domainModel.target.some(function(target) { return vm.annotation.target.some(function(target) {
return target.selector && target.selector.some(function(selector) { return target.selector && target.selector.some(function(selector) {
return selector.type === 'TextQuoteSelector'; return selector.type === 'TextQuoteSelector';
}); });
...@@ -434,7 +425,7 @@ function AnnotationController( ...@@ -434,7 +425,7 @@ function AnnotationController(
}; };
vm.id = function() { vm.id = function() {
return domainModel.id; return vm.annotation.id;
}; };
/** /**
...@@ -445,16 +436,16 @@ function AnnotationController( ...@@ -445,16 +436,16 @@ function AnnotationController(
vm.isHighlight = function() { vm.isHighlight = function() {
if (newlyCreatedByHighlightButton) { if (newlyCreatedByHighlightButton) {
return true; return true;
} else if (isNew(domainModel)) { } else if (isNew(vm.annotation)) {
return false; return false;
} else { } else {
// Once an annotation has been saved to the server there's no longer a // Once an annotation has been saved to the server there's no longer a
// simple property that says whether it's a highlight or not. For // simple property that says whether it's a highlight or not. For
// example there's no domainModel.highlight: true. Instead a highlight is // example there's no vm.annotation.highlight: true. Instead a highlight is
// defined as an annotation that isn't a page note or a reply and that // defined as an annotation that isn't a page note or a reply and that
// has no text or tags. // has no text or tags.
var isPageNote = (domainModel.target || []).length === 0; var isPageNote = (vm.annotation.target || []).length === 0;
return (!isPageNote && !isReply(domainModel) && !vm.hasContent()); return (!isPageNote && !isReply(vm.annotation) && !vm.hasContent());
} }
}; };
...@@ -488,12 +479,12 @@ function AnnotationController( ...@@ -488,12 +479,12 @@ function AnnotationController(
* Creates a new message in reply to this annotation. * Creates a new message in reply to this annotation.
*/ */
vm.reply = function() { vm.reply = function() {
var references = (domainModel.references || []).concat(domainModel.id); var references = (vm.annotation.references || []).concat(vm.annotation.id);
var reply = annotationMapper.createAnnotation({ var reply = annotationMapper.createAnnotation({
references: references, references: references,
uri: domainModel.uri uri: vm.annotation.uri
}); });
reply.group = domainModel.group; reply.group = vm.annotation.group;
if (session.state.userid) { if (session.state.userid) {
if (vm.isPrivate) { if (vm.isPrivate) {
...@@ -510,11 +501,11 @@ function AnnotationController( ...@@ -510,11 +501,11 @@ function AnnotationController(
* @description Reverts an edit in progress and returns to the viewer. * @description Reverts an edit in progress and returns to the viewer.
*/ */
vm.revert = function() { vm.revert = function() {
drafts.remove(domainModel); drafts.remove(vm.annotation);
if (vm.action === 'create') { if (vm.action === 'create') {
$rootScope.$emit(events.ANNOTATION_DELETED, domainModel); $rootScope.$emit(events.ANNOTATION_DELETED, vm.annotation);
} else { } else {
updateView(domainModel); updateView();
view(); view();
} }
}; };
...@@ -525,7 +516,7 @@ function AnnotationController( ...@@ -525,7 +516,7 @@ function AnnotationController(
* @description Saves any edits and returns to the viewer. * @description Saves any edits and returns to the viewer.
*/ */
vm.save = function() { vm.save = function() {
if (!domainModel.user) { if (!vm.annotation.user) {
flash.info('Please sign in to save your annotations.'); flash.info('Please sign in to save your annotations.');
return Promise.resolve(); return Promise.resolve();
} }
...@@ -535,22 +526,21 @@ function AnnotationController( ...@@ -535,22 +526,21 @@ function AnnotationController(
return Promise.resolve(); return Promise.resolve();
} }
var updatedModel = angular.copy(domainModel); var updatedModel = angular.copy(vm.annotation);
// Copy across the non-enumerable local tag for the annotation // Copy across the non-enumerable local tag for the annotation
updatedModel.$$tag = domainModel.$$tag; updatedModel.$$tag = vm.annotation.$$tag;
updateDomainModel(updatedModel, vm, permissions); updateDomainModel(updatedModel, vm, permissions);
var saved = save(updatedModel).then(function (model) { var saved = save(updatedModel).then(function (model) {
var isNew = !domainModel.id; var isNew = !vm.annotation.id;
drafts.remove(domainModel); drafts.remove(vm.annotation);
domainModel = model;
if (isNew) { if (isNew) {
$rootScope.$emit(events.ANNOTATION_CREATED, domainModel); $rootScope.$emit(events.ANNOTATION_CREATED, vm.annotation);
} else { } else {
$rootScope.$emit(events.ANNOTATION_UPDATED, domainModel); $rootScope.$emit(events.ANNOTATION_UPDATED, vm.annotation);
} }
updateView(domainModel); updateView();
}); });
// optimistically switch back to view mode and display the saving // optimistically switch back to view mode and display the saving
...@@ -584,7 +574,7 @@ function AnnotationController( ...@@ -584,7 +574,7 @@ function AnnotationController(
// creating or editing, we cache that and use the same privacy level the // creating or editing, we cache that and use the same privacy level the
// next time they create an annotation. // next time they create an annotation.
// But _don't_ cache it when they change the privacy level of a reply. // But _don't_ cache it when they change the privacy level of a reply.
if (!isReply(domainModel)) { if (!isReply(vm.annotation)) {
permissions.setDefault(privacy); permissions.setDefault(privacy);
} }
vm.isPrivate = (privacy === 'private'); vm.isPrivate = (privacy === 'private');
...@@ -595,23 +585,23 @@ function AnnotationController( ...@@ -595,23 +585,23 @@ function AnnotationController(
}; };
vm.target = function() { vm.target = function() {
return domainModel.target; return vm.annotation.target;
}; };
vm.updated = function() { vm.updated = function() {
return domainModel.updated; return vm.annotation.updated;
}; };
vm.user = function() { vm.user = function() {
return domainModel.user; return vm.annotation.user;
}; };
vm.username = function() { vm.username = function() {
return persona.username(domainModel.user); return persona.username(vm.annotation.user);
}; };
vm.isReply = function () { vm.isReply = function () {
return isReply(domainModel); return isReply(vm.annotation);
}; };
/** /**
......
...@@ -1154,16 +1154,14 @@ describe('annotation', function() { ...@@ -1154,16 +1154,14 @@ describe('annotation', function() {
}); });
it('reverts to the most recently saved version', function () { it('reverts to the most recently saved version', function () {
fakeStore.annotation.update = function (params, ann) {
return Promise.resolve(Object.assign({}, ann));
};
var controller = createDirective({ var controller = createDirective({
id: 'new-annot', id: 'new-annot',
user: 'acct:bill@localhost', user: 'acct:bill@localhost',
text: 'saved-text',
}).controller; }).controller;
controller.edit(); controller.edit();
controller.form.text = 'New annotation text'; controller.form.text = 'New annotation text';
return controller.save().then(function () { return controller.save().then(function () {
controller.edit(); controller.edit();
controller.form.text = 'Updated annotation text'; controller.form.text = 'Updated annotation text';
...@@ -1171,7 +1169,7 @@ describe('annotation', function() { ...@@ -1171,7 +1169,7 @@ describe('annotation', function() {
}).then(function () { }).then(function () {
controller.edit(); controller.edit();
controller.revert(); controller.revert();
assert.equal(controller.form.text, 'Updated annotation text'); assert.equal(controller.form.text, controller.annotation.text);
}); });
}); });
}); });
......
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