@subiz/widget-v4 4.0.88 → 4.0.90

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 (100) hide show
  1. package/assets/less/chatbox.less +2 -0
  2. package/compiled_src/chatbox/Event.js +4 -5
  3. package/compiled_src/chatbox/TextMessage.js +104 -9
  4. package/compiled_src/chatbox/Widget.js +58 -8
  5. package/compiled_src/util/common.js +1 -1
  6. package/package.json +8 -8
  7. package/src/chatbox/Event.js +3 -4
  8. package/src/chatbox/TextMessage.js +82 -15
  9. package/src/chatbox/Widget.js +54 -4
  10. package/src/util/common.js +1 -1
  11. package/dist/132.js +0 -6950
  12. package/dist/178.js +0 -406
  13. package/dist/182ee177a49.132.js +0 -1
  14. package/dist/182ee177a49.178.js +0 -1
  15. package/dist/182ee177a49.213.js +0 -1
  16. package/dist/182ee177a49.335.js +0 -1
  17. package/dist/182ee177a49.362.js +0 -1
  18. package/dist/182ee177a49.441.js +0 -1
  19. package/dist/182ee177a49.605.js +0 -1
  20. package/dist/182ee177a49.678.js +0 -1
  21. package/dist/182ee177a49.782.js +0 -1
  22. package/dist/182ee177a49.app.js +0 -1
  23. package/dist/182ee177a49.index.html +0 -26
  24. package/dist/182ee177a49.library.html +0 -26
  25. package/dist/182ee177a49.library.js +0 -1
  26. package/dist/182ee177a49.preview.html +0 -759
  27. package/dist/182ee177a49.preview.js +0 -1
  28. package/dist/182ee177a49.screenshoot.js +0 -1
  29. package/dist/213.js +0 -3215
  30. package/dist/335.js +0 -411
  31. package/dist/362.js +0 -107
  32. package/dist/441.js +0 -2827
  33. package/dist/605.js +0 -328
  34. package/dist/678.js +0 -107
  35. package/dist/782.js +0 -107
  36. package/dist/HASH +0 -1
  37. package/dist/assets/img/Facebook.64607ec.png +0 -0
  38. package/dist/assets/img/Zalo.6845707.png +0 -0
  39. package/dist/assets/img/award1a.1a58e10.png +0 -0
  40. package/dist/assets/img/award3a.3b74423.png +0 -0
  41. package/dist/assets/img/award4a.ed6ccd4.png +0 -0
  42. package/dist/assets/img/award5.39eb7ae.png +0 -0
  43. package/dist/assets/img/bubble_default.7d5e4ab.svg +0 -20
  44. package/dist/assets/img/caret.998eb8f.svg +0 -1
  45. package/dist/assets/img/convos_empty.2353007.svg +0 -72
  46. package/dist/assets/img/default.06e6ee0.jpg +0 -0
  47. package/dist/assets/img/default_avatar.5b74dc1.png +0 -0
  48. package/dist/assets/img/done_gray.d7e8203.svg +0 -4
  49. package/dist/assets/img/done_green.b817fd4.svg +0 -4
  50. package/dist/assets/img/emoji/messenger/sub_23.529_58.824.d5faf14.png +0 -0
  51. package/dist/assets/img/emoji/messenger/sub_23.529_60.784.1420241.png +0 -0
  52. package/dist/assets/img/emoji/messenger/sub_25.490_0.000.1e6b2c0.png +0 -0
  53. package/dist/assets/img/emoji/messenger/sub_25.490_45.098.3dfbc08.png +0 -0
  54. package/dist/assets/img/emoji/messenger/sub_25.490_5.882.4b49e94.png +0 -0
  55. package/dist/assets/img/emoji/messenger/sub_27.451_72.549.7114f58.png +0 -0
  56. package/dist/assets/img/emoji/messenger/sub_27.451_84.314.231e2b7.png +0 -0
  57. package/dist/assets/img/emoji/messenger/sub_27.451_96.078.ddbb008.png +0 -0
  58. package/dist/assets/img/emoji/messenger/sub_29.412_5.882.61f3359.png +0 -0
  59. package/dist/assets/img/emoji/messenger/sub_58.824_100.000.6df72bc.png +0 -0
  60. package/dist/assets/img/emoji/messenger/sub_58.824_45.098.3d7a7ad.png +0 -0
  61. package/dist/assets/img/emoji/messenger/sub_58.824_47.059.0f4d6e2.png +0 -0
  62. package/dist/assets/img/emoji/messenger/sub_58.824_54.902.65724c1.png +0 -0
  63. package/dist/assets/img/emoji/messenger/sub_58.824_64.706.9da3cba.png +0 -0
  64. package/dist/assets/img/emoji/messenger/sub_58.824_72.549.edca5cf.png +0 -0
  65. package/dist/assets/img/emoji/messenger/sub_58.824_78.431.f910f2a.png +0 -0
  66. package/dist/assets/img/emoji/messenger/sub_58.824_88.235.def4e71.png +0 -0
  67. package/dist/assets/img/emoji/messenger/sub_60.784_11.765.49c03d0.png +0 -0
  68. package/dist/assets/img/emoji/messenger/sub_60.784_27.451.46b54e5.png +0 -0
  69. package/dist/assets/img/emoji/messenger/sub_60.784_29.412.e36d284.png +0 -0
  70. package/dist/assets/img/emoji/messenger/sub_60.784_3.922.bb191b0.png +0 -0
  71. package/dist/assets/img/emoji/messenger/sub_60.784_35.294.af7b927.png +0 -0
  72. package/dist/assets/img/emoji/messenger/sub_60.784_7.843.98f2a99.png +0 -0
  73. package/dist/assets/img/empty.29bed2d.svg +0 -23
  74. package/dist/assets/img/empty_1.0b98af0.svg +0 -11
  75. package/dist/assets/img/examplte_logo.f86535f.svg +0 -1
  76. package/dist/assets/img/external-link.90577cc.svg +0 -1
  77. package/dist/assets/img/fb_inverse_circle.36c2cfc.png +0 -0
  78. package/dist/assets/img/ghn.070f8e3.svg +0 -17
  79. package/dist/assets/img/ghtk.326eac1.svg +0 -18
  80. package/dist/assets/img/image_default.be0698b.png +0 -0
  81. package/dist/assets/img/info.ebce7ea.svg +0 -1
  82. package/dist/assets/img/loader.37000a7.svg +0 -1
  83. package/dist/assets/img/loading_static.8f403ae.svg +0 -10
  84. package/dist/assets/img/main-service1.d536cc2.png +0 -0
  85. package/dist/assets/img/main-service2.9b99549.png +0 -0
  86. package/dist/assets/img/not_found_search.2ddb8d1.png +0 -0
  87. package/dist/assets/img/package.8d37d0f.svg +0 -5
  88. package/dist/assets/img/package_fade.763c001.svg +0 -1
  89. package/dist/assets/img/rating--average.1ea492c.svg +0 -12
  90. package/dist/assets/img/rating--great.e3d712e.svg +0 -16
  91. package/dist/assets/img/rating--poor.2938a25.svg +0 -12
  92. package/dist/assets/img/search.f9705ff.svg +0 -1
  93. package/dist/assets/img/send.6db52a9.svg +0 -3
  94. package/dist/assets/img/shop.f7dc7d8.svg +0 -1
  95. package/dist/assets/img/shop_faded.df7845a.svg +0 -1
  96. package/dist/assets/img/subiz-mono.877e9f5.svg +0 -3
  97. package/dist/assets/img/x.26fdfa3.svg +0 -5
  98. package/dist/malicious-app.js +0 -13
  99. package/dist/screenshoot.html +0 -7
  100. package/dist/screenshoot.js +0 -5953
@@ -97,6 +97,8 @@ body div#sbz92 {
97
97
  }
98
98
 
99
99
  img {
100
+ overflow: hidden;
101
+ flex-shrink: 0;
100
102
  background-repeat: no-repeat;
101
103
  background-position: center;
102
104
  background-size: 100% 100%;
@@ -128,7 +128,7 @@ class Event extends _common.Com {
128
128
 
129
129
  var uri = acc.logo_url || require('../../assets/img/image_default.png');
130
130
 
131
- if (true || _index.default.get(this.props, 'chatbox.hide_agent_profile')) {
131
+ if (_index.default.get(this.props, 'chatbox.hide_agent_profile')) {
132
132
  return (0, _preact.h)("div", {
133
133
  class: "message-sender"
134
134
  }, (0, _preact.h)("img", {
@@ -142,7 +142,7 @@ class Event extends _common.Com {
142
142
 
143
143
  let agentM = this.props.store.matchAgent('*');
144
144
 
145
- if (byid !== 'system' || bytype !== 'dummy') {
145
+ if (byid !== 'system' && bytype !== 'dummy' && bytype !== 'workflow') {
146
146
  uri = _index.default.get(agentM[byid], 'avatar_url') || acc.logo_url || require('../../assets/img/default_avatar.png');
147
147
  }
148
148
 
@@ -330,11 +330,10 @@ class Event extends _common.Com {
330
330
 
331
331
  let byid = _index.default.get(ev, 'by.display_id', _index.default.get(ev, 'by.id'));
332
332
 
333
- if (byid === 'system' || bytype === 'dummy') return 'system';
334
- let user = this.props.store.matchUser();
333
+ if (bytype === 'user') return _index.default.getUserDisplayName(this.props.store.matchUser());
335
334
  let acc = this.props.store.matchAccount();
336
- if (bytype === 'user') return _index.default.getUserDisplayName(user);
337
335
  if (bytype === 'agent') return _index.default.getAgentDisplayName(acc, this.props.store.matchAgent(byid));
336
+ if (byid === 'system' || bytype === 'dummy' || bytype === 'workflow') return acc.name || 'system';
338
337
  return '';
339
338
  }
340
339
 
@@ -100,19 +100,86 @@ class TextMessage extends _common.Com {
100
100
  }
101
101
 
102
102
  renderDelta() {
103
- let deltas = _index.default.get(this.props.ev, 'data.message.quill_delta');
103
+ let deltas = _index.default.get(this.props.ev, 'data.message.quill_delta') || _index.default.get(this.props.ev, 'data.message.text');
104
104
 
105
- deltas = _index.default.parseJSON(deltas) || {};
106
- deltas = deltas.ops || [];
105
+ let quilldeltas = _index.default.parseJSON(deltas) || {};
106
+ quilldeltas = trimQuillDelta(quilldeltas);
107
+ if (!quilldeltas || _index.default.size(quilldeltas.ops) === 0) return null;
107
108
 
108
- let isMarkdown = _index.default.find(deltas, delta => _index.default.get(delta, 'attributes.bold') || _index.default.get(delta, 'attributes.italic') || _index.default.get(delta, 'attributes.underline') || _index.default.get(delta, 'insert.emoji'));
109
+ let $deltas = _index.default.map(quilldeltas.ops, delta => {
110
+ let html = '';
111
+ let insert = delta.insert;
112
+ let attributes = delta.attributes;
109
113
 
110
- if (isMarkdown) {
111
- var text = deltaToMarkdown(deltas);
112
- return this.renderMarkdown(text);
114
+ if (insert.dynamicField) {
115
+ return _index.default.get(insert, 'dynamicField.text', '');
116
+ }
117
+
118
+ if (insert.mention) {
119
+ if (insert.mention.id == '*' || insert.mention.id == 'all') {
120
+ return (0, _preact.h)("span", {
121
+ class: "mention"
122
+ }, "@", this.$t('all'));
123
+ }
124
+
125
+ let ag = store.matchAgent(insert.mention.id);
126
+
127
+ let name = _index.default.getAgentDisplayName(ag, insert.mention.fullname);
128
+
129
+ return (0, _preact.h)("span", {
130
+ class: "mention",
131
+ "data-agent-id": ag.id,
132
+ "data-agent-fullname": name
133
+ }, "@", name);
134
+ }
135
+
136
+ if (insert.emoji) return (0, _preact.h)(_Emoji.default, {
137
+ emojiCode: `:${insert.emoji}:`
138
+ });
139
+
140
+ if (typeof insert === 'string') {
141
+ let $tags = _index.default.extractText(insert).map((tag, i) => {
142
+ if (tag.type === 'link') {
143
+ return (0, _preact.h)("a", {
144
+ href: absLink(tag.text),
145
+ target: "_blank"
146
+ }, tag.text);
147
+ }
148
+
149
+ return tag.text;
150
+ });
151
+
152
+ if (attributes) {
153
+ if (attributes.italic && attributes.bold) return (0, _preact.h)("span", {
154
+ style: "font-weight: bold; font-style: italic"
155
+ }, $tags);
156
+ if (attributes.italic) return (0, _preact.h)("span", {
157
+ style: "font-style: italic"
158
+ }, $tags);
159
+ if (attributes.bold) return (0, _preact.h)("span", {
160
+ style: "font-weight: bold"
161
+ }, $tags);
162
+ }
163
+
164
+ return (0, _preact.h)("span", null, $tags);
165
+ }
166
+
167
+ return null;
168
+ });
169
+
170
+ let byType = _index.default.get(this.ev, 'by.type');
171
+
172
+ if (_index.default.size($deltas) === 0) return null;
173
+
174
+ if (byType === 'agent') {
175
+ return (0, _preact.h)("div", {
176
+ class: "message-agent message-text--agent"
177
+ }, $deltas);
113
178
  }
114
179
 
115
- return this.renderPlainText();
180
+ return (0, _preact.h)("div", {
181
+ class: "message-user message-text--user"
182
+ }, $deltas);
116
183
  }
117
184
 
118
185
  renderMarkdown(text) {
@@ -187,4 +254,32 @@ const emojiCodes = {
187
254
  tired: '😩',
188
255
  surprised: '😮',
189
256
  wink: '😉'
190
- };
257
+ };
258
+
259
+ function trimQuillDelta(delta) {
260
+ if (!delta) return delta;
261
+ if (!delta.ops || !delta.ops.length) return delta;
262
+ let last = delta.ops[delta.ops.length - 1];
263
+ if (typeof last.insert != 'string') return delta;
264
+ let insert = last.insert.trim();
265
+
266
+ if (insert) {
267
+ if (last.insert != insert) {
268
+ let newops = delta.ops.slice();
269
+ let last = newops.pop();
270
+ last.insert = insert;
271
+ newops.push(last);
272
+ return {
273
+ ops: newops
274
+ };
275
+ }
276
+
277
+ return delta;
278
+ }
279
+
280
+ let newops = delta.ops.slice();
281
+ newops.pop();
282
+ return trimQuillDelta({
283
+ ops: newops
284
+ });
285
+ }
@@ -54,6 +54,14 @@ class Widget extends _common.Com {
54
54
  _defineProperty(this, "msgInput", (0, _preact.createRef)());
55
55
 
56
56
  _defineProperty(this, "sendMessage", async message => {
57
+ let hasPostback = _index.default.find(message.attachments, att => att.type === 'postback');
58
+
59
+ if (this.isFormRequired() && hasPostback) {
60
+ this.onStartConvoClick();
61
+ this.fillQuickReplyToQuestion(message);
62
+ return;
63
+ }
64
+
57
65
  let text = message.text;
58
66
 
59
67
  if (!this.props.cid) {
@@ -115,7 +123,8 @@ class Widget extends _common.Com {
115
123
  clickedJoin: false,
116
124
  formData: {},
117
125
  showOrder: false,
118
- order_id: ''
126
+ order_id: '',
127
+ quickReplyMessage: {}
119
128
  });
120
129
  this.props.store.onUpdate(this, _ => this.forceUpdate());
121
130
  }
@@ -133,7 +142,8 @@ class Widget extends _common.Com {
133
142
  if (prevProp.cid !== this.props.cid) {
134
143
  this.closeOrder();
135
144
  this.setState({
136
- clickedJoin: false
145
+ clickedJoin: false,
146
+ quickReplyMessage: {}
137
147
  });
138
148
  setTimeout(() => {
139
149
  this.events && this.events.focus();
@@ -145,6 +155,15 @@ class Widget extends _common.Com {
145
155
  }
146
156
 
147
157
  shouldShowForm() {
158
+ let form;
159
+ let accountSetting = this.props.plugin.chatbox || {};
160
+ let acc = this.props.store.matchAccount() || {};
161
+ if (_index.default.get(accountSetting, 'prechat_form.enabled')) form = accountSetting.prechat_form;
162
+
163
+ let offlineEnabled = _index.default.get(accountSetting, 'offline_form.enabled');
164
+
165
+ if (this.isOffline(acc, accountSetting) && offlineEnabled) form = accountSetting.offline_form;
166
+ if (!this.hasSentMessage(this.props.cid) && form && (_index.default.size(form.groups) || form.group_required)) return true;
148
167
  let formRequired = this.isFormRequired();
149
168
  return formRequired && !this.props.cid || formRequired && this.props.cid && this.state.clickedJoin;
150
169
  }
@@ -260,9 +279,13 @@ class Widget extends _common.Com {
260
279
  fields: [{
261
280
  key: 'to_agent_group',
262
281
  value: _index.default.stringify(groupid)
263
- }]
282
+ }],
283
+ attachments: this.state.quickReplyMessage.attachments
264
284
  }
265
285
  });
286
+ this.setState({
287
+ quickReplyMessage: {}
288
+ });
266
289
  if (form.end_conversation_after_submit) await this.props.store.endConvo(this.props.cid);
267
290
  return;
268
291
  }
@@ -308,7 +331,8 @@ class Widget extends _common.Com {
308
331
  });
309
332
  this.setState({
310
333
  creating: false,
311
- clickedJoin: false
334
+ clickedJoin: false,
335
+ quickReplyMessage: {}
312
336
  });
313
337
  if (!this.props.store.isMobile()) this.focusInput();
314
338
  }
@@ -319,7 +343,8 @@ class Widget extends _common.Com {
319
343
  key: 'question',
320
344
  is_required: true,
321
345
  label: this.props.t.i18n(_index.default.get(form, `i18n_question_label`, {})) || form.question_label,
322
- type: 'text'
346
+ type: 'text',
347
+ value: this.state.quickReplyMessage.text
323
348
  };
324
349
  this.setState({
325
350
  formData
@@ -381,6 +406,7 @@ class Widget extends _common.Com {
381
406
  if (_index.default.size(this.state.formData) === 0) return null;
382
407
 
383
408
  let $fields = _index.default.map(this.state.formData, (field, key) => {
409
+ if (this.state.quickReplyMessage.text && key === 'question') return null;
384
410
  let $field = null;
385
411
  var errclass = field.error ? 'input--error' : '';
386
412
 
@@ -648,6 +674,19 @@ class Widget extends _common.Com {
648
674
  this.focusInput();
649
675
  }
650
676
 
677
+ fillQuickReplyToQuestion(message) {
678
+ let formData = this.state.formData;
679
+
680
+ if (formData) {
681
+ _index.default.set(formData, 'question.value', message.text);
682
+ }
683
+
684
+ this.setState({
685
+ formData,
686
+ quickReplyMessage: message
687
+ });
688
+ }
689
+
651
690
  createPostback(postback) {
652
691
  this.props.store.createPostback(this.props.cid, postback);
653
692
  }
@@ -718,7 +757,8 @@ class Widget extends _common.Com {
718
757
  cid: ''
719
758
  });
720
759
  this.setState({
721
- clickedJoin: true
760
+ clickedJoin: true,
761
+ quickReplyMessage: {}
722
762
  });
723
763
  }
724
764
 
@@ -745,9 +785,8 @@ class Widget extends _common.Com {
745
785
  let formData = this.fillForm(form);
746
786
  let convo = this.props.store.matchConvo(this.props.cid) || {};
747
787
  if (convo.state === 'ended') return true;
748
- if (this.userHasSent(this.props.cid)) return false;
749
788
  if (convo.state && convo.state !== 'pending') delete formData.group;
750
- return !!_index.default.find(formData, field => field.value == null || field.value === '');
789
+ return !!_index.default.find(formData, field => field.is_required && (field.value == null || field.value === ''));
751
790
  }
752
791
 
753
792
  renderConvoSummary(convo) {
@@ -868,6 +907,17 @@ class Widget extends _common.Com {
868
907
  return joined;
869
908
  }
870
909
 
910
+ hasSentMessage(cid) {
911
+ let convo = this.props.store.matchConvo(cid) || {};
912
+
913
+ let joined = _index.default.find(convo.members, mem => {
914
+ if (mem.type === 'bot') return true;
915
+ return mem.last_sent;
916
+ });
917
+
918
+ return joined;
919
+ }
920
+
871
921
  isOffline(acc, accSetting) {
872
922
  let enableWhen = _index.default.get(accSetting, 'offline_form.enable_when');
873
923
 
@@ -894,7 +894,7 @@ function checkRunConditions(freq, conds, lastrun, store) {
894
894
 
895
895
  case 'source':
896
896
  if (size(cond.sources) > 0) {
897
- let source = store.matchTrafficSource();
897
+ let source = store.matchTrafficSource() || 'direct';
898
898
  if (!find(cond.sources, src => norm(src) === norm(source))) return false;
899
899
  }
900
900
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@subiz/widget-v4",
3
- "version": "4.0.88",
3
+ "version": "4.0.90",
4
4
  "main": "./compiled_src/lib.js",
5
5
  "scripts": {
6
6
  "dev": "npx webpack serve --config webpack.dev.js",
@@ -14,28 +14,28 @@
14
14
  "@subiz/storage": "0.0.8",
15
15
  "@subiz/wsclient": "^3.1.7",
16
16
  "cookie_js": "1.4.2",
17
- "core-js": "^3.25.0",
17
+ "core-js": "^3.25.2",
18
18
  "css-loader": "5.2.7",
19
19
  "file-loader": "^6.2.0",
20
20
  "less": "^4.1.3",
21
21
  "less-loader": "^11.0.0",
22
22
  "marked": "^4.1.0",
23
- "preact": "^10.10.6"
23
+ "preact": "^10.11.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@babel/cli": "^7.18.10",
27
- "@babel/core": "^7.18.13",
27
+ "@babel/core": "^7.19.1",
28
28
  "@babel/plugin-proposal-class-properties": "^7.18.6",
29
- "@babel/plugin-transform-react-jsx": "^7.18.10",
30
- "@babel/preset-env": "^7.18.10",
29
+ "@babel/plugin-transform-react-jsx": "^7.19.0",
30
+ "@babel/preset-env": "^7.19.1",
31
31
  "babel-loader": "^8.2.5",
32
32
  "babel-plugin-syntax-jsx": "^6.18.0",
33
33
  "html-webpack-plugin": "^5.5.0",
34
34
  "prettier": "^2.7.1",
35
- "puppeteer": "^15.4.0",
35
+ "puppeteer": "^17.1.3",
36
36
  "webpack": "^5.74.0",
37
37
  "webpack-bundle-analyzer": "^4.6.1",
38
38
  "webpack-cli": "^4.10.0",
39
- "webpack-dev-server": "^4.10.1"
39
+ "webpack-dev-server": "^4.11.1"
40
40
  }
41
41
  }
@@ -101,7 +101,7 @@ export default class Event extends Com {
101
101
 
102
102
  let agentM = this.props.store.matchAgent('*')
103
103
  // should replace with subiz logo
104
- if (byid !== 'system' || bytype !== 'dummy') {
104
+ if (byid !== 'system' && bytype !== 'dummy' && bytype !== 'workflow') {
105
105
  uri = sb.get(agentM[byid], 'avatar_url') || acc.logo_url || require('../../assets/img/default_avatar.png')
106
106
  }
107
107
 
@@ -227,11 +227,10 @@ export default class Event extends Com {
227
227
  getSenderName(ev) {
228
228
  let bytype = sb.get(ev, 'by.type')
229
229
  let byid = sb.get(ev, 'by.display_id', sb.get(ev, 'by.id'))
230
- if (byid === 'system' || bytype === 'dummy') return 'system'
231
- let user = this.props.store.matchUser()
230
+ if (bytype === 'user') return sb.getUserDisplayName(this.props.store.matchUser())
232
231
  let acc = this.props.store.matchAccount()
233
- if (bytype === 'user') return sb.getUserDisplayName(user)
234
232
  if (bytype === 'agent') return sb.getAgentDisplayName(acc, this.props.store.matchAgent(byid))
233
+ if (byid === 'system' || bytype === 'dummy' || bytype === 'workflow') return acc.name || 'system'
235
234
  return ''
236
235
  }
237
236
  }
@@ -65,22 +65,61 @@ export default class TextMessage extends Com {
65
65
  }
66
66
 
67
67
  renderDelta() {
68
- let deltas = sb.get(this.props.ev, 'data.message.quill_delta')
69
- deltas = sb.parseJSON(deltas) || {}
70
- deltas = deltas.ops || []
71
- let isMarkdown = sb.find(
72
- deltas,
73
- (delta) =>
74
- sb.get(delta, 'attributes.bold') ||
75
- sb.get(delta, 'attributes.italic') ||
76
- sb.get(delta, 'attributes.underline') ||
77
- sb.get(delta, 'insert.emoji'),
78
- )
79
- if (isMarkdown) {
80
- var text = deltaToMarkdown(deltas)
81
- return this.renderMarkdown(text)
68
+ let deltas = sb.get(this.props.ev, 'data.message.quill_delta') || sb.get(this.props.ev, 'data.message.text')
69
+ let quilldeltas = sb.parseJSON(deltas) || {}
70
+ // deltas = deltas.ops || []
71
+ quilldeltas = trimQuillDelta(quilldeltas)
72
+ if (!quilldeltas || sb.size(quilldeltas.ops) === 0) return null
73
+ let $deltas = sb.map(quilldeltas.ops, (delta) => {
74
+ let html = ''
75
+ let insert = delta.insert
76
+ let attributes = delta.attributes
77
+
78
+ if (insert.dynamicField) {
79
+ return sb.get(insert, 'dynamicField.text', '')
80
+ }
81
+ if (insert.mention) {
82
+ if (insert.mention.id == '*' || insert.mention.id == 'all') {
83
+ return <span class='mention'>@{this.$t('all')}</span>
84
+ }
85
+
86
+ let ag = store.matchAgent(insert.mention.id)
87
+ let name = sb.getAgentDisplayName(ag, insert.mention.fullname)
88
+ return (
89
+ <span class='mention' data-agent-id={ag.id} data-agent-fullname={name}>
90
+ @{name}
91
+ </span>
92
+ )
93
+ }
94
+ if (insert.emoji) return <Emoji emojiCode={`:${insert.emoji}:`} />
95
+ if (typeof insert === 'string') {
96
+ let $tags = sb.extractText(insert).map((tag, i) => {
97
+ if (tag.type === 'link') {
98
+ return (
99
+ <a href={absLink(tag.text)} target='_blank'>
100
+ {tag.text}
101
+ </a>
102
+ )
103
+ }
104
+ return tag.text
105
+ })
106
+
107
+ if (attributes) {
108
+ if (attributes.italic && attributes.bold)
109
+ return <span style='font-weight: bold; font-style: italic'>{$tags}</span>
110
+ if (attributes.italic) return <span style='font-style: italic'>{$tags}</span>
111
+ if (attributes.bold) return <span style='font-weight: bold'>{$tags}</span>
112
+ }
113
+ return <span>{$tags}</span>
114
+ }
115
+ return null
116
+ })
117
+ let byType = sb.get(this.ev, 'by.type')
118
+ if (sb.size($deltas) === 0) return null
119
+ if (byType === 'agent') {
120
+ return <div class='message-agent message-text--agent'>{$deltas}</div>
82
121
  }
83
- return this.renderPlainText()
122
+ return <div class='message-user message-text--user'>{$deltas}</div>
84
123
  }
85
124
 
86
125
  renderMarkdown(text) {
@@ -154,3 +193,31 @@ const emojiCodes = {
154
193
  surprised: '😮',
155
194
  wink: '😉',
156
195
  }
196
+
197
+ function trimQuillDelta(delta) {
198
+ if (!delta) return delta
199
+ if (!delta.ops || !delta.ops.length) return delta
200
+ let last = delta.ops[delta.ops.length - 1]
201
+ if (typeof last.insert != 'string') return delta
202
+
203
+ let insert = last.insert.trim()
204
+ if (insert) {
205
+ if (last.insert != insert) {
206
+ // just fix the last item
207
+ let newops = delta.ops.slice()
208
+ // let last = sb.cloneDeep(newops.pop())
209
+ let last = newops.pop()
210
+ last.insert = insert
211
+ newops.push(last)
212
+ return {ops: newops}
213
+ }
214
+
215
+ // last item dont change, do nothing
216
+ return delta
217
+ }
218
+
219
+ // remove the last item
220
+ let newops = delta.ops.slice()
221
+ newops.pop()
222
+ return trimQuillDelta({ops: newops})
223
+ }
@@ -31,6 +31,8 @@ export default class Widget extends Com {
31
31
  formData: {},
32
32
  showOrder: false,
33
33
  order_id: '',
34
+ // hot fix for prechat with bot quick reply
35
+ quickReplyMessage: {},
34
36
  })
35
37
 
36
38
  this.props.store.onUpdate(this, (_) => this.forceUpdate())
@@ -49,7 +51,7 @@ export default class Widget extends Com {
49
51
 
50
52
  if (prevProp.cid !== this.props.cid) {
51
53
  this.closeOrder()
52
- this.setState({clickedJoin: false})
54
+ this.setState({clickedJoin: false, quickReplyMessage: {}})
53
55
  // this.props.store.setLastview('chat', { cid: this.props.cid })
54
56
  setTimeout(() => {
55
57
  // focus Events then focus MessageInput
@@ -64,6 +66,17 @@ export default class Widget extends Com {
64
66
  }
65
67
 
66
68
  shouldShowForm() {
69
+ // always show form when ask agentgroup
70
+ let form
71
+ let accountSetting = this.props.plugin.chatbox || {}
72
+ let acc = this.props.store.matchAccount() || {}
73
+ if (sb.get(accountSetting, 'prechat_form.enabled')) form = accountSetting.prechat_form
74
+
75
+ let offlineEnabled = sb.get(accountSetting, 'offline_form.enabled')
76
+ if (this.isOffline(acc, accountSetting) && offlineEnabled) form = accountSetting.offline_form
77
+
78
+ if (!this.hasSentMessage(this.props.cid) && form && (sb.size(form.groups) || form.group_required)) return true
79
+
67
80
  let formRequired = this.isFormRequired()
68
81
  return (formRequired && !this.props.cid) || (formRequired && this.props.cid && this.state.clickedJoin)
69
82
  }
@@ -175,9 +188,13 @@ export default class Widget extends Com {
175
188
  text: firstmsg,
176
189
  format: 'plaintext',
177
190
  fields: [{key: 'to_agent_group', value: sb.stringify(groupid)}],
191
+ attachments: this.state.quickReplyMessage.attachments,
178
192
  },
179
193
  })
180
194
 
195
+ // reset quickReplyMessage
196
+ this.setState({quickReplyMessage: {}})
197
+
181
198
  // end_conversation_after_submit only for offline form or prechat form
182
199
  if (form.end_conversation_after_submit) await this.props.store.endConvo(this.props.cid)
183
200
  return
@@ -202,6 +219,7 @@ export default class Widget extends Com {
202
219
  this.setState({
203
220
  creating: false,
204
221
  clickedJoin: false,
222
+ quickReplyMessage: {},
205
223
  })
206
224
  if (!this.props.store.isMobile()) this.focusInput()
207
225
  }
@@ -214,9 +232,11 @@ export default class Widget extends Com {
214
232
  is_required: true,
215
233
  label: this.props.t.i18n(sb.get(form, `i18n_question_label`, {})) || form.question_label,
216
234
  type: 'text',
235
+ value: this.state.quickReplyMessage.text,
217
236
  }
218
237
 
219
238
  this.setState({formData})
239
+
220
240
  if (!this.props.store.isMobile()) this.focusInput()
221
241
  }
222
242
 
@@ -261,6 +281,9 @@ export default class Widget extends Com {
261
281
  if (sb.size(this.state.formData) === 0) return null
262
282
 
263
283
  let $fields = sb.map(this.state.formData, (field, key) => {
284
+ // not display question when click bot quick reply
285
+ if (this.state.quickReplyMessage.text && key === 'question') return null
286
+
264
287
  let $field = null
265
288
  var errclass = field.error ? 'input--error' : ''
266
289
  if (field.type === 'text') {
@@ -525,7 +548,23 @@ export default class Widget extends Com {
525
548
  this.focusInput()
526
549
  }
527
550
 
551
+ fillQuickReplyToQuestion(message) {
552
+ let formData = this.state.formData
553
+ if (formData) {
554
+ sb.set(formData, 'question.value', message.text)
555
+ }
556
+ this.setState({formData, quickReplyMessage: message})
557
+ }
558
+
528
559
  sendMessage = async (message) => {
560
+ // hot fix click quick reply with prechat with display prechat form
561
+ let hasPostback = sb.find(message.attachments, (att) => att.type === 'postback')
562
+ if (this.isFormRequired() && hasPostback) {
563
+ this.onStartConvoClick()
564
+ this.fillQuickReplyToQuestion(message)
565
+ return
566
+ }
567
+
529
568
  let text = message.text
530
569
  if (!this.props.cid) {
531
570
  this.creating = true
@@ -635,7 +674,7 @@ export default class Widget extends Com {
635
674
  async onStartConvoClick() {
636
675
  var convo = this.props.store.matchConvo(this.props.cid) || {}
637
676
  if (convo.state === 'ended') return this.props.onGo({view: 'chat', cid: ''})
638
- this.setState({clickedJoin: true})
677
+ this.setState({clickedJoin: true, quickReplyMessage: {}})
639
678
  }
640
679
 
641
680
  onEventFormSubmit() {
@@ -675,9 +714,10 @@ export default class Widget extends Com {
675
714
  let convo = this.props.store.matchConvo(this.props.cid) || {}
676
715
 
677
716
  if (convo.state === 'ended') return true
678
- if (this.userHasSent(this.props.cid)) return false
717
+ // allow button "Tiep tuc chat" display when sent quick_replies msg
718
+ //if (this.userHasSent(this.props.cid)) return false
679
719
  if (convo.state && convo.state !== 'pending') delete formData.group
680
- return !!sb.find(formData, (field) => field.value == null || field.value === '')
720
+ return !!sb.find(formData, (field) => field.is_required && (field.value == null || field.value === ''))
681
721
  }
682
722
 
683
723
  renderConvoSummary(convo) {
@@ -778,6 +818,16 @@ export default class Widget extends Com {
778
818
  return joined
779
819
  }
780
820
 
821
+ hasSentMessage(cid) {
822
+ let convo = this.props.store.matchConvo(cid) || {}
823
+ let joined = sb.find(convo.members, (mem) => {
824
+ // sometime bot didnt update last_sent
825
+ if (mem.type === 'bot') return true
826
+ return mem.last_sent
827
+ })
828
+ return joined
829
+ }
830
+
781
831
  isOffline(acc, accSetting) {
782
832
  let enableWhen = sb.get(accSetting, 'offline_form.enable_when')
783
833
  let isonline = true
@@ -824,7 +824,7 @@ export function checkRunConditions(freq, conds, lastrun, store) {
824
824
  break
825
825
  case 'source':
826
826
  if (size(cond.sources) > 0) {
827
- let source = store.matchTrafficSource()
827
+ let source = store.matchTrafficSource() || 'direct'
828
828
  if (!find(cond.sources, (src) => norm(src) === norm(source))) return false
829
829
  }
830
830
  break