@miso.ai/client-sdk-ui 1.10.2-beta.1 → 1.10.2-beta.3
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/dist/cjs/actor/fields.js +4 -0
- package/dist/cjs/actor/tracker.js +27 -11
- package/dist/cjs/actor/utils.js +8 -4
- package/dist/cjs/actor/view.js +20 -4
- package/dist/cjs/actor/views.js +18 -0
- package/dist/cjs/constants.js +3 -2
- package/dist/cjs/layout/container/container.js +52 -11
- package/dist/cjs/layout/input/option-list.js +0 -1
- package/dist/cjs/layout/input/search-box.js +10 -1
- package/dist/cjs/layout/list/collection.js +0 -2
- package/dist/cjs/layout/radio/radio.js +1 -1
- package/dist/cjs/layout/text/typewriter/index.js +1 -2
- package/dist/cjs/layout/trackable.js +0 -1
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/workflow/base.js +12 -0
- package/dist/cjs/workflow/explore.js +5 -1
- package/package.json +2 -2
- package/src/actor/fields.js +4 -0
- package/src/actor/tracker.js +22 -11
- package/src/actor/utils.js +12 -5
- package/src/actor/view.js +12 -4
- package/src/actor/views.js +14 -0
- package/src/constants.js +2 -0
- package/src/layout/container/container.js +43 -2
- package/src/layout/input/option-list.js +0 -1
- package/src/layout/input/search-box.js +10 -2
- package/src/layout/list/collection.js +0 -2
- package/src/layout/radio/radio.js +1 -1
- package/src/layout/text/typewriter/index.js +1 -2
- package/src/layout/trackable.js +0 -1
- package/src/version.js +1 -1
- package/src/workflow/base.js +14 -1
- package/src/workflow/explore.js +5 -1
package/dist/cjs/actor/fields.js
CHANGED
|
@@ -14,6 +14,7 @@ exports.request = request;
|
|
|
14
14
|
exports.response = response;
|
|
15
15
|
exports.session = session;
|
|
16
16
|
exports.suggestions = suggestions;
|
|
17
|
+
exports.tracker = tracker;
|
|
17
18
|
exports.view = view;
|
|
18
19
|
function session() {
|
|
19
20
|
return 'session';
|
|
@@ -39,6 +40,9 @@ function view(role) {
|
|
|
39
40
|
function event() {
|
|
40
41
|
return 'event';
|
|
41
42
|
}
|
|
43
|
+
function tracker() {
|
|
44
|
+
return 'tracker';
|
|
45
|
+
}
|
|
42
46
|
function interaction() {
|
|
43
47
|
return 'interaction';
|
|
44
48
|
}
|
|
@@ -12,41 +12,57 @@ var _utils = require("./utils.js");
|
|
|
12
12
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
13
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
14
14
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
15
|
+
const VALUELESS_ITEMS = [Symbol('valueless')];
|
|
15
16
|
class Tracker {
|
|
16
|
-
constructor(
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
constructor(_ref) {
|
|
18
|
+
let {
|
|
19
|
+
hub,
|
|
20
|
+
role,
|
|
21
|
+
valueless = false,
|
|
22
|
+
options
|
|
23
|
+
} = _ref;
|
|
24
|
+
this._hub = hub;
|
|
25
|
+
this._role = role;
|
|
26
|
+
this._valueless = !!valueless;
|
|
27
|
+
this._options = options;
|
|
19
28
|
// TODO: know item type
|
|
20
29
|
|
|
21
30
|
this._states = new _states.default(this._getSessionId());
|
|
22
31
|
}
|
|
23
32
|
get options() {
|
|
24
|
-
return this.
|
|
33
|
+
return typeof this._options === 'function' ? this._options() : this._options;
|
|
25
34
|
}
|
|
26
35
|
getState(item) {
|
|
27
36
|
return this._states.getFullState(item);
|
|
28
37
|
}
|
|
29
38
|
_trigger(type, items) {
|
|
30
39
|
let meta = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
40
|
+
if (this._valueless) {
|
|
41
|
+
meta = items;
|
|
42
|
+
items = VALUELESS_ITEMS;
|
|
43
|
+
}
|
|
31
44
|
(0, _constants.validateEventType)(type);
|
|
32
45
|
this._syncSession();
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
46
|
+
if (type !== _constants.EVENT_TYPE.SUBMIT) {
|
|
47
|
+
items = this._states.untriggered((0, _commons.asArray)(items), type);
|
|
48
|
+
if (items.length === 0) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
this._states.set(items, type, _constants.TRACKING_STATUS.TRIGGERED);
|
|
36
52
|
}
|
|
37
|
-
this._states.set(items, type, _constants.TRACKING_STATUS.TRIGGERED);
|
|
38
53
|
const property = this._role === 'results' ? 'products' : this._role; // TODO: ad-hoc, see #83
|
|
39
54
|
const misoId = this._getMisoId();
|
|
40
55
|
const request = this._getRequestPayload();
|
|
56
|
+
const values = this._valueless ? undefined : items;
|
|
41
57
|
|
|
42
58
|
// TODO: should trigger 'tracker' and let workflow translate to interactions
|
|
43
|
-
this.
|
|
59
|
+
this._hub.trigger(fields.interaction(), (0, _utils.toInteraction)({
|
|
44
60
|
property,
|
|
45
61
|
misoId,
|
|
46
62
|
request
|
|
47
63
|
}, {
|
|
48
64
|
event: type,
|
|
49
|
-
values
|
|
65
|
+
values,
|
|
50
66
|
meta
|
|
51
67
|
}));
|
|
52
68
|
}
|
|
@@ -76,7 +92,7 @@ class Tracker {
|
|
|
76
92
|
return request && request.payload;
|
|
77
93
|
}
|
|
78
94
|
_getViewState() {
|
|
79
|
-
return this.
|
|
95
|
+
return this._hub.states[fields.view(this._role)];
|
|
80
96
|
}
|
|
81
97
|
}
|
|
82
98
|
exports.default = Tracker;
|
package/dist/cjs/actor/utils.js
CHANGED
|
@@ -34,10 +34,13 @@ function toInteraction(_ref, _ref2) {
|
|
|
34
34
|
// TODO: ad-hoc
|
|
35
35
|
const question_source = request && request._meta && request._meta.question_source;
|
|
36
36
|
const isProductType = isProduct(property);
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const
|
|
37
|
+
const isSubmit = event === _constants.EVENT_TYPE.SUBMIT;
|
|
38
|
+
const isAffiliation = property === _constants.ROLE.AFFILIATION;
|
|
39
|
+
const product_ids = isProductType && values ? values.map(v => typeof v === 'string' ? v : v.product_id).filter(v => v) : !isSubmit ? [] : undefined;
|
|
40
|
+
const items = !isProductType && !isSubmit && values ? values.map(v => v.text || v.id || v) : undefined;
|
|
41
|
+
const value = isSubmit ? values : undefined;
|
|
42
|
+
const channel = isAffiliation ? values[0] && values[0].channel : undefined;
|
|
43
|
+
const positions = isAffiliation ? values.map(v => v.position) : undefined;
|
|
41
44
|
const payload = (0, _commons.trimObj)({
|
|
42
45
|
type: event === 'viewable' ? 'viewable_impression' : event,
|
|
43
46
|
product_ids,
|
|
@@ -48,6 +51,7 @@ function toInteraction(_ref, _ref2) {
|
|
|
48
51
|
property,
|
|
49
52
|
channel,
|
|
50
53
|
items,
|
|
54
|
+
value,
|
|
51
55
|
positions,
|
|
52
56
|
trigger: manual ? 'manual' : 'auto',
|
|
53
57
|
question_source
|
package/dist/cjs/actor/view.js
CHANGED
|
@@ -79,9 +79,12 @@ class ViewActor {
|
|
|
79
79
|
this._layout = layout;
|
|
80
80
|
|
|
81
81
|
// initialize new layout
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
if (layout) {
|
|
83
|
+
layout._view = this;
|
|
84
|
+
// don't use _safeApplyOnLayout, for any error should be thrown
|
|
85
|
+
if (typeof layout.initialize === 'function') {
|
|
86
|
+
layout.initialize(this);
|
|
87
|
+
}
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
// in case the data is already there
|
|
@@ -90,8 +93,21 @@ class ViewActor {
|
|
|
90
93
|
});
|
|
91
94
|
}
|
|
92
95
|
get tracker() {
|
|
96
|
+
const {
|
|
97
|
+
role
|
|
98
|
+
} = this;
|
|
99
|
+
if (role === _constants.ROLE.CONTAINER) {
|
|
100
|
+
return this._views._getContainerTracker();
|
|
101
|
+
}
|
|
93
102
|
if (!this._tracker) {
|
|
94
|
-
|
|
103
|
+
const {
|
|
104
|
+
hub
|
|
105
|
+
} = this;
|
|
106
|
+
this._tracker = new _tracker.default({
|
|
107
|
+
hub,
|
|
108
|
+
role,
|
|
109
|
+
options: () => this._getTrackerOptions()
|
|
110
|
+
});
|
|
95
111
|
}
|
|
96
112
|
return this._tracker;
|
|
97
113
|
}
|
package/dist/cjs/actor/views.js
CHANGED
|
@@ -8,6 +8,7 @@ var _commons = require("@miso.ai/commons");
|
|
|
8
8
|
var _constants = require("../constants.js");
|
|
9
9
|
var fields = _interopRequireWildcard(require("./fields.js"));
|
|
10
10
|
var _view = _interopRequireDefault(require("./view.js"));
|
|
11
|
+
var _tracker = _interopRequireDefault(require("./tracker.js"));
|
|
11
12
|
var _trackers = require("../util/trackers.js");
|
|
12
13
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
14
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
@@ -27,6 +28,7 @@ class ViewsActor {
|
|
|
27
28
|
this._layoutFactory = layouts;
|
|
28
29
|
this._options = options;
|
|
29
30
|
this._containers = new Map();
|
|
31
|
+
this._containerTracker = undefined;
|
|
30
32
|
this._views = {};
|
|
31
33
|
this._trackers = new Trackers(this, roles);
|
|
32
34
|
for (const role of roles) {
|
|
@@ -202,6 +204,19 @@ class ViewsActor {
|
|
|
202
204
|
_getTrackerOptions(role) {
|
|
203
205
|
return (0, _trackers.normalizeTrackerOptions)(this._trackerOptions && this._trackerOptions[role] || false);
|
|
204
206
|
}
|
|
207
|
+
_getContainerTracker() {
|
|
208
|
+
if (!this._containerTracker) {
|
|
209
|
+
const hub = this._hub;
|
|
210
|
+
const role = _constants.ROLE.CONTAINER;
|
|
211
|
+
this._containerTracker = new _tracker.default({
|
|
212
|
+
hub,
|
|
213
|
+
role,
|
|
214
|
+
valueless: true,
|
|
215
|
+
options: () => this._getTrackerOptions(role)
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
return this._containerTracker;
|
|
219
|
+
}
|
|
205
220
|
_error(e) {
|
|
206
221
|
// TODO: hub trigger error event
|
|
207
222
|
console.error(e);
|
|
@@ -242,5 +257,8 @@ class Trackers {
|
|
|
242
257
|
get: () => views.get(role).tracker
|
|
243
258
|
});
|
|
244
259
|
}
|
|
260
|
+
Object.defineProperty(this, _constants.ROLE.CONTAINER, {
|
|
261
|
+
get: () => views._getContainerTracker()
|
|
262
|
+
});
|
|
245
263
|
}
|
|
246
264
|
}
|
package/dist/cjs/constants.js
CHANGED
|
@@ -47,9 +47,10 @@ const LAYOUT_CATEGORY = exports.LAYOUT_CATEGORY = Object.freeze({
|
|
|
47
47
|
const EVENT_TYPE = exports.EVENT_TYPE = Object.freeze({
|
|
48
48
|
IMPRESSION: 'impression',
|
|
49
49
|
VIEWABLE: 'viewable',
|
|
50
|
-
CLICK: 'click'
|
|
50
|
+
CLICK: 'click',
|
|
51
|
+
SUBMIT: 'submit'
|
|
51
52
|
});
|
|
52
|
-
const TRACKING_EVENT_TYPES = exports.TRACKING_EVENT_TYPES = [EVENT_TYPE.IMPRESSION, EVENT_TYPE.VIEWABLE, EVENT_TYPE.CLICK];
|
|
53
|
+
const TRACKING_EVENT_TYPES = exports.TRACKING_EVENT_TYPES = [EVENT_TYPE.IMPRESSION, EVENT_TYPE.VIEWABLE, EVENT_TYPE.CLICK, EVENT_TYPE.SUBMIT];
|
|
53
54
|
const TRACKING_STATUS = exports.TRACKING_STATUS = Object.freeze({
|
|
54
55
|
UNTRACKED: 'untracked',
|
|
55
56
|
TRACKING: 'tracking',
|
|
@@ -7,6 +7,7 @@ exports.default = void 0;
|
|
|
7
7
|
var _constants = require("../../constants.js");
|
|
8
8
|
var _raf = _interopRequireDefault(require("../raf.js"));
|
|
9
9
|
var _misoBanner = _interopRequireDefault(require("../../element/miso-banner.js"));
|
|
10
|
+
var _viewables = _interopRequireDefault(require("../../util/viewables.js"));
|
|
10
11
|
const _excluded = ["logo"];
|
|
11
12
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
13
|
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (e.includes(n)) continue; t[n] = r[n]; } return t; }
|
|
@@ -27,15 +28,19 @@ class ContainerLayout extends _raf.default {
|
|
|
27
28
|
super(Object.assign({
|
|
28
29
|
logo
|
|
29
30
|
}, options));
|
|
31
|
+
this._impression = false;
|
|
32
|
+
this._viewables = new _viewables.default();
|
|
30
33
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
34
|
+
_syncElement(element) {
|
|
35
|
+
if (this._element !== element) {
|
|
36
|
+
this._viewables.untrack(this._element);
|
|
37
|
+
}
|
|
38
|
+
super._syncElement(element);
|
|
39
|
+
}
|
|
40
|
+
_render(element, states) {
|
|
35
41
|
this._syncBanner(element);
|
|
36
|
-
this._syncStatus(element,
|
|
37
|
-
|
|
38
|
-
});
|
|
42
|
+
this._syncStatus(element, states);
|
|
43
|
+
this._trackInteractions(element, states);
|
|
39
44
|
}
|
|
40
45
|
_syncBanner(element) {
|
|
41
46
|
const bannerTagName = _misoBanner.default.tagName;
|
|
@@ -56,17 +61,17 @@ class ContainerLayout extends _raf.default {
|
|
|
56
61
|
} = element;
|
|
57
62
|
const logo = localLogo !== undefined && localLogo !== 'auto' ? localLogo : globalLogo;
|
|
58
63
|
// logo === 'auto': only add banner when a component of main role (like producs, answer) is present
|
|
59
|
-
return logo === 'auto' ? element.components.some(
|
|
64
|
+
return logo === 'auto' ? element.components.some(_ref2 => {
|
|
60
65
|
let {
|
|
61
66
|
role
|
|
62
|
-
} =
|
|
67
|
+
} = _ref2;
|
|
63
68
|
return isMainRole(role);
|
|
64
69
|
}) : !!logo;
|
|
65
70
|
}
|
|
66
|
-
_syncStatus(element,
|
|
71
|
+
_syncStatus(element, _ref3) {
|
|
67
72
|
let {
|
|
68
73
|
state
|
|
69
|
-
} =
|
|
74
|
+
} = _ref3;
|
|
70
75
|
if (!element) {
|
|
71
76
|
return;
|
|
72
77
|
}
|
|
@@ -89,6 +94,42 @@ class ContainerLayout extends _raf.default {
|
|
|
89
94
|
element.removeAttribute('miso-id');
|
|
90
95
|
}
|
|
91
96
|
}
|
|
97
|
+
_trackInteractions(element, _ref4) {
|
|
98
|
+
let {
|
|
99
|
+
state
|
|
100
|
+
} = _ref4;
|
|
101
|
+
const {
|
|
102
|
+
status
|
|
103
|
+
} = state;
|
|
104
|
+
if (status === _constants.STATUS.READY) {
|
|
105
|
+
this._trackImpression();
|
|
106
|
+
this._trackViewable(element); // don't await
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
_trackImpression() {
|
|
110
|
+
const {
|
|
111
|
+
impression: options
|
|
112
|
+
} = this._view.tracker.options || {};
|
|
113
|
+
if (!options) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (this._impression) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
this._impression = true;
|
|
120
|
+
this._view.tracker.impression();
|
|
121
|
+
}
|
|
122
|
+
async _trackViewable(element) {
|
|
123
|
+
const {
|
|
124
|
+
viewable: options
|
|
125
|
+
} = this._view.tracker.options || {};
|
|
126
|
+
if (!options) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (await this._viewables.track(element, options)) {
|
|
130
|
+
this._view.tracker.viewable();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
92
133
|
}
|
|
93
134
|
exports.default = ContainerLayout;
|
|
94
135
|
function isMainRole(role) {
|
|
@@ -158,7 +158,6 @@ class SearchBoxLayout extends _template.default {
|
|
|
158
158
|
this._debounce = (0, _commons.debounce)(this.autocomplete.debounce);
|
|
159
159
|
}
|
|
160
160
|
initialize(view) {
|
|
161
|
-
this._view = view;
|
|
162
161
|
const {
|
|
163
162
|
proxyElement,
|
|
164
163
|
hub
|
|
@@ -378,6 +377,16 @@ class SearchBoxLayout extends _template.default {
|
|
|
378
377
|
this._view.hub.trigger(_index.fields.query(), {
|
|
379
378
|
q: value
|
|
380
379
|
});
|
|
380
|
+
this._trackSubmit(value);
|
|
381
|
+
}
|
|
382
|
+
_trackSubmit(value) {
|
|
383
|
+
const {
|
|
384
|
+
[_constants.EVENT_TYPE.SUBMIT]: options
|
|
385
|
+
} = this._view.tracker.options || {};
|
|
386
|
+
if (!options) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
this._view.tracker.submit(value);
|
|
381
390
|
}
|
|
382
391
|
}
|
|
383
392
|
exports.default = SearchBoxLayout;
|
|
@@ -115,11 +115,9 @@ class CollectionLayout extends _template.default {
|
|
|
115
115
|
templates: Object.assign({}, DEFAULT_TEMPLATES, templates),
|
|
116
116
|
itemType
|
|
117
117
|
}, options));
|
|
118
|
-
this._view = undefined;
|
|
119
118
|
this._initTrackable();
|
|
120
119
|
}
|
|
121
120
|
initialize(view) {
|
|
122
|
-
this._view = view;
|
|
123
121
|
this._unsubscribes.push(view.proxyElement.on('click', this._onClick.bind(this)));
|
|
124
122
|
}
|
|
125
123
|
_preprocess(_ref2) {
|
|
@@ -65,7 +65,7 @@ class RadioLayout extends _template.default {
|
|
|
65
65
|
initialize(view) {
|
|
66
66
|
const {
|
|
67
67
|
proxyElement
|
|
68
|
-
} =
|
|
68
|
+
} = view;
|
|
69
69
|
this._unsubscribes = [...this._unsubscribes, view.hub.on(this._field, () => this._refresh()), proxyElement.on('click', e => this._handleClick(e))];
|
|
70
70
|
this._defaultValue ? this._select(this._defaultValue) : this._clear();
|
|
71
71
|
}
|
|
@@ -5,7 +5,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.TrackableMixin = void 0;
|
|
7
7
|
exports.makeTrackable = makeTrackable;
|
|
8
|
-
var _constants = require("../constants.js");
|
|
9
8
|
var _bindings = _interopRequireDefault(require("../util/bindings.js"));
|
|
10
9
|
var _viewables = _interopRequireDefault(require("../util/viewables.js"));
|
|
11
10
|
var _trackers = require("../util/trackers.js");
|
package/dist/cjs/version.js
CHANGED
|
@@ -36,6 +36,9 @@ function mergeDefaults(workflow) {
|
|
|
36
36
|
interactions: (0, _options.mergeInteractionsOptions)(DEFAULT_INTERACTIONS, interactions)
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
|
+
function getRevision(data) {
|
|
40
|
+
return data && data.value && data.value.revision;
|
|
41
|
+
}
|
|
39
42
|
class Workflow extends _commons.Component {
|
|
40
43
|
constructor(_ref2) {
|
|
41
44
|
var _this;
|
|
@@ -129,6 +132,15 @@ class Workflow extends _commons.Component {
|
|
|
129
132
|
if (session.uuid !== this.session.uuid) {
|
|
130
133
|
return; // ignore data for old session or inactive session
|
|
131
134
|
}
|
|
135
|
+
|
|
136
|
+
// compare revision to omit outdated data, if any
|
|
137
|
+
const revision = getRevision(data);
|
|
138
|
+
if (!(0, _commons.isNullLike)(revision)) {
|
|
139
|
+
const currentRevision = getRevision(this._hub.states[_index.fields.data()]);
|
|
140
|
+
if (!(0, _commons.isNullLike)(currentRevision) && revision <= currentRevision) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
132
144
|
this._sessions.start(); // in case session not started yet
|
|
133
145
|
|
|
134
146
|
data = this._defaultProcessData(data);
|
|
@@ -29,7 +29,11 @@ const DEFAULT_LAYOUTS = Object.freeze({
|
|
|
29
29
|
}]
|
|
30
30
|
});
|
|
31
31
|
const DEFAULT_TRACKERS = Object.freeze({
|
|
32
|
-
[_constants.ROLE.RELATED_QUESTIONS]: {}
|
|
32
|
+
[_constants.ROLE.RELATED_QUESTIONS]: {},
|
|
33
|
+
[_constants.ROLE.CONTAINER]: {},
|
|
34
|
+
[_constants.ROLE.QUERY]: {
|
|
35
|
+
[_constants.EVENT_TYPE.SUBMIT]: {}
|
|
36
|
+
}
|
|
33
37
|
});
|
|
34
38
|
const DEFAULT_OPTIONS = Object.freeze({
|
|
35
39
|
api: DEFAULT_API_OPTIONS,
|
package/package.json
CHANGED
package/src/actor/fields.js
CHANGED
package/src/actor/tracker.js
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import { asArray } from '@miso.ai/commons';
|
|
2
|
-
import { TRACKING_EVENT_TYPES, TRACKING_STATUS, validateEventType } from '../constants.js';
|
|
2
|
+
import { TRACKING_EVENT_TYPES, TRACKING_STATUS, EVENT_TYPE, validateEventType } from '../constants.js';
|
|
3
3
|
import * as fields from './fields.js';
|
|
4
4
|
import States from '../util/states.js';
|
|
5
5
|
import { toInteraction } from './utils.js';
|
|
6
6
|
|
|
7
|
+
const VALUELESS_ITEMS = [Symbol('valueless')];
|
|
8
|
+
|
|
7
9
|
export default class Tracker {
|
|
8
10
|
|
|
9
|
-
constructor(
|
|
10
|
-
this.
|
|
11
|
-
this._role =
|
|
11
|
+
constructor({ hub, role, valueless = false, options }) {
|
|
12
|
+
this._hub = hub;
|
|
13
|
+
this._role = role;
|
|
14
|
+
this._valueless = !!valueless;
|
|
15
|
+
this._options = options;
|
|
12
16
|
// TODO: know item type
|
|
13
17
|
|
|
14
18
|
this._states = new States(this._getSessionId());
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
get options() {
|
|
18
|
-
return this.
|
|
22
|
+
return typeof this._options === 'function' ? this._options() : this._options;
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
getState(item) {
|
|
@@ -23,21 +27,28 @@ export default class Tracker {
|
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
_trigger(type, items, meta = {}) {
|
|
30
|
+
if (this._valueless) {
|
|
31
|
+
meta = items;
|
|
32
|
+
items = VALUELESS_ITEMS;
|
|
33
|
+
}
|
|
26
34
|
validateEventType(type);
|
|
27
35
|
this._syncSession();
|
|
28
36
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
if (type !== EVENT_TYPE.SUBMIT) {
|
|
38
|
+
items = this._states.untriggered(asArray(items), type);
|
|
39
|
+
if (items.length === 0) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
this._states.set(items, type, TRACKING_STATUS.TRIGGERED);
|
|
32
43
|
}
|
|
33
|
-
this._states.set(items, type, TRACKING_STATUS.TRIGGERED);
|
|
34
44
|
|
|
35
45
|
const property = this._role === 'results' ? 'products' : this._role; // TODO: ad-hoc, see #83
|
|
36
46
|
const misoId = this._getMisoId();
|
|
37
47
|
const request = this._getRequestPayload();
|
|
48
|
+
const values = this._valueless ? undefined : items;
|
|
38
49
|
|
|
39
50
|
// TODO: should trigger 'tracker' and let workflow translate to interactions
|
|
40
|
-
this.
|
|
51
|
+
this._hub.trigger(fields.interaction(), toInteraction({ property, misoId, request }, { event: type, values, meta }));
|
|
41
52
|
}
|
|
42
53
|
|
|
43
54
|
_syncSession() {
|
|
@@ -66,7 +77,7 @@ export default class Tracker {
|
|
|
66
77
|
}
|
|
67
78
|
|
|
68
79
|
_getViewState() {
|
|
69
|
-
return this.
|
|
80
|
+
return this._hub.states[fields.view(this._role)];
|
|
70
81
|
}
|
|
71
82
|
|
|
72
83
|
}
|
package/src/actor/utils.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { trimObj, uuidToTimestamp } from '@miso.ai/commons';
|
|
2
|
-
import { ROLE } from '../constants.js';
|
|
2
|
+
import { ROLE, EVENT_TYPE } from '../constants.js';
|
|
3
3
|
|
|
4
4
|
const PRODUCT_TYPED_PROPERTIES = new Set([
|
|
5
5
|
ROLE.PRODUCTS,
|
|
@@ -22,10 +22,16 @@ export function toInteraction({ property, misoId, request }, { event, values, ma
|
|
|
22
22
|
// TODO: ad-hoc
|
|
23
23
|
const question_source = request && request._meta && request._meta.question_source;
|
|
24
24
|
const isProductType = isProduct(property);
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
const
|
|
25
|
+
const isSubmit = event === EVENT_TYPE.SUBMIT;
|
|
26
|
+
const isAffiliation = property === ROLE.AFFILIATION;
|
|
27
|
+
|
|
28
|
+
const product_ids = isProductType && values ? values.map(v => typeof v === 'string' ? v : v.product_id).filter(v => v) : !isSubmit ? [] : undefined;
|
|
29
|
+
const items = !isProductType && !isSubmit && values ? values.map(v => v.text || v.id || v) : undefined;
|
|
30
|
+
const value = isSubmit ? values : undefined;
|
|
31
|
+
|
|
32
|
+
const channel = isAffiliation ? values[0] && values[0].channel : undefined;
|
|
33
|
+
const positions = isAffiliation ? values.map(v => v.position) : undefined;
|
|
34
|
+
|
|
29
35
|
const payload = trimObj({
|
|
30
36
|
type: event === 'viewable' ? 'viewable_impression' : event,
|
|
31
37
|
product_ids,
|
|
@@ -36,6 +42,7 @@ export function toInteraction({ property, misoId, request }, { event, values, ma
|
|
|
36
42
|
property,
|
|
37
43
|
channel,
|
|
38
44
|
items,
|
|
45
|
+
value,
|
|
39
46
|
positions,
|
|
40
47
|
trigger: manual ? 'manual' : 'auto',
|
|
41
48
|
question_source,
|
package/src/actor/view.js
CHANGED
|
@@ -77,9 +77,12 @@ export default class ViewActor {
|
|
|
77
77
|
this._layout = layout;
|
|
78
78
|
|
|
79
79
|
// initialize new layout
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
if (layout) {
|
|
81
|
+
layout._view = this;
|
|
82
|
+
// don't use _safeApplyOnLayout, for any error should be thrown
|
|
83
|
+
if (typeof layout.initialize === 'function') {
|
|
84
|
+
layout.initialize(this);
|
|
85
|
+
}
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
// in case the data is already there
|
|
@@ -87,8 +90,13 @@ export default class ViewActor {
|
|
|
87
90
|
}
|
|
88
91
|
|
|
89
92
|
get tracker() {
|
|
93
|
+
const { role } = this;
|
|
94
|
+
if (role === ROLE.CONTAINER) {
|
|
95
|
+
return this._views._getContainerTracker();
|
|
96
|
+
}
|
|
90
97
|
if (!this._tracker) {
|
|
91
|
-
|
|
98
|
+
const { hub } = this;
|
|
99
|
+
this._tracker = new Tracker({ hub, role, options: () => this._getTrackerOptions() });
|
|
92
100
|
}
|
|
93
101
|
return this._tracker;
|
|
94
102
|
}
|
package/src/actor/views.js
CHANGED
|
@@ -2,6 +2,7 @@ import { defineValues, delegateGetters } from '@miso.ai/commons';
|
|
|
2
2
|
import { STATUS, ROLE } from '../constants.js';
|
|
3
3
|
import * as fields from './fields.js';
|
|
4
4
|
import ViewActor from './view.js';
|
|
5
|
+
import Tracker from './tracker.js';
|
|
5
6
|
import { normalizeTrackerOptions } from '../util/trackers.js';
|
|
6
7
|
|
|
7
8
|
export default class ViewsActor {
|
|
@@ -19,6 +20,7 @@ export default class ViewsActor {
|
|
|
19
20
|
this._layoutFactory = layouts;
|
|
20
21
|
this._options = options;
|
|
21
22
|
this._containers = new Map();
|
|
23
|
+
this._containerTracker = undefined;
|
|
22
24
|
this._views = {};
|
|
23
25
|
this._trackers = new Trackers(this, roles);
|
|
24
26
|
|
|
@@ -201,6 +203,15 @@ export default class ViewsActor {
|
|
|
201
203
|
return normalizeTrackerOptions((this._trackerOptions && this._trackerOptions[role]) || false);
|
|
202
204
|
}
|
|
203
205
|
|
|
206
|
+
_getContainerTracker() {
|
|
207
|
+
if (!this._containerTracker) {
|
|
208
|
+
const hub = this._hub;
|
|
209
|
+
const role = ROLE.CONTAINER;
|
|
210
|
+
this._containerTracker = new Tracker({ hub, role, valueless: true, options: () => this._getTrackerOptions(role) });
|
|
211
|
+
}
|
|
212
|
+
return this._containerTracker;
|
|
213
|
+
}
|
|
214
|
+
|
|
204
215
|
_error(e) {
|
|
205
216
|
// TODO: hub trigger error event
|
|
206
217
|
console.error(e);
|
|
@@ -246,6 +257,9 @@ class Trackers {
|
|
|
246
257
|
get: () => views.get(role).tracker,
|
|
247
258
|
});
|
|
248
259
|
}
|
|
260
|
+
Object.defineProperty(this, ROLE.CONTAINER, {
|
|
261
|
+
get: () => views._getContainerTracker(),
|
|
262
|
+
});
|
|
249
263
|
}
|
|
250
264
|
|
|
251
265
|
}
|
package/src/constants.js
CHANGED
|
@@ -57,12 +57,14 @@ export const EVENT_TYPE = Object.freeze({
|
|
|
57
57
|
IMPRESSION: 'impression',
|
|
58
58
|
VIEWABLE: 'viewable',
|
|
59
59
|
CLICK: 'click',
|
|
60
|
+
SUBMIT: 'submit',
|
|
60
61
|
});
|
|
61
62
|
|
|
62
63
|
export const TRACKING_EVENT_TYPES = [
|
|
63
64
|
EVENT_TYPE.IMPRESSION,
|
|
64
65
|
EVENT_TYPE.VIEWABLE,
|
|
65
66
|
EVENT_TYPE.CLICK,
|
|
67
|
+
EVENT_TYPE.SUBMIT,
|
|
66
68
|
];
|
|
67
69
|
|
|
68
70
|
export const TRACKING_STATUS = Object.freeze({
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ROLE, STATUS, LAYOUT_CATEGORY } from '../../constants.js';
|
|
2
2
|
import RafLayout from '../raf.js';
|
|
3
3
|
import MisoBannerElement from '../../element/miso-banner.js';
|
|
4
|
+
import Viewables from '../../util/viewables.js';
|
|
4
5
|
|
|
5
6
|
const TYPE = 'container';
|
|
6
7
|
|
|
@@ -19,11 +20,21 @@ export default class ContainerLayout extends RafLayout {
|
|
|
19
20
|
logo,
|
|
20
21
|
...options,
|
|
21
22
|
});
|
|
23
|
+
this._impression = false;
|
|
24
|
+
this._viewables = new Viewables();
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
_syncElement(element) {
|
|
28
|
+
if (this._element !== element) {
|
|
29
|
+
this._viewables.untrack(this._element);
|
|
30
|
+
}
|
|
31
|
+
super._syncElement(element);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_render(element, states) {
|
|
25
35
|
this._syncBanner(element);
|
|
26
|
-
this._syncStatus(element,
|
|
36
|
+
this._syncStatus(element, states);
|
|
37
|
+
this._trackInteractions(element, states);
|
|
27
38
|
}
|
|
28
39
|
|
|
29
40
|
_syncBanner(element) {
|
|
@@ -62,6 +73,36 @@ export default class ContainerLayout extends RafLayout {
|
|
|
62
73
|
}
|
|
63
74
|
}
|
|
64
75
|
|
|
76
|
+
_trackInteractions(element, { state }) {
|
|
77
|
+
const { status } = state;
|
|
78
|
+
if (status === STATUS.READY) {
|
|
79
|
+
this._trackImpression();
|
|
80
|
+
this._trackViewable(element); // don't await
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
_trackImpression() {
|
|
85
|
+
const { impression: options } = this._view.tracker.options || {};
|
|
86
|
+
if (!options) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (this._impression) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
this._impression = true;
|
|
93
|
+
this._view.tracker.impression();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async _trackViewable(element) {
|
|
97
|
+
const { viewable: options } = this._view.tracker.options || {};
|
|
98
|
+
if (!options) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (await this._viewables.track(element, options)) {
|
|
102
|
+
this._view.tracker.viewable();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
65
106
|
}
|
|
66
107
|
|
|
67
108
|
function isMainRole(role) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineValues, escapeHtml, findInAncestors, debounce } from '@miso.ai/commons';
|
|
2
|
-
import { LAYOUT_CATEGORY, STATUS } from '../../constants.js';
|
|
2
|
+
import { LAYOUT_CATEGORY, STATUS, EVENT_TYPE } from '../../constants.js';
|
|
3
3
|
import { fields } from '../../actor/index.js';
|
|
4
4
|
import TemplateBasedLayout from '../template.js';
|
|
5
5
|
import { helpers, imageBlock, productInfoBlock } from '../templates.js';
|
|
@@ -134,7 +134,6 @@ export default class SearchBoxLayout extends TemplateBasedLayout {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
initialize(view) {
|
|
137
|
-
this._view = view;
|
|
138
137
|
const { proxyElement, hub } = view;
|
|
139
138
|
this._unsubscribes = [
|
|
140
139
|
...this._unsubscribes,
|
|
@@ -326,6 +325,15 @@ export default class SearchBoxLayout extends TemplateBasedLayout {
|
|
|
326
325
|
this.close();
|
|
327
326
|
// TODO: q -> value
|
|
328
327
|
this._view.hub.trigger(fields.query(), { q: value });
|
|
328
|
+
this._trackSubmit(value);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
_trackSubmit(value) {
|
|
332
|
+
const { [EVENT_TYPE.SUBMIT]: options } = this._view.tracker.options || {};
|
|
333
|
+
if (!options) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
this._view.tracker.submit(value);
|
|
329
337
|
}
|
|
330
338
|
|
|
331
339
|
}
|
|
@@ -86,12 +86,10 @@ export default class CollectionLayout extends TemplateBasedLayout {
|
|
|
86
86
|
itemType,
|
|
87
87
|
...options,
|
|
88
88
|
});
|
|
89
|
-
this._view = undefined;
|
|
90
89
|
this._initTrackable();
|
|
91
90
|
}
|
|
92
91
|
|
|
93
92
|
initialize(view) {
|
|
94
|
-
this._view = view;
|
|
95
93
|
this._unsubscribes.push(view.proxyElement.on('click', this._onClick.bind(this)));
|
|
96
94
|
}
|
|
97
95
|
|
|
@@ -49,7 +49,7 @@ export default class RadioLayout extends TemplateBasedLayout {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
initialize(view) {
|
|
52
|
-
const { proxyElement } =
|
|
52
|
+
const { proxyElement } = view;
|
|
53
53
|
this._unsubscribes = [
|
|
54
54
|
...this._unsubscribes,
|
|
55
55
|
view.hub.on(this._field, () => this._refresh()),
|
package/src/layout/trackable.js
CHANGED
package/src/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '1.10.2-beta.
|
|
1
|
+
export default '1.10.2-beta.3';
|
package/src/workflow/base.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component } from '@miso.ai/commons';
|
|
1
|
+
import { Component, isNullLike } from '@miso.ai/commons';
|
|
2
2
|
import { Hub, SessionMaker, DataActor, ViewsActor, InteractionsActor, fields } from '../actor/index.js';
|
|
3
3
|
import * as sources from '../source/index.js';
|
|
4
4
|
import { STATUS, ROLE } from '../constants.js';
|
|
@@ -22,6 +22,10 @@ function mergeDefaults(workflow, { layouts, interactions, ...defaults } = {}) {
|
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
function getRevision(data) {
|
|
26
|
+
return data && data.value && data.value.revision;
|
|
27
|
+
}
|
|
28
|
+
|
|
25
29
|
export default class Workflow extends Component {
|
|
26
30
|
|
|
27
31
|
constructor({
|
|
@@ -108,6 +112,15 @@ export default class Workflow extends Component {
|
|
|
108
112
|
return; // ignore data for old session or inactive session
|
|
109
113
|
}
|
|
110
114
|
|
|
115
|
+
// compare revision to omit outdated data, if any
|
|
116
|
+
const revision = getRevision(data);
|
|
117
|
+
if (!isNullLike(revision)) {
|
|
118
|
+
const currentRevision = getRevision(this._hub.states[fields.data()]);
|
|
119
|
+
if (!isNullLike(currentRevision) && revision <= currentRevision) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
111
124
|
this._sessions.start(); // in case session not started yet
|
|
112
125
|
|
|
113
126
|
data = this._defaultProcessData(data);
|
package/src/workflow/explore.js
CHANGED
|
@@ -2,7 +2,7 @@ import { API } from '@miso.ai/commons';
|
|
|
2
2
|
import Workflow from './base.js';
|
|
3
3
|
import { mergeApiOptions } from './options.js';
|
|
4
4
|
import { fields } from '../actor/index.js';
|
|
5
|
-
import { ROLE } from '../constants.js';
|
|
5
|
+
import { ROLE, EVENT_TYPE } from '../constants.js';
|
|
6
6
|
import { ListLayout, SearchBoxLayout } from '../layout/index.js';
|
|
7
7
|
|
|
8
8
|
const DEFAULT_API_OPTIONS = Object.freeze({
|
|
@@ -17,6 +17,10 @@ const DEFAULT_LAYOUTS = Object.freeze({
|
|
|
17
17
|
|
|
18
18
|
const DEFAULT_TRACKERS = Object.freeze({
|
|
19
19
|
[ROLE.RELATED_QUESTIONS]: {},
|
|
20
|
+
[ROLE.CONTAINER]: {},
|
|
21
|
+
[ROLE.QUERY]: {
|
|
22
|
+
[EVENT_TYPE.SUBMIT]: {},
|
|
23
|
+
},
|
|
20
24
|
});
|
|
21
25
|
|
|
22
26
|
const DEFAULT_OPTIONS = Object.freeze({
|