From 02ddf277ab0679a2d395077e88f85011383b666a Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Fri, 26 Sep 2025 19:55:27 +0200 Subject: [PATCH] [Editor] Add some telemetry for the commenting feature (bug 1991172) --- src/display/annotation_storage.js | 22 ++++++++++++ src/display/editor/editor.js | 4 +++ web/app.js | 5 +-- web/comment_manager.js | 56 +++++++++++++++++++++++++++++-- 4 files changed, 82 insertions(+), 5 deletions(-) diff --git a/src/display/annotation_storage.js b/src/display/annotation_storage.js index 6d6ff8ad8..468e81508 100644 --- a/src/display/annotation_storage.js +++ b/src/display/annotation_storage.js @@ -222,10 +222,22 @@ class AnnotationStorage { get editorStats() { let stats = null; const typeToEditor = new Map(); + let numberOfEditedComments = 0; + let numberOfDeletedComments = 0; for (const value of this.#storage.values()) { if (!(value instanceof AnnotationEditor)) { + if (value.popup.deleted) { + numberOfDeletedComments += 1; + } else if (value.popup) { + numberOfEditedComments += 1; + } continue; } + if (value.isCommentDeleted) { + numberOfDeletedComments += 1; + } else if (value.hasEditedComment) { + numberOfEditedComments += 1; + } const editorStats = value.telemetryFinalData; if (!editorStats) { continue; @@ -249,6 +261,16 @@ class AnnotationStorage { counters.set(val, count + 1); } } + if (numberOfDeletedComments > 0 || numberOfEditedComments > 0) { + stats ||= Object.create(null); + stats.comments = { + deleted: numberOfDeletedComments, + edited: numberOfEditedComments, + }; + } + if (!stats) { + return null; + } for (const [type, editor] of typeToEditor) { stats[type] = editor.computeTelemetryFinalData(stats[type]); } diff --git a/src/display/editor/editor.js b/src/display/editor/editor.js index 3908736fb..caffc59c2 100644 --- a/src/display/editor/editor.js +++ b/src/display/editor/editor.js @@ -1241,6 +1241,10 @@ class AnnotationEditor { return this.#comment?.hasBeenEdited(); } + get hasDeletedComment() { + return this.#comment?.isDeleted(); + } + get hasComment() { return ( !!this.#comment && !this.#comment.isEmpty() && !this.#comment.isDeleted() diff --git a/web/app.js b/web/app.js index de3d82ea3..2dc66b6be 100644 --- a/web/app.js +++ b/web/app.js @@ -1288,12 +1288,13 @@ const PDFViewerApplication = { this._saveInProgress = false; } - if (this._hasAnnotationEditors) { + const editorStats = this.pdfDocument?.annotationStorage.editorStats; + if (editorStats) { this.externalServices.reportTelemetry({ type: "editing", data: { type: "save", - stats: this.pdfDocument?.annotationStorage.editorStats, + stats: editorStats, }, }); } diff --git a/web/comment_manager.js b/web/comment_manager.js index 5d9bd3ee7..b9a1de7e7 100644 --- a/web/comment_manager.js +++ b/web/comment_manager.js @@ -50,8 +50,18 @@ class CommentManager { dateStyle: "long", }); this.dialogElement = commentDialog.dialog; - this.#dialog = new CommentDialog(commentDialog, overlayManager, ltr); - this.#popup = new CommentPopup(dateFormat, ltr, this.dialogElement); + this.#dialog = new CommentDialog( + commentDialog, + overlayManager, + eventBus, + ltr + ); + this.#popup = new CommentPopup( + eventBus, + dateFormat, + ltr, + this.dialogElement + ); this.#sidebar = new CommentSidebar( sidebar, eventBus, @@ -134,6 +144,8 @@ class CommentManager { class CommentSidebar { #annotations = null; + #eventBus; + #boundCommentClick = this.#commentClick.bind(this); #boundCommentKeydown = this.#commentKeydown.bind(this); @@ -199,6 +211,7 @@ class CommentSidebar { this.#popup = popup; this.#dateFormat = dateFormat; this.#ltr = ltr; + this.#eventBus = eventBus; const style = window.getComputedStyle(sidebar); this.#minWidth = parseFloat(style.getPropertyValue("--sidebar-min-width")); @@ -306,6 +319,13 @@ class CommentSidebar { this.#setCommentsCount(); } this.#sidebar.hidden = false; + this.#eventBus.dispatch("reporttelemetry", { + source: this, + details: { + type: "commentSidebar", + data: { numberOfAnnotations: annotations.length }, + }, + }); } hide() { @@ -672,14 +692,18 @@ class CommentDialog { #isLTR; + #eventBus; + constructor( { dialog, toolbar, title, textInput, cancelButton, saveButton }, overlayManager, + eventBus, ltr ) { this.#dialog = dialog; this.#textInput = textInput; this.#overlayManager = overlayManager; + this.#eventBus = eventBus; this.#saveButton = saveButton; this.#title = title; this.#isLTR = ltr; @@ -863,6 +887,20 @@ class CommentDialog { } #finish() { + if (!this.#editor) { + return; + } + const edited = this.#textInput.value !== this.#commentText; + this.#eventBus.dispatch("reporttelemetry", { + source: this, + details: { + type: "comment", + data: { + edited, + }, + }, + }); + this.#editor?.focusCommentButton(); this.#editor = null; this.#textInput.value = this.#previousText = this.#commentText = ""; @@ -882,6 +920,8 @@ class CommentDialog { class CommentPopup { #buttonsContainer = null; + #eventBus; + #commentDialog; #dateFormat; @@ -910,7 +950,8 @@ class CommentPopup { #visible = false; - constructor(dateFormat, ltr, commentDialog) { + constructor(eventBus, dateFormat, ltr, commentDialog) { + this.#eventBus = eventBus; this.#dateFormat = dateFormat; this.#isLTR = ltr; this.#commentDialog = commentDialog; @@ -994,6 +1035,15 @@ class CommentPopup { ); del.append(delLabel); del.addEventListener("click", () => { + this.#eventBus.dispatch("reporttelemetry", { + source: this, + details: { + type: "comment", + data: { + deleted: true, + }, + }, + }); this.#editor.comment = null; this.destroy(); });