@atlaskit/editor-plugin-analytics
Version:
Analytics plugin for @atlaskit/editor-core
269 lines (262 loc) • 14.4 kB
JavaScript
;
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
});
}