@miso.ai/client-sdk-ui 1.10.3-beta.0 → 1.10.4-beta.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/dist/cjs/actor/autocomplete.js +4 -1
- package/dist/cjs/actor/data.js +15 -6
- package/dist/cjs/actor/fields.js +10 -10
- package/dist/cjs/actor/filters.js +114 -0
- package/dist/cjs/actor/hub.js +2 -1
- package/dist/cjs/actor/sessions.js +3 -5
- package/dist/cjs/actor/view.js +19 -6
- package/dist/cjs/actor/views.js +32 -19
- package/dist/cjs/constants.js +9 -2
- package/dist/cjs/defaults/ask/constants.js +3 -2
- package/dist/cjs/defaults/ask/templates.js +112 -219
- package/dist/cjs/defaults/common/templates.js +234 -0
- package/dist/cjs/defaults/hybrid-search/answer-box.js +115 -0
- package/dist/cjs/defaults/hybrid-search/constants.js +8 -0
- package/dist/cjs/defaults/hybrid-search/controls.js +17 -0
- package/dist/cjs/defaults/hybrid-search/index.js +31 -0
- package/dist/cjs/defaults/hybrid-search/phrases.js +27 -0
- package/dist/cjs/defaults/hybrid-search/templates.js +127 -0
- package/dist/cjs/defaults/index.js +4 -2
- package/dist/cjs/element/container/index.js +7 -0
- package/dist/cjs/element/container/miso-hybrid-search.js +21 -0
- package/dist/cjs/element/role/index.js +21 -0
- package/dist/cjs/element/role/miso-facets.js +20 -0
- package/dist/cjs/element/role/miso-hits.js +20 -0
- package/dist/cjs/element/role/miso-keywords.js +20 -0
- package/dist/cjs/layout/index.js +7 -0
- package/dist/cjs/layout/input/facets.js +294 -0
- package/dist/cjs/layout/input/option-list.js +4 -4
- package/dist/cjs/layout/input/search-box.js +6 -8
- package/dist/cjs/layout/list/collection.js +1 -2
- package/dist/cjs/layout/raf.js +1 -0
- package/dist/cjs/layout/template.js +3 -1
- package/dist/cjs/layout/templates.js +15 -0
- package/dist/cjs/layout/text/text.js +38 -5
- package/dist/cjs/layout/trackable.js +2 -16
- package/dist/cjs/plugin.js +4 -1
- package/dist/cjs/{source/index.js → source.js} +7 -4
- package/dist/cjs/util/bindings.js +3 -0
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/workflow/answer-based.js +335 -0
- package/dist/cjs/workflow/ask.js +98 -271
- package/dist/cjs/workflow/base.js +69 -35
- package/dist/cjs/workflow/explore.js +10 -0
- package/dist/cjs/workflow/hybrid-search.js +140 -0
- package/dist/cjs/workflow/index.js +7 -0
- package/dist/cjs/workflow/processors.js +113 -0
- package/dist/cjs/workflow/recommendation.js +12 -3
- package/dist/cjs/workflow/search.js +34 -12
- package/package.json +2 -2
- package/src/actor/autocomplete.js +4 -2
- package/src/actor/data.js +8 -2
- package/src/actor/fields.js +10 -10
- package/src/actor/filters.js +103 -0
- package/src/actor/hub.js +2 -1
- package/src/actor/sessions.js +3 -4
- package/src/actor/view.js +19 -2
- package/src/actor/views.js +30 -16
- package/src/constants.js +10 -0
- package/src/defaults/ask/constants.js +1 -0
- package/src/defaults/ask/templates.js +70 -179
- package/src/defaults/common/templates.js +173 -0
- package/src/defaults/hybrid-search/answer-box.js +118 -0
- package/src/defaults/hybrid-search/constants.js +2 -0
- package/src/defaults/hybrid-search/controls.js +11 -0
- package/src/defaults/hybrid-search/index.js +9 -0
- package/src/defaults/hybrid-search/phrases.js +22 -0
- package/src/defaults/hybrid-search/templates.js +83 -0
- package/src/defaults/index.js +2 -0
- package/src/element/container/index.js +1 -0
- package/src/element/container/miso-hybrid-search.js +19 -0
- package/src/element/role/index.js +3 -0
- package/src/element/role/miso-facets.js +16 -0
- package/src/element/role/miso-hits.js +16 -0
- package/src/element/role/miso-keywords.js +16 -0
- package/src/layout/index.js +1 -0
- package/src/layout/input/facets.js +242 -0
- package/src/layout/input/option-list.js +3 -4
- package/src/layout/input/search-box.js +6 -7
- package/src/layout/list/collection.js +1 -2
- package/src/layout/raf.js +1 -0
- package/src/layout/template.js +2 -1
- package/src/layout/templates.js +15 -0
- package/src/layout/text/text.js +28 -3
- package/src/layout/trackable.js +2 -15
- package/src/plugin.js +6 -2
- package/src/{source/index.js → source.js} +7 -4
- package/src/util/bindings.js +4 -0
- package/src/version.js +1 -1
- package/src/workflow/answer-based.js +280 -0
- package/src/workflow/ask.js +82 -204
- package/src/workflow/base.js +64 -27
- package/src/workflow/explore.js +10 -1
- package/src/workflow/hybrid-search.js +125 -0
- package/src/workflow/index.js +1 -0
- package/src/workflow/processors.js +95 -0
- package/src/workflow/recommendation.js +9 -2
- package/src/workflow/search.js +35 -7
- package/dist/cjs/source/api.js +0 -23
- package/src/source/api.js +0 -12
|
@@ -84,11 +84,12 @@ class OptionListLayout extends _template.default {
|
|
|
84
84
|
proxyElement,
|
|
85
85
|
hub
|
|
86
86
|
} = view;
|
|
87
|
-
this._unsubscribes = [...this._unsubscribes, proxyElement.on('click', e => this._handleClick(e)), hub.on(_index.fields.suggestions(), () => this._handleInput())
|
|
87
|
+
this._unsubscribes = [...this._unsubscribes, proxyElement.on('click', e => this._handleClick(e)), hub.on(_index.fields.suggestions(), () => this._handleInput()) // TODO: use data mapping
|
|
88
|
+
];
|
|
88
89
|
}
|
|
89
90
|
_preprocess() {
|
|
90
91
|
return super._preprocess({
|
|
91
|
-
state: this._view.hub.states[_index.fields.suggestions()]
|
|
92
|
+
state: this._view.hub.states[_index.fields.suggestions()] // TODO: use data mapping
|
|
92
93
|
});
|
|
93
94
|
}
|
|
94
95
|
_render(element, data, controls) {
|
|
@@ -139,13 +140,12 @@ class OptionListLayout extends _template.default {
|
|
|
139
140
|
return;
|
|
140
141
|
}
|
|
141
142
|
// TODO: q -> value
|
|
142
|
-
this._view.hub.
|
|
143
|
+
this._view.hub.update(_index.fields.query(), {
|
|
143
144
|
q: value
|
|
144
145
|
});
|
|
145
146
|
}
|
|
146
147
|
destroy() {
|
|
147
148
|
this._destroyTrackable();
|
|
148
|
-
this._view = undefined;
|
|
149
149
|
super.destroy();
|
|
150
150
|
}
|
|
151
151
|
}
|
|
@@ -83,7 +83,7 @@ function product(layout, _ref) {
|
|
|
83
83
|
const {
|
|
84
84
|
templates
|
|
85
85
|
} = layout;
|
|
86
|
-
const [openTag, closeTag] =
|
|
86
|
+
const [openTag, closeTag] = templates.helpers.tagPair(layout, product, {
|
|
87
87
|
role: false
|
|
88
88
|
});
|
|
89
89
|
return [openTag, templates.imageBlock(layout, product), templates.productInfoBlock(layout, product), closeTag].join('');
|
|
@@ -118,8 +118,7 @@ const DEFAULT_TEMPLATES = Object.freeze({
|
|
|
118
118
|
query,
|
|
119
119
|
product,
|
|
120
120
|
imageBlock: _templates.imageBlock,
|
|
121
|
-
productInfoBlock: _templates.productInfoBlock
|
|
122
|
-
helpers: _templates.helpers
|
|
121
|
+
productInfoBlock: _templates.productInfoBlock
|
|
123
122
|
});
|
|
124
123
|
const DEFAULT_AUTOCOMPLETE_OPTIONS = Object.freeze({
|
|
125
124
|
className: DEFAULT_AUTOCOMPLETE_CLASSNAME,
|
|
@@ -162,10 +161,9 @@ class SearchBoxLayout extends _template.default {
|
|
|
162
161
|
proxyElement,
|
|
163
162
|
hub
|
|
164
163
|
} = view;
|
|
165
|
-
this._unsubscribes = [...this._unsubscribes, proxyElement.on('input', e => this._syncInput(e)), proxyElement.on('keydown', e => this._handleKeyDown(e)), proxyElement.on('click', e => this._handleClick(e)), proxyElement.on('focusin', e => this._handleFocusIn(e)), proxyElement.on('focusout', e => this._handleFocusOut(e))
|
|
166
|
-
this._unsubscribes.push(hub.on(_index.fields.completions(), () => view.refresh({
|
|
164
|
+
this._unsubscribes = [...this._unsubscribes, proxyElement.on('input', e => this._syncInput(e)), proxyElement.on('keydown', e => this._handleKeyDown(e)), proxyElement.on('click', e => this._handleClick(e)), proxyElement.on('focusin', e => this._handleFocusIn(e)), proxyElement.on('focusout', e => this._handleFocusOut(e)), hub.on(_index.fields.data(_constants.DATA_ASPECT.AUTOCOMPLETE), () => view.refresh({
|
|
167
165
|
force: true
|
|
168
|
-
}))
|
|
166
|
+
}))];
|
|
169
167
|
}
|
|
170
168
|
focus() {
|
|
171
169
|
// TODO: support blur as well
|
|
@@ -197,7 +195,7 @@ class SearchBoxLayout extends _template.default {
|
|
|
197
195
|
state
|
|
198
196
|
});
|
|
199
197
|
return Object.assign({}, state, {
|
|
200
|
-
completions: this._view.hub.states[_index.fields.
|
|
198
|
+
completions: this._view.hub.states[_index.fields.data(_constants.DATA_ASPECT.AUTOCOMPLETE)]
|
|
201
199
|
});
|
|
202
200
|
}
|
|
203
201
|
_render(element, data, controls) {
|
|
@@ -374,7 +372,7 @@ class SearchBoxLayout extends _template.default {
|
|
|
374
372
|
}
|
|
375
373
|
this.close();
|
|
376
374
|
// TODO: q -> value
|
|
377
|
-
this._view.hub.
|
|
375
|
+
this._view.hub.update(_index.fields.query(), {
|
|
378
376
|
q: value
|
|
379
377
|
});
|
|
380
378
|
this._trackSubmit(value);
|
|
@@ -95,8 +95,7 @@ const DEFAULT_TEMPLATES = Object.freeze({
|
|
|
95
95
|
ctaBlock: _templates.ctaBlock,
|
|
96
96
|
cta: _templates.cta,
|
|
97
97
|
imageBlock: _templates.imageBlock,
|
|
98
|
-
indexBlock: _templates.indexBlock
|
|
99
|
-
helpers: _templates.helpers
|
|
98
|
+
indexBlock: _templates.indexBlock
|
|
100
99
|
});
|
|
101
100
|
class CollectionLayout extends _template.default {
|
|
102
101
|
static get category() {
|
package/dist/cjs/layout/raf.js
CHANGED
|
@@ -21,7 +21,9 @@ class TemplateBasedLayout extends _raf.default {
|
|
|
21
21
|
super(options);
|
|
22
22
|
(0, _commons.defineValues)(this, {
|
|
23
23
|
className,
|
|
24
|
-
templates: Object.assign({}, (0, _templates.requiresImplementation)('root'), templates
|
|
24
|
+
templates: Object.assign({}, (0, _templates.requiresImplementation)('root'), templates, {
|
|
25
|
+
helpers: _templates.helpers
|
|
26
|
+
})
|
|
25
27
|
});
|
|
26
28
|
}
|
|
27
29
|
_preprocess(_ref2) {
|
|
@@ -263,6 +263,7 @@ const DEFAULT_DATE_OPTIONS = Object.freeze({
|
|
|
263
263
|
month: 'short',
|
|
264
264
|
day: 'numeric'
|
|
265
265
|
});
|
|
266
|
+
const DEFAULT_NUMBER_OPTIONS = 'en-US';
|
|
266
267
|
function formatDate(date) {
|
|
267
268
|
let fn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_DATE_OPTIONS;
|
|
268
269
|
switch (typeof fn) {
|
|
@@ -276,6 +277,19 @@ function formatDate(date) {
|
|
|
276
277
|
throw new Error(`Invalid date format: ${fn}`);
|
|
277
278
|
}
|
|
278
279
|
}
|
|
280
|
+
function formatNumber(number) {
|
|
281
|
+
let fn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_NUMBER_OPTIONS;
|
|
282
|
+
switch (typeof fn) {
|
|
283
|
+
case 'function':
|
|
284
|
+
return fn(number);
|
|
285
|
+
case 'string':
|
|
286
|
+
return new Intl.NumberFormat(fn).format(number);
|
|
287
|
+
case 'object':
|
|
288
|
+
return new Intl.NumberFormat(fn.locale || DEFAULT_NUMBER_OPTIONS.locale, fn).format(number);
|
|
289
|
+
default:
|
|
290
|
+
throw new Error(`Invalid number format: ${fn}`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
279
293
|
function asFunction() {
|
|
280
294
|
let template = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
|
281
295
|
return typeof template === 'function' ? template : () => template;
|
|
@@ -283,6 +297,7 @@ function asFunction() {
|
|
|
283
297
|
const helpers = exports.helpers = Object.freeze({
|
|
284
298
|
tagPair,
|
|
285
299
|
formatDate,
|
|
300
|
+
formatNumber,
|
|
286
301
|
escapeHtml: _commons.escapeHtml,
|
|
287
302
|
asFunction
|
|
288
303
|
});
|
|
@@ -12,16 +12,49 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
12
12
|
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; }
|
|
13
13
|
const TYPE = 'text';
|
|
14
14
|
const DEFAULT_CLASSNAME = 'miso-text';
|
|
15
|
-
function root(layout,
|
|
15
|
+
function root(layout, _ref) {
|
|
16
|
+
let {
|
|
17
|
+
value
|
|
18
|
+
} = _ref;
|
|
16
19
|
const {
|
|
17
20
|
className,
|
|
18
21
|
role,
|
|
19
22
|
options: {
|
|
23
|
+
raw = false,
|
|
20
24
|
tag
|
|
21
25
|
}
|
|
22
26
|
} = layout;
|
|
23
27
|
const roleAttr = role ? `data-role="${role}"` : '';
|
|
24
|
-
|
|
28
|
+
const content = value !== undefined ? (0, _commons.escapeHtml)(getFormatFunction(layout)(value)) : '';
|
|
29
|
+
return raw ? content : `<${tag} class="${className}" ${roleAttr}>${content}</${tag}>`;
|
|
30
|
+
}
|
|
31
|
+
function getFormatFunction(layout) {
|
|
32
|
+
let {
|
|
33
|
+
format
|
|
34
|
+
} = layout.options;
|
|
35
|
+
const {
|
|
36
|
+
formatDate,
|
|
37
|
+
formatNumber
|
|
38
|
+
} = layout.templates.helpers;
|
|
39
|
+
if (!format) {
|
|
40
|
+
return v => `${v}`;
|
|
41
|
+
}
|
|
42
|
+
switch (typeof format) {
|
|
43
|
+
case 'function':
|
|
44
|
+
return format;
|
|
45
|
+
case 'string':
|
|
46
|
+
format = [format];
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
let [type, args] = format;
|
|
50
|
+
switch (type) {
|
|
51
|
+
case 'date':
|
|
52
|
+
return v => formatDate(v, args);
|
|
53
|
+
case 'number':
|
|
54
|
+
return v => formatNumber(v, args);
|
|
55
|
+
default:
|
|
56
|
+
return v => `${v}`;
|
|
57
|
+
}
|
|
25
58
|
}
|
|
26
59
|
const DEFAULT_TEMPLATES = Object.freeze({
|
|
27
60
|
root
|
|
@@ -40,13 +73,13 @@ class TextLayout extends _template.default {
|
|
|
40
73
|
return DEFAULT_CLASSNAME;
|
|
41
74
|
}
|
|
42
75
|
constructor() {
|
|
43
|
-
let
|
|
76
|
+
let _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
44
77
|
let {
|
|
45
78
|
className = DEFAULT_CLASSNAME,
|
|
46
79
|
templates,
|
|
47
80
|
tag = 'p'
|
|
48
|
-
} =
|
|
49
|
-
options = _objectWithoutPropertiesLoose(
|
|
81
|
+
} = _ref2,
|
|
82
|
+
options = _objectWithoutPropertiesLoose(_ref2, _excluded);
|
|
50
83
|
super(Object.assign({
|
|
51
84
|
className,
|
|
52
85
|
templates: Object.assign({}, DEFAULT_TEMPLATES, templates),
|
|
@@ -5,27 +5,13 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.TrackableMixin = void 0;
|
|
7
7
|
exports.makeTrackable = makeTrackable;
|
|
8
|
+
var _commons = require("@miso.ai/commons");
|
|
8
9
|
var _bindings = _interopRequireDefault(require("../util/bindings.js"));
|
|
9
10
|
var _viewables = _interopRequireDefault(require("../util/viewables.js"));
|
|
10
11
|
var _trackers = require("../util/trackers.js");
|
|
11
12
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
13
|
function makeTrackable(prototype) {
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
function _mixin(target, source) {
|
|
16
|
-
const descriptors = Object.getOwnPropertyDescriptors(source);
|
|
17
|
-
for (const key in descriptors) {
|
|
18
|
-
if (key === 'constructor') {
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
const {
|
|
22
|
-
value
|
|
23
|
-
} = descriptors[key];
|
|
24
|
-
if (target[key] === undefined && typeof value === 'function') {
|
|
25
|
-
target[key] = value;
|
|
26
|
-
}
|
|
27
|
-
// TODO: getter, setter
|
|
28
|
-
}
|
|
14
|
+
(0, _commons.mixin)(prototype, TrackableMixin.prototype);
|
|
29
15
|
}
|
|
30
16
|
class TrackableMixin {
|
|
31
17
|
_initTrackable() {
|
package/dist/cjs/plugin.js
CHANGED
|
@@ -10,7 +10,7 @@ var _index2 = require("./combo/index.js");
|
|
|
10
10
|
var _layouts = _interopRequireDefault(require("./layouts.js"));
|
|
11
11
|
var elements = _interopRequireWildcard(require("./element/index.js"));
|
|
12
12
|
var layouts = _interopRequireWildcard(require("./layout/index.js"));
|
|
13
|
-
var sources = _interopRequireWildcard(require("./source
|
|
13
|
+
var sources = _interopRequireWildcard(require("./source.js"));
|
|
14
14
|
var defaults = _interopRequireWildcard(require("./defaults/index.js"));
|
|
15
15
|
var _styles = require("./styles.js");
|
|
16
16
|
var _misoContainer = _interopRequireDefault(require("./element/container/miso-container.js"));
|
|
@@ -164,6 +164,9 @@ class Ui {
|
|
|
164
164
|
get ask() {
|
|
165
165
|
return this.asks.root;
|
|
166
166
|
}
|
|
167
|
+
get hybridSearch() {
|
|
168
|
+
return this._hybridSearch || (this._hybridSearch = new _index.HybridSearch(this._plugin, this._client));
|
|
169
|
+
}
|
|
167
170
|
get explore() {
|
|
168
171
|
return this._explore || (this._explore = new _index.Explore(this._plugin, this._client));
|
|
169
172
|
}
|
|
@@ -5,8 +5,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.api = api;
|
|
7
7
|
var _commons = require("@miso.ai/commons");
|
|
8
|
-
//export { default as api } from './api.js';
|
|
9
|
-
|
|
10
8
|
function api(client) {
|
|
11
9
|
// TODO: send uuid & unit id
|
|
12
10
|
return async _ref => {
|
|
@@ -16,8 +14,13 @@ function api(client) {
|
|
|
16
14
|
payload,
|
|
17
15
|
options
|
|
18
16
|
} = _ref;
|
|
19
|
-
if (group === _commons.API.GROUP.ASK
|
|
20
|
-
|
|
17
|
+
if (group === _commons.API.GROUP.ASK) {
|
|
18
|
+
switch (name) {
|
|
19
|
+
case _commons.API.NAME.QUESTIONS:
|
|
20
|
+
return client.api.ask.questions(payload, options);
|
|
21
|
+
case _commons.API.NAME.SEARCH:
|
|
22
|
+
return client.api.ask.search(payload, options);
|
|
23
|
+
}
|
|
21
24
|
}
|
|
22
25
|
// because name is in snake case
|
|
23
26
|
return client.api[group]._run(name, payload, options);
|
|
@@ -50,6 +50,9 @@ class Bindings {
|
|
|
50
50
|
get(element) {
|
|
51
51
|
return this._e2b.get(element) || undefined;
|
|
52
52
|
}
|
|
53
|
+
find(key) {
|
|
54
|
+
return this._k2b.get(key) || undefined;
|
|
55
|
+
}
|
|
53
56
|
update(keys, values, elements) {
|
|
54
57
|
const newTuple = tuple(keys, values, elements);
|
|
55
58
|
const oldTuple = {
|
package/dist/cjs/version.js
CHANGED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _commons = require("@miso.ai/commons");
|
|
8
|
+
var _base = _interopRequireDefault(require("./base.js"));
|
|
9
|
+
var _options = require("./options.js");
|
|
10
|
+
var _index = require("../actor/index.js");
|
|
11
|
+
var _constants = require("../constants.js");
|
|
12
|
+
var _index2 = require("../layout/index.js");
|
|
13
|
+
var _index3 = require("../affiliation/index.js");
|
|
14
|
+
var _processors = require("./processors.js");
|
|
15
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
16
|
+
// import * as sources from '../source.js';
|
|
17
|
+
|
|
18
|
+
const DEFAULT_API_OPTIONS = Object.freeze({
|
|
19
|
+
group: _commons.API.GROUP.ASK,
|
|
20
|
+
payload: {
|
|
21
|
+
source_fl: ['cover_image', 'url', 'created_at', 'updated_at', 'published_at'],
|
|
22
|
+
cite_link: 1,
|
|
23
|
+
cite_start: '[',
|
|
24
|
+
cite_end: ']'
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
const DEFAULT_AUTOCOMPLETE_OPTIONS = Object.freeze({
|
|
28
|
+
actor: false,
|
|
29
|
+
api: {
|
|
30
|
+
group: _commons.API.GROUP.ASK,
|
|
31
|
+
name: _commons.API.NAME.AUTOCOMPLETE
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
const DEFAULT_LAYOUTS = Object.freeze({
|
|
35
|
+
[_constants.ROLE.QUERY]: [_index2.SearchBoxLayout.type, {
|
|
36
|
+
placeholder: 'Ask a question'
|
|
37
|
+
}],
|
|
38
|
+
[_constants.ROLE.QUESTION]: [_index2.TextLayout.type, {
|
|
39
|
+
tag: 'h2'
|
|
40
|
+
}],
|
|
41
|
+
[_constants.ROLE.ANSWER]: _index2.TypewriterLayout.type,
|
|
42
|
+
[_constants.ROLE.FEEDBACK]: _index2.FeedbackLayout.type,
|
|
43
|
+
[_constants.ROLE.SOURCES]: [_index2.ListLayout.type, {
|
|
44
|
+
incremental: true,
|
|
45
|
+
itemType: 'article',
|
|
46
|
+
templates: {
|
|
47
|
+
ordered: true
|
|
48
|
+
}
|
|
49
|
+
}],
|
|
50
|
+
[_constants.ROLE.AFFILIATION]: [_index2.AffiliationLayout.type, {
|
|
51
|
+
incremental: true,
|
|
52
|
+
itemType: 'affiliation',
|
|
53
|
+
link: {
|
|
54
|
+
rel: 'noopener nofollow'
|
|
55
|
+
}
|
|
56
|
+
}]
|
|
57
|
+
});
|
|
58
|
+
const DEFAULT_TRACKERS = Object.freeze({
|
|
59
|
+
[_constants.ROLE.SOURCES]: {},
|
|
60
|
+
[_constants.ROLE.AFFILIATION]: {}
|
|
61
|
+
});
|
|
62
|
+
const DEFAULT_OPTIONS = Object.freeze({
|
|
63
|
+
api: DEFAULT_API_OPTIONS,
|
|
64
|
+
autocomplete: DEFAULT_AUTOCOMPLETE_OPTIONS,
|
|
65
|
+
layouts: DEFAULT_LAYOUTS,
|
|
66
|
+
trackers: DEFAULT_TRACKERS
|
|
67
|
+
});
|
|
68
|
+
class AnswerBasedWorkflow extends _base.default {
|
|
69
|
+
// constructor //
|
|
70
|
+
_initProperties(args) {
|
|
71
|
+
super._initProperties(args);
|
|
72
|
+
this._questionId = undefined;
|
|
73
|
+
this._autoQuery = false;
|
|
74
|
+
}
|
|
75
|
+
_initActors(args) {
|
|
76
|
+
super._initActors(args);
|
|
77
|
+
this._feedback = new _index.FeedbackActor(this._hub);
|
|
78
|
+
/*
|
|
79
|
+
this._autocomplete = new AutocompleteActor(this._hub, {
|
|
80
|
+
source: sources.api(this._client),
|
|
81
|
+
options: this._options,
|
|
82
|
+
});
|
|
83
|
+
*/
|
|
84
|
+
}
|
|
85
|
+
_initSubscriptions(args) {
|
|
86
|
+
super._initSubscriptions(args);
|
|
87
|
+
this._unsubscribes = [...this._unsubscribes, this._hub.on(_index.fields.query(), args => this._query(args)), this._hub.on(_index.fields.view(_constants.ROLE.ANSWER), data => this._onAnswerViewUpdate(data)), this._views.get(_constants.ROLE.ANSWER).on('citation-click', event => this._onCitationClick(event))];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// lifecycle //
|
|
91
|
+
restart() {
|
|
92
|
+
// clear question id from previous session
|
|
93
|
+
if (this._questionId) {
|
|
94
|
+
this._questionId = undefined;
|
|
95
|
+
}
|
|
96
|
+
// interrupt event
|
|
97
|
+
// TODO: move to workflow base
|
|
98
|
+
const {
|
|
99
|
+
session,
|
|
100
|
+
status,
|
|
101
|
+
ongoing
|
|
102
|
+
} = this._hub.states[_index.fields.view(_constants.ROLE.ANSWER)] || {};
|
|
103
|
+
if (session && status !== _constants.STATUS.INITIAL && (status !== _constants.STATUS.READY || ongoing)) {
|
|
104
|
+
// it's interrupted by a new question
|
|
105
|
+
const state = Object.freeze({
|
|
106
|
+
session,
|
|
107
|
+
status,
|
|
108
|
+
ongoing
|
|
109
|
+
});
|
|
110
|
+
this._emit('interrupt', state);
|
|
111
|
+
this._emit('finally', state);
|
|
112
|
+
}
|
|
113
|
+
// restart
|
|
114
|
+
super.restart();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// properties //
|
|
118
|
+
get questionId() {
|
|
119
|
+
return this._questionId;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// query //
|
|
123
|
+
autoQuery() {
|
|
124
|
+
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
125
|
+
const {
|
|
126
|
+
setValue = true,
|
|
127
|
+
focus = true
|
|
128
|
+
} = this._autoQuery = options;
|
|
129
|
+
const searchParams = new URLSearchParams(window.location.search);
|
|
130
|
+
const q = searchParams.get('q');
|
|
131
|
+
const qs = searchParams.get('qs');
|
|
132
|
+
const {
|
|
133
|
+
layout
|
|
134
|
+
} = this._views.get(_constants.ROLE.QUERY);
|
|
135
|
+
if (layout) {
|
|
136
|
+
if (q && setValue) {
|
|
137
|
+
layout.value = q;
|
|
138
|
+
}
|
|
139
|
+
if (!q && focus) {
|
|
140
|
+
layout.focus();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (q) {
|
|
144
|
+
this.query({
|
|
145
|
+
q,
|
|
146
|
+
qs
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
query(args) {
|
|
151
|
+
// TODO: also accept "question", "questionSource"?
|
|
152
|
+
if (!args.q) {
|
|
153
|
+
throw new Error(`q is required in query() call`);
|
|
154
|
+
}
|
|
155
|
+
this._hub.update(_index.fields.query(), args);
|
|
156
|
+
}
|
|
157
|
+
_query() {
|
|
158
|
+
let args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
159
|
+
// start a new session
|
|
160
|
+
this.restart();
|
|
161
|
+
|
|
162
|
+
// keep track of question source on this session
|
|
163
|
+
const {
|
|
164
|
+
session
|
|
165
|
+
} = this;
|
|
166
|
+
session.meta.question_source = args.questionSource || _constants.ORGANIC_QUESTION_SOURCE; // might be null, not undefined
|
|
167
|
+
|
|
168
|
+
// build payload and trigger request
|
|
169
|
+
const payload = this._buildPayload(args);
|
|
170
|
+
const event = (0, _options.mergeApiOptions)(this._options.resolved.api, {
|
|
171
|
+
payload,
|
|
172
|
+
session
|
|
173
|
+
});
|
|
174
|
+
this._request(event);
|
|
175
|
+
}
|
|
176
|
+
_request(event) {
|
|
177
|
+
this._hub.update(_index.fields.request(), event);
|
|
178
|
+
}
|
|
179
|
+
_buildPayload() {
|
|
180
|
+
throw new Error(`Not implemented`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// autocomplete //
|
|
184
|
+
/*
|
|
185
|
+
updateCompletions(event) {
|
|
186
|
+
// TODO: verify
|
|
187
|
+
this._hub.update(fields.data(DATA_ASPECT.AUTOCOMPLETE), {
|
|
188
|
+
...event,
|
|
189
|
+
source: 'manual',
|
|
190
|
+
});
|
|
191
|
+
return this;
|
|
192
|
+
}
|
|
193
|
+
*/
|
|
194
|
+
|
|
195
|
+
// data //
|
|
196
|
+
_defaultProcessData(data) {
|
|
197
|
+
data = super._defaultProcessData(data);
|
|
198
|
+
|
|
199
|
+
// capture question ID and register at context
|
|
200
|
+
this._writeQuestionIdFromData(data);
|
|
201
|
+
|
|
202
|
+
// update URL if autoQuery.updateUrl is not false
|
|
203
|
+
this._updateUrlIfNecessary(data);
|
|
204
|
+
|
|
205
|
+
// affiliation
|
|
206
|
+
data = (0, _index3.processData)(data);
|
|
207
|
+
return (0, _processors.writeAnswerStageToMeta)(data);
|
|
208
|
+
}
|
|
209
|
+
_writeQuestionIdFromData(_ref) {
|
|
210
|
+
let {
|
|
211
|
+
value
|
|
212
|
+
} = _ref;
|
|
213
|
+
if (!value) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const {
|
|
217
|
+
question_id: questionId
|
|
218
|
+
} = value;
|
|
219
|
+
this._writeQuestionId(questionId);
|
|
220
|
+
}
|
|
221
|
+
_updateUrlIfNecessary(_ref2) {
|
|
222
|
+
let {
|
|
223
|
+
value,
|
|
224
|
+
request
|
|
225
|
+
} = _ref2;
|
|
226
|
+
if (!value || !this._autoQuery || this._autoQuery.updateUrl === false) {
|
|
227
|
+
// explicitly false
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const {
|
|
231
|
+
question
|
|
232
|
+
} = value;
|
|
233
|
+
const organic = request && request.payload && request.payload._meta && request.payload._meta.question_source === _constants.ORGANIC_QUESTION_SOURCE;
|
|
234
|
+
const url = new URL(window.location);
|
|
235
|
+
const currentQuestion = url.searchParams.get('q');
|
|
236
|
+
// TODO: review this, handle &qs=
|
|
237
|
+
if (question !== currentQuestion && (!organic || this._autoQuery.updateUrl === true)) {
|
|
238
|
+
url.searchParams.set('q', question);
|
|
239
|
+
window.history.replaceState({}, '', url);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// interactions //
|
|
244
|
+
_preprocessInteraction(payload) {
|
|
245
|
+
payload = super._preprocessInteraction(payload) || {};
|
|
246
|
+
const {
|
|
247
|
+
context = {}
|
|
248
|
+
} = payload;
|
|
249
|
+
const {
|
|
250
|
+
custom_context = {}
|
|
251
|
+
} = context;
|
|
252
|
+
let {
|
|
253
|
+
questionId
|
|
254
|
+
} = this;
|
|
255
|
+
return Object.assign({}, payload, {
|
|
256
|
+
context: Object.assign({}, context, {
|
|
257
|
+
custom_context: (0, _commons.trimObj)(Object.assign({
|
|
258
|
+
question_id: questionId
|
|
259
|
+
}, custom_context))
|
|
260
|
+
})
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// handlers //
|
|
265
|
+
_onAnswerViewUpdate(_ref3) {
|
|
266
|
+
let {
|
|
267
|
+
session,
|
|
268
|
+
status,
|
|
269
|
+
ongoing
|
|
270
|
+
} = _ref3;
|
|
271
|
+
if (status === _constants.STATUS.INITIAL) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const done = status === _constants.STATUS.READY && !ongoing;
|
|
275
|
+
const erroneous = status === _constants.STATUS.ERRONEOUS;
|
|
276
|
+
const eventName = done ? 'done' : erroneous ? 'error' : status;
|
|
277
|
+
const state = Object.freeze({
|
|
278
|
+
session,
|
|
279
|
+
status,
|
|
280
|
+
ongoing
|
|
281
|
+
});
|
|
282
|
+
this._emit(eventName, state);
|
|
283
|
+
if (done || erroneous) {
|
|
284
|
+
this._emit('finally', state);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
_onCitationClick(_ref4) {
|
|
288
|
+
let {
|
|
289
|
+
index
|
|
290
|
+
} = _ref4;
|
|
291
|
+
const {
|
|
292
|
+
value
|
|
293
|
+
} = this.states[_index.fields.data()] || {};
|
|
294
|
+
const {
|
|
295
|
+
sources
|
|
296
|
+
} = value || {};
|
|
297
|
+
// index is 1-based
|
|
298
|
+
const source = sources && sources[index - 1];
|
|
299
|
+
if (!source || !source.product_id) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
// TODO: should we track impression as well?
|
|
303
|
+
this._trackers.sources.click([source.product_id], {
|
|
304
|
+
manual: false
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// helpers //
|
|
309
|
+
_writeQuestionId(questionId) {
|
|
310
|
+
if (!this._questionId && questionId) {
|
|
311
|
+
this._questionId = questionId;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
_clearQuestionId() {
|
|
315
|
+
// clear question id from previous session
|
|
316
|
+
this._questionId = undefined;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// destroy //
|
|
320
|
+
_destroy(options) {
|
|
321
|
+
this._feedback._destroy();
|
|
322
|
+
// this._autocomplete._destroy();
|
|
323
|
+
super._destroy(options);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// makeConfigurable(AnswerBasedWorkflow.prototype, ['autocomplete']);
|
|
328
|
+
exports.default = AnswerBasedWorkflow;
|
|
329
|
+
Object.assign(AnswerBasedWorkflow, {
|
|
330
|
+
DEFAULT_API_OPTIONS,
|
|
331
|
+
DEFAULT_AUTOCOMPLETE_OPTIONS,
|
|
332
|
+
DEFAULT_LAYOUTS,
|
|
333
|
+
DEFAULT_TRACKERS,
|
|
334
|
+
DEFAULT_OPTIONS
|
|
335
|
+
});
|