@subiz/widget-v4 4.1.178 → 4.1.184

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 (111) hide show
  1. package/assets/less/base.less +28 -0
  2. package/assets/less/chatbox.less +4 -0
  3. package/assets/less/message.less +0 -8
  4. package/assets/less/widget.less +197 -2
  5. package/assets/less/widget_header.less +2 -1
  6. package/chat.js +0 -1
  7. package/compiled_src/chatbox/ChatButton.js +183 -82
  8. package/compiled_src/chatbox/Widget.js +2 -1
  9. package/compiled_src/chatbox/main.js +44 -53
  10. package/compiled_src/store/store.js +40 -17
  11. package/package.json +5 -5
  12. package/src/chatbox/ChatButton.js +197 -77
  13. package/src/chatbox/Widget.js +4 -2
  14. package/src/chatbox/main.js +30 -52
  15. package/src/store/store.js +56 -18
  16. package/dist/127.js +0 -273
  17. package/dist/132.js +0 -2802
  18. package/dist/185.js +0 -1827
  19. package/dist/1991cfb4656.127.js +0 -1
  20. package/dist/1991cfb4656.132.js +0 -1
  21. package/dist/1991cfb4656.185.js +0 -1
  22. package/dist/1991cfb4656.27.js +0 -1
  23. package/dist/1991cfb4656.278.js +0 -1
  24. package/dist/1991cfb4656.339.js +0 -1
  25. package/dist/1991cfb4656.459.js +0 -1
  26. package/dist/1991cfb4656.490.js +0 -1
  27. package/dist/1991cfb4656.74.js +0 -1
  28. package/dist/1991cfb4656.997.js +0 -1
  29. package/dist/1991cfb4656.app.js +0 -1
  30. package/dist/1991cfb4656.chat.html +0 -1
  31. package/dist/1991cfb4656.chat.js +0 -1
  32. package/dist/1991cfb4656.form.html +0 -1
  33. package/dist/1991cfb4656.form.js +0 -1
  34. package/dist/1991cfb4656.index.html +0 -72
  35. package/dist/1991cfb4656.library.html +0 -72
  36. package/dist/1991cfb4656.library.js +0 -1
  37. package/dist/1991cfb4656.preview.html +0 -759
  38. package/dist/1991cfb4656.preview.js +0 -1
  39. package/dist/1991cfb4656.webview.js +0 -1
  40. package/dist/27.js +0 -107
  41. package/dist/278.js +0 -417
  42. package/dist/459.js +0 -107
  43. package/dist/490.js +0 -305
  44. package/dist/74.js +0 -372
  45. package/dist/997.js +0 -107
  46. package/dist/HASH +0 -1
  47. package/dist/assets/img/Facebook.64607ec.png +0 -0
  48. package/dist/assets/img/Zalo.6845707.png +0 -0
  49. package/dist/assets/img/award1a.1a58e10.png +0 -0
  50. package/dist/assets/img/award3a.3b74423.png +0 -0
  51. package/dist/assets/img/award4a.ed6ccd4.png +0 -0
  52. package/dist/assets/img/award5.39eb7ae.png +0 -0
  53. package/dist/assets/img/bubble_default.7d5e4ab.svg +0 -20
  54. package/dist/assets/img/caret.998eb8f.svg +0 -1
  55. package/dist/assets/img/convos_empty.2353007.svg +0 -72
  56. package/dist/assets/img/default.06e6ee0.jpg +0 -0
  57. package/dist/assets/img/default_avatar.5b74dc1.png +0 -0
  58. package/dist/assets/img/done_gray.d7e8203.svg +0 -4
  59. package/dist/assets/img/done_green.b817fd4.svg +0 -4
  60. package/dist/assets/img/emoji/messenger/sub_23.529_58.824.d5faf14.png +0 -0
  61. package/dist/assets/img/emoji/messenger/sub_23.529_60.784.1420241.png +0 -0
  62. package/dist/assets/img/emoji/messenger/sub_25.490_0.000.1e6b2c0.png +0 -0
  63. package/dist/assets/img/emoji/messenger/sub_25.490_45.098.3dfbc08.png +0 -0
  64. package/dist/assets/img/emoji/messenger/sub_25.490_5.882.4b49e94.png +0 -0
  65. package/dist/assets/img/emoji/messenger/sub_27.451_72.549.7114f58.png +0 -0
  66. package/dist/assets/img/emoji/messenger/sub_27.451_84.314.231e2b7.png +0 -0
  67. package/dist/assets/img/emoji/messenger/sub_27.451_96.078.ddbb008.png +0 -0
  68. package/dist/assets/img/emoji/messenger/sub_29.412_5.882.61f3359.png +0 -0
  69. package/dist/assets/img/emoji/messenger/sub_58.824_100.000.6df72bc.png +0 -0
  70. package/dist/assets/img/emoji/messenger/sub_58.824_45.098.3d7a7ad.png +0 -0
  71. package/dist/assets/img/emoji/messenger/sub_58.824_47.059.0f4d6e2.png +0 -0
  72. package/dist/assets/img/emoji/messenger/sub_58.824_54.902.65724c1.png +0 -0
  73. package/dist/assets/img/emoji/messenger/sub_58.824_64.706.9da3cba.png +0 -0
  74. package/dist/assets/img/emoji/messenger/sub_58.824_72.549.edca5cf.png +0 -0
  75. package/dist/assets/img/emoji/messenger/sub_58.824_78.431.f910f2a.png +0 -0
  76. package/dist/assets/img/emoji/messenger/sub_58.824_88.235.def4e71.png +0 -0
  77. package/dist/assets/img/emoji/messenger/sub_60.784_11.765.49c03d0.png +0 -0
  78. package/dist/assets/img/emoji/messenger/sub_60.784_27.451.46b54e5.png +0 -0
  79. package/dist/assets/img/emoji/messenger/sub_60.784_29.412.e36d284.png +0 -0
  80. package/dist/assets/img/emoji/messenger/sub_60.784_3.922.bb191b0.png +0 -0
  81. package/dist/assets/img/emoji/messenger/sub_60.784_35.294.af7b927.png +0 -0
  82. package/dist/assets/img/emoji/messenger/sub_60.784_7.843.98f2a99.png +0 -0
  83. package/dist/assets/img/empty.29bed2d.svg +0 -23
  84. package/dist/assets/img/empty_1.0b98af0.svg +0 -11
  85. package/dist/assets/img/examplte_logo.f86535f.svg +0 -1
  86. package/dist/assets/img/external-link.90577cc.svg +0 -1
  87. package/dist/assets/img/fb_inverse_circle.36c2cfc.png +0 -0
  88. package/dist/assets/img/ghn.070f8e3.svg +0 -17
  89. package/dist/assets/img/ghtk.326eac1.svg +0 -18
  90. package/dist/assets/img/image_default.be0698b.png +0 -0
  91. package/dist/assets/img/info.ebce7ea.svg +0 -1
  92. package/dist/assets/img/loader.37000a7.svg +0 -1
  93. package/dist/assets/img/loading_static.a27f0ac.svg +0 -10
  94. package/dist/assets/img/main-service1.d536cc2.png +0 -0
  95. package/dist/assets/img/main-service2.9b99549.png +0 -0
  96. package/dist/assets/img/not_found_search.2ddb8d1.png +0 -0
  97. package/dist/assets/img/package.8d37d0f.svg +0 -5
  98. package/dist/assets/img/package_fade.763c001.svg +0 -1
  99. package/dist/assets/img/rating--average.1ea492c.svg +0 -12
  100. package/dist/assets/img/rating--great.e3d712e.svg +0 -16
  101. package/dist/assets/img/rating--poor.2938a25.svg +0 -12
  102. package/dist/assets/img/search.f9705ff.svg +0 -1
  103. package/dist/assets/img/send.6db52a9.svg +0 -3
  104. package/dist/assets/img/shop.f7dc7d8.svg +0 -1
  105. package/dist/assets/img/shop_faded.df7845a.svg +0 -1
  106. package/dist/assets/img/stars_v2.79cf728.png +0 -0
  107. package/dist/assets/img/subiz-mono.8fbc067.svg +0 -3
  108. package/dist/assets/img/x.26fdfa3.svg +0 -5
  109. package/dist/malicious-app.js +0 -17
  110. package/dist/screenshoot.html +0 -7
  111. package/dist/screenshoot.js +0 -11988
@@ -9,12 +9,15 @@ import {h} from 'preact'
9
9
  export default class ChatButton extends Com {
10
10
  constructor(props) {
11
11
  super(props)
12
- this.setState({
13
- opacity: 0, // wait for image to load first before setting opacity to 1
12
+ this.state = {
13
+ hasPreviewDom: false,
14
+ previewExtraCls: 'hidden',
14
15
  previewEvent: {},
15
- displayPreview: false,
16
- })
17
- this.loadGreeting()
16
+ ready: false,
17
+ }
18
+
19
+ this.readyGate = new sb.Gate()
20
+ this.prepareButtonImage()
18
21
 
19
22
  this.props.store.onUpdate(this, (_) => this.forceUpdate())
20
23
  this.props.store.onConvoEvent(this, (ev) => {
@@ -39,9 +42,33 @@ export default class ChatButton extends Com {
39
42
  let message = sb.get(ev, 'data.message.text') || ''
40
43
  if (!message && isquickreplies) return
41
44
  }
45
+ let screen = sb.get(this.props.store.matchLastview(), 'screen')
46
+ let minimized = sb.get(this.props.store.matchLastview(), 'minimized')
47
+ if (screen === 'widget') return
48
+ if (minimized && Date.now() - minimized < Ms30Mins) return
42
49
 
43
- this.setState({displayPreview: true, previewEvent: ev})
50
+ if (this.props.store.isMobile()) {
51
+ // dont show greeting fro mobile anymore
52
+ // need setState previewEvent for display 1 unread in mobile
53
+ this.props.store.updateLastview({previewEvent: ev})
54
+ this.forceUpdate()
55
+ return
56
+ }
57
+
58
+ this.props.store.updateLastview({screen: 'preview', previewEvent: ev})
59
+ this.forceUpdate()
44
60
  })
61
+ this.props.store.onLastview(this, () => {
62
+ let screen = sb.get(this.props.store.matchLastview(), 'screen')
63
+ let previewEvent = sb.get(this.props.store.matchLastview(), 'previewEvent') || {}
64
+ let isOpen = screen === 'preview' && sb.size(previewEvent) > 0
65
+ if (isOpen) {
66
+ this.makeAnimateShowPreview(previewEvent)
67
+ } else {
68
+ this.makeAnimateHidePreview()
69
+ }
70
+ })
71
+ this.loadGreeting()
45
72
  }
46
73
 
47
74
  fixMissingSetting(chatbox) {
@@ -83,29 +110,18 @@ export default class ChatButton extends Com {
83
110
  if (hiddenButtonDesktop) return null
84
111
 
85
112
  let cls = 'widget-layout widget-layout--' + position
86
- let opacity = this.state.opacity
87
113
  if (!this.props.isShow) {
88
114
  cls += ' widget-layout--hidden'
89
- opacity = 0
90
115
  }
91
116
 
92
117
  return (
93
- <div class={cls} style={'opacity: ' + opacity + ' !important'}>
118
+ <div class={cls}>
94
119
  {this.renderPreview()}
95
120
  {this.renderButton()}
96
121
  </div>
97
122
  )
98
123
  }
99
124
 
100
- // this method could be call from the parent
101
- ClearPreview() {
102
- this.setState({previewEvent: {}})
103
- }
104
-
105
- ClosePreview() {
106
- this.setState({displayPreview: false})
107
- }
108
-
109
125
  pickBestGreeting() {
110
126
  let convoM = this.props.store.matchConvo('*')
111
127
  let unseenConvo = sb
@@ -134,32 +150,69 @@ export default class ChatButton extends Com {
134
150
  return unseenConvo[0].last_message_sent
135
151
  }
136
152
 
137
- // this method could be call from the parent
138
- ShowGreeting() {
153
+ showPreview(ev) {
154
+ this.props.store.updateLastview({screen: 'preview', previewEvent: ev})
155
+ }
156
+
157
+ async makeAnimateShowPreview(ev) {
158
+ await this.readyGate.entry()
159
+ let agent = this.getAgentOfPreviewEvent(ev)
160
+ await sb.preloadImg(agent.avatar_url)
161
+ if (this.state.hasPreviewDom) {
162
+ this.setState({previewExtraCls: 'hidden'})
163
+ if (this._animateTimeout) clearTimeout(this._animateTimeout)
164
+ this._animateTimeout = setTimeout(() => {
165
+ this.setState({previewExtraCls: '', previewEvent: ev})
166
+ }, 200)
167
+ return
168
+ }
139
169
  this.setState({
140
- previewEvent: this.pickBestGreeting(),
141
- displayPreview: true,
170
+ hasPreviewDom: true,
171
+ previewExtraCls: 'hidden',
172
+ previewEvent: {},
142
173
  })
174
+ if (this._animateTimeout) clearTimeout(this._animateTimeout)
175
+ this._animateTimeout = setTimeout(() => {
176
+ this.setState({previewExtraCls: '', previewEvent: ev})
177
+ }, 200)
178
+ }
179
+
180
+ makeAnimateHidePreview() {
181
+ if (this._animateTimeout) clearTimeout(this._animateTimeout)
182
+ this.setState({previewExtraCls: 'hidden'})
183
+ this._animateTimeout = setTimeout(() => {
184
+ this.setState({hasPreviewDom: false, previewEvent: {}})
185
+ }, 200)
186
+ }
187
+
188
+ // this method could be call from the parent
189
+ ShowGreeting() {
190
+ let ev = this.pickBestGreeting()
191
+ this.showPreview(ev)
143
192
  }
144
193
 
145
194
  HideGreeting() {
146
- this.setState({previewEvent: {}})
195
+ this.props.store.updateLastview({screen: 'button', minimized: Date.now(), previewEvent: {}})
147
196
  }
148
197
 
149
198
  loadGreeting() {
199
+ let minimized = sb.get(this.props.store.matchLastview(), 'minimized')
200
+ let screen = sb.get(this.props.store.matchLastview(), 'screen')
201
+ if (screen !== 'preview') return
202
+ if (minimized && Date.now() - minimized < Ms30Mins) return
203
+ let previewEvent = sb.get(this.props.store.matchLastview(), 'previewEvent') || {}
204
+ if (sb.size(previewEvent) > 0) {
205
+ this.showPreview(previewEvent)
206
+ return
207
+ }
208
+
150
209
  let accountSetting = this.props.plugin.chatbox
151
- // dont show preview if the user has previously closed it
152
- if (new sb.Date().getTime() - this.props.store.matchClosePreviewAt() < Ms30Mins) return
153
210
 
154
211
  let greeting = sb.get(accountSetting, 'greeting', {})
155
212
  let text = this.props.t.i18n(sb.get(greeting, 'greeting', {})) || greeting.message
156
213
 
157
214
  let enabled = greeting.enabled
158
- if (this.props.store.isMobile()) {
159
- enabled = enabled && !sb.get(greeting, 'mobile_disabled')
160
- }
161
215
  if (!text || !enabled) {
162
- this.setState({previewEvent: {}})
163
216
  return
164
217
  }
165
218
  this.ShowGreeting()
@@ -184,14 +237,14 @@ export default class ChatButton extends Com {
184
237
  }
185
238
 
186
239
  onPreviewClick(e) {
187
- let cid = sb.convoOfEv(this.state.previewEvent)
240
+ let previewEvent = sb.get(this.props.store.matchLastview(), 'previewEvent') || {}
241
+ let cid = sb.convoOfEv(previewEvent)
188
242
  if (!cid) {
189
243
  let convos = sb.map(this.props.store.matchConvo('*'))
190
244
  let convo = sb.find(convos, (convo) => !sb.isConvoSeen(convo, this.props.store.matchUser().id))
191
245
  if (!convo) convo = convos[0] || {}
192
246
  cid = convo.id
193
247
  }
194
- this.closePreview(e)
195
248
  this.props.onClick && this.props.onClick({view: 'chat', cid})
196
249
  }
197
250
 
@@ -201,11 +254,11 @@ export default class ChatButton extends Com {
201
254
  return <div class='chat-button--unread'>{unreadcount}</div>
202
255
  }
203
256
 
204
- renderPreview() {
257
+ getAgentOfPreviewEvent(ev) {
205
258
  let account = this.props.store.matchAccount()
206
- if (!account) return null
207
259
  let accountSetting = this.props.plugin.chatbox
208
- let byid = sb.get(this.state.previewEvent, 'by.display_id', sb.get(this.state.previewEvent, 'by.id'))
260
+ let previewEvent = ev
261
+ let byid = sb.get(previewEvent, 'by.display_id', sb.get(previewEvent, 'by.id'))
209
262
  // let agent = sb.cloneDeep(this.props.store.matchAgent(byid)) || {}
210
263
  // somehow store.matchAgent(id) return undefined so we try to backup with store.matchAgent('*')[id]
211
264
  let agent = this.props.store.matchAgent(byid)
@@ -213,10 +266,6 @@ export default class ChatButton extends Com {
213
266
  agent = sb.cloneDeep(agent)
214
267
  agent.fullname = agent.fullname || sb.get(account, 'name') || this.props.t('agent')
215
268
  agent.avatar_url = agent.avatar_url || account.logo_url || require('../../assets/img/image_default.png')
216
- let position = sb.get(accountSetting, 'desktop_button.position') || 'right'
217
- if (this.props.store.isMobile()) {
218
- position = sb.get(accountSetting, 'mobile_button.position') || position
219
- }
220
269
 
221
270
  if (accountSetting && accountSetting.hide_agent_profile) {
222
271
  agent = Object.assign({}, account, {
@@ -225,44 +274,123 @@ export default class ChatButton extends Com {
225
274
  })
226
275
  }
227
276
 
228
- let cls = 'widget-preview widget-preview--' + position
229
- if (!this.state.displayPreview) cls += ' widget-preview--hidden'
277
+ return agent
278
+ }
230
279
 
231
- var tags = this.renderMessagePreview(this.state.previewEvent)
232
- if (sb.size(tags) === 0) cls += ' widget-preview--hidden'
280
+ renderPreview() {
281
+ if (this.props.store.isMobile()) return
282
+ if (!this.state.hasPreviewDom) return
283
+ let previewEvent = this.state.previewEvent
284
+ let agent = this.getAgentOfPreviewEvent(previewEvent)
285
+
286
+ let account = this.props.store.matchAccount()
287
+ let accountSetting = this.props.plugin.chatbox
288
+ let position = sb.get(accountSetting, 'desktop_button.position') || 'right'
289
+ if (this.props.store.isMobile()) {
290
+ position = sb.get(accountSetting, 'mobile_button.position') || position
291
+ }
292
+
293
+ let tags = this.renderMessagePreview(previewEvent)
294
+ let attachments = sb.get(previewEvent, 'data.message.attachments') || []
295
+ let quickReplies = sb.find(attachments, (att) => att.type === 'quick_replies')
296
+ quickReplies = sb.get(quickReplies, 'quick_replies') || []
297
+ let imgAtt = sb.find(attachments, (att) => sb.includes(att.mimetype, 'image') && att.url)
298
+ let uri = sb.get(imgAtt, 'url') || ''
299
+ if (
300
+ uri &&
301
+ !uri.startsWith('https://') &&
302
+ !uri.startsWith('http://') &&
303
+ !uri.startsWith('content://') &&
304
+ !uri.startsWith('data:')
305
+ ) {
306
+ uri = this.props.store.FileURL + uri
307
+ }
233
308
 
234
309
  return (
235
- <div class={cls}>
236
- <div class='widget-preview--body'>
237
- <img
238
- alt='widget avatar'
239
- class='widget-preview--avatar'
240
- src={this.props.store.replaceFileUrl(agent.avatar_url)}
241
- onError={(ev) => this.onProfileError(ev)}
242
- onload={(_) => this.onProfileReady()}
243
- />
244
- <div class='widget-preview--content' onClick={(e) => this.onPreviewClick(e)}>
245
- <div class='widget-preview--title'>{agent.fullname}</div>
246
- <p class='widget-preview--message'>{tags}</p>
247
- </div>
310
+ <div class={`widget-preview-new-wrapper ${this.state.previewExtraCls}`}>
311
+ <div class={`widget-preview-x-wrapper ${position}`}>
248
312
  <div class='widget-preview--btn-close' onClick={(e) => this.closePreview(e)} />
249
313
  </div>
250
-
251
- <div class='widget-preview--action' onClick={(e) => this.onPreviewClick(e)}>
252
- <div class='widget-preview--action-text'>{this.props.t('chat_now')}</div>
314
+ <div class={`widget-preview-text-message ${position}`} onClick={(e) => this.onPreviewClick(e)}>
315
+ <div class='widget-preview-text-message--header'>
316
+ <img
317
+ alt='widget avatar'
318
+ class='widget-preview--avatar'
319
+ src={this.props.store.replaceFileUrl(agent.avatar_url)}
320
+ />
321
+ <div class='widget-preview-text-message--author'>
322
+ <div class='widget-preview-text-message--author-text'>{agent.fullname}</div>
323
+ {accountSetting.is_online && (
324
+ <div class='badge badge--success'>
325
+ <div class='dot dot--success mr-1' />
326
+ {'ONLINE'}
327
+ </div>
328
+ )}
329
+ </div>
330
+ </div>
331
+ <div class='widget-preview-text-message--content'>{tags}</div>
253
332
  </div>
333
+ {uri && (
334
+ <div class={`widget-preview-image-wrapper ${position}`}>
335
+ <div class='widget-preview-image-item' onClick={() => this.onPreviewClick()}>
336
+ <img src={uri} />
337
+ </div>
338
+ </div>
339
+ )}
340
+ {sb.size(quickReplies) > 0 && (
341
+ <div class={`widget-preview-quick-reply-wrapper ${position}`}>
342
+ {sb.map(quickReplies, (reply) => {
343
+ if (!(reply.title || '').trim()) return
344
+ return (
345
+ <div
346
+ class='widget-preview-quick-reply-item'
347
+ onClick={() => {
348
+ // copy chooseQuickReply function from Event.js
349
+ this.props.onSelectQuickReply({
350
+ text: reply.title,
351
+ format: 'plaintext',
352
+ conversation_id: sb.convoOfEv(previewEvent),
353
+ attachments: [
354
+ {
355
+ type: 'postback',
356
+ postback: {
357
+ event_id: previewEvent.id,
358
+ payload: reply.payload,
359
+ },
360
+ },
361
+ ],
362
+ })
363
+ }}
364
+ >
365
+ {reply.title}
366
+ </div>
367
+ )
368
+ })}
369
+ </div>
370
+ )}
254
371
  </div>
255
372
  )
256
373
  }
257
374
 
258
- onProfileReady() {
259
- if (this._buttonLoaded) this.setState({opacity: 1})
260
- this._profileLoaded = true
261
- }
375
+ async prepareButtonImage() {
376
+ let accountSetting = this.props.plugin.chatbox
377
+ accountSetting = this.fixMissingSetting(accountSetting)
378
+ let imgsrc =
379
+ sb.get(accountSetting, 'desktop_button.background_image_url') ||
380
+ sb.get(accountSetting, 'desktop_button.background_mask_url') ||
381
+ defaultBubble
262
382
 
263
- onButtonReady() {
264
- if (this._profileLoaded) this.setState({opacity: 1})
265
- this._buttonLoaded = true
383
+ if (this.props.store.isMobile()) {
384
+ imgsrc =
385
+ sb.get(accountSetting, 'mobile_button.background_image_url') ||
386
+ sb.get(accountSetting, 'mobile_button.background_mask_url') ||
387
+ imgsrc
388
+ }
389
+
390
+ imgsrc = this.props.store.replaceFileUrl(imgsrc)
391
+ await sb.preloadImg(imgsrc)
392
+ this.setState({ready: true})
393
+ this.readyGate.open()
266
394
  }
267
395
 
268
396
  renderButton() {
@@ -372,19 +500,13 @@ export default class ChatButton extends Com {
372
500
 
373
501
  `
374
502
 
503
+ if (!this.state.ready) cls += ' hidden'
504
+
375
505
  return (
376
506
  <div class={cls} onClick={(e) => this.onClick(e)}>
377
507
  <div class={whitebgcls} style={whiteBgStyle}></div>
378
508
  <div class='chat-button--img-container' style={imgcontainerstyle}>
379
- <img
380
- width='70'
381
- height='70'
382
- alt='chat button'
383
- class='chat-button--img'
384
- onload={() => this.onButtonReady()}
385
- src={imgsrc}
386
- onError={(e, a) => this.onChatButtonError(e, a)}
387
- />
509
+ <img width='70' height='70' alt='chat button' class='chat-button--img' src={imgsrc} />
388
510
  </div>
389
511
  {this.renderUnread()}
390
512
  </div>
@@ -440,9 +562,7 @@ export default class ChatButton extends Com {
440
562
  e.preventDefault()
441
563
  e.stopPropagation()
442
564
  }
443
- this.setState({displayPreview: false})
444
- this.props.store.closePreview()
445
- this.forceUpdate()
565
+ this.HideGreeting()
446
566
  }
447
567
  }
448
568
 
@@ -46,6 +46,7 @@ export default class Widget extends Com {
46
46
  msgInput = createRef()
47
47
  componentDidMount() {
48
48
  if (!this.props.store.isMobile()) this.focusInput()
49
+
49
50
  this.updateFormDataFields()
50
51
  }
51
52
 
@@ -63,7 +64,6 @@ export default class Widget extends Com {
63
64
  if (prevProp.cid !== this.props.cid) {
64
65
  this.closeOrder()
65
66
  this.setState({clickedJoin: false, quickReplyMessage: {}})
66
- // this.props.store.setLastview('chat', { cid: this.props.cid })
67
67
  sb.setTimeout(() => {
68
68
  // focus Events then focus MessageInput
69
69
  // if there is any pending form, MessageInput will be disabled
@@ -132,8 +132,10 @@ export default class Widget extends Com {
132
132
  else $body = this.renderChat()
133
133
 
134
134
  let position = sb.get(accountSetting, 'desktop_button.position') || 'right'
135
- if (this.props.store.isMobile()) position = sb.get(accountSetting, 'mobile_button.position', position)
135
+ if (this.props.store.isMobile() || this.props.store.isEmbededChatMode())
136
+ position = sb.get(accountSetting, 'mobile_button.position', position)
136
137
  let cls = 'widget-container widget-container--' + position
138
+ if (this.props.store.isEmbededChatMode()) cls += ' embed-chat'
137
139
  if (!this.props.store.isMobile() && sb.get(accountSetting, 'window_mode') === 'full')
138
140
  cls += ' widget-container--full'
139
141
  if (!this.props.isShow) cls += ' widget-container--hide'
@@ -45,9 +45,6 @@ export default class Main extends Com {
45
45
 
46
46
  this.setState({
47
47
  cssReady: false,
48
- isShowWidget: false,
49
- widgetView: '',
50
- widgetConvoId: '',
51
48
  })
52
49
 
53
50
  this.checkExpand()
@@ -62,16 +59,6 @@ export default class Main extends Com {
62
59
  })
63
60
  this.refreshCSS()
64
61
  this.setState({cssReady: true})
65
-
66
- if (this.props.store.matchIsOpen() && this.props.store.matchLastview().view) {
67
- if (this.props.store.isMobile()) return // dont auto expand widget on mobile
68
- this.showWidget(
69
- Object.assign({}, this.props.store.matchLastview().opts, {view: this.props.store.matchLastview().view}),
70
- )
71
- } else {
72
- restoreViewport()
73
- if (!this.state.isShowWidget) this.hideWidget()
74
- }
75
62
  }
76
63
 
77
64
  render() {
@@ -88,23 +75,29 @@ export default class Main extends Com {
88
75
  return null
89
76
  }
90
77
  if (typeof sbz_info != 'undefined') sbz_info.render_chatbox_main = sb.Date.now()
78
+ let lastview = this.props.store.matchLastview() || {}
79
+ let isShowWidget = lastview.screen === 'widget'
80
+ let widgetView = lastview.view
81
+ let widgetConvoId = sb.get(lastview, 'opts.cid')
82
+
91
83
  return (
92
- <div class={this.props.store.isMobile() ? 'mobile' : 'desktop'}>
84
+ <div class={this.props.store.isMobile() || this.props.store.isEmbededChatMode() ? 'mobile' : 'desktop'}>
93
85
  <ChatButton
94
86
  plugin={this.props.plugin}
95
87
  store={this.props.store}
96
88
  key='chat_button'
97
89
  ref={this.refButton}
98
- isShow={!this.state.isShowWidget}
90
+ isShow={!isShowWidget}
99
91
  onClick={(e) => this.showWidget(e)}
92
+ onSelectQuickReply={(msg) => this.onSelectQuickReply(msg)}
100
93
  t={this.props.t}
101
94
  />
102
95
  <Widget
103
96
  plugin={this.props.plugin}
104
97
  store={this.props.store}
105
- view={this.state.widgetView}
106
- cid={this.state.widgetConvoId}
107
- isShow={this.state.isShowWidget}
98
+ view={widgetView}
99
+ cid={widgetConvoId}
100
+ isShow={isShowWidget}
108
101
  onClose={(a) => this.onWidgetClose(a)}
109
102
  onGo={(a) => this.showWidget(a)}
110
103
  t={this.props.t}
@@ -124,7 +117,8 @@ export default class Main extends Com {
124
117
  if (!$widget) return
125
118
  if ($widget.wasFormInputed) return
126
119
  if (ev.type !== 'message_sent') return
127
- if (!this.state.isShowWidget) return
120
+ let isShowWidget = sb.get(this.props.store.matchLastview(), 'screen') === 'widget'
121
+ if (!isShowWidget) return
128
122
 
129
123
  let byType = sb.get(ev, 'by.type')
130
124
  let byid = sb.get(ev, 'by.id') || ''
@@ -140,50 +134,39 @@ export default class Main extends Com {
140
134
  if (this.props.store.isMobile()) return
141
135
 
142
136
  if (this.props.plugin._no_auto_expand) return
143
- // dont try to expand widget if user has already closed it
144
- if (this.props.store.matchHasManuallyClosed()) return
137
+ return
145
138
 
146
139
  let convoM = this.props.store.matchConvo('*')
147
140
  let redconvo = sb.find(convoM, (convo) => !sb.isConvoSeen(convo, this.props.store.matchUser().id))
148
141
  if (!redconvo || !redconvo.id) return
149
- if (!this.state.isShowWidget) this.showWidget({view: 'chat', cid: redconvo.id})
142
+ let isShowWidget = sb.get(this.props.store.matchLastview(), 'screen') === 'widget'
143
+ if (!isShowWidget) this.showWidget({view: 'chat', cid: redconvo.id})
150
144
  }
151
145
 
152
- getCurrentView() {
153
- return {
154
- is_show_widget: this.state.isShowWidget,
155
- conversation_id: this.state.widgetConvoId,
156
- view: this.state.widgetView,
157
- }
146
+ onSelectQuickReply(message) {
147
+ this.showWidget({view: 'chat', cid: message.conversation_id})
148
+ this.forceUpdate(() => {
149
+ this.refWidget.current && this.refWidget.current.sendMessage(message)
150
+ })
158
151
  }
159
152
 
160
153
  async showWidget(p) {
161
- this.refButton.current && this.refButton.current.ClearPreview()
162
- if (!p.view) {
163
- let lastview = this.props.store.matchLastview() || {}
164
- p.view = sb.get(lastview, 'view')
165
- p.cid = sb.get(lastview, 'opts.cid')
166
- }
167
- p = p || {}
154
+ //this.refButton.current && this.refButton.current.ClearPreview()
168
155
 
156
+ let cid = p.cid
157
+ let newView = {screen: 'widget', minimized: 0, view: p.view, previewEvent: {}, opts: {cid}}
169
158
  if (!p.view && !p.cid) {
170
159
  let convoM = this.props.store.matchConvo('*')
171
160
  let redconvo = sb.find(convoM, (convo) => !sb.isConvoSeen(convo, this.props.store.matchUser().id))
172
161
  // try to show first convo
173
- p.cid = sb.get(redconvo, 'id', sb.get(convoM, '0.id'))
174
- p.view = 'chat'
162
+ cid = sb.get(redconvo, 'id', sb.get(convoM, '0.id'))
163
+ newView.view = 'chat'
164
+ newView.opts = {cid}
175
165
  }
176
166
 
177
167
  if (this.props.store.isMobile()) modifyViewport()
178
- this.setState({widgetConvoId: p.cid, widgetView: p.view})
179
- this.props.store.setIsOpen(true)
180
-
181
- // lazy load widget
182
- // if (!Widget) Widget = (await loadWidget()).default
183
-
168
+ this.props.store.updateLastview(newView)
184
169
  this.forceUpdate()
185
- this.setState({isShowWidget: true})
186
- this.props.store.setLastview(p.view, {cid: p.cid})
187
170
  }
188
171
 
189
172
  applyMessage(text) {
@@ -204,17 +187,12 @@ export default class Main extends Com {
204
187
 
205
188
  hideWidget() {
206
189
  restoreViewport()
207
- this.props.store.setIsOpen(false)
208
- this.setState({isShowWidget: false})
190
+ this.props.store.updateLastview({screen: 'button', previewEvent: {}})
191
+ this.forceUpdate()
209
192
  }
210
193
 
211
194
  onWidgetClose() {
212
- this.refButton.current && this.refButton.current.ClearPreview()
213
- // need call closePreview if not this will display default agent avatar and account name
214
- this.refButton.current && this.refButton.current.ClosePreview()
215
- this.props.store.setHasManuallyClosed()
216
195
  this.hideWidget()
217
-
218
196
  // emit event close widget
219
197
  this.props.store.emitCloseWidgetEvent()
220
198
  }
@@ -359,6 +359,27 @@ export default class Store {
359
359
  }
360
360
 
361
361
  matchPlugin = () => {
362
+ if (this.is_embed_chat_mode) {
363
+ // only show widget
364
+ let pluginM = this.cache.match('plugin')
365
+ if (sb.size(this.forcePlugins) > 0) pluginM = this.forcePlugins
366
+
367
+ let chatboxs = sb.filter(pluginM, (plugin, id) => plugin && plugin.type === 'chatbox')
368
+ if (sb.size(chatboxs) === 0) {
369
+ console.error('no chatbox found.')
370
+ return
371
+ }
372
+
373
+ let chatbox = sb.find(chatboxs, (chatbox) => !!chatbox)
374
+ if (!chatbox) {
375
+ // take the first chatbox
376
+ chatbox = chatboxs[0]
377
+ }
378
+
379
+ chatbox = sb.cloneDeep(chatbox)
380
+ chatbox.triggers = undefined
381
+ return {[chatbox.id]: chatbox}
382
+ }
362
383
  if (sb.size(this.forcePlugins) > 0) return sb.cloneDeep(this.forcePlugins)
363
384
  return sb.cloneDeep(this.cache.match('plugin')) || {}
364
385
  }
@@ -462,17 +483,10 @@ export default class Store {
462
483
 
463
484
  // last widget screen
464
485
  matchLastview = () => {
465
- let lastView = this.kv.match('lastview') || {}
466
-
467
- // make sure last view of correct user
468
- let user = this.cache.match('user') || {}
469
- if (!user.id) return {}
470
- if (lastView.user_id != user.id) return {}
471
- return lastView
486
+ return this.kv.match('lastview') || {}
472
487
  }
473
488
 
474
489
  matchIsOpen = () => !!this.kv.match('isopen') // is widget currently open
475
- matchHasManuallyClosed = () => !!this.kv.match('hasManuallyClosed') // true if user has ever closed the widget
476
490
  matchClosePreviewAt = () => this.kv.match('close_preview_at') || 0
477
491
 
478
492
  checkFormToken = (accid, token) => this.api.checkFormToken(accid, token)
@@ -563,6 +577,28 @@ export default class Store {
563
577
  )
564
578
  }
565
579
 
580
+ standardlizeLastview() {
581
+ let lastView = this.kv.match('lastview') || {}
582
+
583
+ // make sure last view of correct user
584
+ let user = this.cache.match('user') || {}
585
+ if (!user.id) lastView = {}
586
+ if (lastView.user_id != user.id) lastView = {}
587
+
588
+ if (!lastView.screen) {
589
+ lastView.screen = 'preview' // mode preview allow load default greeting
590
+
591
+ // backward compatible for user currently view chat
592
+ if (lastView.view === 'list' || lastView.view === 'chat') {
593
+ lastView.screen = 'widget'
594
+ }
595
+ }
596
+
597
+ // mobile always has screen button initially
598
+ if (this.isMobile()) lastView.screen = 'button'
599
+ this.kv.put('lastview', lastView)
600
+ }
601
+
566
602
  async _initResource(accid, auth, realtimep) {
567
603
  this.g_sync = {}
568
604
 
@@ -591,6 +627,7 @@ export default class Store {
591
627
  this.seenLoop()
592
628
  if (typeof sbz_info != 'undefined') sbz_info.store_9 = true
593
629
  await this.reconnect({user: user}) // already fetched
630
+ this.standardlizeLastview()
594
631
  if (typeof sbz_info != 'undefined') sbz_info.store_10 = true
595
632
  this.readyGate.open()
596
633
  this.reconnectWs(accid, user.id)
@@ -1212,20 +1249,21 @@ export default class Store {
1212
1249
  onResetCSS = (o, cb) => this.pubsub.on2(o, 'resetCSS', cb)
1213
1250
  onHighlight = (o, cb) => this.pubsub.on2(o, 'highlight', cb)
1214
1251
 
1215
- setHasManuallyClosed = () => this.kv.put('hasManuallyClosed', true)
1216
- setIsOpen = (isopen) => {
1217
- let lastisopen = this.kv.match('isopen')
1218
- this.kv.put('isopen', isopen)
1219
- if (!lastisopen && isopen) {
1220
- sendEventToThirdParty('SUBIZ Chat', 'Opened', 'Subiz Windows')
1221
- }
1222
- }
1223
1252
  closePreview = () => this.kv.put('close_preview_at', new sb.Date().getTime())
1224
- setLastview = (view, opts) => {
1253
+ updateLastview = (obj) => {
1225
1254
  let user = this.cache.match('user') || {}
1226
1255
  if (!user.id) return // store not init
1227
- this.kv.put('lastview', {user_id: user.id, view, opts})
1256
+ let lastview = this.matchLastview() || {}
1257
+ let newview = {
1258
+ ...lastview,
1259
+ ...obj,
1260
+ user_id: user.id,
1261
+ }
1262
+
1263
+ this.kv.put('lastview', newview)
1264
+ this.pubsub.publish('lastview', newview, lastview)
1228
1265
  }
1266
+ onLastview = (o, cb) => this.pubsub.on2(o, 'lastview', cb)
1229
1267
 
1230
1268
  matchLastPluginRun(pluginid) {
1231
1269
  let log = this.kv.match('plugin_run_logs') || {}