@subiz/widget-v4 4.1.218 → 4.1.222

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 (128) hide show
  1. package/assets/img/bar_ho_tro.svg +3 -0
  2. package/{dist/assets/img/bubble_default.6f6eec8.svg → assets/img/bubble_11.svg} +0 -1
  3. package/assets/less/form-page.less +58 -0
  4. package/assets/less/form.less +279 -3
  5. package/assets/less/widget.less +6 -0
  6. package/compiled_src/chatbox/ChatButton.js +22 -3
  7. package/compiled_src/chatbox/Widget.js +13 -18
  8. package/compiled_src/form/Form.js +270 -71
  9. package/compiled_src/form/FormPage.js +17 -4
  10. package/compiled_src/form/form-fields.js +244 -0
  11. package/compiled_src/form/index.js +2 -0
  12. package/compiled_src/form/rating-stars.js +48 -88
  13. package/compiled_src/plugin.js +10 -0
  14. package/compiled_src/store/api.js +5 -7
  15. package/compiled_src/store/api_fake.js +3 -1
  16. package/compiled_src/store/localize.js +33 -0
  17. package/compiled_src/store/store.js +57 -1
  18. package/compiled_src/store/widget-settings.js +67 -0
  19. package/compiled_src/util/common.js +1 -1
  20. package/package.json +3 -3
  21. package/src/chatbox/ChatButton.js +25 -4
  22. package/src/chatbox/Widget.js +21 -21
  23. package/src/form/Form.js +335 -97
  24. package/src/form/FormPage.js +21 -8
  25. package/src/form/form-fields.js +224 -0
  26. package/src/form/index.js +6 -1
  27. package/src/form/rating-stars.js +48 -64
  28. package/src/plugin.js +10 -0
  29. package/src/store/api.js +9 -7
  30. package/src/store/api_fake.js +3 -1
  31. package/src/store/localize.js +30 -0
  32. package/src/store/store.js +64 -1
  33. package/src/store/widget-settings.js +46 -0
  34. package/src/util/common.js +1 -1
  35. package/test.cjs +3 -0
  36. package/webpack.dev.js +2 -0
  37. package/dist/127.js +0 -273
  38. package/dist/132.js +0 -2817
  39. package/dist/185.js +0 -1917
  40. package/dist/1a12ed33347.127.js +0 -1
  41. package/dist/1a12ed33347.132.js +0 -1
  42. package/dist/1a12ed33347.185.js +0 -1
  43. package/dist/1a12ed33347.27.js +0 -1
  44. package/dist/1a12ed33347.278.js +0 -1
  45. package/dist/1a12ed33347.339.js +0 -1
  46. package/dist/1a12ed33347.459.js +0 -1
  47. package/dist/1a12ed33347.490.js +0 -1
  48. package/dist/1a12ed33347.app.js +0 -1
  49. package/dist/1a12ed33347.chat.html +0 -1
  50. package/dist/1a12ed33347.chat.js +0 -1
  51. package/dist/1a12ed33347.form.html +0 -1
  52. package/dist/1a12ed33347.form.js +0 -1
  53. package/dist/1a12ed33347.index.html +0 -72
  54. package/dist/1a12ed33347.library.html +0 -72
  55. package/dist/1a12ed33347.library.js +0 -1
  56. package/dist/1a12ed33347.preview.html +0 -759
  57. package/dist/1a12ed33347.preview.js +0 -1
  58. package/dist/1a12ed33347.webview.js +0 -1
  59. package/dist/27.js +0 -37
  60. package/dist/459.js +0 -37
  61. package/dist/490.js +0 -305
  62. package/dist/HASH +0 -1
  63. package/dist/assets/img/Facebook.64607ec.png +0 -0
  64. package/dist/assets/img/Zalo.6845707.png +0 -0
  65. package/dist/assets/img/award1a.1a58e10.png +0 -0
  66. package/dist/assets/img/award3a.3b74423.png +0 -0
  67. package/dist/assets/img/award4a.ed6ccd4.png +0 -0
  68. package/dist/assets/img/award5.39eb7ae.png +0 -0
  69. package/dist/assets/img/caret.998eb8f.svg +0 -1
  70. package/dist/assets/img/convos_empty.2353007.svg +0 -72
  71. package/dist/assets/img/default.06e6ee0.jpg +0 -0
  72. package/dist/assets/img/default_avatar.e449b04.png +0 -0
  73. package/dist/assets/img/default_logo.dd13a2b.svg +0 -5
  74. package/dist/assets/img/done_gray.d7e8203.svg +0 -4
  75. package/dist/assets/img/done_green.b817fd4.svg +0 -4
  76. package/dist/assets/img/dot_chat.335fd8a.svg +0 -3
  77. package/dist/assets/img/emoji/messenger/sub_23.529_58.824.d5faf14.png +0 -0
  78. package/dist/assets/img/emoji/messenger/sub_23.529_60.784.1420241.png +0 -0
  79. package/dist/assets/img/emoji/messenger/sub_25.490_0.000.1e6b2c0.png +0 -0
  80. package/dist/assets/img/emoji/messenger/sub_25.490_45.098.3dfbc08.png +0 -0
  81. package/dist/assets/img/emoji/messenger/sub_25.490_5.882.4b49e94.png +0 -0
  82. package/dist/assets/img/emoji/messenger/sub_27.451_72.549.7114f58.png +0 -0
  83. package/dist/assets/img/emoji/messenger/sub_27.451_84.314.231e2b7.png +0 -0
  84. package/dist/assets/img/emoji/messenger/sub_27.451_96.078.ddbb008.png +0 -0
  85. package/dist/assets/img/emoji/messenger/sub_29.412_5.882.61f3359.png +0 -0
  86. package/dist/assets/img/emoji/messenger/sub_58.824_100.000.6df72bc.png +0 -0
  87. package/dist/assets/img/emoji/messenger/sub_58.824_45.098.3d7a7ad.png +0 -0
  88. package/dist/assets/img/emoji/messenger/sub_58.824_47.059.0f4d6e2.png +0 -0
  89. package/dist/assets/img/emoji/messenger/sub_58.824_54.902.65724c1.png +0 -0
  90. package/dist/assets/img/emoji/messenger/sub_58.824_64.706.9da3cba.png +0 -0
  91. package/dist/assets/img/emoji/messenger/sub_58.824_72.549.edca5cf.png +0 -0
  92. package/dist/assets/img/emoji/messenger/sub_58.824_78.431.f910f2a.png +0 -0
  93. package/dist/assets/img/emoji/messenger/sub_58.824_88.235.def4e71.png +0 -0
  94. package/dist/assets/img/emoji/messenger/sub_60.784_11.765.49c03d0.png +0 -0
  95. package/dist/assets/img/emoji/messenger/sub_60.784_27.451.46b54e5.png +0 -0
  96. package/dist/assets/img/emoji/messenger/sub_60.784_29.412.e36d284.png +0 -0
  97. package/dist/assets/img/emoji/messenger/sub_60.784_3.922.bb191b0.png +0 -0
  98. package/dist/assets/img/emoji/messenger/sub_60.784_35.294.af7b927.png +0 -0
  99. package/dist/assets/img/emoji/messenger/sub_60.784_7.843.98f2a99.png +0 -0
  100. package/dist/assets/img/empty.29bed2d.svg +0 -23
  101. package/dist/assets/img/empty_1.0b98af0.svg +0 -11
  102. package/dist/assets/img/examplte_logo.f86535f.svg +0 -1
  103. package/dist/assets/img/external-link.90577cc.svg +0 -1
  104. package/dist/assets/img/fb_inverse_circle.36c2cfc.png +0 -0
  105. package/dist/assets/img/ghn.070f8e3.svg +0 -17
  106. package/dist/assets/img/ghtk.326eac1.svg +0 -18
  107. package/dist/assets/img/image_default.be0698b.png +0 -0
  108. package/dist/assets/img/info.ebce7ea.svg +0 -1
  109. package/dist/assets/img/loader.37000a7.svg +0 -1
  110. package/dist/assets/img/loading_static.a27f0ac.svg +0 -10
  111. package/dist/assets/img/main-service1.d536cc2.png +0 -0
  112. package/dist/assets/img/main-service2.9b99549.png +0 -0
  113. package/dist/assets/img/not_found_search.2ddb8d1.png +0 -0
  114. package/dist/assets/img/package.8d37d0f.svg +0 -5
  115. package/dist/assets/img/package_fade.763c001.svg +0 -1
  116. package/dist/assets/img/rating--average.1ea492c.svg +0 -12
  117. package/dist/assets/img/rating--great.e3d712e.svg +0 -16
  118. package/dist/assets/img/rating--poor.2938a25.svg +0 -12
  119. package/dist/assets/img/search.f9705ff.svg +0 -1
  120. package/dist/assets/img/send.6db52a9.svg +0 -3
  121. package/dist/assets/img/shop.f7dc7d8.svg +0 -1
  122. package/dist/assets/img/shop_faded.df7845a.svg +0 -1
  123. package/dist/assets/img/stars_v2.79cf728.png +0 -0
  124. package/dist/assets/img/subiz-mono.8fbc067.svg +0 -3
  125. package/dist/assets/img/x.26fdfa3.svg +0 -5
  126. package/dist/malicious-app.js +0 -17
  127. package/dist/screenshoot.html +0 -7
  128. package/dist/screenshoot.js +0 -14454
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.loadWidgetSettings = loadWidgetSettings;
7
+ exports.mergeWidgetSettings = mergeWidgetSettings;
8
+ const ajax = require('@subiz/ajax/src/ajax.js');
9
+ function failed(response) {
10
+ return !response || response.error || response.body?.error || response.code < 200 || response.code >= 300 || !response.body || typeof response.body !== 'object';
11
+ }
12
+ async function readConfig(query) {
13
+ const request = ajax.withCredentials(false).setParser('json').setQuery(query);
14
+ let response;
15
+ try {
16
+ response = await request.setBaseUrl('https://api2.sbz.vn/4.1').get('/widget-config');
17
+ if (!failed(response)) return response;
18
+ } catch (_) {}
19
+ return request.setBaseUrl('https://api.sbz.vn/4.1').get('/widget-config');
20
+ }
21
+ async function loadWidgetSettings(contextRequest, apiURL, accountId, pageURL, botId) {
22
+ const query = {
23
+ 'account-id': accountId,
24
+ url: pageURL,
25
+ v: '6'
26
+ };
27
+ if (botId) query.bot_id = botId;
28
+ const contextBase = new URL('/4.1', new URL(apiURL, document.location.href)).href;
29
+ const [config, context] = await Promise.all([readConfig(query), contextRequest.setBaseUrl(contextBase).setQuery(query).get('/widget-context')]);
30
+ if (failed(context)) return {
31
+ ...context,
32
+ error: context?.error || true
33
+ };
34
+ if (failed(config)) return {
35
+ ...config,
36
+ error: config?.error || true
37
+ };
38
+ return {
39
+ ...config,
40
+ body: mergeWidgetSettings(config.body, context.body)
41
+ };
42
+ }
43
+ function mergeWidgetSettings(config, context) {
44
+ const dynamicPlugins = new Map((context.plugins || []).map(plugin => [plugin.id, plugin]));
45
+ const plugins = (config.plugins || []).map(plugin => {
46
+ const dynamic = dynamicPlugins.get(plugin.id);
47
+ if (!dynamic) return plugin;
48
+ const merged = {
49
+ ...plugin,
50
+ ...dynamic
51
+ };
52
+ if (dynamic.chatbox) merged.chatbox = {
53
+ ...plugin.chatbox,
54
+ ...dynamic.chatbox
55
+ };
56
+ if (dynamic.notification) merged.notification = {
57
+ ...plugin.notification,
58
+ ...dynamic.notification
59
+ };
60
+ return merged;
61
+ });
62
+ return {
63
+ ...config,
64
+ ...context,
65
+ plugins
66
+ };
67
+ }
@@ -763,7 +763,7 @@ function setCss(id, $css, css) {
763
763
  $css.appendChild(document.createTextNode(css));
764
764
  }
765
765
  }
766
- const DEF_CHAT_BUTTON_BACKGROUND_IMAGE_URL = exports.DEF_CHAT_BUTTON_BACKGROUND_IMAGE_URL = 'https://vcdn.subiz-cdn.com/file/bubble_11.svg?v=5';
766
+ const DEF_CHAT_BUTTON_BACKGROUND_IMAGE_URL = exports.DEF_CHAT_BUTTON_BACKGROUND_IMAGE_URL = require('../../assets/img/bubble_11.svg?inline');
767
767
  const DEFVAL = exports.DEFVAL = {
768
768
  primary_color: '#0265FF',
769
769
  secondary_color: '#555555',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@subiz/widget-v4",
3
- "version": "4.1.218",
3
+ "version": "4.1.222",
4
4
  "main": "./compiled_src/lib.js",
5
5
  "scripts": {
6
6
  "dev": "npx webpack serve --config webpack.dev.js",
@@ -32,9 +32,9 @@
32
32
  "globals": "^17.13.0",
33
33
  "html-webpack-plugin": "^5.6.8",
34
34
  "jsdom": "^30.1.3",
35
- "puppeteer": "^25.14.0",
35
+ "puppeteer": "^25.16.0",
36
36
  "tape": "^5.10.3",
37
- "webpack": "^5.112.0",
37
+ "webpack": "^5.112.1",
38
38
  "webpack-bundle-analyzer": "^5.4.0",
39
39
  "webpack-cli": "^7.2.3",
40
40
  "webpack-dev-server": "^6.1.0"
@@ -2,6 +2,10 @@ import sb from '../util/index.js'
2
2
  import Emoji from './Emoji.js'
3
3
  var defaultBubble = require('../../assets/img/bubble_default.svg')
4
4
  const dotChatIcon = require('../../assets/img/dot_chat.svg')
5
+ const builtInButtonIcons = {
6
+ bubble_11: require('../../assets/img/bubble_11.svg?inline'),
7
+ bar_ho_tro: require('../../assets/img/bar_ho_tro.svg?inline'),
8
+ }
5
9
  let defaultAvatar = require('../../assets/img/default_avatar.png')
6
10
  import {Com} from '../common.js'
7
11
  import Block from './block.js'
@@ -61,7 +65,10 @@ export default class ChatButton extends Com {
61
65
  let screen = sb.get(this.props.store.matchLastview(), 'screen')
62
66
  let previewEvent = sb.get(this.props.store.matchLastview(), 'previewEvent') || {}
63
67
  let isOpen = screen === 'preview' && sb.size(previewEvent) > 0
64
- if (isOpen) {
68
+ if (isOpen && this.state.hasPreviewDom && !this.state.previewExtraCls && isSamePreview(this.state.previewEvent, previewEvent)) {
69
+ // same message re-rendered (e.g. after setLocale): swap content without replaying the animation
70
+ this.setState({previewEvent})
71
+ } else if (isOpen) {
65
72
  this.makeAnimateShowPreview(previewEvent)
66
73
  } else {
67
74
  this.makeAnimateHidePreview()
@@ -414,7 +421,7 @@ export default class ChatButton extends Com {
414
421
 
415
422
  const imageUrl = (url) => {
416
423
  const asset = normalizeButtonImageUrl(this.props.store.replaceFileUrl(url))
417
- return asset === dotChatIcon ? asset : addVersionParam(asset)
424
+ return asset === dotChatIcon || (asset && asset.startsWith('data:')) ? asset : addVersionParam(asset)
418
425
  }
419
426
  imgsrc = imageUrl(imgsrc)
420
427
  imgurl = imageUrl(imgurl)
@@ -635,13 +642,26 @@ function randomAgent(agentM) {
635
642
 
636
643
  const Ms30Mins = 30 * 60 * 1000
637
644
 
645
+ // Same preview message, possibly with re-localized content: same event id, or the same
646
+ // convo-less chatbox greeting from the same agent.
647
+ export function isSamePreview(a, b) {
648
+ if (!sb.size(a) || !sb.size(b)) return false
649
+ if (a.id || b.id) return a.id === b.id
650
+ if (sb.get(a, 'data.message.conversation_id') || sb.get(b, 'data.message.conversation_id')) return false
651
+ return sb.get(a, 'by.id') === sb.get(b, 'by.id')
652
+ }
653
+
638
654
  function normalizeButtonImageUrl(url) {
655
+ if (/^https:\/\/vcdn\.subiz-cdn\.com\/file\/bubble_11\.svg(?=$|[?#])/.test(url)) return builtInButtonIcons.bubble_11
656
+ // These public CDN redirects can cache another website's CORS origin.
657
+ // Embed the stock masks so their appearance does not depend on that cache.
658
+ const builtIn = /^https:\/\/(?:storage\.googleapis\.com\/public-gcs\.subiz-cdn\.com|public-gcs\.subiz-cdn\.com|vcdn\.subiz-cdn\.com)\/dashboard-v4\/public\/img\/bubble\/(bubble_11|bar_ho_tro)\.svg(?=$|[?#])/.exec(url)
659
+ if (builtIn) return builtInButtonIcons[builtIn[1]]
639
660
  if (/^https:\/\/(?:storage\.googleapis\.com\/public-gcs\.subiz-cdn\.com|public-gcs\.subiz-cdn\.com|vcdn\.subiz-cdn\.com)\/dashboard-v4\/public\/img\/bubble\/dot_chat\.svg(?=$|[?#])/.test(url)) return dotChatIcon
640
661
  return url
641
662
  }
642
663
 
643
- // v=5 trick to clear cache
644
- const DEF_BG_URL = 'https://vcdn.subiz-cdn.com/file/bubble_11.svg?v=5'
664
+ const DEF_BG_URL = builtInButtonIcons.bubble_11
645
665
  const DEF_BG = 'linear-gradient(280.16deg, #0053d3 22.03%, #0265ff 78.2%)'
646
666
 
647
667
  function addVersionParam(url, version = 8) {
@@ -680,6 +700,7 @@ function preloadButtonImage(src, {crossOrigin = false} = {}) {
680
700
  // Pin the mask bytes with a data URL: some sites allow data: images but block blob:.
681
701
  // If inline images or fetch are blocked, preload the original URL with CORS for CSS masks.
682
702
  async function prepareButtonMask(src) {
703
+ if (src.startsWith('data:')) return await preloadButtonImage(src) ? src : null
683
704
  try {
684
705
  const response = await fetch(src)
685
706
  if (!response.ok) return null
@@ -47,7 +47,7 @@ export default class Widget extends Com {
47
47
 
48
48
  msgInput = createRef()
49
49
  componentDidMount() {
50
- if (!this.props.store.isMobile()) this.focusInput()
50
+ if (this.props.isShow && !this.props.store.isMobile()) this.focusInput()
51
51
 
52
52
  this.updateFormDataFields()
53
53
  }
@@ -87,7 +87,8 @@ export default class Widget extends Com {
87
87
  }
88
88
 
89
89
  shouldShowForm() {
90
- return !!(this.state.restartForm || this.state.clickedJoin)
90
+ return this.props.view !== 'list' && !!(this.props.view === 'offline_form' || this.props.view === 'prechat_form' ||
91
+ this.state.restartForm || this.state.clickedJoin)
91
92
  }
92
93
 
93
94
  render() {
@@ -306,9 +307,10 @@ export default class Widget extends Com {
306
307
  }
307
308
  }
308
309
 
309
- this.setState({formData})
310
- // dont steal focus on mobile or trying to override form
311
- if (!this.props.store.isMobile() && !form._updated) this.focusInput()
310
+ this.setState({formData}, () => {
311
+ // Wait for the populated form's inputs to mount before focusing.
312
+ if (this.props.isShow && !this.props.store.isMobile()) this.focusInput()
313
+ })
312
314
  }
313
315
 
314
316
  onBooleanChange(key, e) {
@@ -744,22 +746,20 @@ export default class Widget extends Com {
744
746
  focusInput(text, repeat = true) {
745
747
  if (this.props.store.isPreview) return
746
748
  if (this.shouldShowForm()) {
747
- // focus on the element
748
- let focusfieldkey = ''
749
- sb.map(this.state.formData, (field, key) => {
750
- if (focusfieldkey || field.type !== 'text') return
751
- if (!field.value || field.error) focusfieldkey = key
752
- })
753
- var $input = this['form_' + encode(focusfieldkey)]
754
- if ($input) $input.focus()
755
- sb.setTimeout(() => {
756
- sb.map(this.state.formData, (field, key) => {
757
- if (focusfieldkey || field.type !== 'text') return
758
- if (!field.value || field.error) focusfieldkey = key
759
- })
760
- var $input = this['form_' + encode(focusfieldkey)]
761
- if ($input) $input.focus()
762
- }, 200)
749
+ const focusField = () => {
750
+ if (!this.props.isShow || !this.shouldShowForm()) return
751
+ const inputs = Object.keys(this.state.formData).map(key => this['form_' + encode(key)])
752
+ .filter(input => input && !input.disabled && !input.readOnly &&
753
+ (input.tagName === 'TEXTAREA' || (input.tagName === 'INPUT' &&
754
+ ['text', 'email', 'tel', 'number', 'url', 'search', 'password'].includes(input.type))))
755
+ // Do not move the caret again after the user starts typing or tabs to another field.
756
+ if (inputs.some(input => input.ownerDocument.activeElement === input)) return
757
+ const input = inputs.find(input => input.value.trim() === '')
758
+ || inputs.find(input => input.getAttribute('aria-invalid') === 'true')
759
+ if (input) input.focus()
760
+ }
761
+ focusField()
762
+ if (repeat) sb.setTimeout(focusField, 200)
763
763
  return
764
764
  }
765
765