@miso.ai/client-sdk-ui 1.11.5-beta.2 → 1.11.5-beta.4

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.
@@ -231,13 +231,14 @@ class ViewActor {
231
231
  for (const {
232
232
  role
233
233
  } of element.components) {
234
- // not a data role -> nonempty
234
+ // not a data role -> irrelevant
235
235
  if (!(0, _constants.isDataRole)(role)) {
236
- return false;
236
+ continue;
237
237
  }
238
238
  let sliced = value && value[role];
239
239
  if (role === _constants.ROLE.QUERY_SUGGESTIONS) {
240
240
  // special case: query suggestions -> look up value from hub
241
+ // TODO: adhoc
241
242
  const data = this.hub.states[fields.suggestions()];
242
243
  sliced = data && data.value;
243
244
  }
@@ -146,7 +146,7 @@ class GalleryLayout extends _collection.default {
146
146
  const {
147
147
  itemSizes
148
148
  } = this._galleryContext;
149
- const values = this._writeItemSizes(this._getItems(state), itemSizes);
149
+ const values = this._writeItemSizes(this._getItems(state) || [], itemSizes);
150
150
  listElement.innerHTML = this.templates.items(this, state, values);
151
151
  }
152
152
  _appendItems(listElement, _ref5) {
@@ -160,13 +160,12 @@ class GalleryLayout extends _collection.default {
160
160
  this._syncItemSizes(listElement);
161
161
  // append new items
162
162
  const offset = listElement.children.length;
163
- const values = this._writeItemSizes(this._getItems(state).slice(offset), itemSizes, offset);
163
+ const values = this._writeItemSizes((this._getItems(state) || []).slice(offset), itemSizes, offset);
164
164
  listElement.insertAdjacentHTML('beforeend', this.templates.items(this, state, values, {
165
165
  offset
166
166
  }));
167
167
  }
168
- _writeItemSizes() {
169
- let values = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
168
+ _writeItemSizes(values) {
170
169
  let [large = 0, medium = 0] = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
171
170
  let offset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
172
171
  let omitted = 0;
@@ -4,4 +4,4 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
- var _default = exports.default = '1.11.5-beta.2';
7
+ var _default = exports.default = '1.11.5-beta.4';
@@ -14,8 +14,7 @@ var _index4 = require("./options/index.js");
14
14
  var _processors = require("./processors.js");
15
15
  var _useLink = require("./use-link.js");
16
16
  var _trackers = require("../util/trackers.js");
17
- const _excluded = ["setValue", "focus", "param", "sourceParam"],
18
- _excluded2 = ["event"];
17
+ const _excluded = ["event"];
19
18
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
20
19
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
21
20
  const DEFAULT_API_OPTIONS = Object.freeze(Object.assign({}, _base.default.DEFAULT_API_OPTIONS, {
@@ -84,22 +83,6 @@ const ROLES_OPTIONS = (0, _index4.mergeRolesOptions)(_base.default.ROLES_OPTIONS
84
83
  main: _constants.ROLE.ANSWER,
85
84
  members: Object.keys(DEFAULT_LAYOUTS)
86
85
  });
87
- function normalizeAutoQueryOptions() {
88
- let _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
89
- let {
90
- setValue = true,
91
- focus = true,
92
- param = 'q',
93
- sourceParam = 'qs'
94
- } = _ref,
95
- options = _objectWithoutPropertiesLoose(_ref, _excluded);
96
- return Object.assign({
97
- setValue,
98
- focus,
99
- param,
100
- sourceParam
101
- }, options);
102
- }
103
86
  class AnswerBasedWorkflow extends _base.default {
104
87
  // constructor //
105
88
  _initProperties(args) {
@@ -170,32 +153,7 @@ class AnswerBasedWorkflow extends _base.default {
170
153
  // query //
171
154
  autoQuery() {
172
155
  let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
173
- const {
174
- setValue,
175
- focus,
176
- param,
177
- sourceParam
178
- } = this._autoQuery = normalizeAutoQueryOptions(options);
179
- const searchParams = new URLSearchParams(window.location.search);
180
- const q = searchParams.get(param);
181
- const qs = searchParams.get(sourceParam);
182
- const {
183
- layout
184
- } = this._views.get(_constants.ROLE.QUERY);
185
- if (layout) {
186
- if (q && setValue) {
187
- layout.value = q;
188
- }
189
- if (!q && focus) {
190
- layout.focus();
191
- }
192
- }
193
- if (q) {
194
- this.query({
195
- q,
196
- qs
197
- });
198
- }
156
+ _index4.autoQuery.call(this, options);
199
157
  }
200
158
  query(args) {
201
159
  // TODO: also accept "question", "questionSource"?
@@ -228,9 +186,20 @@ class AnswerBasedWorkflow extends _base.default {
228
186
  _buildPayload() {
229
187
  throw new Error(`Not implemented`);
230
188
  }
189
+ _writeWikiLinkTemplateToPayload(payload) {
190
+ if (!this._autoQuery || !this._autoQuery.param || this._autoQuery.param === _index4.DEFAULT_AUTO_QUERY_PARAM) {
191
+ return payload;
192
+ }
193
+ return Object.assign({}, payload, {
194
+ wiki_link_template: `?${this._autoQuery.param}=%s`
195
+ });
196
+ }
231
197
 
232
198
  // data //
233
199
  _updateData(data) {
200
+ // capture question ID and register at context
201
+ this._writeQuestionIdFromData(data);
202
+
234
203
  // if it's the head response, write question id and return
235
204
  if (data.value && !data.value.answer_stage) {
236
205
  this._handleHeadResponse(data);
@@ -241,51 +210,49 @@ class AnswerBasedWorkflow extends _base.default {
241
210
  // update URL if autoQuery.updateUrl is not false
242
211
  this._updateUrlIfNecessary(data);
243
212
  }
244
- _handleHeadResponse(data) {
245
- // capture question ID and register at context
246
- this._writeQuestionIdFromData(data);
247
- }
213
+ _handleHeadResponse(data) {}
248
214
  _defaultProcessData(data) {
249
215
  data = super._defaultProcessData(data);
250
216
  data = (0, _index3.processData)(data);
251
217
  data = (0, _processors.writeAnswerStageToMeta)(data);
252
218
  return data;
253
219
  }
254
- _writeQuestionIdFromData(_ref2) {
220
+ _writeQuestionIdFromData(_ref) {
255
221
  let {
256
222
  value
257
- } = _ref2;
258
- if (!value) {
259
- return;
260
- }
261
- const {
262
- question_id: questionId
263
- } = value;
264
- this._writeQuestionId(questionId);
223
+ } = _ref;
224
+ value && this._writeQuestionId(value.question_id);
265
225
  }
266
- _updateUrlIfNecessary(_ref3) {
226
+ _updateUrlIfNecessary(_ref2) {
267
227
  let {
268
228
  value,
269
229
  request
270
- } = _ref3;
271
- if (!value || !this._autoQuery || this._autoQuery.updateUrl === false) {
272
- // explicitly false
230
+ } = _ref2;
231
+ if (!this._autoQuery || !this._autoQuery.updateUrl) {
273
232
  return;
274
233
  }
275
234
  const {
276
- param
235
+ param,
236
+ sourceParam
277
237
  } = this._autoQuery;
278
- const {
279
- question
280
- } = value;
281
- const organic = request && request.payload && request.payload._meta && request.payload._meta.question_source === _constants.ORGANIC_QUESTION_SOURCE;
238
+ const question = value && value.question || request && request.payload && request.payload.question;
239
+ const questionSource = request && request.payload && request.payload._meta && request.payload._meta.question_source;
240
+ if (!question) {
241
+ return; // at initial phase
242
+ }
282
243
  const url = new URL(window.location);
283
244
  const currentQuestion = url.searchParams.get(param);
284
- // TODO: review this, handle &qs=
285
- if (question !== currentQuestion && (!organic || this._autoQuery.updateUrl === true)) {
286
- url.searchParams.set(param, question);
287
- window.history.replaceState({}, '', url);
245
+ const currentQuestionSource = url.searchParams.get(sourceParam) || _constants.ORGANIC_QUESTION_SOURCE;
246
+ if (question === currentQuestion && questionSource === currentQuestionSource) {
247
+ return;
248
+ }
249
+ url.searchParams.set(param, question);
250
+ if (questionSource === _constants.ORGANIC_QUESTION_SOURCE) {
251
+ url.searchParams.delete(sourceParam);
252
+ } else {
253
+ url.searchParams.set(sourceParam, questionSource);
288
254
  }
255
+ window.history.replaceState({}, '', url);
289
256
  }
290
257
 
291
258
  // interactions //
@@ -293,6 +260,7 @@ class AnswerBasedWorkflow extends _base.default {
293
260
  payload = super._defaultProcessInteraction(payload, args);
294
261
  payload = this._writeAskPropertiesToInteraction(payload, args);
295
262
  payload = this._writeAnswerClickInteraction(payload, args);
263
+ payload = (0, _processors.writeEventTargetToInteraction)(payload, args);
296
264
  return payload;
297
265
  }
298
266
  _writeAskPropertiesToInteraction() {
@@ -338,11 +306,11 @@ class AnswerBasedWorkflow extends _base.default {
338
306
  }
339
307
 
340
308
  // handlers //
341
- _onCitationClick(_ref4) {
309
+ _onCitationClick(_ref3) {
342
310
  let {
343
311
  index,
344
312
  event
345
- } = _ref4;
313
+ } = _ref3;
346
314
  if ((0, _trackers.isTracked)(event)) {
347
315
  return;
348
316
  }
@@ -359,13 +327,17 @@ class AnswerBasedWorkflow extends _base.default {
359
327
  }
360
328
  // TODO: should we track impression as well?
361
329
  (0, _trackers.markAsTracked)(event);
362
- this._views.trackers.sources.click([source.product_id]);
330
+ // distinguish from regular sources element click
331
+ // TODO
332
+ this._views.trackers.sources.click([source.product_id], {
333
+ event_target: 'citation-link'
334
+ });
363
335
  }
364
- _onAnswerLinkClick(_ref5) {
336
+ _onAnswerLinkClick(_ref4) {
365
337
  let {
366
338
  event
367
- } = _ref5,
368
- item = _objectWithoutPropertiesLoose(_ref5, _excluded2);
339
+ } = _ref4,
340
+ item = _objectWithoutPropertiesLoose(_ref4, _excluded);
369
341
  if ((0, _trackers.isTracked)(event)) {
370
342
  return;
371
343
  }
@@ -134,18 +134,20 @@ class Ask extends _answerBased.default {
134
134
  q,
135
135
  qs
136
136
  } = _ref,
137
- payload = _objectWithoutPropertiesLoose(_ref, _excluded);
137
+ options = _objectWithoutPropertiesLoose(_ref, _excluded);
138
138
  const {
139
139
  parentQuestionId
140
140
  } = this;
141
- return (0, _commons.trimObj)(Object.assign({}, payload, {
141
+ let payload = (0, _commons.trimObj)(Object.assign({}, options, {
142
142
  question: q,
143
143
  // question, not q
144
144
  parent_question_id: parentQuestionId,
145
- _meta: Object.assign({}, payload._meta, {
145
+ _meta: Object.assign({}, options._meta, {
146
146
  question_source: qs || _constants.ORGANIC_QUESTION_SOURCE // might be null, not undefined
147
147
  })
148
148
  }));
149
+ payload = this._writeWikiLinkTemplateToPayload(payload);
150
+ return payload;
149
151
  }
150
152
 
151
153
  // interactions //
@@ -137,8 +137,7 @@ class Explore extends _base.default {
137
137
  const related_questions = value.related_questions.map(linkFn ? text => ({
138
138
  text,
139
139
  url: this._getSubmitUrl({
140
- q: text
141
- }, {
140
+ q: text,
142
141
  generated: true
143
142
  })
144
143
  }) : text => ({
@@ -166,14 +165,13 @@ class Explore extends _base.default {
166
165
  this._hub.update(_index.fields.query(), args);
167
166
  }
168
167
  _query(args) {
169
- this._submitToPage(args);
168
+ this._submitToPage(Object.assign({}, args, {
169
+ generated: false
170
+ }));
170
171
  }
171
172
  _getSubmitUrl(args) {
172
- let {
173
- generated = false
174
- } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
175
173
  let url = _useLink.UseLinkMixin.prototype._getSubmitUrl.call(this, args);
176
- if (!generated || !this._productId) {
174
+ if (!args.generated || !this._productId) {
177
175
  return url;
178
176
  }
179
177
  return `${url}&qs=${encodeURIComponent(this._productId)}`;
@@ -48,6 +48,7 @@ class HybridSearchAnswer extends _answerBased.default {
48
48
  // query //
49
49
  _buildPayload(payload) {
50
50
  payload = this._writeQuestionSourceToPayload(payload);
51
+ payload = this._writeWikiLinkTemplateToPayload(payload);
51
52
  payload = this._superworkflow._results._writeFiltersToPayload(payload);
52
53
  return payload;
53
54
  }
@@ -68,6 +69,7 @@ class HybridSearchAnswer extends _answerBased.default {
68
69
  _defaultProcessData(data) {
69
70
  data = super._defaultProcessData(data);
70
71
  data = (0, _processors.writeKeywordsToData)(data);
72
+ data = (0, _processors.writeMisoIdFromSession)(data);
71
73
  return data;
72
74
  }
73
75
  _updateDataInHub(data) {
@@ -82,6 +84,8 @@ class HybridSearchAnswer extends _answerBased.default {
82
84
  }
83
85
  _handleHeadResponse(data) {
84
86
  super._handleHeadResponse(data);
87
+ // keep track of miso_id in session
88
+ (0, _processors.writeMisoIdToSession)(data);
85
89
  // share the search results with its sibling
86
90
  this._dispatchDataToSibling(data);
87
91
  }
@@ -91,6 +95,14 @@ class HybridSearchAnswer extends _answerBased.default {
91
95
  }));
92
96
  }
93
97
 
98
+ // interactions //
99
+ _defaultProcessInteraction(payload, args) {
100
+ return this._superworkflow._defaultProcessInteraction(payload, args);
101
+ }
102
+ _defaultProcessInteraction0(payload, args) {
103
+ return super._defaultProcessInteraction(payload, args);
104
+ }
105
+
94
106
  // destroy //
95
107
  _destroy(options) {
96
108
  this._destroyAutocomplete();
@@ -8,6 +8,7 @@ var _commons = require("@miso.ai/commons");
8
8
  var _searchBased = _interopRequireDefault(require("./search-based.js"));
9
9
  var _index = require("../actor/index.js");
10
10
  var _constants = require("../constants.js");
11
+ var _processors = require("./processors.js");
11
12
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
13
  // we want to override members, not adding to it
13
14
  const ROLES_OPTIONS = Object.assign({}, _searchBased.default.ROLES_OPTIONS, {
@@ -66,6 +67,21 @@ class HybridSearchResults extends _searchBased.default {
66
67
  }))
67
68
  });
68
69
  }
70
+
71
+ // data //
72
+ _defaultProcessData(data) {
73
+ data = super._defaultProcessData(data);
74
+ (0, _processors.writeMisoIdToSession)(data);
75
+ return data;
76
+ }
77
+
78
+ // interactions //
79
+ _defaultProcessInteraction(payload, args) {
80
+ return this._superworkflow._defaultProcessInteraction(payload, args);
81
+ }
82
+ _defaultProcessInteraction0(payload, args) {
83
+ return super._defaultProcessInteraction(payload, args);
84
+ }
69
85
  }
70
86
  exports.default = HybridSearchResults;
71
87
  Object.assign(HybridSearchResults, {
@@ -81,14 +81,7 @@ class HybridSearch extends _base.default {
81
81
  }
82
82
  }
83
83
  _initActors() {
84
- const hub = this._hub;
85
- const client = this._client;
86
- const options = this._options;
87
84
  this._views = new _hybridSearchViews.default(this);
88
- this._interactions = new _index.InteractionsActor(hub, {
89
- client,
90
- options
91
- });
92
85
  }
93
86
  _initSession() {
94
87
  this._results._sessions.restart();
@@ -128,7 +121,9 @@ class HybridSearch extends _base.default {
128
121
 
129
122
  // interactions //
130
123
  _defaultProcessInteraction(payload, args) {
131
- return this._answer._defaultProcessInteraction(payload, args);
124
+ payload = this._answer._defaultProcessInteraction0(payload, args);
125
+ payload = this._results._defaultProcessInteraction0(payload, args);
126
+ return payload;
132
127
  }
133
128
 
134
129
  // destroy //
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.DEFAULT_AUTO_QUERY_SOURCE_PARAM = exports.DEFAULT_AUTO_QUERY_PARAM = void 0;
7
+ exports.autoQuery = autoQuery;
8
+ exports.normalizeAutoQueryOptions = normalizeAutoQueryOptions;
9
+ var _commons = require("@miso.ai/commons");
10
+ var _constants = require("../../constants.js");
11
+ const _excluded = ["setValue", "focus", "updateUrl", "param", "sourceParam"];
12
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
13
+ const DEFAULT_AUTO_QUERY_PARAM = exports.DEFAULT_AUTO_QUERY_PARAM = 'q';
14
+ const DEFAULT_AUTO_QUERY_SOURCE_PARAM = exports.DEFAULT_AUTO_QUERY_SOURCE_PARAM = 'qs';
15
+ function normalizeAutoQueryOptions() {
16
+ let _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
17
+ let {
18
+ setValue = true,
19
+ focus = true,
20
+ updateUrl = true,
21
+ param = DEFAULT_AUTO_QUERY_PARAM,
22
+ sourceParam = DEFAULT_AUTO_QUERY_SOURCE_PARAM
23
+ } = _ref,
24
+ options = _objectWithoutPropertiesLoose(_ref, _excluded);
25
+ return Object.assign({
26
+ setValue,
27
+ focus,
28
+ updateUrl,
29
+ param,
30
+ sourceParam
31
+ }, options);
32
+ }
33
+ function autoQuery() {
34
+ let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
35
+ // TODO: support getting value from URL path
36
+ const {
37
+ setValue,
38
+ focus,
39
+ param,
40
+ sourceParam
41
+ } = this._autoQuery = normalizeAutoQueryOptions(options);
42
+ const searchParams = new URLSearchParams(window.location.search);
43
+ const q = searchParams.get(param);
44
+ const qs = searchParams.get(sourceParam) || undefined;
45
+ const {
46
+ layout
47
+ } = this._views.get(_constants.ROLE.QUERY);
48
+ if (layout) {
49
+ if (q && setValue) {
50
+ layout.value = q;
51
+ }
52
+ if (!q && focus) {
53
+ layout.focus();
54
+ }
55
+ }
56
+ if (q) {
57
+ this.query((0, _commons.trimObj)({
58
+ q,
59
+ qs
60
+ }));
61
+ }
62
+ }
@@ -112,4 +112,15 @@ Object.keys(_configurable).forEach(function (key) {
112
112
  return _configurable[key];
113
113
  }
114
114
  });
115
+ });
116
+ var _autoQuery = require("./auto-query.js");
117
+ Object.keys(_autoQuery).forEach(function (key) {
118
+ if (key === "default" || key === "__esModule") return;
119
+ if (key in exports && exports[key] === _autoQuery[key]) return;
120
+ Object.defineProperty(exports, key, {
121
+ enumerable: true,
122
+ get: function () {
123
+ return _autoQuery[key];
124
+ }
125
+ });
115
126
  });
@@ -12,9 +12,12 @@ exports.retainFacetCountsInData = retainFacetCountsInData;
12
12
  exports.writeAffiliationInfoToInteraction = writeAffiliationInfoToInteraction;
13
13
  exports.writeAnswerStageToMeta = writeAnswerStageToMeta;
14
14
  exports.writeDataStatus = writeDataStatus;
15
+ exports.writeEventTargetToInteraction = writeEventTargetToInteraction;
15
16
  exports.writeExhaustionToData = writeExhaustionToData;
16
17
  exports.writeKeywordsToData = writeKeywordsToData;
18
+ exports.writeMisoIdFromSession = writeMisoIdFromSession;
17
19
  exports.writeMisoIdToMeta = writeMisoIdToMeta;
20
+ exports.writeMisoIdToSession = writeMisoIdToSession;
18
21
  var _commons = require("@miso.ai/commons");
19
22
  var _constants = require("../constants.js");
20
23
  var _index = require("../actor/index.js");
@@ -97,6 +100,44 @@ function writeMisoIdToMeta(data) {
97
100
  })
98
101
  });
99
102
  }
103
+ function writeMisoIdToSession(data) {
104
+ const {
105
+ session,
106
+ value
107
+ } = data;
108
+ if (!session || !value) {
109
+ return;
110
+ }
111
+ const {
112
+ miso_id
113
+ } = value;
114
+ if (!miso_id) {
115
+ return;
116
+ }
117
+ session.meta.miso_id = miso_id;
118
+ }
119
+ function writeMisoIdFromSession(data) {
120
+ const {
121
+ session,
122
+ value
123
+ } = data;
124
+ if (!session || !value) {
125
+ return data;
126
+ }
127
+ const {
128
+ meta: {
129
+ miso_id
130
+ } = {}
131
+ } = session;
132
+ if (!miso_id) {
133
+ return data;
134
+ }
135
+ return Object.assign({}, data, {
136
+ meta: Object.assign({}, data.meta, {
137
+ miso_id
138
+ })
139
+ });
140
+ }
100
141
  function writeKeywordsToData(data) {
101
142
  const {
102
143
  request,
@@ -282,6 +323,21 @@ function writeAffiliationInfoToInteraction(payload, args) {
282
323
  }
283
324
  });
284
325
  }
326
+ function writeEventTargetToInteraction(payload) {
327
+ let {
328
+ event_target
329
+ } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
330
+ if (!event_target) {
331
+ return payload;
332
+ }
333
+ return mergeInteraction(payload, {
334
+ context: {
335
+ custom_context: {
336
+ event_target
337
+ }
338
+ }
339
+ });
340
+ }
285
341
  function mergeInteraction() {
286
342
  let base = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
287
343
  let patch = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -80,6 +80,10 @@ class SearchBasedWorkflow extends _base.default {
80
80
  }
81
81
 
82
82
  // query //
83
+ autoQuery() {
84
+ let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
85
+ _index3.autoQuery.call(this, options);
86
+ }
83
87
  query(args) {
84
88
  if (!args.q) {
85
89
  throw new Error(`q is required in query() call`);
@@ -49,31 +49,6 @@ class Search extends _searchBased.default {
49
49
  super._initProperties(args);
50
50
  this._initAutocomplete(args);
51
51
  }
52
- autoQuery() {
53
- let {
54
- setValue = true,
55
- focus = true,
56
- param = 'q'
57
- } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
58
- // TODO: align implementation with AnswerBasedWorkflow
59
- const q = new URLSearchParams(window.location.search).get(param);
60
- const {
61
- layout
62
- } = this._views.get(_constants.ROLE.QUERY);
63
- if (layout) {
64
- if (q && setValue) {
65
- layout.value = q;
66
- }
67
- if (!q && focus) {
68
- layout.focus();
69
- }
70
- }
71
- if (q) {
72
- this.query({
73
- q
74
- });
75
- }
76
- }
77
52
  notifyViewUpdate() {
78
53
  let role = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _constants.ROLE.PRODUCTS;
79
54
  for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.UseLinkMixin = void 0;
7
7
  exports.enableUseLink = enableUseLink;
8
8
  var _commons = require("@miso.ai/commons");
9
+ const _excluded = ["q"];
10
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
9
11
  function enableUseLink(prototype) {
10
12
  (0, _commons.mixin)(prototype, UseLinkMixin.prototype);
11
13
  }
@@ -34,11 +36,16 @@ class UseLinkMixin {
34
36
  window.open(url, target, windowFeatures);
35
37
  }
36
38
  }
37
- _getSubmitUrl(args) {
39
+ _getSubmitUrl() {
40
+ let _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
41
+ let {
42
+ q
43
+ } = _ref,
44
+ args = _objectWithoutPropertiesLoose(_ref, _excluded);
38
45
  if (!this._linkFn) {
39
46
  throw new Error('useLink(fn) must be called before _getSubmitUrl()');
40
47
  }
41
- return this._linkFn[0](args.q);
48
+ return this._linkFn[0](q, args);
42
49
  }
43
50
  }
44
51
  exports.UseLinkMixin = UseLinkMixin;
package/package.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "url": "https://github.com/MisoAI/miso-client-js-sdk/issues"
27
27
  },
28
28
  "dependencies": {
29
- "@miso.ai/commons": "1.11.5-beta.2"
29
+ "@miso.ai/commons": "1.11.5-beta.4"
30
30
  },
31
- "version": "1.11.5-beta.2"
31
+ "version": "1.11.5-beta.4"
32
32
  }
package/src/actor/view.js CHANGED
@@ -185,13 +185,14 @@ export default class ViewActor {
185
185
  }
186
186
  // iterate over all member components' roles
187
187
  for (const { role } of element.components) {
188
- // not a data role -> nonempty
188
+ // not a data role -> irrelevant
189
189
  if (!isDataRole(role)) {
190
- return false;
190
+ continue;
191
191
  }
192
192
  let sliced = value && value[role];
193
193
  if (role === ROLE.QUERY_SUGGESTIONS) {
194
194
  // special case: query suggestions -> look up value from hub
195
+ // TODO: adhoc
195
196
  const data = this.hub.states[fields.suggestions()];
196
197
  sliced = data && data.value;
197
198
  }
@@ -127,7 +127,7 @@ export default class GalleryLayout extends CollectionLayout {
127
127
 
128
128
  _updateItems(listElement, { state }) {
129
129
  const { itemSizes } = this._galleryContext;
130
- const values = this._writeItemSizes(this._getItems(state), itemSizes);
130
+ const values = this._writeItemSizes(this._getItems(state) || [], itemSizes);
131
131
  listElement.innerHTML = this.templates.items(this, state, values);
132
132
  }
133
133
 
@@ -137,11 +137,11 @@ export default class GalleryLayout extends CollectionLayout {
137
137
  this._syncItemSizes(listElement);
138
138
  // append new items
139
139
  const offset = listElement.children.length;
140
- const values = this._writeItemSizes(this._getItems(state).slice(offset), itemSizes, offset);
140
+ const values = this._writeItemSizes((this._getItems(state) || []).slice(offset), itemSizes, offset);
141
141
  listElement.insertAdjacentHTML('beforeend', this.templates.items(this, state, values, { offset }));
142
142
  }
143
143
 
144
- _writeItemSizes(values = [], [large = 0, medium = 0] = [], offset = 0) {
144
+ _writeItemSizes(values, [large = 0, medium = 0] = [], offset = 0) {
145
145
  let omitted = 0;
146
146
  if (offset > 0) {
147
147
  omitted = Math.min(offset, large);
package/src/version.js CHANGED
@@ -1 +1 @@
1
- export default '1.11.5-beta.2';
1
+ export default '1.11.5-beta.4';
@@ -2,10 +2,10 @@ import { API } from '@miso.ai/commons';
2
2
  import Workflow from './base.js';
3
3
  import { fields, FeedbackActor } from '../actor/index.js';
4
4
  import { ROLE, STATUS, ORGANIC_QUESTION_SOURCE } from '../constants.js';
5
- import { SearchBoxLayout, TextLayout, ListLayout, CarouselLayout, GalleryLayout, TypewriterLayout, FeedbackLayout, AffiliationLayout } from '../layout/index.js';
5
+ import { SearchBoxLayout, TextLayout, ListLayout, GalleryLayout, TypewriterLayout, FeedbackLayout, AffiliationLayout } from '../layout/index.js';
6
6
  import { processData as processAffiliationData } from '../affiliation/index.js';
7
- import { mergeRolesOptions, DEFAULT_TRACKER_OPTIONS } from './options/index.js';
8
- import { writeAnswerStageToMeta, mergeInteraction } from './processors.js';
7
+ import { mergeRolesOptions, autoQuery as autoQueryFn, DEFAULT_AUTO_QUERY_PARAM, DEFAULT_TRACKER_OPTIONS } from './options/index.js';
8
+ import { writeAnswerStageToMeta, writeEventTargetToInteraction, mergeInteraction } from './processors.js';
9
9
  import { enableUseLink } from './use-link.js';
10
10
  import { isTracked, markAsTracked } from '../util/trackers.js';
11
11
 
@@ -67,10 +67,6 @@ const ROLES_OPTIONS = mergeRolesOptions(Workflow.ROLES_OPTIONS, {
67
67
  members: Object.keys(DEFAULT_LAYOUTS),
68
68
  });
69
69
 
70
- function normalizeAutoQueryOptions({ setValue = true, focus = true, param = 'q', sourceParam = 'qs', ...options } = {}) {
71
- return { setValue, focus, param, sourceParam, ...options };
72
- }
73
-
74
70
  export default class AnswerBasedWorkflow extends Workflow {
75
71
 
76
72
  // constructor //
@@ -146,22 +142,7 @@ export default class AnswerBasedWorkflow extends Workflow {
146
142
 
147
143
  // query //
148
144
  autoQuery(options = {}) {
149
- const { setValue, focus, param, sourceParam } = this._autoQuery = normalizeAutoQueryOptions(options);
150
- const searchParams = new URLSearchParams(window.location.search);
151
- const q = searchParams.get(param);
152
- const qs = searchParams.get(sourceParam);
153
- const { layout } = this._views.get(ROLE.QUERY);
154
- if (layout) {
155
- if (q && setValue) {
156
- layout.value = q;
157
- }
158
- if (!q && focus) {
159
- layout.focus();
160
- }
161
- }
162
- if (q) {
163
- this.query({ q, qs });
164
- }
145
+ autoQueryFn.call(this, options);
165
146
  }
166
147
 
167
148
  query(args) {
@@ -196,8 +177,21 @@ export default class AnswerBasedWorkflow extends Workflow {
196
177
  throw new Error(`Not implemented`);
197
178
  }
198
179
 
180
+ _writeWikiLinkTemplateToPayload(payload) {
181
+ if (!this._autoQuery || !this._autoQuery.param || this._autoQuery.param === DEFAULT_AUTO_QUERY_PARAM) {
182
+ return payload;
183
+ }
184
+ return {
185
+ ...payload,
186
+ wiki_link_template: `?${this._autoQuery.param}=%s`,
187
+ };
188
+ }
189
+
199
190
  // data //
200
191
  _updateData(data) {
192
+ // capture question ID and register at context
193
+ this._writeQuestionIdFromData(data);
194
+
201
195
  // if it's the head response, write question id and return
202
196
  if (data.value && !data.value.answer_stage) {
203
197
  this._handleHeadResponse(data);
@@ -209,10 +203,7 @@ export default class AnswerBasedWorkflow extends Workflow {
209
203
  this._updateUrlIfNecessary(data);
210
204
  }
211
205
 
212
- _handleHeadResponse(data) {
213
- // capture question ID and register at context
214
- this._writeQuestionIdFromData(data);
215
- }
206
+ _handleHeadResponse(data) {}
216
207
 
217
208
  _defaultProcessData(data) {
218
209
  data = super._defaultProcessData(data);
@@ -222,28 +213,34 @@ export default class AnswerBasedWorkflow extends Workflow {
222
213
  }
223
214
 
224
215
  _writeQuestionIdFromData({ value }) {
225
- if (!value) {
226
- return;
227
- }
228
- const { question_id: questionId } = value;
229
- this._writeQuestionId(questionId);
216
+ value && this._writeQuestionId(value.question_id);
230
217
  }
231
218
 
232
219
  _updateUrlIfNecessary({ value, request }) {
233
- if (!value || !this._autoQuery || this._autoQuery.updateUrl === false) { // explicitly false
220
+ if (!this._autoQuery || !this._autoQuery.updateUrl) {
234
221
  return;
235
222
  }
236
- const { param } = this._autoQuery;
237
- const { question } = value;
238
- const organic = request && request.payload && request.payload._meta && request.payload._meta.question_source === ORGANIC_QUESTION_SOURCE;
223
+ const { param, sourceParam } = this._autoQuery;
224
+ const question = (value && value.question) || (request && request.payload && request.payload.question);
225
+ const questionSource = request && request.payload && request.payload._meta && request.payload._meta.question_source;
226
+
227
+ if (!question) {
228
+ return; // at initial phase
229
+ }
239
230
 
240
231
  const url = new URL(window.location);
241
232
  const currentQuestion = url.searchParams.get(param);
242
- // TODO: review this, handle &qs=
243
- if (question !== currentQuestion && (!organic || this._autoQuery.updateUrl === true)) {
244
- url.searchParams.set(param, question);
245
- window.history.replaceState({}, '', url);
233
+ const currentQuestionSource = url.searchParams.get(sourceParam) || ORGANIC_QUESTION_SOURCE;
234
+ if (question === currentQuestion && questionSource === currentQuestionSource) {
235
+ return;
236
+ }
237
+ url.searchParams.set(param, question);
238
+ if (questionSource === ORGANIC_QUESTION_SOURCE) {
239
+ url.searchParams.delete(sourceParam);
240
+ } else {
241
+ url.searchParams.set(sourceParam, questionSource);
246
242
  }
243
+ window.history.replaceState({}, '', url);
247
244
  }
248
245
 
249
246
  // interactions //
@@ -251,6 +248,7 @@ export default class AnswerBasedWorkflow extends Workflow {
251
248
  payload = super._defaultProcessInteraction(payload, args);
252
249
  payload = this._writeAskPropertiesToInteraction(payload, args);
253
250
  payload = this._writeAnswerClickInteraction(payload, args);
251
+ payload = writeEventTargetToInteraction(payload, args);
254
252
  return payload;
255
253
  }
256
254
 
@@ -304,7 +302,9 @@ export default class AnswerBasedWorkflow extends Workflow {
304
302
  }
305
303
  // TODO: should we track impression as well?
306
304
  markAsTracked(event);
307
- this._views.trackers.sources.click([source.product_id]);
305
+ // distinguish from regular sources element click
306
+ // TODO
307
+ this._views.trackers.sources.click([source.product_id], { event_target: 'citation-link' });
308
308
  }
309
309
 
310
310
  _onAnswerLinkClick({ event, ...item }) {
@@ -115,17 +115,19 @@ export default class Ask extends AnswerBasedWorkflow {
115
115
  }
116
116
 
117
117
  // query //
118
- _buildPayload({ q, qs, ...payload } = {}) {
118
+ _buildPayload({ q, qs, ...options } = {}) {
119
119
  const { parentQuestionId } = this;
120
- return trimObj({
121
- ...payload,
120
+ let payload = trimObj({
121
+ ...options,
122
122
  question: q, // question, not q
123
123
  parent_question_id: parentQuestionId,
124
124
  _meta: {
125
- ...payload._meta,
125
+ ...options._meta,
126
126
  question_source: qs || ORGANIC_QUESTION_SOURCE, // might be null, not undefined
127
127
  },
128
128
  });
129
+ payload = this._writeWikiLinkTemplateToPayload(payload);
130
+ return payload;
129
131
  }
130
132
 
131
133
  // interactions //
@@ -121,7 +121,7 @@ export default class Explore extends Workflow {
121
121
  return data;
122
122
  }
123
123
  const linkFn = this._linkFn && this._linkFn[0];
124
- const related_questions = value.related_questions.map(linkFn ? (text => ({ text, url: this._getSubmitUrl({ q: text }, { generated: true }) })) : (text => ({ text })));
124
+ const related_questions = value.related_questions.map(linkFn ? (text => ({ text, url: this._getSubmitUrl({ q: text, generated: true }) })) : (text => ({ text })));
125
125
  return {
126
126
  ...data,
127
127
  value: {
@@ -143,12 +143,12 @@ export default class Explore extends Workflow {
143
143
  }
144
144
 
145
145
  _query(args) {
146
- this._submitToPage(args);
146
+ this._submitToPage({ ...args, generated: false });
147
147
  }
148
148
 
149
- _getSubmitUrl(args, { generated = false } = {}) {
149
+ _getSubmitUrl(args) {
150
150
  let url = UseLinkMixin.prototype._getSubmitUrl.call(this, args);
151
- if (!generated || !this._productId) {
151
+ if (!args.generated || !this._productId) {
152
152
  return url;
153
153
  }
154
154
  return `${url}&qs=${encodeURIComponent(this._productId)}`;
@@ -1,7 +1,7 @@
1
1
  import AnswerBasedWorkflow from './answer-based.js';
2
2
  import { ROLE, STATUS, ORGANIC_QUESTION_SOURCE } from '../constants.js';
3
3
  import { mergeRolesOptions } from './options/index.js';
4
- import { writeKeywordsToData } from './processors.js';
4
+ import { writeKeywordsToData, writeMisoIdToSession, writeMisoIdFromSession } from './processors.js';
5
5
  import { makeAutocompletable } from './autocompletable.js';
6
6
 
7
7
  const ROLES_OPTIONS = mergeRolesOptions(AnswerBasedWorkflow.ROLES_OPTIONS, {
@@ -44,6 +44,7 @@ export default class HybridSearchAnswer extends AnswerBasedWorkflow {
44
44
  // query //
45
45
  _buildPayload(payload) {
46
46
  payload = this._writeQuestionSourceToPayload(payload);
47
+ payload = this._writeWikiLinkTemplateToPayload(payload);
47
48
  payload = this._superworkflow._results._writeFiltersToPayload(payload);
48
49
  return payload;
49
50
  }
@@ -62,6 +63,7 @@ export default class HybridSearchAnswer extends AnswerBasedWorkflow {
62
63
  _defaultProcessData(data) {
63
64
  data = super._defaultProcessData(data);
64
65
  data = writeKeywordsToData(data);
66
+ data = writeMisoIdFromSession(data);
65
67
  return data;
66
68
  }
67
69
 
@@ -78,6 +80,8 @@ export default class HybridSearchAnswer extends AnswerBasedWorkflow {
78
80
 
79
81
  _handleHeadResponse(data) {
80
82
  super._handleHeadResponse(data);
83
+ // keep track of miso_id in session
84
+ writeMisoIdToSession(data);
81
85
  // share the search results with its sibling
82
86
  this._dispatchDataToSibling(data);
83
87
  }
@@ -86,6 +90,15 @@ export default class HybridSearchAnswer extends AnswerBasedWorkflow {
86
90
  this._resultsSession && this._superworkflow._results.updateData({ ...data, session: this._resultsSession });
87
91
  }
88
92
 
93
+ // interactions //
94
+ _defaultProcessInteraction(payload, args) {
95
+ return this._superworkflow._defaultProcessInteraction(payload, args);
96
+ }
97
+
98
+ _defaultProcessInteraction0(payload, args) {
99
+ return super._defaultProcessInteraction(payload, args);
100
+ }
101
+
89
102
  // destroy //
90
103
  _destroy(options) {
91
104
  this._destroyAutocomplete();
@@ -2,6 +2,7 @@ import { trimObj } from '@miso.ai/commons';
2
2
  import SearchBasedWorkflow from './search-based.js';
3
3
  import { fields } from '../actor/index.js';
4
4
  import { ROLE } from '../constants.js';
5
+ import { writeMisoIdToSession } from './processors.js';
5
6
 
6
7
  // we want to override members, not adding to it
7
8
  const ROLES_OPTIONS = {
@@ -69,6 +70,22 @@ export default class HybridSearchResults extends SearchBasedWorkflow {
69
70
  };
70
71
  }
71
72
 
73
+ // data //
74
+ _defaultProcessData(data) {
75
+ data = super._defaultProcessData(data);
76
+ writeMisoIdToSession(data);
77
+ return data;
78
+ }
79
+
80
+ // interactions //
81
+ _defaultProcessInteraction(payload, args) {
82
+ return this._superworkflow._defaultProcessInteraction(payload, args);
83
+ }
84
+
85
+ _defaultProcessInteraction0(payload, args) {
86
+ return super._defaultProcessInteraction(payload, args);
87
+ }
88
+
72
89
  }
73
90
 
74
91
  Object.assign(HybridSearchResults, {
@@ -98,12 +98,7 @@ export default class HybridSearch extends Workflow {
98
98
  }
99
99
 
100
100
  _initActors() {
101
- const hub = this._hub;
102
- const client = this._client;
103
- const options = this._options;
104
-
105
101
  this._views = new HybridSearchViewsActor(this);
106
- this._interactions = new InteractionsActor(hub, { client, options });
107
102
  }
108
103
 
109
104
  _initSession() {
@@ -149,7 +144,9 @@ export default class HybridSearch extends Workflow {
149
144
 
150
145
  // interactions //
151
146
  _defaultProcessInteraction(payload, args) {
152
- return this._answer._defaultProcessInteraction(payload, args);
147
+ payload = this._answer._defaultProcessInteraction0(payload, args);
148
+ payload = this._results._defaultProcessInteraction0(payload, args);
149
+ return payload;
153
150
  }
154
151
 
155
152
  // destroy //
@@ -0,0 +1,29 @@
1
+ import { trimObj } from '@miso.ai/commons';
2
+ import { ROLE } from '../../constants.js';
3
+
4
+ export const DEFAULT_AUTO_QUERY_PARAM = 'q';
5
+ export const DEFAULT_AUTO_QUERY_SOURCE_PARAM = 'qs';
6
+
7
+ export function normalizeAutoQueryOptions({ setValue = true, focus = true, updateUrl = true, param = DEFAULT_AUTO_QUERY_PARAM, sourceParam = DEFAULT_AUTO_QUERY_SOURCE_PARAM, ...options } = {}) {
8
+ return { setValue, focus, updateUrl, param, sourceParam, ...options };
9
+ }
10
+
11
+ export function autoQuery(options = {}) {
12
+ // TODO: support getting value from URL path
13
+ const { setValue, focus, param, sourceParam } = this._autoQuery = normalizeAutoQueryOptions(options);
14
+ const searchParams = new URLSearchParams(window.location.search);
15
+ const q = searchParams.get(param);
16
+ const qs = searchParams.get(sourceParam) || undefined;
17
+ const { layout } = this._views.get(ROLE.QUERY);
18
+ if (layout) {
19
+ if (q && setValue) {
20
+ layout.value = q;
21
+ }
22
+ if (!q && focus) {
23
+ layout.focus();
24
+ }
25
+ }
26
+ if (q) {
27
+ this.query(trimObj({ q, qs }));
28
+ }
29
+ }
@@ -8,3 +8,4 @@ export * from './filters.js';
8
8
  export * from './pagination.js';
9
9
  export * from './options.js';
10
10
  export * from './configurable.js';
11
+ export * from './auto-query.js';
@@ -68,6 +68,36 @@ export function writeMisoIdToMeta(data) {
68
68
  };
69
69
  }
70
70
 
71
+ export function writeMisoIdToSession(data) {
72
+ const { session, value } = data;
73
+ if (!session || !value) {
74
+ return;
75
+ }
76
+ const { miso_id } = value;
77
+ if (!miso_id) {
78
+ return;
79
+ }
80
+ session.meta.miso_id = miso_id;
81
+ }
82
+
83
+ export function writeMisoIdFromSession(data) {
84
+ const { session, value } = data;
85
+ if (!session || !value) {
86
+ return data;
87
+ }
88
+ const { meta: { miso_id } = {} } = session;
89
+ if (!miso_id) {
90
+ return data;
91
+ }
92
+ return {
93
+ ...data,
94
+ meta: {
95
+ ...data.meta,
96
+ miso_id,
97
+ },
98
+ };
99
+ }
100
+
71
101
  export function writeKeywordsToData(data) {
72
102
  const { request, value } = data;
73
103
  // we still want the value in loading status
@@ -230,6 +260,19 @@ export function writeAffiliationInfoToInteraction(payload, args) {
230
260
  });
231
261
  }
232
262
 
263
+ export function writeEventTargetToInteraction(payload, { event_target } = {}) {
264
+ if (!event_target) {
265
+ return payload;
266
+ }
267
+ return mergeInteraction(payload, {
268
+ context: {
269
+ custom_context: {
270
+ event_target,
271
+ },
272
+ },
273
+ });
274
+ }
275
+
233
276
  export function mergeInteraction(base = {}, patch = {}) {
234
277
  return trimObj({
235
278
  ...base,
@@ -4,7 +4,7 @@ import { fields } from '../actor/index.js';
4
4
  import { STATUS, ROLE, WORKFLOW_CONFIGURABLE } from '../constants.js';
5
5
  import { SearchBoxLayout, ListLayout, TextLayout, FacetsLayout, SelectLayout, MoreButtonLayout } from '../layout/index.js';
6
6
  import { mappingSortData, writeKeywordsToData, retainFacetCountsInData, writeExhaustionToData, concatItemsFromMoreResponse } from './processors.js';
7
- import { mergeRolesOptions, makeConfigurable, DEFAULT_TRACKER_OPTIONS } from './options/index.js';
7
+ import { mergeRolesOptions, autoQuery as autoQueryFn, makeConfigurable, DEFAULT_TRACKER_OPTIONS } from './options/index.js';
8
8
 
9
9
  const DEFAULT_ROWS = 10;
10
10
  const DEFAULT_PAGE_LIMIT = 10;
@@ -87,6 +87,10 @@ export default class SearchBasedWorkflow extends Workflow {
87
87
  }
88
88
 
89
89
  // query //
90
+ autoQuery(options = {}) {
91
+ autoQueryFn.call(this, options);
92
+ }
93
+
90
94
  query(args) {
91
95
  if (!args.q) {
92
96
  throw new Error(`q is required in query() call`);
@@ -57,23 +57,6 @@ export default class Search extends SearchBasedWorkflow {
57
57
  this._initAutocomplete(args);
58
58
  }
59
59
 
60
- autoQuery({ setValue = true, focus = true, param = 'q' } = {}) {
61
- // TODO: align implementation with AnswerBasedWorkflow
62
- const q = new URLSearchParams(window.location.search).get(param);
63
- const { layout } = this._views.get(ROLE.QUERY);
64
- if (layout) {
65
- if (q && setValue) {
66
- layout.value = q;
67
- }
68
- if (!q && focus) {
69
- layout.focus();
70
- }
71
- }
72
- if (q) {
73
- this.query({ q });
74
- }
75
- }
76
-
77
60
  notifyViewUpdate(role = ROLE.PRODUCTS, ...args) {
78
61
  super.notifyViewUpdate(role, ...args);
79
62
  return this;
@@ -30,11 +30,11 @@ export class UseLinkMixin {
30
30
  }
31
31
  }
32
32
 
33
- _getSubmitUrl(args) {
33
+ _getSubmitUrl({ q, ...args } = {}) {
34
34
  if (!this._linkFn) {
35
35
  throw new Error('useLink(fn) must be called before _getSubmitUrl()');
36
36
  }
37
- return this._linkFn[0](args.q);
37
+ return this._linkFn[0](q, args);
38
38
  }
39
39
 
40
40
  }