@subiz/widget-v4 4.1.210 → 4.1.211

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.
Files changed (124) hide show
  1. package/README.md +21 -2
  2. package/assets/less/chatbox.less +9 -0
  3. package/assets/less/form.less +10 -0
  4. package/assets/less/message.less +114 -13
  5. package/assets/less/widget.less +21 -16
  6. package/compiled_src/call/css.js +1 -1
  7. package/compiled_src/chatbox/Buttons.js +7 -4
  8. package/compiled_src/chatbox/ChatButton.js +115 -29
  9. package/compiled_src/chatbox/Event.js +37 -5
  10. package/compiled_src/chatbox/Events.js +348 -129
  11. package/compiled_src/chatbox/FormAttachment.js +1 -1
  12. package/compiled_src/chatbox/Header.js +6 -2
  13. package/compiled_src/chatbox/InfoMessage.js +1 -1
  14. package/compiled_src/chatbox/MessageInput.js +24 -9
  15. package/compiled_src/chatbox/OrderAttachments.js +13 -13
  16. package/compiled_src/chatbox/QuoteMessage.js +3 -2
  17. package/compiled_src/chatbox/TextMessage.js +22 -18
  18. package/compiled_src/chatbox/Widget.js +200 -129
  19. package/compiled_src/chatbox/block.js +39 -29
  20. package/compiled_src/chatbox/conversation-history.js +44 -0
  21. package/compiled_src/chatbox/event-layout.js +59 -0
  22. package/compiled_src/chatbox/greeting.js +32 -0
  23. package/compiled_src/common.js +13 -9
  24. package/compiled_src/form/css.js +1 -1
  25. package/compiled_src/lib.js +22 -32
  26. package/compiled_src/notif/css.js +1 -1
  27. package/compiled_src/plugin.js +11 -18
  28. package/compiled_src/popup/css.js +1 -1
  29. package/compiled_src/popup/popup.js +1 -1
  30. package/compiled_src/store/api.js +0 -2
  31. package/compiled_src/store/api_fake.js +1 -1
  32. package/compiled_src/store/attachments.js +28 -0
  33. package/compiled_src/store/chain.js +24 -10
  34. package/compiled_src/store/kv.js +9 -13
  35. package/compiled_src/store/lsdb.js +1 -1
  36. package/compiled_src/store/store.js +356 -117
  37. package/compiled_src/util/common.js +48 -41
  38. package/compiled_src/util/event.js +15 -0
  39. package/compiled_src/util/pubsub.js +28 -13
  40. package/compiled_src/util/url.js +18 -0
  41. package/dist/.HASH +1 -0
  42. package/dist/127.js +13 -13
  43. package/dist/132.js +1 -1
  44. package/dist/1a0ddc53cbb.127.js +1 -0
  45. package/dist/{1a0d80f6e72.132.js → 1a0ddc53cbb.132.js} +1 -1
  46. package/dist/1a0ddc53cbb.278.js +1 -0
  47. package/dist/{1a0d80f6e72.339.js → 1a0ddc53cbb.339.js} +1 -1
  48. package/dist/1a0ddc53cbb.app.js +1 -0
  49. package/dist/{1a0d80f6e72.chat.html → 1a0ddc53cbb.chat.html} +1 -1
  50. package/dist/1a0ddc53cbb.chat.js +1 -0
  51. package/dist/{1a0d80f6e72.form.html → 1a0ddc53cbb.form.html} +1 -1
  52. package/dist/1a0ddc53cbb.form.js +1 -0
  53. package/dist/{1a0d80f6e72.index.html → 1a0ddc53cbb.index.html} +1 -1
  54. package/dist/{1a0d80f6e72.library.html → 1a0ddc53cbb.library.html} +1 -1
  55. package/dist/1a0ddc53cbb.library.js +1 -0
  56. package/dist/{1a0d80f6e72.preview.html → 1a0ddc53cbb.preview.html} +1 -1
  57. package/dist/1a0ddc53cbb.preview.js +1 -0
  58. package/dist/1a0ddc53cbb.webview.js +1 -0
  59. package/dist/screenshoot.js +1492 -616
  60. package/package.json +10 -3
  61. package/src/chatbox/Buttons.js +5 -4
  62. package/src/chatbox/ChatButton.js +120 -39
  63. package/src/chatbox/Event.js +38 -5
  64. package/src/chatbox/Events.js +306 -134
  65. package/src/chatbox/FormAttachment.js +1 -1
  66. package/src/chatbox/Header.js +4 -4
  67. package/src/chatbox/InfoMessage.js +1 -1
  68. package/src/chatbox/MessageInput.js +15 -10
  69. package/src/chatbox/OrderAttachments.js +13 -13
  70. package/src/chatbox/QuoteMessage.js +3 -2
  71. package/src/chatbox/TextMessage.js +21 -20
  72. package/src/chatbox/Widget.js +150 -150
  73. package/src/chatbox/block.js +29 -27
  74. package/src/chatbox/conversation-history.js +37 -0
  75. package/src/chatbox/event-layout.js +72 -0
  76. package/src/chatbox/greeting.js +13 -0
  77. package/src/common.js +15 -9
  78. package/src/lib.js +22 -35
  79. package/src/plugin.js +11 -13
  80. package/src/popup/popup.js +1 -1
  81. package/src/store/api.js +0 -2
  82. package/src/store/api_fake.js +1 -1
  83. package/src/store/attachments.js +21 -0
  84. package/src/store/chain.js +25 -12
  85. package/src/store/kv.js +10 -19
  86. package/src/store/lsdb.js +1 -1
  87. package/src/store/store.js +313 -130
  88. package/src/util/common.js +48 -43
  89. package/src/util/event.js +7 -0
  90. package/src/util/pubsub.js +27 -8
  91. package/src/util/url.js +14 -0
  92. package/test.cjs +59 -0
  93. package/assets/less/contact.less +0 -126
  94. package/compiled_src/util/common.test.js +0 -414
  95. package/compiled_src/util/event.test.js +0 -38
  96. package/compiled_src/util/extractor.test.js +0 -299
  97. package/compiled_src/util/extractor.test2.js +0 -61
  98. package/compiled_src/util/gate.test.js +0 -18
  99. package/compiled_src/util/pubsub.test.js +0 -35
  100. package/dist/1a0d80f6e72.127.js +0 -1
  101. package/dist/1a0d80f6e72.278.js +0 -1
  102. package/dist/1a0d80f6e72.74.js +0 -1
  103. package/dist/1a0d80f6e72.997.js +0 -1
  104. package/dist/1a0d80f6e72.app.js +0 -1
  105. package/dist/1a0d80f6e72.chat.js +0 -1
  106. package/dist/1a0d80f6e72.form.js +0 -1
  107. package/dist/1a0d80f6e72.library.js +0 -1
  108. package/dist/1a0d80f6e72.preview.js +0 -1
  109. package/dist/1a0d80f6e72.webview.js +0 -1
  110. package/dist/74.js +0 -376
  111. package/dist/997.js +0 -37
  112. package/dist/HASH +0 -1
  113. package/src/contact/contact.js +0 -400
  114. package/src/contact/css.js +0 -1
  115. package/src/util/common.test.js +0 -301
  116. package/src/util/event.test.js +0 -31
  117. package/src/util/extractor.test.js +0 -240
  118. package/src/util/extractor.test2.js +0 -56
  119. package/src/util/gate.test.js +0 -20
  120. package/src/util/pubsub.test.js +0 -37
  121. /package/dist/{1a0d80f6e72.185.js → 1a0ddc53cbb.185.js} +0 -0
  122. /package/dist/{1a0d80f6e72.27.js → 1a0ddc53cbb.27.js} +0 -0
  123. /package/dist/{1a0d80f6e72.459.js → 1a0ddc53cbb.459.js} +0 -0
  124. /package/dist/{1a0d80f6e72.490.js → 1a0ddc53cbb.490.js} +0 -0
@@ -11,6 +11,7 @@ var _AgentsAvatar = _interopRequireDefault(require("./AgentsAvatar.js"));
11
11
  var _Header = _interopRequireDefault(require("./Header.js"));
12
12
  var _MessageInput = _interopRequireDefault(require("./MessageInput.js"));
13
13
  var _Events = _interopRequireDefault(require("./Events.js"));
14
+ var _greeting = require("./greeting.js");
14
15
  var _Rating = _interopRequireDefault(require("./Rating.js"));
15
16
  var _ConnectionStatus = _interopRequireDefault(require("./ConnectionStatus.js"));
16
17
  var _ListField = _interopRequireDefault(require("./ListField.js"));
@@ -32,51 +33,12 @@ class Widget extends _common.Com {
32
33
  constructor(props) {
33
34
  super(props);
34
35
  _defineProperty(this, "msgInput", (0, _preact.createRef)());
35
- _defineProperty(this, "findMissingRTMessage", async started => {
36
- let messageSentDict = this.props.store.listAgentRealtimeMessageSent();
37
- let res1 = await this.props.store.sbzListConversations();
38
- let conversions = _index.default.get(res1, 'body.conversations') || [];
39
- let cids = _index.default.map(conversions, convo => convo.id);
40
- let promises = _index.default.map(cids, cid => this.props.store.sbzListMessages(cid));
41
- let msgReses = await Promise.all(promises);
42
- _index.default.map(msgReses, msgRes => {
43
- let messages = _index.default.get(msgRes, 'body') || [];
44
- _index.default.map(messages, ev => {
45
- if (ev.created < started) return;
46
- if (ev.type !== 'message_sent') return;
47
- if (_index.default.get(ev, 'by.type') === 'user') return;
48
- console.log('check ev pass condition', ev);
49
- if (!messageSentDict[ev.id] && !this.debugAgentSentEvIds[ev.id]) {
50
- let accid = _index.default.get(this.props.store.matchAccount(), 'id');
51
- this.props.store.sendBugReport(`[WIDGET ${accid}] Missing agent message sent evid ${ev.id}, cid ${_index.default.convoOfEv(ev)}, ev_created ${ev.created}, FE timestamp ${Date.now()}, Init Debug timestamp ${started}`);
52
- this.debugAgentSentEvIds[ev.id] = true;
53
- }
54
- });
55
- });
56
- });
57
- _defineProperty(this, "debugRTAgentMessageSent", async () => {
58
- try {
59
- let debugAccountIds = this.props.store.matchDebugAccountIds();
60
- let accid = _index.default.get(this.props.store.matchAccount(), 'id');
61
- if (!debugAccountIds.includes(accid)) return;
62
- console.log('START check RT agent message with BE source');
63
- let started = Date.now();
64
- this.findMissingRTMessage(started);
65
- for (let i = 0; i < 100_000; i++) {
66
- await _index.default.sleep(120_000);
67
- console.log('LOOP check RT agent message with BE source', i);
68
- this.findMissingRTMessage(started);
69
- }
70
- } catch (err) {
71
- this.props.store.sendBugReport(`debugRTAgentMessageSent ERROR ${err.stack}`);
72
- }
73
- });
74
36
  _defineProperty(this, "formMounted", form => {
75
37
  let formData = this.fillForm(form);
76
38
  if (form.question_required || this.state.quickReplyMessage.text) {
77
39
  formData['question'] = {
78
40
  key: 'question',
79
- is_required: true,
41
+ is_required: false,
80
42
  label: this.props.t.i18n(_index.default.get(form, `i18n_question_label`, {})) || form.question_label,
81
43
  type: 'text',
82
44
  value: this.state.quickReplyMessage.text
@@ -87,14 +49,118 @@ class Widget extends _common.Com {
87
49
  });
88
50
  if (!this.props.store.isMobile() && !form._updated) this.focusInput();
89
51
  });
90
- _defineProperty(this, "sendMessage", async message => {
91
- let hasPostback = _index.default.find(message.attachments, att => att.type === 'postback');
92
- if (this.isFormRequired() && hasPostback) {
93
- this.onStartConvoClick();
94
- this.fillQuickReplyToQuestion(message);
52
+ _defineProperty(this, "sendMessage", async (message, {
53
+ formSubmitted = false,
54
+ groupId
55
+ } = {}) => {
56
+ if (this._sendingNewConversation) return false;
57
+ if (_index.default.get(this.props.store.matchConvo(this.props.cid), 'state') === 'ended') {
58
+ const form = this.startingConversationForm();
59
+ if (form && (_index.default.size(form.groups) || form.group_required || this.isFormRequired(''))) {
60
+ this.setState({
61
+ restartForm: true,
62
+ quickReplyMessage: message
63
+ });
64
+ return;
65
+ }
66
+ this._sendingNewConversation = true;
67
+ this.setState({
68
+ creating: true,
69
+ creatingError: null
70
+ });
71
+ try {
72
+ const {
73
+ convo,
74
+ error
75
+ } = await this.props.store.startConvo({
76
+ locale: this.props.t.locale(),
77
+ message: message.text,
78
+ initial_by: this.props.store.matchUser().id
79
+ });
80
+ if (error) {
81
+ this.setState({
82
+ creatingError: error
83
+ });
84
+ return false;
85
+ }
86
+ await this.props.store.sendConvoEvent(convo.id, 'message_sent', {
87
+ message
88
+ });
89
+ this.props.onGo({
90
+ view: 'chat',
91
+ cid: convo.id
92
+ });
93
+ } finally {
94
+ this._sendingNewConversation = false;
95
+ this.setState({
96
+ creating: false
97
+ });
98
+ }
99
+ return;
100
+ }
101
+ const form = this.startingConversationForm();
102
+ const needsGroup = form && !this.hasSentMessage(this.props.cid) && (_index.default.size(form.groups) || form.group_required);
103
+ if (!formSubmitted && (this.isFormRequired() || needsGroup)) {
104
+ this.setState({
105
+ clickedJoin: true,
106
+ quickReplyMessage: message
107
+ });
95
108
  return;
96
109
  }
97
110
  let text = message.text;
111
+ const localConvo = this.props.store.matchConvo(this.props.cid);
112
+ if (localConvo && localConvo.light_type === 'widget') {
113
+ this._sendingNewConversation = true;
114
+ this.setState({
115
+ creating: true,
116
+ creatingError: null
117
+ });
118
+ try {
119
+ const {
120
+ convo,
121
+ error
122
+ } = await this.props.store.startConvo({
123
+ locale: localConvo.locale,
124
+ message: text,
125
+ group_id: groupId,
126
+ initial_by: this.props.store.matchUser().id,
127
+ greeting_message: localConvo.events[0]
128
+ });
129
+ if (error || !convo) {
130
+ this.setState({
131
+ creatingError: error || 'start_failed'
132
+ });
133
+ return false;
134
+ }
135
+ this.props.store.promoteWidgetConvo(localConvo.id, convo.id);
136
+ const sending = this.props.store.sendConvoEvent(convo.id, 'message_sent', {
137
+ message
138
+ });
139
+ this.props.onGo({
140
+ view: 'chat',
141
+ cid: convo.id
142
+ });
143
+ await sending;
144
+ this.userSent[convo.id] = true;
145
+ if (formSubmitted && form && form.end_conversation_after_submit) await this.props.store.endConvo(convo.id);
146
+ this.setState({
147
+ clickedJoin: false,
148
+ restartForm: false,
149
+ quickReplyMessage: {}
150
+ });
151
+ } catch (error) {
152
+ this.setState({
153
+ creatingError: error
154
+ });
155
+ return false;
156
+ } finally {
157
+ this._sendingNewConversation = false;
158
+ this.setState({
159
+ creating: false
160
+ });
161
+ }
162
+ return;
163
+ }
98
164
  if (this.props.cid && this.props.cid.startsWith('cslight')) {
99
165
  let cid = this.props.cid;
100
166
  this._creating = (this._creating || 0) + 1;
@@ -165,14 +231,15 @@ class Widget extends _common.Com {
165
231
  });
166
232
  return;
167
233
  }
168
- await this.props.store.sendConvoEvent(convo.id, 'message_sent', {
234
+ const sending = this.props.store.sendConvoEvent(convo.id, 'message_sent', {
169
235
  message
170
236
  });
171
- if (_creating != this._creating) return;
172
237
  this.props.onGo({
173
238
  view: 'chat',
174
239
  cid: convo.id
175
240
  });
241
+ await sending;
242
+ if (_creating != this._creating) return;
176
243
  this.setState({
177
244
  creating: false
178
245
  });
@@ -212,7 +279,6 @@ class Widget extends _common.Com {
212
279
  this.focusInput();
213
280
  });
214
281
  this.userSent = {};
215
- this.debugAgentSentEvIds = {};
216
282
  this.wasFormInputed = false;
217
283
  this.setState({
218
284
  creating: false,
@@ -228,7 +294,6 @@ class Widget extends _common.Com {
228
294
  componentDidMount() {
229
295
  if (!this.props.store.isMobile()) this.focusInput();
230
296
  this.updateFormDataFields();
231
- this.debugRTAgentMessageSent();
232
297
  }
233
298
  applyMessage(text) {
234
299
  this.focusInput(text);
@@ -243,6 +308,7 @@ class Widget extends _common.Com {
243
308
  this.closeOrder();
244
309
  this.setState({
245
310
  clickedJoin: false,
311
+ restartForm: false,
246
312
  quickReplyMessage: {}
247
313
  });
248
314
  _index.default.setTimeout(() => {
@@ -252,19 +318,14 @@ class Widget extends _common.Com {
252
318
  this.updateFormDataFields();
253
319
  }
254
320
  }
321
+ componentWillUnmount() {
322
+ this.cancelContainerFocus();
323
+ }
255
324
  ShowTyping() {
256
325
  this.$events && this.$events.ShowTyping();
257
326
  }
258
327
  shouldShowForm() {
259
- let form;
260
- let accountSetting = this.props.plugin.chatbox || {};
261
- let acc = this.props.store.matchAccount() || {};
262
- if (_index.default.get(accountSetting, 'prechat_form.enabled')) form = accountSetting.prechat_form;
263
- let offlineEnabled = _index.default.get(accountSetting, 'offline_form.enabled');
264
- if (this.isOffline(acc, accountSetting) && offlineEnabled) form = accountSetting.offline_form;
265
- if (!this.hasSentMessage(this.props.cid) && form && (_index.default.size(form.groups) || form.group_required)) return true;
266
- let formRequired = this.isFormRequired();
267
- return formRequired && !this.props.cid || formRequired && this.props.cid && this.state.clickedJoin;
328
+ return !!(this.state.restartForm || this.state.clickedJoin);
268
329
  }
269
330
  render() {
270
331
  let showform = this.shouldShowForm();
@@ -328,42 +389,22 @@ class Widget extends _common.Com {
328
389
  _index.default.map(formData, (field, key) => {
329
390
  field = Object.assign({}, field);
330
391
  field.is_empty_error = false;
331
- if (field.is_required && (field.value === undefined || field.value === '' || _index.default.size(field.value) === 0 && Array.isArray(field.value))) {
392
+ if (field.is_required && (field.value == null || typeof field.value === 'string' && !field.value.trim() || _index.default.size(field.value) === 0 && Array.isArray(field.value))) {
332
393
  field.is_empty_error = true;
333
394
  field.error = this.props.t('error_empty');
334
395
  } else field.error = '';
335
- if (key.substr(5) === 'emails') {
336
- if (field.is_required) {
337
- if (!_index.default.isEmail(field.value)) {
338
- field.error = this.props.t('error_email_invalid');
339
- }
340
- } else {
341
- let value = field.value || '';
342
- if (typeof value !== 'string') value = value + '';
343
- if (value.trim() && !_index.default.isEmail(field.value)) {
344
- field.error = this.props.t('error_email_invalid');
345
- }
346
- }
396
+ if (key === 'attr:emails' && !field.error && String(field.value ?? '').trim() && !_index.default.isEmail(field.value)) {
397
+ field.error = this.props.t('error_email_invalid');
347
398
  }
348
- if (key.substr(5) === 'phones') {
349
- if (field.is_required) {
350
- if (!_index.default.isPhone(field.value)) {
351
- field.error = this.props.t('error_phone_invalid');
352
- }
353
- } else {
354
- let value = field.value || '';
355
- if (typeof value !== 'string') value = value + '';
356
- if (value.trim() && !_index.default.isPhone(field.value)) {
357
- field.error = this.props.t('error_phone_invalid');
358
- }
359
- }
399
+ if (key === 'attr:phones' && !field.error && String(field.value ?? '').trim() && !_index.default.isPhone(field.value)) {
400
+ field.error = this.props.t('error_phone_invalid');
360
401
  }
361
402
  newformdata[key] = field;
362
403
  });
363
404
  return newformdata;
364
405
  }
365
406
  async submitPrechatForm(form, formName) {
366
- if (this.state.submiting) return;
407
+ if (this.state.submiting || this.state.creating) return;
367
408
  let newData = this.validateForm(this.state.formData);
368
409
  let hasError = _index.default.find(newData, field => !!field.error);
369
410
  if (hasError) {
@@ -378,6 +419,8 @@ class Widget extends _common.Com {
378
419
  let is_lead = false;
379
420
  _index.default.map(data, (field, key) => {
380
421
  if (!key.startsWith('attr:')) return;
422
+ if (key === 'attr:phones') field.value = String(field.value ?? '').trim().replace(/[(). \-]/g, '');
423
+ if (key === 'attr:emails') field.value = String(field.value ?? '').trim();
381
424
  var attr = {
382
425
  key: key.substr(5)
383
426
  };
@@ -392,7 +435,8 @@ class Widget extends _common.Com {
392
435
  key: 'lead_source',
393
436
  text: 'widget'
394
437
  });
395
- let firstmsg = _index.default.get(data, 'question.value', this.props.t('get_started'));
438
+ const question = String(_index.default.get(data, 'question.value', '') ?? '').trim();
439
+ const firstmsg = question || (/^vi(?:[-_]|$)/i.test(navigator.language || '') ? 'Xin chào' : 'Hello');
396
440
  let groupid = _index.default.get(data, 'group.value', '');
397
441
  this._creating = (this._creating || 0) + 1;
398
442
  let _creating = this._creating;
@@ -410,7 +454,7 @@ class Widget extends _common.Com {
410
454
  await _index.default.sleep(500);
411
455
  if (_creating != this._creating) return;
412
456
  if (this.props.cid && this.props.cid.startsWith('cslight')) {
413
- this.sendMessage({
457
+ await this.sendMessage({
414
458
  text: firstmsg,
415
459
  format: 'plaintext',
416
460
  fields: [{
@@ -418,10 +462,13 @@ class Widget extends _common.Com {
418
462
  value: _index.default.stringify(groupid)
419
463
  }],
420
464
  attachments: this.state.quickReplyMessage.attachments
465
+ }, {
466
+ formSubmitted: true,
467
+ groupId: groupid
421
468
  });
422
469
  return;
423
470
  }
424
- if (this.props.cid) {
471
+ if (this.props.cid && _index.default.get(this.props.store.matchConvo(this.props.cid), 'state') !== 'ended') {
425
472
  this.setState({
426
473
  clickedJoin: false
427
474
  });
@@ -447,7 +494,8 @@ class Widget extends _common.Com {
447
494
  }
448
495
  let message = {
449
496
  text: firstmsg,
450
- format: 'plaintext'
497
+ format: 'plaintext',
498
+ attachments: this.state.quickReplyMessage.attachments
451
499
  };
452
500
  let req = {
453
501
  group_id: groupid,
@@ -476,9 +524,12 @@ class Widget extends _common.Com {
476
524
  message
477
525
  });
478
526
  if (_creating != this._creating) return;
527
+ if (form.end_conversation_after_submit) await this.props.store.endConvo(newconvoid);
528
+ if (_creating != this._creating) return;
479
529
  this.setState({
480
530
  creating: false,
481
531
  clickedJoin: false,
532
+ restartForm: false,
482
533
  quickReplyMessage: {}
483
534
  });
484
535
  this.props.onGo({
@@ -509,6 +560,7 @@ class Widget extends _common.Com {
509
560
  if (!field) return;
510
561
  field.value = val;
511
562
  field.error = undefined;
563
+ field.is_empty_error = false;
512
564
  this.setState({
513
565
  formData
514
566
  });
@@ -629,7 +681,7 @@ class Widget extends _common.Com {
629
681
  renderFields(form, formName) {
630
682
  if (_index.default.size(this.state.formData) === 0) return null;
631
683
  let $fields = _index.default.map(this.state.formData, (field, key) => {
632
- if (this.state.quickReplyMessage.text && key === 'question') return null;
684
+ if (key === 'question' && _index.default.find(this.state.quickReplyMessage.attachments, att => att.type === 'postback')) return null;
633
685
  let $field = null;
634
686
  var errclass = field.error ? 'input--error' : '';
635
687
  if (field.type === 'text') {
@@ -662,7 +714,11 @@ class Widget extends _common.Com {
662
714
  this['form_' + encode(key)] = ref;
663
715
  },
664
716
  class: `form-control ${errclass}`,
665
- type: "text"
717
+ type: key === 'attr:phones' ? 'tel' : key === 'attr:emails' ? 'email' : 'text',
718
+ inputMode: key === 'attr:phones' ? 'tel' : key === 'attr:emails' ? 'email' : undefined,
719
+ autoComplete: key === 'attr:phones' ? 'tel' : key === 'attr:emails' ? 'email' : undefined,
720
+ "aria-invalid": field.error ? 'true' : undefined,
721
+ "aria-describedby": field.error ? 'form-error_' + encode(key) : undefined
666
722
  });
667
723
  }
668
724
  } else if (field.type === 'number') {
@@ -746,6 +802,8 @@ class Widget extends _common.Com {
746
802
  }, label, field.is_required ? (0, _preact.h)("span", {
747
803
  class: "red"
748
804
  }, "*") : ''), $field, field.error ? (0, _preact.h)("div", {
805
+ id: 'form-error_' + encode(key),
806
+ role: "alert",
749
807
  class: "form-control--valid-input--error"
750
808
  }, field.error) : '');
751
809
  });
@@ -758,9 +816,10 @@ class Widget extends _common.Com {
758
816
  if (!title) title = _index.default.get(form, 'title', this.props.t('capture_lead_title'));
759
817
  title = title.replace('↵', '');
760
818
  let btncls = 'btn--start-convo ';
761
- let hasError = _index.default.find(this.state.formData, field => !!field.error);
762
819
  let newData = this.validateForm(this.state.formData);
763
820
  let hasEmptyError = _index.default.find(newData, field => !!field.is_empty_error);
821
+ const phone = String(_index.default.get(this.state.formData, 'attr:phones.value', '') ?? '').trim();
822
+ const phoneTooShort = phone.length > 0 && phone.length < 5;
764
823
  let bodycls = 'widget-body';
765
824
  if (this.props.store.isEmbededChatMode()) bodycls += ' embed-chat';
766
825
  return (0, _preact.h)("div", {
@@ -780,12 +839,28 @@ class Widget extends _common.Com {
780
839
  }), (0, _preact.h)("button", {
781
840
  class: btncls,
782
841
  onClick: _ => this.submitPrechatForm(form, formName),
783
- disabled: !!hasEmptyError || !!hasError || this.state.creating
842
+ disabled: !!hasEmptyError || phoneTooShort || this.state.creating
784
843
  }, submitLabel)));
785
844
  }
786
845
  goToConvo(cid) {
846
+ let view = 'chat';
847
+ if (!cid) {
848
+ const greeting = (0, _greeting.widgetGreeting)(this.props.plugin.chatbox, this.props.t);
849
+ cid = this.props.store.startWidgetConvo(greeting, this.props.t.locale()).convo.id;
850
+ const form = this.startingConversationForm();
851
+ const setting = this.props.plugin.chatbox || {};
852
+ const offline = _index.default.get(setting, 'offline_form.enabled') && this.isOffline(this.props.store.matchAccount() || {}, setting);
853
+ if (form && (offline || this.isFormRequired(cid) || _index.default.size(form.groups) || form.group_required)) {
854
+ view = offline ? 'offline_form' : 'prechat_form';
855
+ this.setState({
856
+ clickedJoin: false,
857
+ restartForm: false,
858
+ quickReplyMessage: {}
859
+ });
860
+ }
861
+ }
787
862
  this.props.onGo({
788
- view: 'chat',
863
+ view,
789
864
  cid
790
865
  });
791
866
  }
@@ -859,7 +934,7 @@ class Widget extends _common.Com {
859
934
  _index.default.setTimeout(() => $input && $input.blur());
860
935
  _index.default.setTimeout(() => $input && $input.blur(), 300);
861
936
  }
862
- focusInput(text) {
937
+ focusInput(text, repeat = true) {
863
938
  if (this.props.store.isPreview) return;
864
939
  if (this.shouldShowForm()) {
865
940
  let focusfieldkey = '';
@@ -884,6 +959,7 @@ class Widget extends _common.Com {
884
959
  if (text != undefined && typeof text === 'string') {
885
960
  $input.applyMessage(text);
886
961
  }
962
+ if (!repeat) return $input.focus();
887
963
  _index.default.setTimeout(() => $input.focus());
888
964
  _index.default.setTimeout(() => $input.focus(), 300);
889
965
  }
@@ -920,21 +996,13 @@ class Widget extends _common.Com {
920
996
  onSend: this.sendMessage,
921
997
  t: this.props.t
922
998
  });
923
- let convo = this.props.store.matchConvo(this.props.cid) || {};
924
- if (this.isFormRequired() || convo.state === 'ended') {
925
- $input = (0, _preact.h)("div", {
926
- class: "message-input message-input--preform"
927
- }, (0, _preact.h)("button", {
928
- class: "btn--start-convo",
929
- onClick: e => this.onStartConvoClick(e)
930
- }, this.props.t('offline_preform_btn')));
931
- }
932
999
  let bodycls = 'widget-body';
933
1000
  if (this.props.store.isEmbededChatMode()) bodycls += ' embed-chat';
934
1001
  return (0, _preact.h)("div", {
935
1002
  class: bodycls
936
1003
  }, (0, _preact.h)("div", {
937
1004
  class: "widget-body--content",
1005
+ onPointerDown: () => this.cancelContainerFocus(),
938
1006
  onClick: e => this.onContainerClick(e)
939
1007
  }, (0, _preact.h)(_Events.default, {
940
1008
  isShow: this.props.isShow,
@@ -944,7 +1012,7 @@ class Widget extends _common.Com {
944
1012
  onFormSubmit: e => this.onEventFormSubmit(e),
945
1013
  ref: ref => this.$events = ref,
946
1014
  cid: this.props.cid,
947
- creating: this.creating,
1015
+ creating: this.state.creating,
948
1016
  onSend: e => this.sendMessage(e),
949
1017
  onPostback: e => this.createPostback(e),
950
1018
  t: this.props.t,
@@ -971,42 +1039,45 @@ class Widget extends _common.Com {
971
1039
  t: this.props.t
972
1040
  }), $input);
973
1041
  }
974
- async onStartConvoClick() {
975
- var convo = this.props.store.matchConvo(this.props.cid) || {};
976
- if (convo.state === 'ended') return this.props.onGo({
977
- view: 'chat',
978
- cid: ''
979
- });
980
- this.setState({
981
- clickedJoin: true,
982
- quickReplyMessage: {}
983
- });
984
- }
985
1042
  onEventFormSubmit() {
986
1043
  this.focusInput();
987
1044
  }
988
- onContainerClick() {
989
- var selection = window.getSelection();
990
- if (selection.toString().length > 0) return;
991
- if (!this.props.store.isMobile()) this.focusInput();
1045
+ cancelContainerFocus() {
1046
+ if (this.containerFocusTimer) _index.default.clearTimeout(this.containerFocusTimer);
1047
+ this.containerFocusTimer = null;
992
1048
  }
993
- isFormRequired() {
994
- let form;
995
- let accountSetting = this.props.plugin.chatbox || {};
996
- let acc = this.props.store.matchAccount() || {};
997
- if (_index.default.get(accountSetting, 'prechat_form.enabled')) form = accountSetting.prechat_form;
998
- let offlineEnabled = _index.default.get(accountSetting, 'offline_form.enabled');
999
- if (this.isOffline(acc, accountSetting) && offlineEnabled) form = accountSetting.offline_form;
1049
+ onContainerClick(event) {
1050
+ this.cancelContainerFocus();
1051
+ const target = event && event.target;
1052
+ const element = target && (target.nodeType === 1 ? target : target.parentElement);
1053
+ if (element && element.closest('a, button, input, textarea, select, [contenteditable]')) return;
1054
+ if (event && event.detail > 1) return;
1055
+ if (this.props.store.isMobile() || window.getSelection()?.toString()) return;
1056
+ const cid = this.props.cid;
1057
+ this.containerFocusTimer = _index.default.setTimeout(() => {
1058
+ this.containerFocusTimer = null;
1059
+ if (!this._isMounted || this.props.cid !== cid || this.props.isShow === false || window.getSelection()?.toString()) return;
1060
+ this.focusInput(undefined, false);
1061
+ }, 300);
1062
+ }
1063
+ startingConversationForm() {
1064
+ const setting = this.props.plugin.chatbox || {};
1065
+ const account = this.props.store.matchAccount() || {};
1066
+ if (this.isOffline(account, setting) && _index.default.get(setting, 'offline_form.enabled')) return setting.offline_form;
1067
+ if (_index.default.get(setting, 'prechat_form.enabled')) return setting.prechat_form;
1068
+ }
1069
+ isFormRequired(cid = this.props.cid) {
1070
+ let form = this.startingConversationForm();
1000
1071
  if (!form) return false;
1001
1072
  let formData = this.fillForm(form);
1002
- let convo = this.props.store.matchConvo(this.props.cid) || {};
1073
+ let convo = this.props.store.matchConvo(cid) || {};
1003
1074
  if (convo.state === 'ended') return true;
1004
1075
  if (convo.state && convo.state !== 'pending') delete formData.group;
1005
1076
  let isAllFiledEmpty = true;
1006
1077
  _index.default.each(formData, field => {
1007
1078
  if (field.value) isAllFiledEmpty = false;
1008
1079
  });
1009
- if (isAllFiledEmpty && !this.userHasSent(this.props.cid)) return true;
1080
+ if (isAllFiledEmpty && !this.userHasSent(cid)) return true;
1010
1081
  return !!_index.default.find(formData, field => field.is_required && (field.value == null || field.value === ''));
1011
1082
  }
1012
1083
  renderConvoSummary(convo) {
@@ -1151,7 +1222,7 @@ class Widget extends _common.Com {
1151
1222
  if (form.question_required) {
1152
1223
  data['question'] = {
1153
1224
  key: 'question',
1154
- is_required: true,
1225
+ is_required: false,
1155
1226
  label: this.props.t.i18n(_index.default.get(form, `i18n_question_label`, {})) || form.question_label,
1156
1227
  type: 'text'
1157
1228
  };