@atlaskit/editor-plugin-metrics 1.1.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +8 -8
- package/CHANGELOG.md +12 -0
- package/dist/cjs/metricsPlugin.js +8 -32
- package/dist/cjs/pm-plugins/main.js +65 -28
- package/dist/cjs/pm-plugins/utils/active-session-timer.js +6 -5
- package/dist/cjs/pm-plugins/utils/analytics.js +46 -0
- package/dist/cjs/pm-plugins/utils/isNonTextUndo.js +28 -0
- package/dist/es2019/metricsPlugin.js +8 -31
- package/dist/es2019/pm-plugins/main.js +71 -32
- package/dist/es2019/pm-plugins/utils/active-session-timer.js +6 -5
- package/dist/es2019/pm-plugins/utils/analytics.js +38 -0
- package/dist/es2019/pm-plugins/utils/isNonTextUndo.js +24 -0
- package/dist/esm/metricsPlugin.js +8 -32
- package/dist/esm/pm-plugins/main.js +65 -28
- package/dist/esm/pm-plugins/utils/active-session-timer.js +6 -5
- package/dist/esm/pm-plugins/utils/analytics.js +40 -0
- package/dist/esm/pm-plugins/utils/isNonTextUndo.js +22 -0
- package/dist/types/metricsPluginType.d.ts +2 -1
- package/dist/types/pm-plugins/main.d.ts +3 -2
- package/dist/types/pm-plugins/utils/analytics.d.ts +11 -0
- package/dist/types/pm-plugins/utils/isNonTextUndo.d.ts +2 -0
- package/dist/types-ts4.5/metricsPluginType.d.ts +4 -1
- package/dist/types-ts4.5/pm-plugins/main.d.ts +3 -2
- package/dist/types-ts4.5/pm-plugins/utils/analytics.d.ts +11 -0
- package/dist/types-ts4.5/pm-plugins/utils/isNonTextUndo.d.ts +2 -0
- package/package.json +5 -3
package/.eslintrc.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
rules: {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
'@typescript-eslint/no-duplicate-imports': 'error',
|
|
4
|
+
'@typescript-eslint/no-explicit-any': 'error',
|
|
5
5
|
},
|
|
6
6
|
overrides: [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
{
|
|
8
|
+
files: ['**/__tests__/**/*.{js,ts,tsx}', '**/examples/**/*.{js,ts,tsx}'],
|
|
9
|
+
rules: {
|
|
10
|
+
'@typescript-eslint/no-explicit-any': 'off',
|
|
11
|
+
},
|
|
11
12
|
},
|
|
12
|
-
},
|
|
13
13
|
],
|
|
14
|
-
|
|
14
|
+
};
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-metrics
|
|
2
2
|
|
|
3
|
+
## 2.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- [#105399](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/105399)
|
|
8
|
+
[`ed98e34b5912b`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/ed98e34b5912b) -
|
|
9
|
+
ED-26234 Fire analytics at end of editor session
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies
|
|
14
|
+
|
|
3
15
|
## 1.1.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.metricsPlugin = void 0;
|
|
7
7
|
var _main = require("./pm-plugins/main");
|
|
8
|
+
var _analytics = require("./pm-plugins/utils/analytics");
|
|
8
9
|
/**
|
|
9
10
|
* Metrics plugin to be added to an `EditorPresetBuilder` and used with `ComposableEditor`
|
|
10
11
|
* from `@atlaskit/editor-core`.
|
|
@@ -29,43 +30,18 @@ var metricsPlugin = exports.metricsPlugin = function metricsPlugin(_ref) {
|
|
|
29
30
|
return tr;
|
|
30
31
|
}
|
|
31
32
|
var newTr = tr;
|
|
33
|
+
var pluginState = api === null || api === void 0 ? void 0 : api.metrics.sharedState.currentState();
|
|
34
|
+
if (pluginState && pluginState.totalActionCount > 0 && pluginState.activeSessionTime > 0) {
|
|
35
|
+
var payloadToSend = (0, _analytics.getAnalyticsPayload)({
|
|
36
|
+
pluginState: pluginState
|
|
37
|
+
});
|
|
38
|
+
api === null || api === void 0 || api.analytics.actions.attachAnalyticsEvent(payloadToSend)(newTr);
|
|
39
|
+
}
|
|
32
40
|
newTr.setMeta(_main.metricsKey, {
|
|
33
41
|
stopActiveSession: true
|
|
34
42
|
});
|
|
35
43
|
return newTr;
|
|
36
44
|
};
|
|
37
|
-
},
|
|
38
|
-
fireSessionAnalytics: function fireSessionAnalytics() {
|
|
39
|
-
return function (_ref3) {
|
|
40
|
-
var tr = _ref3.tr;
|
|
41
|
-
if (!api) {
|
|
42
|
-
return tr;
|
|
43
|
-
}
|
|
44
|
-
var state = api.metrics.sharedState.currentState();
|
|
45
|
-
if (!state) {
|
|
46
|
-
return tr;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// const eventAttributes = {
|
|
50
|
-
// efficiency: {
|
|
51
|
-
// totalActiveTime: state.activeSessionTime || 0,
|
|
52
|
-
// totalActionCount: state.totalActionCount || 0,
|
|
53
|
-
// actionTypeCount: state.actionTypeCount,
|
|
54
|
-
// totalInactiveTime:
|
|
55
|
-
// Date.now() - (state.editSessionStartTime || 0) - (state.activeSessionTime || 0),
|
|
56
|
-
// },
|
|
57
|
-
// TODO: Add effectiveness attributes
|
|
58
|
-
// effectiveness: {
|
|
59
|
-
// undoCount: 0,
|
|
60
|
-
// repeatedActionCount: 0,
|
|
61
|
-
// safeInsertCount: 0,
|
|
62
|
-
// },
|
|
63
|
-
// };
|
|
64
|
-
|
|
65
|
-
// fire analytics event
|
|
66
|
-
// api.analytics.actions.attachAnalyticsEvent({});
|
|
67
|
-
return tr;
|
|
68
|
-
};
|
|
69
45
|
}
|
|
70
46
|
},
|
|
71
47
|
getSharedState: function getSharedState(editorState) {
|
|
@@ -6,27 +6,31 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
});
|
|
7
7
|
exports.metricsKey = exports.initialPluginState = exports.createPlugin = void 0;
|
|
8
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
+
var _bindEventListener = require("bind-event-listener");
|
|
9
10
|
var _steps = require("@atlaskit/adf-schema/steps");
|
|
10
11
|
var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
|
|
11
12
|
var _utils = require("@atlaskit/editor-common/utils");
|
|
12
13
|
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
13
14
|
var _activeSessionTimer = require("./utils/active-session-timer");
|
|
15
|
+
var _analytics = require("./utils/analytics");
|
|
16
|
+
var _isNonTextUndo = require("./utils/isNonTextUndo");
|
|
14
17
|
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; }
|
|
15
18
|
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; }
|
|
16
19
|
var metricsKey = exports.metricsKey = new _state.PluginKey('metricsPlugin');
|
|
17
20
|
var initialPluginState = exports.initialPluginState = {
|
|
18
|
-
editSessionStartTime: undefined,
|
|
19
21
|
intentToStartEditTime: undefined,
|
|
20
22
|
lastSelection: undefined,
|
|
21
23
|
activeSessionTime: 0,
|
|
22
24
|
totalActionCount: 0,
|
|
25
|
+
contentSizeChanged: 0,
|
|
23
26
|
timeOfLastTextInput: undefined,
|
|
24
27
|
actionTypeCount: {
|
|
25
28
|
textInputCount: 0,
|
|
26
29
|
nodeInsertionCount: 0,
|
|
27
30
|
nodeAttributeChangeCount: 0,
|
|
28
31
|
contentMovedCount: 0,
|
|
29
|
-
nodeDeletionCount: 0
|
|
32
|
+
nodeDeletionCount: 0,
|
|
33
|
+
undoCount: 0
|
|
30
34
|
}
|
|
31
35
|
};
|
|
32
36
|
var createPlugin = exports.createPlugin = function createPlugin(api) {
|
|
@@ -35,23 +39,28 @@ var createPlugin = exports.createPlugin = function createPlugin(api) {
|
|
|
35
39
|
key: metricsKey,
|
|
36
40
|
state: {
|
|
37
41
|
init: function init() {
|
|
38
|
-
return
|
|
39
|
-
editSessionStartTime: performance.now()
|
|
40
|
-
});
|
|
42
|
+
return initialPluginState;
|
|
41
43
|
},
|
|
42
|
-
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
45
|
+
apply: function apply(tr, pluginState, oldState, newState) {
|
|
43
46
|
var meta = tr.getMeta(metricsKey);
|
|
44
47
|
var intentToStartEditTime = (meta === null || meta === void 0 ? void 0 : meta.intentToStartEditTime) || pluginState.intentToStartEditTime;
|
|
45
48
|
if (meta && meta.stopActiveSession) {
|
|
46
|
-
|
|
47
|
-
return _objectSpread(_objectSpread({}, pluginState), {}, {
|
|
48
|
-
intentToStartEditTime: undefined,
|
|
49
|
-
lastSelection: undefined
|
|
50
|
-
});
|
|
49
|
+
return initialPluginState;
|
|
51
50
|
}
|
|
52
51
|
if (!intentToStartEditTime) {
|
|
53
|
-
|
|
52
|
+
if (tr.docChanged && !tr.getMeta('replaceDocument')) {
|
|
53
|
+
intentToStartEditTime = performance.now();
|
|
54
|
+
} else {
|
|
55
|
+
return pluginState;
|
|
56
|
+
}
|
|
54
57
|
}
|
|
58
|
+
var undoCount = (0, _isNonTextUndo.isNonTextUndo)(tr) ? 1 : 0;
|
|
59
|
+
var newActionTypeCount = pluginState.actionTypeCount ? _objectSpread(_objectSpread({}, pluginState.actionTypeCount), {}, {
|
|
60
|
+
undoCount: pluginState.actionTypeCount.undoCount + undoCount
|
|
61
|
+
}) : _objectSpread(_objectSpread({}, initialPluginState.actionTypeCount), {}, {
|
|
62
|
+
undoCount: undoCount
|
|
63
|
+
});
|
|
55
64
|
var canIgnoreTr = function canIgnoreTr() {
|
|
56
65
|
return !tr.steps.every(function (e) {
|
|
57
66
|
return e instanceof _steps.AnalyticsStep;
|
|
@@ -60,36 +69,64 @@ var createPlugin = exports.createPlugin = function createPlugin(api) {
|
|
|
60
69
|
if (tr.docChanged && canIgnoreTr()) {
|
|
61
70
|
var now = performance.now();
|
|
62
71
|
timer.startTimer();
|
|
63
|
-
|
|
72
|
+
var actionTypeCount = pluginState.actionTypeCount,
|
|
73
|
+
timeOfLastTextInput = pluginState.timeOfLastTextInput,
|
|
74
|
+
totalActionCount = pluginState.totalActionCount,
|
|
75
|
+
activeSessionTime = pluginState.activeSessionTime;
|
|
64
76
|
// If previous and current action is text insertion, then don't increase total action count
|
|
65
77
|
var isActionTextInput = (0, _utils.isTextInput)(tr);
|
|
78
|
+
var newActiveSessionTime = activeSessionTime + (now - intentToStartEditTime);
|
|
79
|
+
var newTextInputCount = isActionTextInput ? actionTypeCount.textInputCount + 1 : actionTypeCount.textInputCount;
|
|
80
|
+
var newTotalActionCount = pluginState.totalActionCount + 1;
|
|
66
81
|
if (pluginState.timeOfLastTextInput && isActionTextInput) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
timeOfLastTextInput: now
|
|
71
|
-
});
|
|
82
|
+
newActiveSessionTime = activeSessionTime + (now - (timeOfLastTextInput || 0));
|
|
83
|
+
newTextInputCount = actionTypeCount.textInputCount;
|
|
84
|
+
newTotalActionCount = totalActionCount;
|
|
72
85
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
86
|
+
var newPluginState = _objectSpread(_objectSpread({}, pluginState), {}, {
|
|
87
|
+
activeSessionTime: newActiveSessionTime,
|
|
88
|
+
totalActionCount: newTotalActionCount,
|
|
89
|
+
timeOfLastTextInput: isActionTextInput ? now : undefined,
|
|
90
|
+
contentSizeChanged: pluginState.contentSizeChanged + (newState.doc.content.size - oldState.doc.content.size),
|
|
91
|
+
actionTypeCount: _objectSpread(_objectSpread({}, newActionTypeCount), {}, {
|
|
92
|
+
textInputCount: newTextInputCount
|
|
93
|
+
})
|
|
79
94
|
});
|
|
95
|
+
return newPluginState;
|
|
80
96
|
}
|
|
81
97
|
return _objectSpread(_objectSpread({}, pluginState), {}, {
|
|
82
98
|
lastSelection: (meta === null || meta === void 0 ? void 0 : meta.newSelection) || pluginState.lastSelection,
|
|
83
|
-
intentToStartEditTime:
|
|
99
|
+
intentToStartEditTime: intentToStartEditTime,
|
|
100
|
+
actionTypeCount: newActionTypeCount
|
|
84
101
|
});
|
|
85
102
|
}
|
|
86
103
|
},
|
|
87
|
-
view: function view() {
|
|
104
|
+
view: function view(_view) {
|
|
105
|
+
var fireAnalyticsEvent = function fireAnalyticsEvent() {
|
|
106
|
+
var pluginState = metricsKey.getState(_view.state);
|
|
107
|
+
if (!pluginState) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
var payloadToSend = (0, _analytics.getAnalyticsPayload)({
|
|
111
|
+
pluginState: pluginState
|
|
112
|
+
});
|
|
113
|
+
if (pluginState && pluginState.totalActionCount > 0 && pluginState.activeSessionTime > 0) {
|
|
114
|
+
api === null || api === void 0 || api.analytics.actions.fireAnalyticsEvent(payloadToSend, undefined, {
|
|
115
|
+
immediate: true
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
var unbindBeforeUnload = (0, _bindEventListener.bind)(window, {
|
|
120
|
+
type: 'beforeunload',
|
|
121
|
+
listener: function listener() {
|
|
122
|
+
fireAnalyticsEvent();
|
|
123
|
+
}
|
|
124
|
+
});
|
|
88
125
|
return {
|
|
89
126
|
destroy: function destroy() {
|
|
90
|
-
|
|
91
|
-
api === null || api === void 0 || api.core.actions.execute(api === null || api === void 0 || (_api$metrics = api.metrics) === null || _api$metrics === void 0 ? void 0 : _api$metrics.commands.fireSessionAnalytics());
|
|
127
|
+
fireAnalyticsEvent();
|
|
92
128
|
timer.cleanupTimer();
|
|
129
|
+
unbindBeforeUnload();
|
|
93
130
|
}
|
|
94
131
|
};
|
|
95
132
|
},
|
|
@@ -8,6 +8,7 @@ exports.ActiveSessionTimer = void 0;
|
|
|
8
8
|
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
9
9
|
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
10
10
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
11
|
+
var ACTIVE_SESSION_IDLE_TIME = 5000;
|
|
11
12
|
var ActiveSessionTimer = exports.ActiveSessionTimer = /*#__PURE__*/(0, _createClass2.default)(function ActiveSessionTimer(api) {
|
|
12
13
|
var _this = this;
|
|
13
14
|
(0, _classCallCheck2.default)(this, ActiveSessionTimer);
|
|
@@ -16,14 +17,14 @@ var ActiveSessionTimer = exports.ActiveSessionTimer = /*#__PURE__*/(0, _createCl
|
|
|
16
17
|
clearTimeout(_this.timerId);
|
|
17
18
|
}
|
|
18
19
|
_this.timerId = window.setTimeout(function () {
|
|
20
|
+
if (_this.api) {
|
|
21
|
+
var _this$api$metrics;
|
|
22
|
+
_this.api.core.actions.execute((_this$api$metrics = _this.api.metrics) === null || _this$api$metrics === void 0 ? void 0 : _this$api$metrics.commands.stopActiveSession());
|
|
23
|
+
}
|
|
19
24
|
_this.cleanupTimer();
|
|
20
|
-
},
|
|
25
|
+
}, ACTIVE_SESSION_IDLE_TIME);
|
|
21
26
|
});
|
|
22
27
|
(0, _defineProperty2.default)(this, "cleanupTimer", function () {
|
|
23
|
-
if (_this.api) {
|
|
24
|
-
var _this$api$metrics;
|
|
25
|
-
_this.api.core.actions.execute((_this$api$metrics = _this.api.metrics) === null || _this$api$metrics === void 0 ? void 0 : _this$api$metrics.commands.stopActiveSession());
|
|
26
|
-
}
|
|
27
28
|
if (_this.timerId) {
|
|
28
29
|
clearTimeout(_this.timerId);
|
|
29
30
|
_this.timerId = null;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getPayloadAttributes = exports.getAnalyticsPayload = void 0;
|
|
7
|
+
var _analytics = require("@atlaskit/editor-common/analytics");
|
|
8
|
+
var getPayloadAttributes = exports.getPayloadAttributes = function getPayloadAttributes(_ref) {
|
|
9
|
+
var pluginState = _ref.pluginState,
|
|
10
|
+
contentSizeChanged = _ref.contentSizeChanged;
|
|
11
|
+
return {
|
|
12
|
+
efficiency: {
|
|
13
|
+
totalActiveTime: pluginState.activeSessionTime,
|
|
14
|
+
totalActionCount: pluginState.totalActionCount,
|
|
15
|
+
actionByTypeCount: pluginState.actionTypeCount
|
|
16
|
+
},
|
|
17
|
+
effectiveness: {
|
|
18
|
+
undoCount: pluginState.actionTypeCount.undoCount,
|
|
19
|
+
repeatedActionCount: 0,
|
|
20
|
+
safeInsertCount: 0
|
|
21
|
+
},
|
|
22
|
+
contentSizeChanged: contentSizeChanged
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
var getAnalyticsPayload = exports.getAnalyticsPayload = function getAnalyticsPayload(_ref2) {
|
|
26
|
+
var pluginState = _ref2.pluginState;
|
|
27
|
+
return {
|
|
28
|
+
action: _analytics.ACTION.ENDED,
|
|
29
|
+
actionSubject: _analytics.ACTION_SUBJECT.ACTIVITY_SESSION,
|
|
30
|
+
actionSubjectId: _analytics.ACTION_SUBJECT_ID.ACTIVITY,
|
|
31
|
+
attributes: {
|
|
32
|
+
efficiency: {
|
|
33
|
+
totalActiveTime: pluginState.activeSessionTime,
|
|
34
|
+
totalActionCount: pluginState.totalActionCount,
|
|
35
|
+
actionByTypeCount: pluginState.actionTypeCount
|
|
36
|
+
},
|
|
37
|
+
effectiveness: {
|
|
38
|
+
undoCount: pluginState.actionTypeCount.undoCount,
|
|
39
|
+
repeatedActionCount: 0,
|
|
40
|
+
safeInsertCount: 0
|
|
41
|
+
},
|
|
42
|
+
contentSizeChanged: pluginState.contentSizeChanged
|
|
43
|
+
},
|
|
44
|
+
eventType: _analytics.EVENT_TYPE.TRACK
|
|
45
|
+
};
|
|
46
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isNonTextUndo = void 0;
|
|
7
|
+
var _transform = require("@atlaskit/editor-prosemirror/transform");
|
|
8
|
+
var isNonTextUndo = exports.isNonTextUndo = function isNonTextUndo(tr) {
|
|
9
|
+
if (tr.getMeta('undoRedoPlugin$') === undefined) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
var hasNonTextChange = false;
|
|
13
|
+
tr.steps.forEach(function (step) {
|
|
14
|
+
if (step instanceof _transform.ReplaceStep) {
|
|
15
|
+
var slice = step.slice;
|
|
16
|
+
if (slice.content) {
|
|
17
|
+
slice.content.forEach(function (node) {
|
|
18
|
+
if (node.type.name !== 'text') {
|
|
19
|
+
hasNonTextChange = true;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
} else {
|
|
24
|
+
hasNonTextChange = true;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return hasNonTextChange;
|
|
28
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createPlugin, initialPluginState, metricsKey } from './pm-plugins/main';
|
|
2
|
+
import { getAnalyticsPayload } from './pm-plugins/utils/analytics';
|
|
2
3
|
/**
|
|
3
4
|
* Metrics plugin to be added to an `EditorPresetBuilder` and used with `ComposableEditor`
|
|
4
5
|
* from `@atlaskit/editor-core`.
|
|
@@ -21,41 +22,17 @@ export const metricsPlugin = ({
|
|
|
21
22
|
return tr;
|
|
22
23
|
}
|
|
23
24
|
const newTr = tr;
|
|
25
|
+
const pluginState = api === null || api === void 0 ? void 0 : api.metrics.sharedState.currentState();
|
|
26
|
+
if (pluginState && pluginState.totalActionCount > 0 && pluginState.activeSessionTime > 0) {
|
|
27
|
+
const payloadToSend = getAnalyticsPayload({
|
|
28
|
+
pluginState
|
|
29
|
+
});
|
|
30
|
+
api === null || api === void 0 ? void 0 : api.analytics.actions.attachAnalyticsEvent(payloadToSend)(newTr);
|
|
31
|
+
}
|
|
24
32
|
newTr.setMeta(metricsKey, {
|
|
25
33
|
stopActiveSession: true
|
|
26
34
|
});
|
|
27
35
|
return newTr;
|
|
28
|
-
},
|
|
29
|
-
fireSessionAnalytics: () => ({
|
|
30
|
-
tr
|
|
31
|
-
}) => {
|
|
32
|
-
if (!api) {
|
|
33
|
-
return tr;
|
|
34
|
-
}
|
|
35
|
-
const state = api.metrics.sharedState.currentState();
|
|
36
|
-
if (!state) {
|
|
37
|
-
return tr;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// const eventAttributes = {
|
|
41
|
-
// efficiency: {
|
|
42
|
-
// totalActiveTime: state.activeSessionTime || 0,
|
|
43
|
-
// totalActionCount: state.totalActionCount || 0,
|
|
44
|
-
// actionTypeCount: state.actionTypeCount,
|
|
45
|
-
// totalInactiveTime:
|
|
46
|
-
// Date.now() - (state.editSessionStartTime || 0) - (state.activeSessionTime || 0),
|
|
47
|
-
// },
|
|
48
|
-
// TODO: Add effectiveness attributes
|
|
49
|
-
// effectiveness: {
|
|
50
|
-
// undoCount: 0,
|
|
51
|
-
// repeatedActionCount: 0,
|
|
52
|
-
// safeInsertCount: 0,
|
|
53
|
-
// },
|
|
54
|
-
// };
|
|
55
|
-
|
|
56
|
-
// fire analytics event
|
|
57
|
-
// api.analytics.actions.attachAnalyticsEvent({});
|
|
58
|
-
return tr;
|
|
59
36
|
}
|
|
60
37
|
},
|
|
61
38
|
getSharedState(editorState) {
|
|
@@ -1,22 +1,26 @@
|
|
|
1
|
+
import { bind } from 'bind-event-listener';
|
|
1
2
|
import { AnalyticsStep } from '@atlaskit/adf-schema/steps';
|
|
2
3
|
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
3
4
|
import { isTextInput } from '@atlaskit/editor-common/utils';
|
|
4
5
|
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
5
6
|
import { ActiveSessionTimer } from './utils/active-session-timer';
|
|
7
|
+
import { getAnalyticsPayload } from './utils/analytics';
|
|
8
|
+
import { isNonTextUndo } from './utils/isNonTextUndo';
|
|
6
9
|
export const metricsKey = new PluginKey('metricsPlugin');
|
|
7
10
|
export const initialPluginState = {
|
|
8
|
-
editSessionStartTime: undefined,
|
|
9
11
|
intentToStartEditTime: undefined,
|
|
10
12
|
lastSelection: undefined,
|
|
11
13
|
activeSessionTime: 0,
|
|
12
14
|
totalActionCount: 0,
|
|
15
|
+
contentSizeChanged: 0,
|
|
13
16
|
timeOfLastTextInput: undefined,
|
|
14
17
|
actionTypeCount: {
|
|
15
18
|
textInputCount: 0,
|
|
16
19
|
nodeInsertionCount: 0,
|
|
17
20
|
nodeAttributeChangeCount: 0,
|
|
18
21
|
contentMovedCount: 0,
|
|
19
|
-
nodeDeletionCount: 0
|
|
22
|
+
nodeDeletionCount: 0,
|
|
23
|
+
undoCount: 0
|
|
20
24
|
}
|
|
21
25
|
};
|
|
22
26
|
export const createPlugin = api => {
|
|
@@ -25,62 +29,97 @@ export const createPlugin = api => {
|
|
|
25
29
|
key: metricsKey,
|
|
26
30
|
state: {
|
|
27
31
|
init: () => {
|
|
28
|
-
return
|
|
29
|
-
...initialPluginState,
|
|
30
|
-
editSessionStartTime: performance.now()
|
|
31
|
-
};
|
|
32
|
+
return initialPluginState;
|
|
32
33
|
},
|
|
33
|
-
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
35
|
+
apply(tr, pluginState, oldState, newState) {
|
|
34
36
|
const meta = tr.getMeta(metricsKey);
|
|
35
|
-
|
|
37
|
+
let intentToStartEditTime = (meta === null || meta === void 0 ? void 0 : meta.intentToStartEditTime) || pluginState.intentToStartEditTime;
|
|
36
38
|
if (meta && meta.stopActiveSession) {
|
|
37
|
-
|
|
38
|
-
return {
|
|
39
|
-
...pluginState,
|
|
40
|
-
intentToStartEditTime: undefined,
|
|
41
|
-
lastSelection: undefined
|
|
42
|
-
};
|
|
39
|
+
return initialPluginState;
|
|
43
40
|
}
|
|
44
41
|
if (!intentToStartEditTime) {
|
|
45
|
-
|
|
42
|
+
if (tr.docChanged && !tr.getMeta('replaceDocument')) {
|
|
43
|
+
intentToStartEditTime = performance.now();
|
|
44
|
+
} else {
|
|
45
|
+
return pluginState;
|
|
46
|
+
}
|
|
46
47
|
}
|
|
48
|
+
const undoCount = isNonTextUndo(tr) ? 1 : 0;
|
|
49
|
+
const newActionTypeCount = pluginState.actionTypeCount ? {
|
|
50
|
+
...pluginState.actionTypeCount,
|
|
51
|
+
undoCount: pluginState.actionTypeCount.undoCount + undoCount
|
|
52
|
+
} : {
|
|
53
|
+
...initialPluginState.actionTypeCount,
|
|
54
|
+
undoCount
|
|
55
|
+
};
|
|
47
56
|
const canIgnoreTr = () => !tr.steps.every(e => e instanceof AnalyticsStep);
|
|
48
57
|
if (tr.docChanged && canIgnoreTr()) {
|
|
49
58
|
const now = performance.now();
|
|
50
59
|
timer.startTimer();
|
|
51
|
-
|
|
60
|
+
const {
|
|
61
|
+
actionTypeCount,
|
|
62
|
+
timeOfLastTextInput,
|
|
63
|
+
totalActionCount,
|
|
64
|
+
activeSessionTime
|
|
65
|
+
} = pluginState;
|
|
52
66
|
// If previous and current action is text insertion, then don't increase total action count
|
|
53
67
|
const isActionTextInput = isTextInput(tr);
|
|
68
|
+
let newActiveSessionTime = activeSessionTime + (now - intentToStartEditTime);
|
|
69
|
+
let newTextInputCount = isActionTextInput ? actionTypeCount.textInputCount + 1 : actionTypeCount.textInputCount;
|
|
70
|
+
let newTotalActionCount = pluginState.totalActionCount + 1;
|
|
54
71
|
if (pluginState.timeOfLastTextInput && isActionTextInput) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
totalActionCount: pluginState.totalActionCount,
|
|
59
|
-
timeOfLastTextInput: now
|
|
60
|
-
};
|
|
72
|
+
newActiveSessionTime = activeSessionTime + (now - (timeOfLastTextInput || 0));
|
|
73
|
+
newTextInputCount = actionTypeCount.textInputCount;
|
|
74
|
+
newTotalActionCount = totalActionCount;
|
|
61
75
|
}
|
|
62
|
-
|
|
63
|
-
// TODO: Add actionTypeCount
|
|
64
|
-
return {
|
|
76
|
+
const newPluginState = {
|
|
65
77
|
...pluginState,
|
|
66
|
-
activeSessionTime:
|
|
67
|
-
totalActionCount:
|
|
68
|
-
timeOfLastTextInput: isActionTextInput ? now : undefined
|
|
78
|
+
activeSessionTime: newActiveSessionTime,
|
|
79
|
+
totalActionCount: newTotalActionCount,
|
|
80
|
+
timeOfLastTextInput: isActionTextInput ? now : undefined,
|
|
81
|
+
contentSizeChanged: pluginState.contentSizeChanged + (newState.doc.content.size - oldState.doc.content.size),
|
|
82
|
+
actionTypeCount: {
|
|
83
|
+
...newActionTypeCount,
|
|
84
|
+
textInputCount: newTextInputCount
|
|
85
|
+
}
|
|
69
86
|
};
|
|
87
|
+
return newPluginState;
|
|
70
88
|
}
|
|
71
89
|
return {
|
|
72
90
|
...pluginState,
|
|
73
91
|
lastSelection: (meta === null || meta === void 0 ? void 0 : meta.newSelection) || pluginState.lastSelection,
|
|
74
|
-
intentToStartEditTime
|
|
92
|
+
intentToStartEditTime,
|
|
93
|
+
actionTypeCount: newActionTypeCount
|
|
75
94
|
};
|
|
76
95
|
}
|
|
77
96
|
},
|
|
78
|
-
view() {
|
|
97
|
+
view(view) {
|
|
98
|
+
const fireAnalyticsEvent = () => {
|
|
99
|
+
const pluginState = metricsKey.getState(view.state);
|
|
100
|
+
if (!pluginState) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const payloadToSend = getAnalyticsPayload({
|
|
104
|
+
pluginState
|
|
105
|
+
});
|
|
106
|
+
if (pluginState && pluginState.totalActionCount > 0 && pluginState.activeSessionTime > 0) {
|
|
107
|
+
api === null || api === void 0 ? void 0 : api.analytics.actions.fireAnalyticsEvent(payloadToSend, undefined, {
|
|
108
|
+
immediate: true
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const unbindBeforeUnload = bind(window, {
|
|
113
|
+
type: 'beforeunload',
|
|
114
|
+
listener: () => {
|
|
115
|
+
fireAnalyticsEvent();
|
|
116
|
+
}
|
|
117
|
+
});
|
|
79
118
|
return {
|
|
80
119
|
destroy() {
|
|
81
|
-
|
|
82
|
-
api === null || api === void 0 ? void 0 : api.core.actions.execute(api === null || api === void 0 ? void 0 : (_api$metrics = api.metrics) === null || _api$metrics === void 0 ? void 0 : _api$metrics.commands.fireSessionAnalytics());
|
|
120
|
+
fireAnalyticsEvent();
|
|
83
121
|
timer.cleanupTimer();
|
|
122
|
+
unbindBeforeUnload();
|
|
84
123
|
}
|
|
85
124
|
};
|
|
86
125
|
},
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
const ACTIVE_SESSION_IDLE_TIME = 5000;
|
|
2
3
|
export class ActiveSessionTimer {
|
|
3
4
|
constructor(api) {
|
|
4
5
|
_defineProperty(this, "startTimer", () => {
|
|
@@ -6,14 +7,14 @@ export class ActiveSessionTimer {
|
|
|
6
7
|
clearTimeout(this.timerId);
|
|
7
8
|
}
|
|
8
9
|
this.timerId = window.setTimeout(() => {
|
|
10
|
+
if (this.api) {
|
|
11
|
+
var _this$api$metrics;
|
|
12
|
+
this.api.core.actions.execute((_this$api$metrics = this.api.metrics) === null || _this$api$metrics === void 0 ? void 0 : _this$api$metrics.commands.stopActiveSession());
|
|
13
|
+
}
|
|
9
14
|
this.cleanupTimer();
|
|
10
|
-
},
|
|
15
|
+
}, ACTIVE_SESSION_IDLE_TIME);
|
|
11
16
|
});
|
|
12
17
|
_defineProperty(this, "cleanupTimer", () => {
|
|
13
|
-
if (this.api) {
|
|
14
|
-
var _this$api$metrics;
|
|
15
|
-
this.api.core.actions.execute((_this$api$metrics = this.api.metrics) === null || _this$api$metrics === void 0 ? void 0 : _this$api$metrics.commands.stopActiveSession());
|
|
16
|
-
}
|
|
17
18
|
if (this.timerId) {
|
|
18
19
|
clearTimeout(this.timerId);
|
|
19
20
|
this.timerId = null;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
|
|
2
|
+
export const getPayloadAttributes = ({
|
|
3
|
+
pluginState,
|
|
4
|
+
contentSizeChanged
|
|
5
|
+
}) => ({
|
|
6
|
+
efficiency: {
|
|
7
|
+
totalActiveTime: pluginState.activeSessionTime,
|
|
8
|
+
totalActionCount: pluginState.totalActionCount,
|
|
9
|
+
actionByTypeCount: pluginState.actionTypeCount
|
|
10
|
+
},
|
|
11
|
+
effectiveness: {
|
|
12
|
+
undoCount: pluginState.actionTypeCount.undoCount,
|
|
13
|
+
repeatedActionCount: 0,
|
|
14
|
+
safeInsertCount: 0
|
|
15
|
+
},
|
|
16
|
+
contentSizeChanged
|
|
17
|
+
});
|
|
18
|
+
export const getAnalyticsPayload = ({
|
|
19
|
+
pluginState
|
|
20
|
+
}) => ({
|
|
21
|
+
action: ACTION.ENDED,
|
|
22
|
+
actionSubject: ACTION_SUBJECT.ACTIVITY_SESSION,
|
|
23
|
+
actionSubjectId: ACTION_SUBJECT_ID.ACTIVITY,
|
|
24
|
+
attributes: {
|
|
25
|
+
efficiency: {
|
|
26
|
+
totalActiveTime: pluginState.activeSessionTime,
|
|
27
|
+
totalActionCount: pluginState.totalActionCount,
|
|
28
|
+
actionByTypeCount: pluginState.actionTypeCount
|
|
29
|
+
},
|
|
30
|
+
effectiveness: {
|
|
31
|
+
undoCount: pluginState.actionTypeCount.undoCount,
|
|
32
|
+
repeatedActionCount: 0,
|
|
33
|
+
safeInsertCount: 0
|
|
34
|
+
},
|
|
35
|
+
contentSizeChanged: pluginState.contentSizeChanged
|
|
36
|
+
},
|
|
37
|
+
eventType: EVENT_TYPE.TRACK
|
|
38
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
|
|
2
|
+
export const isNonTextUndo = tr => {
|
|
3
|
+
if (tr.getMeta('undoRedoPlugin$') === undefined) {
|
|
4
|
+
return false;
|
|
5
|
+
}
|
|
6
|
+
let hasNonTextChange = false;
|
|
7
|
+
tr.steps.forEach(step => {
|
|
8
|
+
if (step instanceof ReplaceStep) {
|
|
9
|
+
const {
|
|
10
|
+
slice
|
|
11
|
+
} = step;
|
|
12
|
+
if (slice.content) {
|
|
13
|
+
slice.content.forEach(node => {
|
|
14
|
+
if (node.type.name !== 'text') {
|
|
15
|
+
hasNonTextChange = true;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
} else {
|
|
20
|
+
hasNonTextChange = true;
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
return hasNonTextChange;
|
|
24
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createPlugin, initialPluginState, metricsKey } from './pm-plugins/main';
|
|
2
|
+
import { getAnalyticsPayload } from './pm-plugins/utils/analytics';
|
|
2
3
|
/**
|
|
3
4
|
* Metrics plugin to be added to an `EditorPresetBuilder` and used with `ComposableEditor`
|
|
4
5
|
* from `@atlaskit/editor-core`.
|
|
@@ -23,43 +24,18 @@ export var metricsPlugin = function metricsPlugin(_ref) {
|
|
|
23
24
|
return tr;
|
|
24
25
|
}
|
|
25
26
|
var newTr = tr;
|
|
27
|
+
var pluginState = api === null || api === void 0 ? void 0 : api.metrics.sharedState.currentState();
|
|
28
|
+
if (pluginState && pluginState.totalActionCount > 0 && pluginState.activeSessionTime > 0) {
|
|
29
|
+
var payloadToSend = getAnalyticsPayload({
|
|
30
|
+
pluginState: pluginState
|
|
31
|
+
});
|
|
32
|
+
api === null || api === void 0 || api.analytics.actions.attachAnalyticsEvent(payloadToSend)(newTr);
|
|
33
|
+
}
|
|
26
34
|
newTr.setMeta(metricsKey, {
|
|
27
35
|
stopActiveSession: true
|
|
28
36
|
});
|
|
29
37
|
return newTr;
|
|
30
38
|
};
|
|
31
|
-
},
|
|
32
|
-
fireSessionAnalytics: function fireSessionAnalytics() {
|
|
33
|
-
return function (_ref3) {
|
|
34
|
-
var tr = _ref3.tr;
|
|
35
|
-
if (!api) {
|
|
36
|
-
return tr;
|
|
37
|
-
}
|
|
38
|
-
var state = api.metrics.sharedState.currentState();
|
|
39
|
-
if (!state) {
|
|
40
|
-
return tr;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// const eventAttributes = {
|
|
44
|
-
// efficiency: {
|
|
45
|
-
// totalActiveTime: state.activeSessionTime || 0,
|
|
46
|
-
// totalActionCount: state.totalActionCount || 0,
|
|
47
|
-
// actionTypeCount: state.actionTypeCount,
|
|
48
|
-
// totalInactiveTime:
|
|
49
|
-
// Date.now() - (state.editSessionStartTime || 0) - (state.activeSessionTime || 0),
|
|
50
|
-
// },
|
|
51
|
-
// TODO: Add effectiveness attributes
|
|
52
|
-
// effectiveness: {
|
|
53
|
-
// undoCount: 0,
|
|
54
|
-
// repeatedActionCount: 0,
|
|
55
|
-
// safeInsertCount: 0,
|
|
56
|
-
// },
|
|
57
|
-
// };
|
|
58
|
-
|
|
59
|
-
// fire analytics event
|
|
60
|
-
// api.analytics.actions.attachAnalyticsEvent({});
|
|
61
|
-
return tr;
|
|
62
|
-
};
|
|
63
39
|
}
|
|
64
40
|
},
|
|
65
41
|
getSharedState: function getSharedState(editorState) {
|
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
2
|
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; }
|
|
3
3
|
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; }
|
|
4
|
+
import { bind } from 'bind-event-listener';
|
|
4
5
|
import { AnalyticsStep } from '@atlaskit/adf-schema/steps';
|
|
5
6
|
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
6
7
|
import { isTextInput } from '@atlaskit/editor-common/utils';
|
|
7
8
|
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
8
9
|
import { ActiveSessionTimer } from './utils/active-session-timer';
|
|
10
|
+
import { getAnalyticsPayload } from './utils/analytics';
|
|
11
|
+
import { isNonTextUndo } from './utils/isNonTextUndo';
|
|
9
12
|
export var metricsKey = new PluginKey('metricsPlugin');
|
|
10
13
|
export var initialPluginState = {
|
|
11
|
-
editSessionStartTime: undefined,
|
|
12
14
|
intentToStartEditTime: undefined,
|
|
13
15
|
lastSelection: undefined,
|
|
14
16
|
activeSessionTime: 0,
|
|
15
17
|
totalActionCount: 0,
|
|
18
|
+
contentSizeChanged: 0,
|
|
16
19
|
timeOfLastTextInput: undefined,
|
|
17
20
|
actionTypeCount: {
|
|
18
21
|
textInputCount: 0,
|
|
19
22
|
nodeInsertionCount: 0,
|
|
20
23
|
nodeAttributeChangeCount: 0,
|
|
21
24
|
contentMovedCount: 0,
|
|
22
|
-
nodeDeletionCount: 0
|
|
25
|
+
nodeDeletionCount: 0,
|
|
26
|
+
undoCount: 0
|
|
23
27
|
}
|
|
24
28
|
};
|
|
25
29
|
export var createPlugin = function createPlugin(api) {
|
|
@@ -28,23 +32,28 @@ export var createPlugin = function createPlugin(api) {
|
|
|
28
32
|
key: metricsKey,
|
|
29
33
|
state: {
|
|
30
34
|
init: function init() {
|
|
31
|
-
return
|
|
32
|
-
editSessionStartTime: performance.now()
|
|
33
|
-
});
|
|
35
|
+
return initialPluginState;
|
|
34
36
|
},
|
|
35
|
-
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/max-params
|
|
38
|
+
apply: function apply(tr, pluginState, oldState, newState) {
|
|
36
39
|
var meta = tr.getMeta(metricsKey);
|
|
37
40
|
var intentToStartEditTime = (meta === null || meta === void 0 ? void 0 : meta.intentToStartEditTime) || pluginState.intentToStartEditTime;
|
|
38
41
|
if (meta && meta.stopActiveSession) {
|
|
39
|
-
|
|
40
|
-
return _objectSpread(_objectSpread({}, pluginState), {}, {
|
|
41
|
-
intentToStartEditTime: undefined,
|
|
42
|
-
lastSelection: undefined
|
|
43
|
-
});
|
|
42
|
+
return initialPluginState;
|
|
44
43
|
}
|
|
45
44
|
if (!intentToStartEditTime) {
|
|
46
|
-
|
|
45
|
+
if (tr.docChanged && !tr.getMeta('replaceDocument')) {
|
|
46
|
+
intentToStartEditTime = performance.now();
|
|
47
|
+
} else {
|
|
48
|
+
return pluginState;
|
|
49
|
+
}
|
|
47
50
|
}
|
|
51
|
+
var undoCount = isNonTextUndo(tr) ? 1 : 0;
|
|
52
|
+
var newActionTypeCount = pluginState.actionTypeCount ? _objectSpread(_objectSpread({}, pluginState.actionTypeCount), {}, {
|
|
53
|
+
undoCount: pluginState.actionTypeCount.undoCount + undoCount
|
|
54
|
+
}) : _objectSpread(_objectSpread({}, initialPluginState.actionTypeCount), {}, {
|
|
55
|
+
undoCount: undoCount
|
|
56
|
+
});
|
|
48
57
|
var canIgnoreTr = function canIgnoreTr() {
|
|
49
58
|
return !tr.steps.every(function (e) {
|
|
50
59
|
return e instanceof AnalyticsStep;
|
|
@@ -53,36 +62,64 @@ export var createPlugin = function createPlugin(api) {
|
|
|
53
62
|
if (tr.docChanged && canIgnoreTr()) {
|
|
54
63
|
var now = performance.now();
|
|
55
64
|
timer.startTimer();
|
|
56
|
-
|
|
65
|
+
var actionTypeCount = pluginState.actionTypeCount,
|
|
66
|
+
timeOfLastTextInput = pluginState.timeOfLastTextInput,
|
|
67
|
+
totalActionCount = pluginState.totalActionCount,
|
|
68
|
+
activeSessionTime = pluginState.activeSessionTime;
|
|
57
69
|
// If previous and current action is text insertion, then don't increase total action count
|
|
58
70
|
var isActionTextInput = isTextInput(tr);
|
|
71
|
+
var newActiveSessionTime = activeSessionTime + (now - intentToStartEditTime);
|
|
72
|
+
var newTextInputCount = isActionTextInput ? actionTypeCount.textInputCount + 1 : actionTypeCount.textInputCount;
|
|
73
|
+
var newTotalActionCount = pluginState.totalActionCount + 1;
|
|
59
74
|
if (pluginState.timeOfLastTextInput && isActionTextInput) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
timeOfLastTextInput: now
|
|
64
|
-
});
|
|
75
|
+
newActiveSessionTime = activeSessionTime + (now - (timeOfLastTextInput || 0));
|
|
76
|
+
newTextInputCount = actionTypeCount.textInputCount;
|
|
77
|
+
newTotalActionCount = totalActionCount;
|
|
65
78
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
79
|
+
var newPluginState = _objectSpread(_objectSpread({}, pluginState), {}, {
|
|
80
|
+
activeSessionTime: newActiveSessionTime,
|
|
81
|
+
totalActionCount: newTotalActionCount,
|
|
82
|
+
timeOfLastTextInput: isActionTextInput ? now : undefined,
|
|
83
|
+
contentSizeChanged: pluginState.contentSizeChanged + (newState.doc.content.size - oldState.doc.content.size),
|
|
84
|
+
actionTypeCount: _objectSpread(_objectSpread({}, newActionTypeCount), {}, {
|
|
85
|
+
textInputCount: newTextInputCount
|
|
86
|
+
})
|
|
72
87
|
});
|
|
88
|
+
return newPluginState;
|
|
73
89
|
}
|
|
74
90
|
return _objectSpread(_objectSpread({}, pluginState), {}, {
|
|
75
91
|
lastSelection: (meta === null || meta === void 0 ? void 0 : meta.newSelection) || pluginState.lastSelection,
|
|
76
|
-
intentToStartEditTime:
|
|
92
|
+
intentToStartEditTime: intentToStartEditTime,
|
|
93
|
+
actionTypeCount: newActionTypeCount
|
|
77
94
|
});
|
|
78
95
|
}
|
|
79
96
|
},
|
|
80
|
-
view: function view() {
|
|
97
|
+
view: function view(_view) {
|
|
98
|
+
var fireAnalyticsEvent = function fireAnalyticsEvent() {
|
|
99
|
+
var pluginState = metricsKey.getState(_view.state);
|
|
100
|
+
if (!pluginState) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
var payloadToSend = getAnalyticsPayload({
|
|
104
|
+
pluginState: pluginState
|
|
105
|
+
});
|
|
106
|
+
if (pluginState && pluginState.totalActionCount > 0 && pluginState.activeSessionTime > 0) {
|
|
107
|
+
api === null || api === void 0 || api.analytics.actions.fireAnalyticsEvent(payloadToSend, undefined, {
|
|
108
|
+
immediate: true
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
var unbindBeforeUnload = bind(window, {
|
|
113
|
+
type: 'beforeunload',
|
|
114
|
+
listener: function listener() {
|
|
115
|
+
fireAnalyticsEvent();
|
|
116
|
+
}
|
|
117
|
+
});
|
|
81
118
|
return {
|
|
82
119
|
destroy: function destroy() {
|
|
83
|
-
|
|
84
|
-
api === null || api === void 0 || api.core.actions.execute(api === null || api === void 0 || (_api$metrics = api.metrics) === null || _api$metrics === void 0 ? void 0 : _api$metrics.commands.fireSessionAnalytics());
|
|
120
|
+
fireAnalyticsEvent();
|
|
85
121
|
timer.cleanupTimer();
|
|
122
|
+
unbindBeforeUnload();
|
|
86
123
|
}
|
|
87
124
|
};
|
|
88
125
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import _createClass from "@babel/runtime/helpers/createClass";
|
|
2
2
|
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
3
3
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
4
|
+
var ACTIVE_SESSION_IDLE_TIME = 5000;
|
|
4
5
|
export var ActiveSessionTimer = /*#__PURE__*/_createClass(function ActiveSessionTimer(api) {
|
|
5
6
|
var _this = this;
|
|
6
7
|
_classCallCheck(this, ActiveSessionTimer);
|
|
@@ -9,14 +10,14 @@ export var ActiveSessionTimer = /*#__PURE__*/_createClass(function ActiveSession
|
|
|
9
10
|
clearTimeout(_this.timerId);
|
|
10
11
|
}
|
|
11
12
|
_this.timerId = window.setTimeout(function () {
|
|
13
|
+
if (_this.api) {
|
|
14
|
+
var _this$api$metrics;
|
|
15
|
+
_this.api.core.actions.execute((_this$api$metrics = _this.api.metrics) === null || _this$api$metrics === void 0 ? void 0 : _this$api$metrics.commands.stopActiveSession());
|
|
16
|
+
}
|
|
12
17
|
_this.cleanupTimer();
|
|
13
|
-
},
|
|
18
|
+
}, ACTIVE_SESSION_IDLE_TIME);
|
|
14
19
|
});
|
|
15
20
|
_defineProperty(this, "cleanupTimer", function () {
|
|
16
|
-
if (_this.api) {
|
|
17
|
-
var _this$api$metrics;
|
|
18
|
-
_this.api.core.actions.execute((_this$api$metrics = _this.api.metrics) === null || _this$api$metrics === void 0 ? void 0 : _this$api$metrics.commands.stopActiveSession());
|
|
19
|
-
}
|
|
20
21
|
if (_this.timerId) {
|
|
21
22
|
clearTimeout(_this.timerId);
|
|
22
23
|
_this.timerId = null;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
|
|
2
|
+
export var getPayloadAttributes = function getPayloadAttributes(_ref) {
|
|
3
|
+
var pluginState = _ref.pluginState,
|
|
4
|
+
contentSizeChanged = _ref.contentSizeChanged;
|
|
5
|
+
return {
|
|
6
|
+
efficiency: {
|
|
7
|
+
totalActiveTime: pluginState.activeSessionTime,
|
|
8
|
+
totalActionCount: pluginState.totalActionCount,
|
|
9
|
+
actionByTypeCount: pluginState.actionTypeCount
|
|
10
|
+
},
|
|
11
|
+
effectiveness: {
|
|
12
|
+
undoCount: pluginState.actionTypeCount.undoCount,
|
|
13
|
+
repeatedActionCount: 0,
|
|
14
|
+
safeInsertCount: 0
|
|
15
|
+
},
|
|
16
|
+
contentSizeChanged: contentSizeChanged
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export var getAnalyticsPayload = function getAnalyticsPayload(_ref2) {
|
|
20
|
+
var pluginState = _ref2.pluginState;
|
|
21
|
+
return {
|
|
22
|
+
action: ACTION.ENDED,
|
|
23
|
+
actionSubject: ACTION_SUBJECT.ACTIVITY_SESSION,
|
|
24
|
+
actionSubjectId: ACTION_SUBJECT_ID.ACTIVITY,
|
|
25
|
+
attributes: {
|
|
26
|
+
efficiency: {
|
|
27
|
+
totalActiveTime: pluginState.activeSessionTime,
|
|
28
|
+
totalActionCount: pluginState.totalActionCount,
|
|
29
|
+
actionByTypeCount: pluginState.actionTypeCount
|
|
30
|
+
},
|
|
31
|
+
effectiveness: {
|
|
32
|
+
undoCount: pluginState.actionTypeCount.undoCount,
|
|
33
|
+
repeatedActionCount: 0,
|
|
34
|
+
safeInsertCount: 0
|
|
35
|
+
},
|
|
36
|
+
contentSizeChanged: pluginState.contentSizeChanged
|
|
37
|
+
},
|
|
38
|
+
eventType: EVENT_TYPE.TRACK
|
|
39
|
+
};
|
|
40
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
|
|
2
|
+
export var isNonTextUndo = function isNonTextUndo(tr) {
|
|
3
|
+
if (tr.getMeta('undoRedoPlugin$') === undefined) {
|
|
4
|
+
return false;
|
|
5
|
+
}
|
|
6
|
+
var hasNonTextChange = false;
|
|
7
|
+
tr.steps.forEach(function (step) {
|
|
8
|
+
if (step instanceof ReplaceStep) {
|
|
9
|
+
var slice = step.slice;
|
|
10
|
+
if (slice.content) {
|
|
11
|
+
slice.content.forEach(function (node) {
|
|
12
|
+
if (node.type.name !== 'text') {
|
|
13
|
+
hasNonTextChange = true;
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
} else {
|
|
18
|
+
hasNonTextChange = true;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
return hasNonTextChange;
|
|
22
|
+
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { EditorCommand, NextEditorPlugin } from '@atlaskit/editor-common/types';
|
|
2
|
+
import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
|
|
2
3
|
import type { MetricsState } from './pm-plugins/main';
|
|
3
4
|
export type MetricsPlugin = NextEditorPlugin<'metrics', {
|
|
5
|
+
dependencies: [AnalyticsPlugin];
|
|
4
6
|
sharedState: MetricsState;
|
|
5
7
|
commands: {
|
|
6
8
|
stopActiveSession: () => EditorCommand;
|
|
7
|
-
fireSessionAnalytics: () => EditorCommand;
|
|
8
9
|
};
|
|
9
10
|
}>;
|
|
@@ -4,12 +4,12 @@ import { PluginKey, Selection } from '@atlaskit/editor-prosemirror/state';
|
|
|
4
4
|
import { type MetricsPlugin } from '../metricsPluginType';
|
|
5
5
|
export declare const metricsKey: PluginKey<any>;
|
|
6
6
|
export type MetricsState = {
|
|
7
|
-
editSessionStartTime?: number;
|
|
8
7
|
intentToStartEditTime?: number;
|
|
9
8
|
activeSessionTime: number;
|
|
10
9
|
totalActionCount: number;
|
|
10
|
+
contentSizeChanged: number;
|
|
11
11
|
lastSelection?: Selection;
|
|
12
|
-
actionTypeCount
|
|
12
|
+
actionTypeCount: ActionByType;
|
|
13
13
|
timeOfLastTextInput?: number;
|
|
14
14
|
};
|
|
15
15
|
export type ActionByType = {
|
|
@@ -18,6 +18,7 @@ export type ActionByType = {
|
|
|
18
18
|
nodeAttributeChangeCount: number;
|
|
19
19
|
contentMovedCount: number;
|
|
20
20
|
nodeDeletionCount: number;
|
|
21
|
+
undoCount: number;
|
|
21
22
|
};
|
|
22
23
|
export declare const initialPluginState: MetricsState;
|
|
23
24
|
export declare const createPlugin: (api: ExtractInjectionAPI<MetricsPlugin> | undefined) => SafePlugin<MetricsState>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ActiveSessionEventPayload, type ActiveSessionEventAttributes } from '@atlaskit/editor-common/analytics';
|
|
2
|
+
import type { MetricsState } from '../main';
|
|
3
|
+
type Props = {
|
|
4
|
+
pluginState: MetricsState;
|
|
5
|
+
contentSizeChanged: number;
|
|
6
|
+
};
|
|
7
|
+
export declare const getPayloadAttributes: ({ pluginState, contentSizeChanged, }: Props) => ActiveSessionEventAttributes;
|
|
8
|
+
export declare const getAnalyticsPayload: ({ pluginState, }: {
|
|
9
|
+
pluginState: MetricsState;
|
|
10
|
+
}) => ActiveSessionEventPayload;
|
|
11
|
+
export {};
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { EditorCommand, NextEditorPlugin } from '@atlaskit/editor-common/types';
|
|
2
|
+
import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
|
|
2
3
|
import type { MetricsState } from './pm-plugins/main';
|
|
3
4
|
export type MetricsPlugin = NextEditorPlugin<'metrics', {
|
|
5
|
+
dependencies: [
|
|
6
|
+
AnalyticsPlugin
|
|
7
|
+
];
|
|
4
8
|
sharedState: MetricsState;
|
|
5
9
|
commands: {
|
|
6
10
|
stopActiveSession: () => EditorCommand;
|
|
7
|
-
fireSessionAnalytics: () => EditorCommand;
|
|
8
11
|
};
|
|
9
12
|
}>;
|
|
@@ -4,12 +4,12 @@ import { PluginKey, Selection } from '@atlaskit/editor-prosemirror/state';
|
|
|
4
4
|
import { type MetricsPlugin } from '../metricsPluginType';
|
|
5
5
|
export declare const metricsKey: PluginKey<any>;
|
|
6
6
|
export type MetricsState = {
|
|
7
|
-
editSessionStartTime?: number;
|
|
8
7
|
intentToStartEditTime?: number;
|
|
9
8
|
activeSessionTime: number;
|
|
10
9
|
totalActionCount: number;
|
|
10
|
+
contentSizeChanged: number;
|
|
11
11
|
lastSelection?: Selection;
|
|
12
|
-
actionTypeCount
|
|
12
|
+
actionTypeCount: ActionByType;
|
|
13
13
|
timeOfLastTextInput?: number;
|
|
14
14
|
};
|
|
15
15
|
export type ActionByType = {
|
|
@@ -18,6 +18,7 @@ export type ActionByType = {
|
|
|
18
18
|
nodeAttributeChangeCount: number;
|
|
19
19
|
contentMovedCount: number;
|
|
20
20
|
nodeDeletionCount: number;
|
|
21
|
+
undoCount: number;
|
|
21
22
|
};
|
|
22
23
|
export declare const initialPluginState: MetricsState;
|
|
23
24
|
export declare const createPlugin: (api: ExtractInjectionAPI<MetricsPlugin> | undefined) => SafePlugin<MetricsState>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ActiveSessionEventPayload, type ActiveSessionEventAttributes } from '@atlaskit/editor-common/analytics';
|
|
2
|
+
import type { MetricsState } from '../main';
|
|
3
|
+
type Props = {
|
|
4
|
+
pluginState: MetricsState;
|
|
5
|
+
contentSizeChanged: number;
|
|
6
|
+
};
|
|
7
|
+
export declare const getPayloadAttributes: ({ pluginState, contentSizeChanged, }: Props) => ActiveSessionEventAttributes;
|
|
8
|
+
export declare const getAnalyticsPayload: ({ pluginState, }: {
|
|
9
|
+
pluginState: MetricsState;
|
|
10
|
+
}) => ActiveSessionEventPayload;
|
|
11
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-metrics",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Metrics plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -25,10 +25,12 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@atlaskit/adf-schema": "^46.1.0",
|
|
28
|
-
"@atlaskit/editor-common": "^99.
|
|
28
|
+
"@atlaskit/editor-common": "^99.5.0",
|
|
29
|
+
"@atlaskit/editor-plugin-analytics": "^1.11.0",
|
|
29
30
|
"@atlaskit/editor-prosemirror": "6.2.1",
|
|
30
31
|
"@atlaskit/platform-feature-flags": "^0.3.0",
|
|
31
|
-
"@babel/runtime": "^7.0.0"
|
|
32
|
+
"@babel/runtime": "^7.0.0",
|
|
33
|
+
"bind-event-listener": "^3.0.0"
|
|
32
34
|
},
|
|
33
35
|
"peerDependencies": {
|
|
34
36
|
"react": "^16.8.0"
|