[Annotation] Save the comment changes made in reading mode (bug 1987427)

This commit is contained in:
Calixte Denizet 2025-09-15 19:49:49 +02:00
parent 394fa2c184
commit 5feb4674d6
4 changed files with 113 additions and 24 deletions

View File

@ -29,6 +29,7 @@
import {
AnnotationBorderStyleType,
AnnotationEditorPrefix,
AnnotationEditorType,
AnnotationPrefix,
AnnotationType,
@ -242,6 +243,16 @@ class AnnotationElement {
return point;
}
get commentText() {
const { data } = this;
return (
this.annotationStorage.getRawValue(`${AnnotationEditorPrefix}${data.id}`)
?.popup?.contents ||
data.contentsObj?.str ||
""
);
}
removePopup() {
(this.#popupElement?.popup || this.popup)?.remove();
this.#popupElement = this.popup = null;
@ -2335,6 +2346,8 @@ class PopupElement {
#firstElement = null;
#commentText = null;
constructor({
container,
color,
@ -2495,7 +2508,16 @@ class PopupElement {
}
getData() {
return this.#firstElement.data;
const { richText, color, opacity, creationDate, modificationDate } =
this.#firstElement.data;
return {
contentsObj: { str: this.comment },
richText,
color,
opacity,
creationDate,
modificationDate,
};
}
get elementBeforePopup() {
@ -2503,22 +2525,35 @@ class PopupElement {
}
get comment() {
return this.#firstElement.data.contentsObj?.str || "";
this.#commentText ||= this.#firstElement.commentText;
return this.#commentText;
}
set comment(text) {
const element = this.#firstElement;
if (text) {
element.data.contentsObj = { str: text };
// TODO: Support saving the text.
// element.annotationStorage.setValue(element.data.id, {
// popup: { contents: text },
// });
} else {
element.data.contentsObj = null;
const { data } = element;
if (text === this.comment) {
return;
}
const popup = { deleted: !text, contents: text || "" };
if (!element.annotationStorage.updateEditor(data.id, { popup })) {
element.annotationStorage.setValue(
`${AnnotationEditorPrefix}${data.id}`,
{
id: data.id,
annotationType: data.annotationType,
pageIndex: element.parent.page._pageIndex,
popup,
popupRef: data.popupRef,
modificationDate: new Date(),
}
);
}
this.#commentText = text;
if (!text) {
element.removePopup();
}
element.data.modificationDate = new Date();
}
get parentBoundingClientRect() {
@ -2703,6 +2738,10 @@ class PopupElement {
}
updateEdited({ rect, popup, deleted }) {
if (this.#commentManager) {
this.#commentText = deleted ? null : popup.text;
return;
}
if (deleted || popup?.deleted) {
this.remove();
return;

View File

@ -31,6 +31,8 @@ class AnnotationStorage {
#modifiedIds = null;
#editorsMap = null;
#storage = new Map();
constructor() {
@ -83,6 +85,13 @@ class AnnotationStorage {
* @param {string} key
*/
remove(key) {
const storedValue = this.#storage.get(key);
if (storedValue === undefined) {
return;
}
if (storedValue instanceof AnnotationEditor) {
this.#editorsMap.delete(storedValue.annotationElementId);
}
this.#storage.delete(key);
if (this.#storage.size === 0) {
@ -122,11 +131,11 @@ class AnnotationStorage {
this.#setModified();
}
if (
value instanceof AnnotationEditor &&
typeof this.onAnnotationEditor === "function"
) {
this.onAnnotationEditor(value.constructor._type);
if (value instanceof AnnotationEditor) {
(this.#editorsMap ||= new Map()).set(value.annotationElementId, value);
if (typeof this.onAnnotationEditor === "function") {
this.onAnnotationEditor(value.constructor._type);
}
}
}
@ -250,6 +259,15 @@ class AnnotationStorage {
this.#modifiedIds = null;
}
updateEditor(annotationId, data) {
const value = this.#editorsMap?.get(annotationId);
if (value) {
value.updateFromAnnotationLayer(data);
return true;
}
return false;
}
/**
* @returns {{ids: Set<string>, hash: string}}
*/
@ -258,15 +276,13 @@ class AnnotationStorage {
return this.#modifiedIds;
}
const ids = [];
for (const value of this.#storage.values()) {
if (
!(value instanceof AnnotationEditor) ||
!value.annotationElementId ||
!value.serialize()
) {
continue;
if (this.#editorsMap) {
for (const value of this.#editorsMap.values()) {
if (!value.serialize()) {
continue;
}
ids.push(value.annotationElementId);
}
ids.push(value.annotationElementId);
}
return (this.#modifiedIds = {
ids: new Set(ids),

View File

@ -1243,6 +1243,16 @@ class AnnotationEditor {
}
this.#comment ||= new Comment(this);
this.#comment.setInitialText(comment, richText);
if (!this.annotationElementId) {
return;
}
const storedData = this._uiManager.getAndRemoveDataFromAnnotationStorage(
this.annotationElementId
);
if (storedData) {
this.updateFromAnnotationLayer(storedData);
}
}
get hasEditedComment() {
@ -1288,6 +1298,10 @@ class AnnotationEditor {
}
}
updateFromAnnotationLayer({ popup: { contents, deleted } }) {
this.#comment.data = deleted ? null : contents;
}
get parentBoundingClientRect() {
return this.parent.boundingClientRect;
}

View File

@ -1288,6 +1288,26 @@ class AnnotationEditorUIManager {
this.#floatingToolbar.show(textLayer, boxes, this.direction === "ltr");
}
/**
* Some annotations may have been modified in the annotation layer
* (e.g. comments added or modified).
* So this function retrieves the data from the storage and removes
* them from the storage in order to be able to save them later.
* @param {string} annotationId
* @returns {Object|null} The data associated to the annotation or null.
*/
getAndRemoveDataFromAnnotationStorage(annotationId) {
if (!this.#annotationStorage) {
return null;
}
const key = `${AnnotationEditorPrefix}${annotationId}`;
const storedValue = this.#annotationStorage.getRawValue(key);
if (storedValue) {
this.#annotationStorage.remove(key);
}
return storedValue;
}
/**
* Add an editor in the annotation storage.
* @param {AnnotationEditor} editor