[Editor] Add some telemetry for the highlight feature (bug 1866437)
This commit is contained in:
parent
e42b114e80
commit
65342d2bee
@ -212,6 +212,41 @@ class AnnotationStorage {
|
||||
? { map, hash: hash.hexdigest(), transfer }
|
||||
: SerializableEmpty;
|
||||
}
|
||||
|
||||
get editorStats() {
|
||||
const stats = Object.create(null);
|
||||
const typeToEditor = new Map();
|
||||
for (const value of this.#storage.values()) {
|
||||
if (!(value instanceof AnnotationEditor)) {
|
||||
continue;
|
||||
}
|
||||
const editorStats = value.telemetryFinalData;
|
||||
if (!editorStats) {
|
||||
continue;
|
||||
}
|
||||
const { type } = editorStats;
|
||||
if (!typeToEditor.has(type)) {
|
||||
typeToEditor.set(type, Object.getPrototypeOf(value).constructor);
|
||||
}
|
||||
const map = (stats[type] ||= new Map());
|
||||
for (const [key, val] of Object.entries(editorStats)) {
|
||||
if (key === "type") {
|
||||
continue;
|
||||
}
|
||||
let counters = map.get(key);
|
||||
if (!counters) {
|
||||
counters = new Map();
|
||||
map.set(key, counters);
|
||||
}
|
||||
const count = counters.get(val) ?? 0;
|
||||
counters.set(val, count + 1);
|
||||
}
|
||||
}
|
||||
for (const [type, editor] of typeToEditor) {
|
||||
stats[type] = editor.computeTelemetryFinalData(stats[type]);
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -146,15 +146,8 @@ class AltText {
|
||||
this.#altTextTooltipTimeout = setTimeout(() => {
|
||||
this.#altTextTooltipTimeout = null;
|
||||
this.#altTextTooltip.classList.add("show");
|
||||
this.#editor._uiManager._eventBus.dispatch("reporttelemetry", {
|
||||
source: this,
|
||||
details: {
|
||||
type: "editing",
|
||||
subtype: this.#editor.editorType,
|
||||
data: {
|
||||
this.#editor._reportTelemetry({
|
||||
action: "alt_text_tooltip",
|
||||
},
|
||||
},
|
||||
});
|
||||
}, DELAY_TO_SHOW_TOOLTIP);
|
||||
});
|
||||
|
@ -471,6 +471,7 @@ class AnnotationEditorLayer {
|
||||
editor.fixAndSetPosition();
|
||||
editor.onceAdded();
|
||||
this.#uiManager.addToAnnotationStorage(editor);
|
||||
editor._reportTelemetry(editor.telemetryInitialData);
|
||||
}
|
||||
|
||||
moveEditorInDOM(editor) {
|
||||
|
@ -72,6 +72,8 @@ class AnnotationEditor {
|
||||
|
||||
#prevDragY = 0;
|
||||
|
||||
#telemetryTimeouts = null;
|
||||
|
||||
_initialOptions = Object.create(null);
|
||||
|
||||
_uiManager = null;
|
||||
@ -90,6 +92,11 @@ class AnnotationEditor {
|
||||
|
||||
static _zIndex = 1;
|
||||
|
||||
// Time to wait (in ms) before sending the telemetry data.
|
||||
// We wait a bit to avoid sending too many requests when changing something
|
||||
// like the thickness of a line.
|
||||
static _telemetryTimeout = 1000;
|
||||
|
||||
static get _resizerKeyboardManager() {
|
||||
const resize = AnnotationEditor.prototype._resizeWithKeyboard;
|
||||
const small = AnnotationEditorUIManager.TRANSLATE_SMALL;
|
||||
@ -1323,6 +1330,12 @@ class AnnotationEditor {
|
||||
}
|
||||
this.#stopResizing();
|
||||
this.removeEditToolbar();
|
||||
if (this.#telemetryTimeouts) {
|
||||
for (const timeout of this.#telemetryTimeouts.values()) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
this.#telemetryTimeouts = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1598,6 +1611,50 @@ class AnnotationEditor {
|
||||
static canCreateNewEmptyEditor() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data to report to the telemetry when the editor is added.
|
||||
* @returns {Object}
|
||||
*/
|
||||
get telemetryInitialData() {
|
||||
return { action: "added" };
|
||||
}
|
||||
|
||||
/**
|
||||
* The telemetry data to use when saving/printing.
|
||||
* @returns {Object|null}
|
||||
*/
|
||||
get telemetryFinalData() {
|
||||
return null;
|
||||
}
|
||||
|
||||
_reportTelemetry(data, mustWait = false) {
|
||||
if (mustWait) {
|
||||
this.#telemetryTimeouts ||= new Map();
|
||||
const { action } = data;
|
||||
let timeout = this.#telemetryTimeouts.get(action);
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
timeout = setTimeout(() => {
|
||||
this._reportTelemetry(data);
|
||||
this.#telemetryTimeouts.delete(action);
|
||||
if (this.#telemetryTimeouts.size === 0) {
|
||||
this.#telemetryTimeouts = null;
|
||||
}
|
||||
}, AnnotationEditor._telemetryTimeout);
|
||||
this.#telemetryTimeouts.set(action, timeout);
|
||||
return;
|
||||
}
|
||||
data.type ||= this.editorType;
|
||||
this._uiManager._eventBus.dispatch("reporttelemetry", {
|
||||
source: this,
|
||||
details: {
|
||||
type: "editing",
|
||||
data,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// This class is used to fake an editor which has been deleted.
|
||||
|
@ -52,6 +52,8 @@ class HighlightEditor extends AnnotationEditor {
|
||||
|
||||
#thickness;
|
||||
|
||||
#methodOfCreation = "";
|
||||
|
||||
static _defaultColor = null;
|
||||
|
||||
static _defaultOpacity = 1;
|
||||
@ -76,6 +78,7 @@ class HighlightEditor extends AnnotationEditor {
|
||||
this.#thickness = params.thickness || HighlightEditor._defaultThickness;
|
||||
this.#opacity = params.opacity || HighlightEditor._defaultOpacity;
|
||||
this.#boxes = params.boxes || null;
|
||||
this.#methodOfCreation = params.methodOfCreation || "";
|
||||
this._isDraggable = false;
|
||||
|
||||
if (params.highlightId > -1) {
|
||||
@ -89,6 +92,34 @@ class HighlightEditor extends AnnotationEditor {
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
get telemetryInitialData() {
|
||||
return {
|
||||
action: "added",
|
||||
type: this.#telemetryType,
|
||||
color: this._uiManager.highlightColorNames.get(this.color),
|
||||
thickness: this.#thickness,
|
||||
methodOfCreation: this.#methodOfCreation,
|
||||
};
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
get telemetryFinalData() {
|
||||
return {
|
||||
type: this.#telemetryType,
|
||||
color: this._uiManager.highlightColorNames.get(this.color),
|
||||
};
|
||||
}
|
||||
|
||||
static computeTelemetryFinalData(data) {
|
||||
// We want to know how many colors have been used.
|
||||
return { numberOfColors: data.get("color").size };
|
||||
}
|
||||
|
||||
get #telemetryType() {
|
||||
return this.#isFreeHighlight ? "free_highlight" : "highlight";
|
||||
}
|
||||
|
||||
#createOutlines() {
|
||||
const outliner = new Outliner(this.#boxes, /* borderWidth = */ 0.001);
|
||||
this.#highlightOutlines = outliner.getOutlines();
|
||||
@ -274,6 +305,14 @@ class HighlightEditor extends AnnotationEditor {
|
||||
overwriteIfSameType: true,
|
||||
keepUndo: true,
|
||||
});
|
||||
|
||||
this._reportTelemetry(
|
||||
{
|
||||
action: "color_changed",
|
||||
color: this._uiManager.highlightColorNames.get(color),
|
||||
},
|
||||
/* mustWait = */ true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -295,6 +334,10 @@ class HighlightEditor extends AnnotationEditor {
|
||||
overwriteIfSameType: true,
|
||||
keepUndo: true,
|
||||
});
|
||||
this._reportTelemetry(
|
||||
{ action: "thickness_changed", thickness },
|
||||
/* mustWait = */ true
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
@ -349,6 +392,9 @@ class HighlightEditor extends AnnotationEditor {
|
||||
remove() {
|
||||
super.remove();
|
||||
this.#cleanDrawLayer();
|
||||
this._reportTelemetry({
|
||||
action: "deleted",
|
||||
});
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
|
@ -326,15 +326,8 @@ class StampEditor extends AnnotationEditor {
|
||||
// There are multiple ways to add an image to the page, so here we just
|
||||
// count the number of times an image is added to the page whatever the way
|
||||
// is.
|
||||
this._uiManager._eventBus.dispatch("reporttelemetry", {
|
||||
source: this,
|
||||
details: {
|
||||
type: "editing",
|
||||
subtype: this.editorType,
|
||||
data: {
|
||||
this._reportTelemetry({
|
||||
action: "inserted_image",
|
||||
},
|
||||
},
|
||||
});
|
||||
this.addAltTextButton();
|
||||
if (this.#bitmapFileName) {
|
||||
|
@ -871,6 +871,16 @@ class AnnotationEditorUIManager {
|
||||
);
|
||||
}
|
||||
|
||||
get highlightColorNames() {
|
||||
return shadow(
|
||||
this,
|
||||
"highlightColorNames",
|
||||
this.highlightColors
|
||||
? new Map(Array.from(this.highlightColors, e => e.reverse()))
|
||||
: null
|
||||
);
|
||||
}
|
||||
|
||||
setMainHighlightColorPicker(colorPicker) {
|
||||
this.#mainHighlightColorPicker = colorPicker;
|
||||
}
|
||||
@ -932,7 +942,7 @@ class AnnotationEditorUIManager {
|
||||
this.viewParameters.rotation = pagesRotation;
|
||||
}
|
||||
|
||||
highlightSelection() {
|
||||
highlightSelection(methodOfCreation = "") {
|
||||
const selection = document.getSelection();
|
||||
if (!selection || selection.isCollapsed) {
|
||||
return;
|
||||
@ -953,7 +963,10 @@ class AnnotationEditorUIManager {
|
||||
}
|
||||
for (const layer of this.#allLayers.values()) {
|
||||
if (layer.hasTextLayer(textLayer)) {
|
||||
layer.createAndAddNewEditor({ x: 0, y: 0 }, false, { boxes });
|
||||
layer.createAndAddNewEditor({ x: 0, y: 0 }, false, {
|
||||
methodOfCreation,
|
||||
boxes,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1020,7 +1033,7 @@ class AnnotationEditorUIManager {
|
||||
window.removeEventListener("pointerup", pointerup);
|
||||
window.removeEventListener("blur", pointerup);
|
||||
if (e.type === "pointerup") {
|
||||
this.highlightSelection();
|
||||
this.highlightSelection("main_toolbar");
|
||||
}
|
||||
};
|
||||
window.addEventListener("pointerup", pointerup);
|
||||
@ -1050,7 +1063,7 @@ class AnnotationEditorUIManager {
|
||||
this.isShiftKeyDown = false;
|
||||
if (this.#highlightWhenShiftUp) {
|
||||
this.#highlightWhenShiftUp = false;
|
||||
this.highlightSelection();
|
||||
this.highlightSelection("main_toolbar");
|
||||
}
|
||||
if (!this.hasSelection) {
|
||||
return;
|
||||
@ -1240,7 +1253,7 @@ class AnnotationEditorUIManager {
|
||||
this.isShiftKeyDown = false;
|
||||
if (this.#highlightWhenShiftUp) {
|
||||
this.#highlightWhenShiftUp = false;
|
||||
this.highlightSelection();
|
||||
this.highlightSelection("main_toolbar");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1249,15 +1262,18 @@ class AnnotationEditorUIManager {
|
||||
* Execute an action for a given name.
|
||||
* For example, the user can click on the "Undo" entry in the context menu
|
||||
* and it'll trigger the undo action.
|
||||
* @param {Object} details
|
||||
*/
|
||||
onEditingAction(details) {
|
||||
if (
|
||||
["undo", "redo", "delete", "selectAll", "highlightSelection"].includes(
|
||||
details.name
|
||||
)
|
||||
) {
|
||||
this[details.name]();
|
||||
onEditingAction({ name }) {
|
||||
switch (name) {
|
||||
case "undo":
|
||||
case "redo":
|
||||
case "delete":
|
||||
case "selectAll":
|
||||
this[name]();
|
||||
break;
|
||||
case "highlightSelection":
|
||||
this.highlightSelection("context_menu");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,17 +248,12 @@ class AltTextManager {
|
||||
}
|
||||
|
||||
#close() {
|
||||
this.#eventBus.dispatch("reporttelemetry", {
|
||||
source: this,
|
||||
details: {
|
||||
type: "editing",
|
||||
subtype: this.#currentEditor.editorType,
|
||||
data: this.#telemetryData || {
|
||||
this.#currentEditor._reportTelemetry(
|
||||
this.#telemetryData || {
|
||||
action: "alt_text_cancel",
|
||||
alt_text_keyboard: !this.#hasUsedPointer,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
this.#telemetryData = null;
|
||||
|
||||
this.#removeOnClickListeners();
|
||||
|
17
web/app.js
17
web/app.js
@ -1152,7 +1152,10 @@ const PDFViewerApplication = {
|
||||
if (this._hasAnnotationEditors) {
|
||||
this.externalServices.reportTelemetry({
|
||||
type: "editing",
|
||||
data: { type: "save" },
|
||||
data: {
|
||||
type: "save",
|
||||
stats: this.pdfDocument?.annotationStorage.editorStats,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -1727,13 +1730,6 @@ const PDFViewerApplication = {
|
||||
annotationStorage.onAnnotationEditor = typeStr => {
|
||||
this._hasAnnotationEditors = !!typeStr;
|
||||
this.setTitle();
|
||||
|
||||
if (typeStr) {
|
||||
this.externalServices.reportTelemetry({
|
||||
type: "editing",
|
||||
data: { type: typeStr },
|
||||
});
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
@ -1857,7 +1853,10 @@ const PDFViewerApplication = {
|
||||
if (this._hasAnnotationEditors) {
|
||||
this.externalServices.reportTelemetry({
|
||||
type: "editing",
|
||||
data: { type: "print" },
|
||||
data: {
|
||||
type: "print",
|
||||
stats: this.pdfDocument?.annotationStorage.editorStats,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user