UNPKG

@atlaskit/editor-plugin-analytics

Version:

Analytics plugin for @atlaskit/editor-core

269 lines (262 loc) 14.4 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.analyticsPlugin = void 0; var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _react = require("react"); var _steps = require("@atlaskit/adf-schema/steps"); var _useAnalyticsEvents2 = require("@atlaskit/analytics-next/useAnalyticsEvents"); var _analytics = require("@atlaskit/editor-common/analytics"); var _isPerformanceApiAvailable = require("@atlaskit/editor-common/is-performance-api-available"); var _measureRender = require("@atlaskit/editor-common/performance/measure-render"); var _safePlugin = require("@atlaskit/editor-common/safe-plugin"); var _attachPayloadIntoTransaction = require("./pm-plugins/analytics-api/attach-payload-into-transaction"); var _editorStateContext = require("./pm-plugins/analytics-api/editor-state-context"); var _consts = require("./pm-plugins/consts"); var _pluginKey = require("./pm-plugins/plugin-key"); var _undoRedoInputSource = require("./pm-plugins/undo-redo-input-source"); function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } 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) { (0, _defineProperty2.default)(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; } function createPlugin(options, featureFlags) { if (!options) { return; } var hasRequiredPerformanceAPIs = (0, _isPerformanceApiAvailable.isPerformanceAPIAvailable)(); return new _safePlugin.SafePlugin({ key: _pluginKey.analyticsPluginKey, state: { init: function init() { return _objectSpread(_objectSpread({}, options), {}, { fireAnalytics: (0, _analytics.fireAnalyticsEvent)(options.createAnalyticsEvent) }); }, apply: function apply(tr, pluginState, _, state) { var _tr$getMeta; var _ref = (_tr$getMeta = tr.getMeta(_pluginKey.analyticsPluginKey)) !== null && _tr$getMeta !== void 0 ? _tr$getMeta : {}, createAnalyticsEvent = _ref.createAnalyticsEvent; // When the createAnalyticsEvent is reconfigured if (options.createAnalyticsEvent && options.createAnalyticsEvent !== pluginState.createAnalyticsEvent || pluginState.createAnalyticsEvent !== createAnalyticsEvent && createAnalyticsEvent) { var _options$createAnalyt; return _objectSpread(_objectSpread({}, pluginState), {}, { createAnalyticsEvent: (_options$createAnalyt = options.createAnalyticsEvent) !== null && _options$createAnalyt !== void 0 ? _options$createAnalyt : createAnalyticsEvent }); } if (featureFlags.catchAllTracking) { var analyticsEventWithChannel = (0, _analytics.getAnalyticsEventsFromTransaction)(tr); if (analyticsEventWithChannel.length > 0) { var _iterator = _createForOfIteratorHelper(analyticsEventWithChannel), _step; try { var _loop = function _loop() { var _step$value = _step.value, payload = _step$value.payload, channel = _step$value.channel; // Measures how much time it takes to update the DOM after each ProseMirror document update // that has an analytics event. if (hasRequiredPerformanceAPIs && tr.docChanged && payload.action !== _analytics.ACTION.INSERTED && payload.action !== _analytics.ACTION.DELETED) { var measureName = "".concat(payload.actionSubject, ":").concat(payload.action, ":").concat(payload.actionSubjectId); (0, _measureRender.measureRender)( // NOTE this name could be resulting in misleading data -- where if multiple payloads are // received before a render completes -- the measurement value will be inaccurate (this is // due to measureRender requiring unique measureNames) measureName, function (_ref2) { var duration = _ref2.duration, distortedDuration = _ref2.distortedDuration; (0, _analytics.fireAnalyticsEvent)(pluginState.createAnalyticsEvent)({ payload: extendPayload({ payload: payload, duration: duration, distortedDuration: distortedDuration }), channel: channel }); }); } }; for (_iterator.s(); !(_step = _iterator.n()).done;) { _loop(); } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } } } return pluginState; } } }); } /** * Analytics plugin to be added to an `EditorPresetBuilder` and used with `ComposableEditor` * from `@atlaskit/editor-core`. */ var analyticsPlugin = exports.analyticsPlugin = function analyticsPlugin(_ref3) { var _api$featureFlags; var _ref3$config = _ref3.config, options = _ref3$config === void 0 ? {} : _ref3$config, api = _ref3.api; var featureFlags = (api === null || api === void 0 || (_api$featureFlags = api.featureFlags) === null || _api$featureFlags === void 0 ? void 0 : _api$featureFlags.sharedState.currentState()) || {}; var analyticsEventPropQueue = new Set(); return { name: 'analytics', getSharedState: function getSharedState(editorState) { var _analyticsPluginKey$g; if (!editorState) { return { createAnalyticsEvent: null, attachAnalyticsEvent: null, performanceTracking: undefined }; } var _ref4 = (_analyticsPluginKey$g = _pluginKey.analyticsPluginKey.getState(editorState)) !== null && _analyticsPluginKey$g !== void 0 ? _analyticsPluginKey$g : {}, createAnalyticsEvent = _ref4.createAnalyticsEvent, performanceTracking = _ref4.performanceTracking; return { createAnalyticsEvent: createAnalyticsEvent, attachAnalyticsEvent: (0, _attachPayloadIntoTransaction.createAttachPayloadIntoTransaction)(editorState.selection), performanceTracking: performanceTracking }; }, actions: { attachAnalyticsEvent: function attachAnalyticsEvent(payload) { var channel = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _consts.editorAnalyticsChannel; return function (tr) { var _api$analytics$shared, _api$analytics; var _ref5 = (_api$analytics$shared = api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.sharedState.currentState()) !== null && _api$analytics$shared !== void 0 ? _api$analytics$shared : {}, createAnalyticsEvent = _ref5.createAnalyticsEvent, attachAnalyticsEvent = _ref5.attachAnalyticsEvent; if (!tr || !createAnalyticsEvent || !attachAnalyticsEvent) { analyticsEventPropQueue.add({ payload: payload, channel: channel }); return false; } attachAnalyticsEvent({ tr: tr, payload: payload, channel: channel }); return true; }; }, fireAnalyticsEvent: function fireAnalyticsEvent(payload) { var _api$analytics$shared2, _api$analytics2; var channel = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _consts.editorAnalyticsChannel; var options = arguments.length > 2 ? arguments[2] : undefined; var _ref6 = (_api$analytics$shared2 = api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.sharedState.currentState()) !== null && _api$analytics$shared2 !== void 0 ? _api$analytics$shared2 : {}, createAnalyticsEvent = _ref6.createAnalyticsEvent; if (options !== null && options !== void 0 && options.context) { payload = (0, _editorStateContext.getStateContext)(options.context.selection, payload); } if (!createAnalyticsEvent) { analyticsEventPropQueue.add({ payload: payload, channel: channel }); return; } // This cast is needed to satisfy TS when using custom payloads which is only allowed by // the fireAnalyticsEvent function in EditorAnalyticsAPI for now. // createAnalyticsEvent actually fires our standard AnalyticsEventPayload type which is less strict // so this is safe. var firedPayload = payload; (0, _analytics.fireAnalyticsEvent)(createAnalyticsEvent, options)({ payload: firedPayload, channel: channel }); } }, usePluginHook: function usePluginHook(_ref7) { var editorView = _ref7.editorView; var _useAnalyticsEvents = (0, _useAnalyticsEvents2.useAnalyticsEvents)(), createAnalyticsEvent = _useAnalyticsEvents.createAnalyticsEvent; (0, _react.useLayoutEffect)(function () { var dispatch = editorView.dispatch, tr = editorView.state.tr; tr.setMeta(_pluginKey.analyticsPluginKey, { createAnalyticsEvent: createAnalyticsEvent }); dispatch(tr); // Attach all analytics events to the transaction analyticsEventPropQueue.forEach(function (_ref8) { var _createAnalyticsEvent; var payload = _ref8.payload, channel = _ref8.channel; (_createAnalyticsEvent = createAnalyticsEvent(payload)) === null || _createAnalyticsEvent === void 0 || _createAnalyticsEvent.fire(channel !== null && channel !== void 0 ? channel : _consts.editorAnalyticsChannel); }); // Clear the queue analyticsEventPropQueue.clear(); }, [createAnalyticsEvent, editorView]); }, pmPlugins: function pmPlugins() { return [{ name: 'analyticsPlugin', plugin: function plugin() { return createPlugin(options, featureFlags); } }]; }, onEditorViewStateUpdated: function onEditorViewStateUpdated(_ref9) { var originalTransaction = _ref9.originalTransaction, transactions = _ref9.transactions, newEditorState = _ref9.newEditorState; var pluginState = _pluginKey.analyticsPluginKey.getState(newEditorState); if (!pluginState || !pluginState.createAnalyticsEvent) { return; } // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any var steps = transactions.reduce(function (acc, tr) { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any var payloads = tr.steps // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any .filter(function (step) { return step instanceof _steps.AnalyticsStep; }).map(function (x) { return x.analyticsEvents; }).reduce(function (acc, val) { return acc.concat(val); }, []); acc.push.apply(acc, (0, _toConsumableArray2.default)(payloads)); return acc; }, []); if (steps.length === 0) { return; } var createAnalyticsEvent = pluginState.createAnalyticsEvent; var undoAnaltyicsEventTransformer = (0, _undoRedoInputSource.generateUndoRedoInputSoucePayload)(originalTransaction); steps.forEach(function (_ref0) { var payload = _ref0.payload, channel = _ref0.channel; var nextPayload = undoAnaltyicsEventTransformer(payload); (0, _analytics.fireAnalyticsEvent)(createAnalyticsEvent)({ payload: nextPayload, channel: channel }); }); } }; }; function extendPayload(_ref1) { var payload = _ref1.payload, duration = _ref1.duration, distortedDuration = _ref1.distortedDuration; return _objectSpread(_objectSpread({}, payload), {}, { attributes: _objectSpread(_objectSpread({}, payload.attributes), {}, { duration: duration, distortedDuration: distortedDuration }), eventType: _analytics.EVENT_TYPE.OPERATIONAL }); }