UNPKG

@atlaskit/editor-plugin-base

Version:

Base plugin for @atlaskit/editor-core

202 lines (198 loc) 11.3 kB
import _defineProperty from "@babel/runtime/helpers/defineProperty"; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } import { ACTION, ACTION_SUBJECT, BROWSER_FREEZE_INTERACTION_TYPE, EVENT_TYPE } from '@atlaskit/editor-common/analytics'; import { countNodes } from '@atlaskit/editor-common/count-nodes'; import { isPerformanceAPIAvailable, isPerformanceObserverAvailable } from '@atlaskit/editor-common/is-performance-api-available'; import { SafePlugin } from '@atlaskit/editor-common/safe-plugin'; import { getAnalyticsEventSeverity } from '@atlaskit/editor-common/utils/analytics'; import { PluginKey } from '@atlaskit/editor-prosemirror/state'; import { setInteractionType } from './utils/frozen-editor'; import InputLatencyTracker from './utils/input-latency-tracking'; export var frozenEditorPluginKey = new PluginKey('frozenEditor'); var DEFAULT_KEYSTROKE_SAMPLING_LIMIT = 100; var DEFAULT_SLOW_THRESHOLD = 300; export var DEFAULT_FREEZE_THRESHOLD = 600; export var NORMAL_SEVERITY_THRESHOLD = 2000; export var DEGRADED_SEVERITY_THRESHOLD = 3000; var DEFAULT_TRACK_SEVERITY_ENABLED = false; export var DEFAULT_TRACK_SEVERITY_THRESHOLD_NORMAL = 100; export var DEFAULT_TRACK_SEVERITY_THRESHOLD_DEGRADED = 500; var dispatchLongTaskEvent = function dispatchLongTaskEvent(contextIdentifierPlugin) { return function (dispatchAnalyticsEvent, view, time, getNodeCount, interactionType, severity) { var _contextIdentifierPlu; var state = view.state; var nodesCount = getNodeCount(state); return dispatchAnalyticsEvent({ action: ACTION.BROWSER_FREEZE, actionSubject: ACTION_SUBJECT.EDITOR, attributes: _objectSpread(_objectSpread({ objectId: contextIdentifierPlugin === null || contextIdentifierPlugin === void 0 || (_contextIdentifierPlu = contextIdentifierPlugin.sharedState.currentState()) === null || _contextIdentifierPlu === void 0 || (_contextIdentifierPlu = _contextIdentifierPlu.contextIdentifierProvider) === null || _contextIdentifierPlu === void 0 ? void 0 : _contextIdentifierPlu.objectId, freezeTime: time, nodeSize: state.doc.nodeSize }, nodesCount), {}, { interactionType: interactionType, severity: severity }), eventType: EVENT_TYPE.OPERATIONAL }); }; }; export default (function (contextIdentifierPlugin) { return function (dispatchAnalyticsEvent, inputTracking, browserFreezeTracking) { var interactionType; var inputLatencyTracker = null; if (browserFreezeTracking !== null && browserFreezeTracking !== void 0 && browserFreezeTracking.trackInteractionType) { interactionType = setInteractionType(BROWSER_FREEZE_INTERACTION_TYPE.LOADING); } var samplingRate = inputTracking && typeof inputTracking.samplingRate === 'number' ? inputTracking.samplingRate : DEFAULT_KEYSTROKE_SAMPLING_LIMIT; // TODO: ED-26959 - get right values here based on appearance var slowThreshold = DEFAULT_SLOW_THRESHOLD; // TODO: ED-26959 - get right values here based on appearance var freezeThreshold = DEFAULT_FREEZE_THRESHOLD; var allowCountNodes = inputTracking && inputTracking.countNodes; var prevNodeCountState = null; var prevNodeCount = {}; // Cache the result as we were calling this multiple times // and has potential to be expensive var getNodeCount = function getNodeCount(state) { if (state === prevNodeCountState) { return prevNodeCount; } prevNodeCount = allowCountNodes ? countNodes(state) : {}; prevNodeCountState = state; return prevNodeCount; }; var shouldTrackSeverity = (inputTracking === null || inputTracking === void 0 ? void 0 : inputTracking.trackSeverity) || DEFAULT_TRACK_SEVERITY_ENABLED; var severityThresholdNormal = (inputTracking === null || inputTracking === void 0 ? void 0 : inputTracking.severityNormalThreshold) || DEFAULT_TRACK_SEVERITY_THRESHOLD_NORMAL; var severityThresholdDegraded = (inputTracking === null || inputTracking === void 0 ? void 0 : inputTracking.severityDegradedThreshold) || DEFAULT_TRACK_SEVERITY_THRESHOLD_DEGRADED; var createDispatchSample = function createDispatchSample(action, view) { return function (time, severity) { var _contextIdentifierPlu2; var state = view.state; var nodesCount = getNodeCount(state); var samplePayload = { action: action, actionSubject: ACTION_SUBJECT.EDITOR, attributes: _objectSpread(_objectSpread({ time: time, nodeSize: state.doc.nodeSize }, nodesCount), {}, { objectId: contextIdentifierPlugin === null || contextIdentifierPlugin === void 0 || (_contextIdentifierPlu2 = contextIdentifierPlugin.sharedState.currentState()) === null || _contextIdentifierPlu2 === void 0 || (_contextIdentifierPlu2 = _contextIdentifierPlu2.contextIdentifierProvider) === null || _contextIdentifierPlu2 === void 0 ? void 0 : _contextIdentifierPlu2.objectId, severity: shouldTrackSeverity ? severity : undefined }), eventType: EVENT_TYPE.OPERATIONAL }; dispatchAnalyticsEvent(samplePayload); }; }; var createDispatchAverage = function createDispatchAverage(action, view) { return function (_ref, severity) { var _contextIdentifierPlu3; var mean = _ref.mean, median = _ref.median, sampleSize = _ref.sampleSize; var state = view.state; var nodeCount = getNodeCount(state); var averagePayload = { action: action, actionSubject: ACTION_SUBJECT.EDITOR, attributes: _objectSpread(_objectSpread({ mean: mean, median: median, sampleSize: sampleSize }, nodeCount), {}, { nodeSize: state.doc.nodeSize, severity: shouldTrackSeverity ? severity : undefined, objectId: contextIdentifierPlugin === null || contextIdentifierPlugin === void 0 || (_contextIdentifierPlu3 = contextIdentifierPlugin.sharedState.currentState()) === null || _contextIdentifierPlu3 === void 0 || (_contextIdentifierPlu3 = _contextIdentifierPlu3.contextIdentifierProvider) === null || _contextIdentifierPlu3 === void 0 ? void 0 : _contextIdentifierPlu3.objectId }), eventType: EVENT_TYPE.OPERATIONAL }; dispatchAnalyticsEvent(averagePayload); }; }; return new SafePlugin({ key: frozenEditorPluginKey, props: isPerformanceAPIAvailable() ? { handleTextInput: function handleTextInput(view) { if (browserFreezeTracking !== null && browserFreezeTracking !== void 0 && browserFreezeTracking.trackInteractionType) { interactionType = BROWSER_FREEZE_INTERACTION_TYPE.TYPING; } if (inputLatencyTracker) { var end = inputLatencyTracker.start(); // This is called after all handleTextInput events are executed which means first handleTextInput time incorporates following handleTextInput processing time // Also this is called before browser rendering so it doesn't count it. requestAnimationFrame(end); } return false; }, handleDOMEvents: browserFreezeTracking !== null && browserFreezeTracking !== void 0 && browserFreezeTracking.trackInteractionType ? { click: function click() { interactionType = setInteractionType(BROWSER_FREEZE_INTERACTION_TYPE.CLICKING); return false; }, paste: function paste() { interactionType = setInteractionType(BROWSER_FREEZE_INTERACTION_TYPE.PASTING); return false; } } : undefined } : undefined, view: function view(_view) { if (!isPerformanceObserverAvailable()) { return {}; } inputLatencyTracker = new InputLatencyTracker({ samplingRate: samplingRate, slowThreshold: slowThreshold, normalThreshold: severityThresholdNormal, degradedThreshold: severityThresholdDegraded, dispatchSample: createDispatchSample(ACTION.INPUT_PERF_SAMPLING, _view), dispatchAverage: createDispatchAverage(ACTION.INPUT_PERF_SAMPLING_AVG, _view), onSlowInput: function onSlowInput(time) { var _contextIdentifierPlu4; var state = _view.state; var nodesCount = getNodeCount(state); dispatchAnalyticsEvent({ action: ACTION.SLOW_INPUT, actionSubject: ACTION_SUBJECT.EDITOR, attributes: _objectSpread(_objectSpread({ time: time, nodeSize: state.doc.nodeSize }, nodesCount), {}, { objectId: contextIdentifierPlugin === null || contextIdentifierPlugin === void 0 || (_contextIdentifierPlu4 = contextIdentifierPlugin.sharedState.currentState()) === null || _contextIdentifierPlu4 === void 0 || (_contextIdentifierPlu4 = _contextIdentifierPlu4.contextIdentifierProvider) === null || _contextIdentifierPlu4 === void 0 ? void 0 : _contextIdentifierPlu4.objectId }), eventType: EVENT_TYPE.OPERATIONAL }); } }); var observer; try { observer = new PerformanceObserver(function (list) { var perfEntries = list.getEntries(); for (var i = 0; i < perfEntries.length; i++) { var duration = perfEntries[i].duration; if (duration > freezeThreshold) { dispatchLongTaskEvent(contextIdentifierPlugin)(dispatchAnalyticsEvent, _view, duration, getNodeCount, browserFreezeTracking !== null && browserFreezeTracking !== void 0 && browserFreezeTracking.trackInteractionType ? interactionType : undefined, getAnalyticsEventSeverity(duration, // Ignored via go/ees007 // eslint-disable-next-line @atlaskit/editor/enforce-todo-comment-format //TODO: get right values here severityThresholdNormal, severityThresholdDegraded)); } } }); // register observer for long task notifications observer.observe({ entryTypes: ['longtask'] }); } catch (e) {} return { destroy: function destroy() { var _inputLatencyTracker, _observer; (_inputLatencyTracker = inputLatencyTracker) === null || _inputLatencyTracker === void 0 || _inputLatencyTracker.flush(); (_observer = observer) === null || _observer === void 0 || _observer.disconnect(); } }; } }); }; });