diff --git a/src/common/annotation-manager.js b/src/common/annotation-manager.js index d76c9205..d5f82f5a 100644 --- a/src/common/annotation-manager.js +++ b/src/common/annotation-manager.js @@ -23,6 +23,7 @@ class AnnotationManager { this._onSave = options.onSave; this._onDelete = options.onDelete; this._onChangeHistory = options.onChangeHistory; + this._trashesAnnotations = options.trashesAnnotations; this._adjustTextAnnotationPosition = options.adjustTextAnnotationPosition; this.render = () => { options.onRender([...this._annotations]); @@ -62,14 +63,17 @@ class AnnotationManager { } // Called when deletions come from the client side - unsetAnnotations(ids) { + unsetAnnotations(ids, permanentlyDeleted) { // Deletions we haven't applied yet are outside changes that our history // can no longer be replayed over. Ones we have applied are our own - // deletions coming back to us, and undoing them is still valid. - let externalIDs = ids.filter(id => this._annotations.some(x => x.id === id)); + // deletions coming back to us, and undoing them is still valid, unless + // the annotations were permanently deleted. + let clearIDs = permanentlyDeleted + ? ids + : ids.filter(id => this._annotations.some(x => x.id === id)); this._annotations = this._annotations.filter(x => !ids.includes(x.id)); - if (externalIDs.length) { - this._clearInterferingHistory(externalIDs); + if (clearIDs.length) { + this._clearInterferingHistory(clearIDs); } this.render(); } @@ -601,8 +605,11 @@ class AnnotationManager { if (annotation) { annotation.dateModified = (new Date()).toISOString(); } - // Assign new id when undeleting to reduce sync conflicts - if (!prevAnnotation) { + // Assign a new ID when undeleting to reduce sync conflicts, + // except if the client trashes instead of permanently deleting - + // in that case, keep the old ID so the client can match the + // trashed annotation and untrash it. + if (!prevAnnotation && !this._trashesAnnotations) { let newID = this._generateObjectKey(); mapping.set(annotation.id, newID); annotation.id = newID; @@ -635,8 +642,10 @@ class AnnotationManager { if (annotation) { annotation.dateModified = (new Date()).toISOString(); } - // Assign new id when undeleting to reduce sync conflicts - if (!prevAnnotation) { + // Assign new id when undeleting to reduce sync conflicts. Clients that + // trash annotations keep the deleted one around, so undeleting restores + // it in place and the id has to stay the same for the client to find it. + if (!prevAnnotation && !this._trashesAnnotations) { let newID = this._generateObjectKey(); mapping.set(annotation.id, newID); annotation.id = newID; diff --git a/src/common/reader.js b/src/common/reader.js index 71583fdd..86b5583f 100644 --- a/src/common/reader.js +++ b/src/common/reader.js @@ -130,6 +130,7 @@ class Reader { this._onSetPopupPosition = options.onSetPopupPosition; this._onChangeUndoHistory = options.onChangeUndoHistory; this._externalUndoHistory = !!options.onChangeUndoHistory; + this._trashesAnnotations = !!options.trashesAnnotations; for (let ftl of options.ftl) { addFTL(ftl); @@ -361,6 +362,7 @@ class Reader { this._updateState({ filter }); }, onChangeHistory: this._onChangeUndoHistory, + trashesAnnotations: this._trashesAnnotations, adjustTextAnnotationPosition: (annotation, option) => { return this._primaryView.adjustTextAnnotationPosition(annotation, option); } @@ -947,8 +949,8 @@ class Reader { this._annotationManager.setAnnotations(annotations); } - unsetAnnotations(ids) { - this._annotationManager.unsetAnnotations(ids); + unsetAnnotations(ids, permanentlyDeleted) { + this._annotationManager.unsetAnnotations(ids, permanentlyDeleted); } openContextMenu(params) {