@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
package/README.md CHANGED
@@ -4,14 +4,33 @@
4
4
  $ npm run dev
5
5
  ```
6
6
 
7
- visit: http://localhost:8081/acqcsmrppbftadjzxnvo
7
+ visit: http://localhost:8082/?account_id=acqcsmrppbftadjzxnvo
8
8
 
9
9
  ### To test widget in injection mode
10
10
 
11
11
  ``` sh
12
12
  $ npm run dev
13
13
  ```
14
- visit: http://localhost:8081/inject.html?account_id=acqcsmrppbftadjzxnvo
14
+ visit: http://localhost:8082/inject.html?account_id=acqcsmrppbftadjzxnvo
15
+
16
+ ### Checks and release artifacts
17
+
18
+ Use Node.js 26 for development and tests.
19
+
20
+ ```sh
21
+ npm ci
22
+ npm run check
23
+ npm run build
24
+ ```
25
+
26
+ `check` runs the undefined-variable lint check and the tests against source
27
+ code in a DOM environment. Tests cover rendering, recalled messages, safe
28
+ URLs/HTML, upload failures, subscriptions, tickets and cache pagination.
29
+
30
+ `build` updates `compiled_src` before creating the browser bundles. `npm pack`
31
+ also runs the checks and regenerates the library through `prepack`, so the
32
+ published package includes the current source changes. Test files and the
33
+ test runner are excluded from the package.
15
34
 
16
35
 
17
36
  NOTE:
@@ -109,6 +109,15 @@ body div#sbz92 {
109
109
  outline: none;
110
110
  }
111
111
 
112
+ button:focus-visible {
113
+ outline: 2px solid @gray-600;
114
+ outline-offset: 3px;
115
+ }
116
+
117
+ button:not(:disabled) {
118
+ cursor: pointer;
119
+ }
120
+
112
121
  img {
113
122
  overflow: hidden;
114
123
  flex-shrink: 0;
@@ -43,6 +43,8 @@ input.form-control[disabled] {
43
43
  }
44
44
 
45
45
  input[type='text'].form-control.input--error,
46
+ input[type='tel'].form-control.input--error,
47
+ input[type='email'].form-control.input--error,
46
48
  input[type='number'].form-control.input--error,
47
49
  input[type='date'].form-control.input--error,
48
50
  textarea.form-control.input--error,
@@ -52,6 +54,8 @@ select.form-control.input--error {
52
54
  }
53
55
 
54
56
  input[type='text'].form-control,
57
+ input[type='tel'].form-control,
58
+ input[type='email'].form-control,
55
59
  input[type='number'].form-control,
56
60
  input[type='date'].form-control,
57
61
  textarea.form-control,
@@ -87,6 +91,8 @@ input[type='date'].form-control {
87
91
  }
88
92
 
89
93
  input[type='text'].form-control:focus,
94
+ input[type='tel'].form-control:focus,
95
+ input[type='email'].form-control:focus,
90
96
  input[type='number'].form-control:focus,
91
97
  input[type='date'].form-control:focus,
92
98
  textarea.form-control:focus,
@@ -95,6 +101,8 @@ select.form-control:focus {
95
101
  }
96
102
 
97
103
  input[type='text'].form-control.input--error:focus,
104
+ input[type='tel'].form-control.input--error:focus,
105
+ input[type='email'].form-control.input--error:focus,
98
106
  input[type='number'].form-control.input--error:focus,
99
107
  input[type='date'].form-control.input--error:focus,
100
108
  textarea.form-control.input--error:focus,
@@ -462,6 +470,8 @@ select {
462
470
  box-shadow: 0 0 0 1px #eeeeee;
463
471
 
464
472
  input[type='text'].form-control,
473
+ input[type='tel'].form-control,
474
+ input[type='email'].form-control,
465
475
  input[type='number'].form-control,
466
476
  input[type='date'].form-control,
467
477
  select.form-control,
@@ -1,3 +1,47 @@
1
+ .conversation-history-error {
2
+ display: flex;
3
+ align-items: center;
4
+ gap: 8px;
5
+ margin: 8px 0 16px;
6
+ padding: 8px 10px;
7
+ border-radius: 10px;
8
+ background: #f5f6f8;
9
+ color: #737782;
10
+ font-size: 12px;
11
+ line-height: 18px;
12
+ text-align: left;
13
+ }
14
+
15
+ .conversation-history-error__text {
16
+ flex: 1;
17
+ min-width: 0;
18
+ }
19
+
20
+ .conversation-history-error__retry {
21
+ position: relative;
22
+ flex-shrink: 0;
23
+ border: 0;
24
+ border-radius: 6px;
25
+ padding: 5px 8px;
26
+ background: transparent;
27
+ color: #545866;
28
+ font-size: 12px;
29
+ font-weight: 600;
30
+ line-height: 18px;
31
+ cursor: pointer;
32
+ &:hover:not(:disabled) { background: #e8eaf0; }
33
+ &:disabled { cursor: wait; }
34
+ &:focus-visible { outline: 2px solid #737782; outline-offset: 2px; }
35
+ }
36
+
37
+ .conversation-history-error__spinner {
38
+ position: absolute;
39
+ inset: 0;
40
+ display: flex;
41
+ align-items: center;
42
+ justify-content: center;
43
+ }
44
+
1
45
  .widget-body--events-container {
2
46
  overflow-x: hidden;
3
47
  overflow-y: auto;
@@ -127,7 +171,7 @@
127
171
  background-color: @red;
128
172
  }
129
173
 
130
- .message-text--agent.message-text--deleted {
174
+ .message-text--deleted {
131
175
  opacity: 0.7;
132
176
  font-style: italic;
133
177
  }
@@ -147,14 +191,28 @@
147
191
  }
148
192
 
149
193
  .message-block-pre {
194
+ display: block;
195
+ min-width: 0;
150
196
  max-width: 100%;
151
- margin: 0;
152
- overflow-x: auto;
153
- white-space: pre;
197
+ margin: 4px 0;
198
+ padding: 10px 12px;
199
+ border: 1px solid rgba(0, 0, 0, 0.06);
200
+ border-radius: 8px;
201
+ background: rgba(0, 0, 0, 0.03);
202
+ white-space: pre-wrap;
203
+ overflow-wrap: anywhere;
204
+ word-break: normal;
154
205
  }
155
206
 
156
- .message-block-code {
157
- font-family: monospace;
207
+ .message-block-code,
208
+ .message-block-code * {
209
+ font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
210
+ font-size: 13px;
211
+ line-height: 20px;
212
+ }
213
+
214
+ .message-block-pre .message-block-code {
215
+ display: block;
158
216
  }
159
217
 
160
218
  .message-user__container {
@@ -274,6 +332,54 @@ a.message_error {
274
332
  margin: 10px;
275
333
  }
276
334
 
335
+ .info-message--ended {
336
+ padding-top: 16px;
337
+ }
338
+
339
+ .conversation-event {
340
+ display: block;
341
+ min-width: 0;
342
+ border: 0;
343
+ padding: 0;
344
+ margin: 0;
345
+ }
346
+
347
+ .conversation-history-start {
348
+ display: block;
349
+ height: 1px;
350
+ flex-shrink: 0;
351
+ }
352
+
353
+ .message-user__container--send {
354
+ // Transitions can interpolate the !important styles used by the widget reset.
355
+ transition: transform 260ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
356
+ transform: translate3d(0, 0, 0);
357
+ }
358
+
359
+ .message-user__container--send.message-user__container--enter {
360
+ transform: translate3d(0, 14px, 0);
361
+ }
362
+
363
+ @media (prefers-reduced-motion: reduce) {
364
+ .message-user__container--send {
365
+ transition: none !important;
366
+ }
367
+ .message-user__container--send.message-user__container--enter {
368
+ transform: none !important;
369
+ }
370
+ }
371
+
372
+ .conversation-event--history {
373
+ filter: grayscale(0.8);
374
+ }
375
+
376
+ .conversation-event--history + .conversation-event:not(.conversation-event--history)::before {
377
+ content: '';
378
+ display: block;
379
+ border-top: 2px solid @primary-color-faded;
380
+ margin: 60px 40px;
381
+ }
382
+
277
383
  .message-sender {
278
384
  margin-top: 10px;
279
385
  }
@@ -528,20 +634,15 @@ a.message_error {
528
634
  object-fit: cover;
529
635
  }
530
636
 
531
- .url-button-attachment:first-child {
532
- border-radius: 0;
533
- }
534
- .url-button-attachment:last-child {
535
- }
536
-
537
637
  .url-button-attachment {
538
638
  padding: 10px;
539
639
  display: block;
540
640
  padding-top: 9px;
541
- padding-button: 9px;
641
+ padding-bottom: 9px;
542
642
  text-align: center;
543
643
  border: none;
544
644
  border-top: 1px solid #d8d8d8;
645
+ border-radius: 0;
545
646
  width: 100%;
546
647
  .color(@link);
547
648
  &:hover {
@@ -139,15 +139,6 @@
139
139
  text-align: center;
140
140
  }
141
141
 
142
- .widget-body--empty-icon {
143
- width: 128px;
144
- height: 128px;
145
- .inline-svg-code(~'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><g data-name="Layer 2"><path d="M130.9 366.9a9.9 9.9 0 0 0-2.9 7.1 10.33 10.33 0 0 0 2.9 7.1 9.73 9.73 0 0 0 7.1 2.9 10.33 10.33 0 0 0 7.1-2.9 9.73 9.73 0 0 0 2.9-7.1 10.52 10.52 0 0 0-2.9-7.1 9.9 9.9 0 0 0-7.1-2.9 10.33 10.33 0 0 0-7.1 2.9zM483.7 282a183.07 183.07 0 0 0 28.3-98C512 82.5 429.5 0 328 0a183.16 183.16 0 0 0-96 27 10 10 0 0 0 10.5 17.1A162.67 162.67 0 0 1 328 20c90.4 0 164 73.6 164 164a162.73 162.73 0 0 1-27.9 91.5 10.07 10.07 0 0 0 .3 11.6l11 40.9-44-11.8a10 10 0 0 0-8.4 1.5 164.32 164.32 0 0 1-151.8 20.2 138.68 138.68 0 0 0-97.1-97.1 164.31 164.31 0 0 1 14.5-143.2 10 10 0 0 0-17-10.6 184 184 0 0 0-19.9 149.6c-4.5-.4-9.1-.7-13.7-.7C61.9 236 0 297.9 0 374a136.88 136.88 0 0 0 22.9 76.1L12.2 490a10 10 0 0 0 12.3 12.3l40.9-11a137.71 137.71 0 0 0 146.5-.8 10 10 0 0 0-10.7-16.9 118.09 118.09 0 0 1-128.6-1.5 9.73 9.73 0 0 0-8.1-1.3L36 478.5l7.4-27.7a10 10 0 0 0-1.5-8.4 117.95 117.95 0 1 1 207-28 10 10 0 0 0 18.8 6.8 137.72 137.72 0 0 0 8.3-47.3 124.44 124.44 0 0 0-.7-13.7 183.93 183.93 0 0 0 155.3-23.6l56.2 15.1a10 10 0 0 0 12.3-12.3zm-246 162.2a10.52 10.52 0 0 0-7.1 2.9 9.73 9.73 0 0 0-2.9 7.1 10.33 10.33 0 0 0 2.9 7.1 9.73 9.73 0 0 0 7.1 2.9 10.52 10.52 0 0 0 7.1-2.9 9.73 9.73 0 0 0 2.9-7.1 9.9 9.9 0 0 0-10-10zM205.3 70.7a10.52 10.52 0 0 0 7.1-2.9 9.73 9.73 0 0 0 2.9-7.1 10.14 10.14 0 0 0-2.9-7.1 9.9 9.9 0 0 0-7.1-2.9 10.52 10.52 0 0 0-7.1 2.9 9.9 9.9 0 0 0-2.9 7.1 10.52 10.52 0 0 0 2.9 7.1 9.9 9.9 0 0 0 7.1 2.9zm-19.4 296.2a9.9 9.9 0 0 0-2.9 7.1 10.33 10.33 0 0 0 2.9 7.1 9.73 9.73 0 0 0 7.1 2.9 10.33 10.33 0 0 0 7.1-2.9 9.73 9.73 0 0 0 2.9-7.1 10.33 10.33 0 0 0-2.9-7.1 9.9 9.9 0 0 0-7.1-2.9 10.33 10.33 0 0 0-7.1 2.9zm210.8-201.1a9.8 9.8 0 0 0 5.2-1.5l1.3-.8a9.92 9.92 0 0 0 3.3-13.7 10.06 10.06 0 0 0-13.8-3.3l-1.3.8a10 10 0 0 0 5.3 18.5zm-68.1 107.4a58 58 0 0 0 57.9-57.9 7 7 0 0 0-7-7H277.7a7 7 0 0 0-7 7 58 58 0 0 0 57.9 57.9zm-43.5-50.9h87.1l-1 3.7a43.93 43.93 0 0 1-85.2 0zm-25.8-56.5a9.8 9.8 0 0 0 5.2-1.5l1.3-.8a9.92 9.92 0 0 0 3.3-13.7 10.06 10.06 0 0 0-13.8-3.3l-1.3.8a10 10 0 0 0 5.3 18.5zM75.9 366.9A9.9 9.9 0 0 0 73 374a10.33 10.33 0 0 0 2.9 7.1A9.73 9.73 0 0 0 83 384a10.33 10.33 0 0 0 7.1-2.9A9.73 9.73 0 0 0 93 374a10.33 10.33 0 0 0-2.9-7.1A9.9 9.9 0 0 0 83 364a10.33 10.33 0 0 0-7.1 2.9z" fill="#4285f4" data-name="Capa 1"/></g></svg>'
146
- );
147
- -webkit-mask-image: @-inline-svg-url;
148
- border-radius: 0;
149
- background-color: @primary-color;
150
- }
151
142
 
152
143
  .mobile {
153
144
  .widget-container {
@@ -418,17 +409,32 @@
418
409
  position: relative;
419
410
  scale: 1;
420
411
  transition:
421
- filter 250ms ease,
422
- opacity 0.15s ease-in,
423
- scale 0.125s ease-in-out,
424
- transform 0.2s ease-in-out;
412
+ opacity 200ms ease-in,
413
+ scale 250ms cubic-bezier(0.34, 1.5, 0.64, 1);
425
414
 
426
415
  &.hidden {
416
+ visibility: hidden;
427
417
  opacity: 0;
418
+ scale: 0.2;
419
+ pointer-events: none;
420
+ }
421
+ }
422
+
423
+ // Also animate when the cached image is ready before the first browser paint.
424
+ @starting-style {
425
+ .chat-button:not(.hidden) {
426
+ opacity: 0 !important;
427
+ scale: 0.25 !important;
428
428
  }
429
429
  }
430
430
 
431
- .chat-button:hover {
431
+ @media (prefers-reduced-motion: reduce) {
432
+ .chat-button {
433
+ transition: none !important;
434
+ }
435
+ }
436
+
437
+ .chat-button:not(.hidden):hover {
432
438
  filter: brightness(1.1);
433
439
  scale: 1.05;
434
440
  }
@@ -729,13 +735,12 @@
729
735
  z-index: @z-index-3;
730
736
  background: @white;
731
737
  box-shadow: 0px 5px 30px rgba(0, 0, 0, 0.15);
732
- border: 1px solid @border;
733
738
  border-radius: 15px;
734
739
  max-width: 100%;
735
740
  min-width: 210px;
736
741
  margin-bottom: 15px;
737
742
  padding: 15px 20px;
738
- positive: relative;
743
+ position: relative;
739
744
  cursor: pointer;
740
745
 
741
746
  &.right {
@@ -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 = require('css-loader!less-loader!../../assets/less/notif.less');
7
+ var _default = exports.default = require('css-loader?import=false!less-loader!../../assets/less/notif.less');
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = Buttons;
7
7
  var _index = _interopRequireDefault(require("../util/index.js"));
8
8
  var _preact = require("preact");
9
+ var _url = require("../util/url.js");
9
10
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
11
  function Buttons(props) {
11
12
  if (_index.default.size(props.buttons) === 0) return null;
@@ -13,7 +14,7 @@ function Buttons(props) {
13
14
  if (button.type === 'call_button') {
14
15
  return (0, _preact.h)("a", {
15
16
  class: "url-button-attachment",
16
- href: 'tel:' + button.phone_number,
17
+ href: (0, _url.safeUrl)('tel:' + button.phone_number),
17
18
  key: index,
18
19
  title: button.title
19
20
  }, props.t.i18n(button.i18n_title) || props.t.i18n(button.old_i18n_title) || button.title || `${props.t('call')} ${button.phone_number}`);
@@ -21,16 +22,18 @@ function Buttons(props) {
21
22
  if (button.type === 'url_button') {
22
23
  return (0, _preact.h)("a", {
23
24
  class: "url-button-attachment",
24
- href: button.url,
25
+ href: (0, _url.safeUrl)(button.url) || undefined,
25
26
  target: "_blank",
27
+ rel: "noopener noreferrer",
26
28
  key: index,
27
29
  title: button.title
28
30
  }, props.t.i18n(button.i18n_title) || props.t.i18n(button.old_i18n_title) || button.title || button.url);
29
31
  }
30
32
  if (button.type === 'postback_button') {
31
- return (0, _preact.h)("a", {
33
+ return (0, _preact.h)("button", {
34
+ type: "button",
32
35
  class: "url-button-attachment",
33
- href: "javascript:;",
36
+ key: index,
34
37
  onClick: _ => props.onSelect(button)
35
38
  }, props.t.i18n(button.i18n_title) || button.title);
36
39
  }
@@ -345,22 +345,7 @@ class ChatButton extends _common.Com {
345
345
  }, reply.title);
346
346
  })));
347
347
  }
348
- async prepareButtonImage() {
349
- let accountSetting = this.props.plugin.chatbox;
350
- accountSetting = this.fixMissingSetting(accountSetting);
351
- let imgsrc = _index.default.get(accountSetting, 'desktop_button.background_image_url') || _index.default.get(accountSetting, 'desktop_button.background_mask_url') || defaultBubble;
352
- if (this.props.store.isMobile()) {
353
- imgsrc = _index.default.get(accountSetting, 'mobile_button.background_image_url') || _index.default.get(accountSetting, 'mobile_button.background_mask_url') || imgsrc;
354
- }
355
- imgsrc = this.props.store.replaceFileUrl(imgsrc);
356
- await _index.default.preloadImg(imgsrc);
357
- this.setState({
358
- ready: true
359
- });
360
- this.readyGate.open();
361
- }
362
- renderButton() {
363
- if (typeof sbz_info != 'undefined') sbz_info.render_chatbox_button = _index.default.Date.now();
348
+ getButtonAppearance() {
364
349
  let accountSetting = this.props.plugin.chatbox;
365
350
  accountSetting = this.fixMissingSetting(accountSetting);
366
351
  let imgsrc = _index.default.get(accountSetting, 'desktop_button.background_image_url') || _index.default.get(accountSetting, 'desktop_button.background_mask_url') || defaultBubble;
@@ -382,22 +367,89 @@ class ChatButton extends _common.Com {
382
367
  maskurl = mobilemaskurl;
383
368
  }
384
369
  }
370
+ const imageUrl = url => addVersionParam(this.props.store.replaceFileUrl(url));
371
+ imgsrc = imageUrl(imgsrc);
372
+ imgurl = imageUrl(imgurl);
373
+ maskurl = imageUrl(maskurl);
374
+ const defaultMask = !imgurl && !maskurl ? DEF_BG_URL : '';
375
+ const urls = [...new Set([imgsrc, imgurl && encodeURI(imgurl), maskurl && encodeURI(maskurl), defaultMask].filter(Boolean))];
376
+ return {
377
+ imgsrc,
378
+ imgurl,
379
+ maskurl,
380
+ background,
381
+ position,
382
+ imgwidth,
383
+ imgheight,
384
+ urls,
385
+ maskAsset: maskurl ? encodeURI(maskurl) : defaultMask,
386
+ key: JSON.stringify([urls, maskurl, defaultMask])
387
+ };
388
+ }
389
+ componentDidUpdate() {
390
+ if (this.getButtonAppearance().key !== this._buttonImageKey) this.prepareButtonImage();
391
+ }
392
+ async prepareButtonImage() {
393
+ const appearance = this.getButtonAppearance();
394
+ const version = this._buttonImageVersion = (this._buttonImageVersion || 0) + 1;
395
+ this._buttonImageKey = appearance.key;
396
+ this.readyGate.close();
397
+ const [loaded, mask] = await Promise.all([Promise.all(appearance.urls.filter(url => url !== appearance.maskAsset).map(url => preloadButtonImage(url))), appearance.maskAsset ? prepareButtonMask(appearance.maskAsset) : Promise.resolve(null)]);
398
+ const stale = () => !this._isMounted || this._buttonImageVersion !== version;
399
+ if (stale()) {
400
+ if (mask) URL.revokeObjectURL(mask);
401
+ return;
402
+ }
403
+ const fallback = loaded.some(success => !success) || !!appearance.maskAsset && !mask;
404
+ const ready = !fallback || (await preloadButtonImage(defaultBubble));
405
+ if (stale()) {
406
+ if (mask) URL.revokeObjectURL(mask);
407
+ return;
408
+ }
409
+ if (this._buttonMaskUrl) URL.revokeObjectURL(this._buttonMaskUrl);
410
+ this._buttonMaskUrl = mask;
411
+ this.setState({
412
+ ready,
413
+ readyImageKey: appearance.key,
414
+ fallbackButtonImage: fallback,
415
+ buttonMaskUrl: mask
416
+ });
417
+ this.readyGate.open();
418
+ }
419
+ componentWillUnmount() {
420
+ if (this._buttonMaskUrl) URL.revokeObjectURL(this._buttonMaskUrl);
421
+ if (this._animateTimeout) clearTimeout(this._animateTimeout);
422
+ }
423
+ renderButton() {
424
+ if (typeof sbz_info != 'undefined') sbz_info.render_chatbox_button = _index.default.Date.now();
425
+ const appearance = this.getButtonAppearance();
426
+ let {
427
+ imgsrc,
428
+ imgurl,
429
+ maskurl,
430
+ background,
431
+ position,
432
+ imgwidth,
433
+ imgheight
434
+ } = appearance;
435
+ if (appearance.maskAsset) {
436
+ maskurl = this.state.readyImageKey === appearance.key ? this.state.buttonMaskUrl : '';
437
+ if (imgsrc === appearance.maskAsset) imgsrc = maskurl || '';
438
+ }
439
+ if (this.state.readyImageKey === appearance.key && this.state.fallbackButtonImage) {
440
+ imgsrc = imgurl = defaultBubble;
441
+ maskurl = '';
442
+ }
385
443
  let whitebgcls = 'chat-button--white-bg';
386
444
  if (!imgurl && maskurl) whitebgcls += ' chat-button--white-bg--mask-only';
387
445
  let imgcontainerstyle = '';
388
- imgurl = this.props.store.replaceFileUrl(imgurl);
389
- imgsrc = this.props.store.replaceFileUrl(imgsrc);
390
- maskurl = this.props.store.replaceFileUrl(maskurl);
391
- imgurl = addVersionParam(imgurl);
392
- imgsrc = addVersionParam(imgsrc);
393
- maskurl = addVersionParam(maskurl);
394
446
  if (!imgurl && !maskurl) {
395
447
  whitebgcls += ' chat-button--white-bg--mask-only';
396
448
  imgcontainerstyle = `
397
- -webkit-mask-image: url("${encodeURI(DEF_BG_URL)}") !important;
398
- -o-mask-image: url("${encodeURI(DEF_BG_URL)}") !important;
399
- -moz-mask-image: url("${encodeURI(DEF_BG_URL)}") !important;
400
- mask-image: url("${encodeURI(DEF_BG_URL)}") !important;
449
+ -webkit-mask-image: none !important;
450
+ -o-mask-image: none !important;
451
+ -moz-mask-image: none !important;
452
+ mask-image: none !important;
401
453
  height: ${imgheight}px !important;
402
454
  width: ${imgwidth}px !important;
403
455
  background: ${background} !important;
@@ -450,9 +502,12 @@ class ChatButton extends _common.Com {
450
502
  height: ${imgheight - 4}px !important;
451
503
 
452
504
  `;
453
- if (!this.state.ready) cls += ' hidden';
454
- return (0, _preact.h)("div", {
505
+ if (!this.state.ready || this.state.readyImageKey !== appearance.key) cls += ' hidden';
506
+ return (0, _preact.h)("button", {
507
+ type: "button",
455
508
  class: cls,
509
+ "aria-label": this.props.t('chat'),
510
+ "aria-expanded": !this.props.isShow,
456
511
  onClick: e => this.onClick(e)
457
512
  }, (0, _preact.h)("div", {
458
513
  class: whitebgcls,
@@ -492,7 +547,9 @@ class ChatButton extends _common.Com {
492
547
  return defmsg;
493
548
  }
494
549
  return (0, _preact.h)(_block.default, {
495
- block: block
550
+ block: block,
551
+ store: this.props.store,
552
+ t: this.props.t
496
553
  });
497
554
  }
498
555
  let message = _index.default.get(ev, 'data.message.text', defmsg);
@@ -535,4 +592,33 @@ function addVersionParam(url, version = 8) {
535
592
  const newUrl = base + (hasQuery ? '&' : '?') + `v=${version}`;
536
593
  return hash ? newUrl + '#' + hash : newUrl;
537
594
  }
595
+ }
596
+ function preloadButtonImage(src, {
597
+ crossOrigin = false
598
+ } = {}) {
599
+ return new Promise(resolve => {
600
+ const image = new Image();
601
+ if (crossOrigin) image.crossOrigin = 'anonymous';
602
+ image.onerror = () => resolve(false);
603
+ image.onload = async () => {
604
+ try {
605
+ if (image.decode) await image.decode();
606
+ resolve(true);
607
+ } catch (_) {
608
+ resolve(false);
609
+ }
610
+ };
611
+ image.src = src;
612
+ });
613
+ }
614
+ async function prepareButtonMask(src) {
615
+ let url;
616
+ try {
617
+ const response = await fetch(src);
618
+ if (!response.ok) return null;
619
+ url = URL.createObjectURL(await response.blob());
620
+ if (await preloadButtonImage(url)) return url;
621
+ } catch (_) {}
622
+ if (url) URL.revokeObjectURL(url);
623
+ return null;
538
624
  }
@@ -56,10 +56,26 @@ class Event extends _common.Com {
56
56
  this.state = {
57
57
  pongs: _index.default.get(this.props.event, 'ev.data.message.pongs') || []
58
58
  };
59
+ const _ev = this.props.event.ev;
60
+ const age = (this.props.store.now ? this.props.store.now() : _index.default.Date.now()) - _ev.created;
61
+ this.animateSend = !props.isHistory && _ev.type === 'message_sent' && _index.default.get(_ev, 'by.type') === 'user' && _ev.id === _index.default.get(_ev, 'source_event_id') && age >= 0 && age < 2000;
59
62
  this.textMessageRef = (0, _preact.createRef)();
60
63
  this.fileAttachmentRef = (0, _preact.createRef)();
61
64
  this.imageAttachmentRef = (0, _preact.createRef)();
62
65
  }
66
+ componentDidMount() {
67
+ if (!this.animateSend) return;
68
+ this.sendAnimationFrame = window.requestAnimationFrame(() => {
69
+ this.sendAnimationFrame = window.requestAnimationFrame(() => {
70
+ if (this._isMounted) this.setState({
71
+ sendEntered: true
72
+ });
73
+ });
74
+ });
75
+ }
76
+ componentWillUnmount() {
77
+ if (this.sendAnimationFrame) window.cancelAnimationFrame(this.sendAnimationFrame);
78
+ }
63
79
  render() {
64
80
  var ev = _index.default.get(this.props.event, 'ev', {});
65
81
  switch (ev.type) {
@@ -104,6 +120,7 @@ class Event extends _common.Com {
104
120
  return !!_index.default.find(pongs, pong => pong.type === 'dislike');
105
121
  }
106
122
  chooseQuickReply(reply) {
123
+ if (this.props.isHistory) return;
107
124
  let ev = _index.default.get(this.props.event, 'ev', {});
108
125
  let message = {
109
126
  text: reply.title,
@@ -121,6 +138,7 @@ class Event extends _common.Com {
121
138
  this.props.onSend(message);
122
139
  }
123
140
  choosePostbackBtn(button) {
141
+ if (this.props.isHistory) return;
124
142
  let ev = _index.default.get(this.props.event, 'ev', {});
125
143
  let message = {
126
144
  text: button.title,
@@ -261,6 +279,17 @@ class Event extends _common.Com {
261
279
  }
262
280
  renderMessageSent() {
263
281
  let ev = this.props.event.ev;
282
+ if (_index.default.get(ev, 'data.message.deleted')) {
283
+ let byType = _index.default.get(ev, 'by.type') === 'user' ? 'user' : 'agent';
284
+ return (0, _preact.h)("div", {
285
+ class: 'convo-event ' + byType + '-message',
286
+ "data-ev-id": ev.id
287
+ }, (0, _preact.h)(_TextMessage.default, {
288
+ ev: ev,
289
+ t: this.props.t,
290
+ store: this.props.store
291
+ }));
292
+ }
264
293
  let attachments = _index.default.get(ev, 'data.message.attachments');
265
294
  let hasForm = _index.default.find(attachments, att => att.type === 'form');
266
295
  if (_index.default.isEmptyMessage(_index.default.get(ev, 'data.message'))) return null;
@@ -331,13 +360,13 @@ class Event extends _common.Com {
331
360
  t: this.props.t,
332
361
  store: this.props.store,
333
362
  ev: ev
334
- }), (0, _preact.h)(_QuickReplies.default, {
363
+ }), !this.props.isHistory && (0, _preact.h)(_QuickReplies.default, {
335
364
  store: this.props.store,
336
365
  ev: ev,
337
366
  onSelect: e => this.chooseQuickReply(e)
338
367
  }), this.renderReactions(), this.renderAIDevNote())));
339
368
  }
340
- if (_index.default.get(ev, 'data.message.idempotency_key') === ev.id && this.props.store.now() - ev.created < 900) {
369
+ if (_index.default.get(ev, 'source_event_id') === ev.id && this.props.store.now() - ev.created < 900) {
341
370
  _index.default.setTimeout(() => this.setState({
342
371
  refresh: this.state.refresh
343
372
  }), 1000);
@@ -345,7 +374,7 @@ class Event extends _common.Com {
345
374
  let $dot = (0, _preact.h)("div", {
346
375
  class: "sending_dot"
347
376
  });
348
- if (this.props.store.now() - ev.created < 900 || _index.default.get(ev, 'data.message.idempotency_key') !== ev.id) {
377
+ if (this.props.store.now() - ev.created < 900 || _index.default.get(ev, 'source_event_id') !== ev.id) {
349
378
  $dot = (0, _preact.h)("div", {
350
379
  class: "sending_dot sending_dot__hide"
351
380
  });
@@ -373,10 +402,11 @@ class Event extends _common.Com {
373
402
  class: "convo-event user-message",
374
403
  "data-ev-id": ev.id
375
404
  }, $sender, (0, _preact.h)("div", {
376
- class: "message-user__container"
405
+ class: 'message-user__container' + (this.animateSend && !this.props.isHistory ? ' message-user__container--send' + (this.state.sendEntered ? '' : ' message-user__container--enter') : '')
377
406
  }, $dot, (0, _preact.h)(_TextMessage.default, {
378
407
  ref: this.textMessageRef,
379
408
  store: this.props.store,
409
+ t: this.props.t,
380
410
  ev: ev,
381
411
  refresh: this.state.refresh,
382
412
  onLink: this.props.onLink
@@ -412,6 +442,7 @@ class Event extends _common.Com {
412
442
  })), $error);
413
443
  }
414
444
  async onSendReaction(action) {
445
+ if (this.props.isHistory) return;
415
446
  if (this._reacting) return;
416
447
  let ev = this.props.event.ev;
417
448
  let cid = _index.default.get(ev, 'data.message.conversation_id');
@@ -463,6 +494,7 @@ class Event extends _common.Com {
463
494
  this._reacting = false;
464
495
  }
465
496
  renderReactions() {
497
+ if (this.props.isHistory) return null;
466
498
  if (this.props.store.isAIDevMode()) return null;
467
499
  let cid = _index.default.get(this.props.event.ev, 'data.message.conversation_id', '');
468
500
  if (cid.startsWith('cslight')) return null;
@@ -550,7 +582,7 @@ class Event extends _common.Com {
550
582
  let byid = _index.default.get(ev, 'by.display_id', _index.default.get(ev, 'by.id')) || '';
551
583
  if (bytype === 'user') return _index.default.getUserDisplayName(this.props.store.matchUser());
552
584
  let acc = this.props.store.matchAccount();
553
- if (bytype === 'agent') return _index.default.getAgentDisplayName(acc, this.props.store.matchAgent(byid));
585
+ if (bytype === 'agent' || _index.default.get(ev, 'by.display_type') === 'agent') return _index.default.getAgentDisplayName(acc, this.props.store.matchAgent(byid));
554
586
  if (byid.startsWith('at')) return _index.default.getAgentDisplayName(acc, this.props.store.matchAgent(byid));
555
587
  if (bytype === 'bot') {
556
588
  let bot = this.props.store.matchBot()[byid];