@subiz/widget-v4 4.1.113 → 4.1.115

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 (37) hide show
  1. package/assets/less/message.less +148 -9
  2. package/compiled_src/chatbox/Event.js +131 -2
  3. package/compiled_src/chatbox/Events.js +24 -11
  4. package/compiled_src/chatbox/ImageAttachments.js +1 -1
  5. package/compiled_src/chatbox/InfoMessage.js +1 -0
  6. package/compiled_src/chatbox/PoweredBy.js +1 -0
  7. package/compiled_src/chatbox/Widget.js +16 -11
  8. package/compiled_src/lib.js +3 -0
  9. package/compiled_src/store/store.js +25 -1
  10. package/dist/195ec5442e3.278.js +1 -0
  11. package/dist/{19565267fde.app.js → 195ec5442e3.app.js} +1 -1
  12. package/dist/{19565267fde.index.html → 195ec5442e3.index.html} +1 -1
  13. package/dist/{19565267fde.library.html → 195ec5442e3.library.html} +1 -1
  14. package/dist/{19565267fde.library.js → 195ec5442e3.library.js} +1 -1
  15. package/dist/{19565267fde.preview.html → 195ec5442e3.preview.html} +1 -1
  16. package/dist/{19565267fde.preview.js → 195ec5442e3.preview.js} +1 -1
  17. package/dist/{19565267fde.webview.js → 195ec5442e3.webview.js} +1 -1
  18. package/dist/278.js +1 -1
  19. package/dist/HASH +1 -1
  20. package/package.json +3 -3
  21. package/src/chatbox/Event.js +151 -28
  22. package/src/chatbox/Events.js +27 -13
  23. package/src/chatbox/ImageAttachments.js +1 -1
  24. package/src/chatbox/InfoMessage.js +3 -0
  25. package/src/chatbox/PoweredBy.js +2 -0
  26. package/src/chatbox/Widget.js +10 -3
  27. package/src/lib.js +4 -0
  28. package/src/store/store.js +29 -1
  29. package/dist/19565267fde.278.js +0 -1
  30. /package/dist/{19565267fde.127.js → 195ec5442e3.127.js} +0 -0
  31. /package/dist/{19565267fde.132.js → 195ec5442e3.132.js} +0 -0
  32. /package/dist/{19565267fde.185.js → 195ec5442e3.185.js} +0 -0
  33. /package/dist/{19565267fde.27.js → 195ec5442e3.27.js} +0 -0
  34. /package/dist/{19565267fde.459.js → 195ec5442e3.459.js} +0 -0
  35. /package/dist/{19565267fde.490.js → 195ec5442e3.490.js} +0 -0
  36. /package/dist/{19565267fde.74.js → 195ec5442e3.74.js} +0 -0
  37. /package/dist/{19565267fde.997.js → 195ec5442e3.997.js} +0 -0
@@ -25,7 +25,9 @@ import {h, createRef} from 'preact'
25
25
  export default class Event extends Com {
26
26
  constructor(props) {
27
27
  super(props)
28
+ this.props.store.onConvoEvent(this, (ev) => this.onEvent(ev))
28
29
  this.refForm = createRef()
30
+ this.state = {pongs: sb.get(this.props.event, 'ev.data.message.pongs') || []}
29
31
  }
30
32
 
31
33
  render() {
@@ -49,6 +51,21 @@ export default class Event extends Com {
49
51
  this.refForm.current && this.refForm.current.focus()
50
52
  }
51
53
 
54
+ onEvent(ev) {
55
+ if (ev.id !== sb.get(this.props.event, 'ev.id')) return
56
+ this.setState({pongs: sb.get(ev, 'data.message.pongs') || []})
57
+ }
58
+
59
+ hasLike() {
60
+ let {pongs} = this.state
61
+ return !!sb.find(pongs, (pong) => pong.type === 'like')
62
+ }
63
+
64
+ hasDislike() {
65
+ let {pongs} = this.state
66
+ return !!sb.find(pongs, (pong) => pong.type === 'dislike')
67
+ }
68
+
52
69
  chooseQuickReply(reply) {
53
70
  let ev = sb.get(this.props.event, 'ev', {})
54
71
  let message = {text: reply.title, format: 'plaintext'}
@@ -143,44 +160,53 @@ export default class Event extends Com {
143
160
  </div>
144
161
  )
145
162
  }
163
+ if (this.props.store.isAIDevMode()) $time = null
146
164
 
147
165
  let t = new sb.Date(ev.created).toLocaleString()
148
166
  // message of agent
149
167
  if (sb.get(ev, 'by.type') !== 'user') {
150
168
  let $sender = null
151
169
  if (this.props.event.mode === 'lead') $sender = this.renderAgentAvatar(ev)
170
+ let innercls = 'agent-convo-event-inner'
171
+ if (this.props.isLast) innercls += ' last'
152
172
  return (
153
173
  <div class='convo-event agent-message' title={t}>
154
174
  {$time}
155
175
  {$sender}
156
- <TextMessage store={this.props.store} ev={ev} onLink={this.props.onLink} />
157
- <ImageAttachments store={this.props.store} ev={ev} />
158
- <AudioAttachments store={this.props.store} ev={ev} />
159
- <VideoAttachments store={this.props.store} ev={ev} />
160
- <FileAttachments store={this.props.store} ev={ev} />
161
- <ButtonAttachments store={this.props.store} ev={ev} t={this.props.t} />
162
- <FormAttachments
163
- store={this.props.store}
164
- onSubmit={this.props.onFormSubmit}
165
- ref={this.refForm}
166
- ev={ev}
167
- t={this.props.t}
168
- />
169
- <FormSubmitAttachments store={this.props.store} ev={ev} t={this.props.t} />
170
- <GenericTemplateAttachments
171
- store={this.props.store}
172
- ev={ev}
173
- onSelect={(e) => this.choosePostbackBtn(e)}
174
- t={this.props.t}
175
- />
176
- <ProductAttachments
177
- t={this.props.t}
178
- onPostback={(e) => this.choosePostbackBtn(e)}
179
- store={this.props.store}
180
- ev={ev}
181
- />
182
- <OrderAttachments t={this.props.t} store={this.props.store} ev={ev} />
183
- <QuickReplies store={this.props.store} ev={ev} onSelect={(e) => this.chooseQuickReply(e)} />
176
+ <div class='agent-convo-event-wrapper'>
177
+ <div class={innercls}>
178
+ <TextMessage store={this.props.store} ev={ev} onLink={this.props.onLink} />
179
+ <ImageAttachments store={this.props.store} ev={ev} />
180
+ <AudioAttachments store={this.props.store} ev={ev} />
181
+ <VideoAttachments store={this.props.store} ev={ev} />
182
+ <FileAttachments store={this.props.store} ev={ev} />
183
+ <ButtonAttachments store={this.props.store} ev={ev} t={this.props.t} />
184
+ <FormAttachments
185
+ store={this.props.store}
186
+ onSubmit={this.props.onFormSubmit}
187
+ ref={this.refForm}
188
+ ev={ev}
189
+ t={this.props.t}
190
+ />
191
+ <FormSubmitAttachments store={this.props.store} ev={ev} t={this.props.t} />
192
+ <GenericTemplateAttachments
193
+ store={this.props.store}
194
+ ev={ev}
195
+ onSelect={(e) => this.choosePostbackBtn(e)}
196
+ t={this.props.t}
197
+ />
198
+ <ProductAttachments
199
+ t={this.props.t}
200
+ onPostback={(e) => this.choosePostbackBtn(e)}
201
+ store={this.props.store}
202
+ ev={ev}
203
+ />
204
+ <OrderAttachments t={this.props.t} store={this.props.store} ev={ev} />
205
+ <QuickReplies store={this.props.store} ev={ev} onSelect={(e) => this.chooseQuickReply(e)} />
206
+ {this.renderReactions()}
207
+ {this.renderAIDevNote()}
208
+ </div>
209
+ </div>
184
210
  </div>
185
211
  )
186
212
  }
@@ -232,6 +258,103 @@ export default class Event extends Com {
232
258
  )
233
259
  }
234
260
 
261
+ async onSendReaction(action) {
262
+ if (this._reacting) return
263
+ let ev = this.props.event.ev
264
+ let cid = sb.get(ev, 'data.message.conversation_id')
265
+ ev = {
266
+ type: 'message_pong',
267
+ data: {
268
+ message: {
269
+ id: ev.id,
270
+ pongs: [
271
+ {
272
+ type: action,
273
+ },
274
+ ],
275
+ conversion_id: cid,
276
+ },
277
+ },
278
+ }
279
+ if (action == 'dislike' && this.props.store.isAIDevMode()) {
280
+ this.props.store.emitConvoEvent({
281
+ type: 'bad_feedbacked',
282
+ data: {
283
+ event: ev,
284
+ id: ev.id,
285
+ conversion_id: cid,
286
+ },
287
+ })
288
+ }
289
+
290
+ let {pongs} = this.state
291
+ if (action === 'like') {
292
+ pongs = pongs.filter((pong) => pong.type !== 'dislike')
293
+ pongs.push({type: 'like'})
294
+ } else if (action === 'dislike') {
295
+ pongs = pongs.filter((pong) => pong.type !== 'like')
296
+ pongs.push({type: 'dislike'})
297
+ } else if (action === 'remove_like') {
298
+ pongs = pongs.filter((pong) => pong.type !== 'like')
299
+ } else if (action === 'remove_dislike') {
300
+ pongs = pongs.filter((pong) => pong.type !== 'dislike')
301
+ }
302
+ this.setState({pongs})
303
+ this._reacting = true
304
+ let res = await this.props.store.sendReaction(cid, ev)
305
+ this._reacting = false
306
+ }
307
+
308
+ renderReactions() {
309
+ if (this.props.store.isAIDevMode()) return null
310
+
311
+ let ev = sb.get(this.props.event, 'ev') || {}
312
+ let pongs = sb.get(ev, 'data.message.pongs') || []
313
+ let hasLike = this.hasLike()
314
+ let hasDislike = this.hasDislike()
315
+
316
+ let likecls = 'message-event-action-btn'
317
+ if (hasLike) likecls += ' active'
318
+ let dislikecls = 'message-event-action-btn'
319
+ if (hasDislike) dislikecls += ' active'
320
+ return (
321
+ <div class='message-event-actions'>
322
+ <div class={likecls} onClick={() => this.onSendReaction(hasLike ? 'remove_like' : 'like')}>
323
+ <div class='message-event-action-btn-svg like' />
324
+ </div>
325
+ <div class={dislikecls} onClick={() => this.onSendReaction(hasDislike ? 'remove_dislike' : 'dislike')}>
326
+ <div class='message-event-action-btn-svg dislike' />
327
+ </div>
328
+ </div>
329
+ )
330
+ }
331
+
332
+ renderAIDevNote() {
333
+ if (!this.props.isLast) return null
334
+ if (!this.props.store.isAIDevMode()) return null
335
+
336
+ let ev = sb.get(this.props.event, 'ev') || {}
337
+ let pongs = sb.get(ev, 'data.message.pongs') || []
338
+ let hasLike = this.hasLike()
339
+ let hasDislike = this.hasDislike()
340
+
341
+ let likecls = 'message-event-action-btn message-event-action-btn__dev message-event-action-btn_like'
342
+ if (hasLike) likecls += ' active'
343
+ let dislikecls = 'message-event-action-btn message-event-action-btn__dev message-event-action-btn_dislike'
344
+ if (hasDislike) dislikecls += ' active'
345
+
346
+ return (
347
+ <div class='message-event-actions'>
348
+ <div class={likecls} onClick={() => this.onSendReaction(hasLike ? 'remove_like' : 'like')}>
349
+ <div class='message-event-action-btn-svg like' /> Good
350
+ </div>
351
+ <div class={dislikecls} onClick={() => this.onSendReaction(hasDislike ? 'remove_dislike' : 'dislike')}>
352
+ <div class='message-event-action-btn-svg dislike' /> Bad
353
+ </div>
354
+ </div>
355
+ )
356
+ }
357
+
235
358
  renderUpdateConvo(ev) {
236
359
  var state = sb.get(ev, 'data.conversation.state')
237
360
  if (state === 'active') return null
@@ -55,19 +55,33 @@ export default class Events extends Com {
55
55
  events = sb.map(events, (ev) => ({ev: ev}))
56
56
  processEvents(convo, events)
57
57
 
58
- let $events = sb.map(events, (ev) => (
59
- <Event
60
- chatbox={this.props.chatbox}
61
- onLink={this.props.onLink}
62
- store={this.props.store}
63
- onFormSubmit={this.props.onFormSubmit}
64
- onSend={this.props.onSend}
65
- onPostback={this.props.onPostback}
66
- event={ev}
67
- t={this.props.t}
68
- key={ev.ev.id}
69
- />
70
- ))
58
+ let lastAgentMessageSentId = ''
59
+ for (let i = events.length - 1; i >= 0; i--) {
60
+ let ev = sb.get(events, [i, 'ev']) || {}
61
+ let bytype = sb.get(ev, 'by.type')
62
+ if (bytype !== 'user' && ev.type === 'message_sent') {
63
+ lastAgentMessageSentId = ev.id
64
+ break
65
+ }
66
+ }
67
+
68
+ let $events = sb.map(events, (ev) => {
69
+ let isLast = ev.ev.id === lastAgentMessageSentId
70
+ return (
71
+ <Event
72
+ isLast={isLast}
73
+ chatbox={this.props.chatbox}
74
+ onLink={this.props.onLink}
75
+ store={this.props.store}
76
+ onFormSubmit={this.props.onFormSubmit}
77
+ onSend={this.props.onSend}
78
+ onPostback={this.props.onPostback}
79
+ event={ev}
80
+ t={this.props.t}
81
+ key={ev.ev.id}
82
+ />
83
+ )
84
+ })
71
85
 
72
86
  let $loading = null
73
87
  let style = 'opacity: 1 !important'
@@ -21,7 +21,7 @@ export default class ImageAttachment extends Com {
21
21
  const DEFAULT_MAX_WIDTH = 267
22
22
 
23
23
  let $container = this.containerRef.current
24
- this.frameWidth = $container ? $container.offsetWidth : DEFAULT_MAX_WIDTH
24
+ this.frameWidth = $container ? $container.parentNode.parentNode.offsetWidth : DEFAULT_MAX_WIDTH
25
25
  this.frameHeight = MAX_HEIGHT
26
26
  this.setState({
27
27
  ready: true,
@@ -15,6 +15,7 @@ export default class InfoMessage extends Com {
15
15
  render() {
16
16
  let ev = this.props.ev.ev
17
17
  var $time = null
18
+
18
19
  if (this.props.ev.show_time) {
19
20
  $time = (
20
21
  <div class='convo-event-time'>
@@ -23,6 +24,8 @@ export default class InfoMessage extends Com {
23
24
  )
24
25
  }
25
26
 
27
+ if (this.props.store.isAIDevMode()) $time = null
28
+
26
29
  // if (this.ev.type === 'conversation_rating_requested') return <div class="info-message"></div>
27
30
  if (ev.type === 'conversation_rated') {
28
31
  var rating = sb.get(ev, 'data.conversation.ratings.0', {no_rating: true, rating: 0})
@@ -1,8 +1,10 @@
1
1
  var logo = require('../../assets/img/subiz-mono.svg')
2
2
  import {h} from 'preact'
3
+
3
4
  export default function PoweredBy(props) {
4
5
  // dont render on mobile
5
6
  if (props.store.isMobile()) return null
7
+ if (props.store.isAIDevMode()) return null
6
8
  let user = props.store.matchUser() || {}
7
9
  var link = `https://subiz.com.vn/?utm_source=${
8
10
  window.location.host || `id-${user.account_id}`
@@ -130,11 +130,13 @@ export default class Widget extends Com {
130
130
  let position = sb.get(accountSetting, 'desktop_button.position') || 'right'
131
131
  if (this.props.store.isMobile()) position = sb.get(accountSetting, 'mobile_button.position', position)
132
132
  let cls = 'widget-container widget-container--' + position
133
- if (!this.props.store.isMobile() && sb.get(accountSetting, 'window_mode') === 'full')
133
+ if ( !this.props.store.isMobile() && sb.get(accountSetting, 'window_mode') === 'full')
134
134
  cls += ' widget-container--full'
135
135
  if (!this.props.isShow) cls += ' widget-container--hide'
136
- return (
137
- <div class={cls}>
136
+ let hideheader = sb.get(accountSetting, 'header.hidden', false)
137
+ var $header
138
+ if (!hideheader) {
139
+ $header = (
138
140
  <Header
139
141
  t={this.props.t}
140
142
  plugin={this.props.plugin}
@@ -144,6 +146,11 @@ export default class Widget extends Com {
144
146
  onClose={this.props.onClose}
145
147
  onGotoList={() => this.props.onGo({view: 'list'})}
146
148
  />
149
+ )
150
+ }
151
+ return (
152
+ <div class={cls}>
153
+ {$header}
147
154
  {$body}
148
155
  </div>
149
156
  )
package/src/lib.js CHANGED
@@ -35,6 +35,10 @@ class Widget {
35
35
  cb()
36
36
  }
37
37
 
38
+ setAIDevMode(mode) {
39
+ this.store.setAIDevMode(mode)
40
+ }
41
+
38
42
  setTrackPageView(val) {
39
43
  this.store.setTrackPageview(val)
40
44
  }
@@ -210,6 +210,18 @@ export default class Store {
210
210
  this.pubsub.publish('highlight', pluginid, ref)
211
211
  }
212
212
 
213
+ emitConvoEvent(ev) {
214
+ this.pubsub.publish('events', ev)
215
+ }
216
+
217
+ setAIDevMode(mode) {
218
+ this.ai_dev_mode = mode
219
+ }
220
+
221
+ isAIDevMode() {
222
+ return this.ai_dev_mode
223
+ }
224
+
213
225
  overrideDeviceType(type) {
214
226
  this.forceDevice = type
215
227
  this.pubsub.publish('account')
@@ -858,7 +870,17 @@ export default class Store {
858
870
 
859
871
  if (!sb.get(oldev, 'data.message')) return
860
872
  var pongs = sb.get(oldev, 'data.message.pongs', [])
861
- pongs = pongs.concat(sb.get(ev, 'data.message.pongs', []))
873
+ let newpongs = sb.get(ev, 'data.message.pongs', []) || []
874
+ pongs = pongs.concat(newpongs)
875
+ if (newpongs.length === 1) {
876
+ let pongtype = newpongs[0].type
877
+ if (pongtype === 'like' || pongtype === 'remove_dislike') {
878
+ pongs = pongs.filter((pong) => pong.type !== 'dislike')
879
+ }
880
+ if (pongtype === 'dislike' || pongtype === 'remove_like') {
881
+ pongs = pongs.filter((pong) => pong.type !== 'like')
882
+ }
883
+ }
862
884
  oldev.data.message.pongs = pongs
863
885
  sb.map(pongs, (pong) => {
864
886
  if (pong.member_id == user.id && pong.type === 'seen') {
@@ -867,6 +889,7 @@ export default class Store {
867
889
  }
868
890
  })
869
891
  this.chain.put(cid, oldev.id, sb.getMs(oldev.created), oldev)
892
+ this.pubsub.publish('events', oldev)
870
893
  }
871
894
 
872
895
  if (ev.type === 'message_sent') {
@@ -901,6 +924,7 @@ export default class Store {
901
924
  _typing = sb.throttle((convoid) => this.api.sendTyping(convoid), 3000, {trailing: false})
902
925
 
903
926
  async startConvo(req) {
927
+ if (!req) req = {}
904
928
  await this.readyGate.entry()
905
929
  this.convoGate.close()
906
930
 
@@ -925,6 +949,10 @@ export default class Store {
925
949
  return this.api.createPostback(cid, postback)
926
950
  }
927
951
 
952
+ sendReaction(cid, ev) {
953
+ return this.api.sendConvoEvent(cid, ev)
954
+ }
955
+
928
956
  async sendConvoEvent(cid, type, data) {
929
957
  await this.readyGate.entry()
930
958
  var idempotency = sb.randomString(36)