@subiz/widget-v4 4.1.210 → 4.1.212

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 (130) hide show
  1. package/README.md +21 -2
  2. package/assets/img/dot_chat.svg +3 -0
  3. package/assets/less/chatbox.less +9 -0
  4. package/assets/less/form.less +10 -0
  5. package/assets/less/message.less +114 -13
  6. package/assets/less/widget.less +15 -18
  7. package/compiled_src/call/css.js +1 -1
  8. package/compiled_src/chatbox/Buttons.js +7 -4
  9. package/compiled_src/chatbox/ChatButton.js +127 -29
  10. package/compiled_src/chatbox/Event.js +37 -5
  11. package/compiled_src/chatbox/Events.js +348 -129
  12. package/compiled_src/chatbox/FormAttachment.js +1 -1
  13. package/compiled_src/chatbox/Header.js +7 -2
  14. package/compiled_src/chatbox/InfoMessage.js +1 -1
  15. package/compiled_src/chatbox/MessageInput.js +24 -9
  16. package/compiled_src/chatbox/OrderAttachments.js +13 -13
  17. package/compiled_src/chatbox/QuoteMessage.js +3 -2
  18. package/compiled_src/chatbox/TextMessage.js +22 -18
  19. package/compiled_src/chatbox/Widget.js +200 -129
  20. package/compiled_src/chatbox/block.js +39 -29
  21. package/compiled_src/chatbox/conversation-history.js +44 -0
  22. package/compiled_src/chatbox/event-layout.js +59 -0
  23. package/compiled_src/chatbox/greeting.js +32 -0
  24. package/compiled_src/chatbox/main.js +3 -0
  25. package/compiled_src/common.js +13 -9
  26. package/compiled_src/form/css.js +1 -1
  27. package/compiled_src/lib.js +22 -32
  28. package/compiled_src/notif/css.js +1 -1
  29. package/compiled_src/plugin.js +11 -18
  30. package/compiled_src/popup/css.js +1 -1
  31. package/compiled_src/popup/popup.js +1 -1
  32. package/compiled_src/store/api.js +0 -2
  33. package/compiled_src/store/api_fake.js +1 -1
  34. package/compiled_src/store/attachments.js +28 -0
  35. package/compiled_src/store/chain.js +24 -10
  36. package/compiled_src/store/kv.js +9 -13
  37. package/compiled_src/store/lsdb.js +1 -1
  38. package/compiled_src/store/store.js +356 -117
  39. package/compiled_src/util/common.js +48 -41
  40. package/compiled_src/util/event.js +15 -0
  41. package/compiled_src/util/pubsub.js +28 -13
  42. package/compiled_src/util/url.js +18 -0
  43. package/dist/127.js +13 -13
  44. package/dist/132.js +1 -1
  45. package/dist/1a0f6f3ff1a.127.js +1 -0
  46. package/dist/{1a0d80f6e72.132.js → 1a0f6f3ff1a.132.js} +1 -1
  47. package/dist/{1a0d80f6e72.27.js → 1a0f6f3ff1a.27.js} +1 -1
  48. package/dist/1a0f6f3ff1a.278.js +1 -0
  49. package/dist/{1a0d80f6e72.339.js → 1a0f6f3ff1a.339.js} +1 -1
  50. package/dist/1a0f6f3ff1a.459.js +1 -0
  51. package/dist/1a0f6f3ff1a.app.js +1 -0
  52. package/dist/{1a0d80f6e72.chat.html → 1a0f6f3ff1a.chat.html} +1 -1
  53. package/dist/1a0f6f3ff1a.chat.js +1 -0
  54. package/dist/{1a0d80f6e72.form.html → 1a0f6f3ff1a.form.html} +1 -1
  55. package/dist/1a0f6f3ff1a.form.js +1 -0
  56. package/dist/{1a0d80f6e72.index.html → 1a0f6f3ff1a.index.html} +1 -1
  57. package/dist/{1a0d80f6e72.library.html → 1a0f6f3ff1a.library.html} +1 -1
  58. package/dist/1a0f6f3ff1a.library.js +1 -0
  59. package/dist/{1a0d80f6e72.preview.html → 1a0f6f3ff1a.preview.html} +1 -1
  60. package/dist/1a0f6f3ff1a.preview.js +1 -0
  61. package/dist/1a0f6f3ff1a.webview.js +1 -0
  62. package/dist/27.js +2 -2
  63. package/dist/459.js +2 -2
  64. package/dist/HASH +1 -1
  65. package/dist/assets/img/dot_chat.335fd8a.svg +3 -0
  66. package/dist/screenshoot.js +1523 -619
  67. package/package.json +12 -5
  68. package/src/chatbox/Buttons.js +5 -4
  69. package/src/chatbox/ChatButton.js +133 -39
  70. package/src/chatbox/Event.js +38 -5
  71. package/src/chatbox/Events.js +306 -134
  72. package/src/chatbox/FormAttachment.js +1 -1
  73. package/src/chatbox/Header.js +5 -4
  74. package/src/chatbox/InfoMessage.js +1 -1
  75. package/src/chatbox/MessageInput.js +15 -10
  76. package/src/chatbox/OrderAttachments.js +13 -13
  77. package/src/chatbox/QuoteMessage.js +3 -2
  78. package/src/chatbox/TextMessage.js +21 -20
  79. package/src/chatbox/Widget.js +150 -150
  80. package/src/chatbox/block.js +29 -27
  81. package/src/chatbox/conversation-history.js +37 -0
  82. package/src/chatbox/event-layout.js +72 -0
  83. package/src/chatbox/greeting.js +13 -0
  84. package/src/chatbox/main.js +3 -0
  85. package/src/common.js +15 -9
  86. package/src/lib.js +22 -35
  87. package/src/plugin.js +11 -13
  88. package/src/popup/popup.js +1 -1
  89. package/src/store/api.js +0 -2
  90. package/src/store/api_fake.js +1 -1
  91. package/src/store/attachments.js +21 -0
  92. package/src/store/chain.js +25 -12
  93. package/src/store/kv.js +10 -19
  94. package/src/store/lsdb.js +1 -1
  95. package/src/store/store.js +313 -130
  96. package/src/util/common.js +48 -43
  97. package/src/util/event.js +7 -0
  98. package/src/util/pubsub.js +27 -8
  99. package/src/util/url.js +14 -0
  100. package/test.cjs +59 -0
  101. package/assets/less/contact.less +0 -126
  102. package/compiled_src/util/common.test.js +0 -414
  103. package/compiled_src/util/event.test.js +0 -38
  104. package/compiled_src/util/extractor.test.js +0 -299
  105. package/compiled_src/util/extractor.test2.js +0 -61
  106. package/compiled_src/util/gate.test.js +0 -18
  107. package/compiled_src/util/pubsub.test.js +0 -35
  108. package/dist/1a0d80f6e72.127.js +0 -1
  109. package/dist/1a0d80f6e72.278.js +0 -1
  110. package/dist/1a0d80f6e72.459.js +0 -1
  111. package/dist/1a0d80f6e72.74.js +0 -1
  112. package/dist/1a0d80f6e72.997.js +0 -1
  113. package/dist/1a0d80f6e72.app.js +0 -1
  114. package/dist/1a0d80f6e72.chat.js +0 -1
  115. package/dist/1a0d80f6e72.form.js +0 -1
  116. package/dist/1a0d80f6e72.library.js +0 -1
  117. package/dist/1a0d80f6e72.preview.js +0 -1
  118. package/dist/1a0d80f6e72.webview.js +0 -1
  119. package/dist/74.js +0 -376
  120. package/dist/997.js +0 -37
  121. package/src/contact/contact.js +0 -400
  122. package/src/contact/css.js +0 -1
  123. package/src/util/common.test.js +0 -301
  124. package/src/util/event.test.js +0 -31
  125. package/src/util/extractor.test.js +0 -240
  126. package/src/util/extractor.test2.js +0 -56
  127. package/src/util/gate.test.js +0 -20
  128. package/src/util/pubsub.test.js +0 -37
  129. /package/dist/{1a0d80f6e72.185.js → 1a0f6f3ff1a.185.js} +0 -0
  130. /package/dist/{1a0d80f6e72.490.js → 1a0f6f3ff1a.490.js} +0 -0
@@ -39,14 +39,15 @@ export default class MessageInput extends Com {
39
39
  }
40
40
 
41
41
  render() {
42
+ const disabled = this.hasUnfinishRequiredFormOrLock() || this.props.disabled || this.props.is_loading
42
43
  var $buttons = (
43
44
  <div class='d-flex flex-row align-self-end'>
44
45
  {this.renderEmoji()}
45
- <div
46
- data-tooltip-poasition='top-left'
46
+ <button type='button' disabled={disabled} aria-label={this.props.t('attachment')}
47
+ data-tooltip-position='top-left'
47
48
  class='message-input--button message-input--attachment-button'
48
49
  onClick={(e) => this.selectFile(e)}
49
- ></div>
50
+ ></button>
50
51
  </div>
51
52
  )
52
53
  var msg = sb.trim(this.state.msgValue)
@@ -54,11 +55,11 @@ export default class MessageInput extends Com {
54
55
  $buttons = (
55
56
  <div class='d-flex flex-row align-self-end'>
56
57
  {this.renderEmoji()}
57
- <div
58
+ <button type='button' disabled={disabled} aria-label={this.props.t('send')}
58
59
  data-tooltip-position='top-left'
59
60
  class='message-input--button message-input--send-button'
60
61
  onClick={() => this.submitMessage()}
61
- ></div>
62
+ ></button>
62
63
  </div>
63
64
  )
64
65
  }
@@ -72,7 +73,6 @@ export default class MessageInput extends Com {
72
73
  }
73
74
 
74
75
  var containercls = ''
75
- var disabled = this.hasUnfinishRequiredFormOrLock() || this.props.disabled
76
76
  if (disabled || this.props.is_loading) containercls = 'message-input--disabled'
77
77
 
78
78
  return (
@@ -85,6 +85,7 @@ export default class MessageInput extends Com {
85
85
  <input class='d-none' ref={this.refFileInput} type='file' onChange={(e) => this.sendFile(e)} multiple />
86
86
  <textarea
87
87
  disabled={disabled}
88
+ aria-label={this.props.t('message_input_help')}
88
89
  class='message-input--input'
89
90
  maxlength='1024'
90
91
  placeholder={this.props.t('message_input_help').substr(0, 25)}
@@ -266,7 +267,7 @@ export default class MessageInput extends Com {
266
267
  return (
267
268
  <div>
268
269
  {this.renderEmojiPopup()}
269
- <div class='message-input--button message-input--emoji-button' onClick={(e) => this.toggleEmoji(e)}></div>
270
+ <button type='button' aria-label='Emoji' aria-expanded={!!this.state.openEmoji} disabled={this.props.disabled || this.props.is_loading} class='message-input--button message-input--emoji-button' onClick={(e) => this.toggleEmoji(e)}></button>
270
271
  </div>
271
272
  )
272
273
  }
@@ -303,12 +304,14 @@ export default class MessageInput extends Com {
303
304
  return e.preventDefault()
304
305
  }
305
306
  this._forceUpdate()
306
- this.sendTyping()
307
307
  }
308
308
 
309
309
  onChange(e) {
310
- this.setState({msgValue: e.target.value})
310
+ const value = e.target.value
311
+ const changed = value !== (this.state.msgValue || '')
312
+ this.setState({msgValue: value})
311
313
  this.resizeInput()
314
+ if (changed && sb.trim(value)) this.sendTyping()
312
315
  }
313
316
 
314
317
  selectFile() {
@@ -345,14 +348,16 @@ export default class MessageInput extends Com {
345
348
  if (!msg) return
346
349
  if (this.props.disabled) return
347
350
  if (this.props.onSend) {
348
- await this.props.onSend({text: msg, format: 'plaintext'})
351
+ if (await this.props.onSend({text: msg, format: 'plaintext'}) === false) return
349
352
  }
350
353
  this.setState({openEmoji: false, msgValue: ''})
351
354
  this.resizeInput()
352
355
  }
353
356
 
354
357
  sendTyping() {
358
+ if (this.props.disabled || this.props.is_loading) return
355
359
  if (!this.props.cid) return
360
+ if (sb.get(this.props.store.matchConvo(this.props.cid), 'state') === 'ended') return
356
361
  return this.props.store.typing(this.props.cid)
357
362
  }
358
363
 
@@ -201,12 +201,12 @@ export default class ProductAttachments extends Com {
201
201
  }
202
202
 
203
203
  function getProductPropName(product, seperator = ' - ') {
204
- let options = op.get(product, 'options', [])
205
- let props = op.get(product, 'props', [])
206
- let multiPropsOptions = op.filter(options, (option) => op.size(option.values) > 1)
204
+ let options = sb.get(product, 'options', [])
205
+ let props = sb.get(product, 'props', [])
206
+ let multiPropsOptions = sb.filter(options, (option) => sb.size(option.values) > 1)
207
207
  let names = []
208
- op.each(multiPropsOptions, (option) => {
209
- let prop = op.find(props, (prop) => prop.key === option.key) || {}
208
+ sb.each(multiPropsOptions, (option) => {
209
+ let prop = sb.find(props, (prop) => prop.key === option.key) || {}
210
210
  if (prop.value) names.push(prop.value)
211
211
  })
212
212
  return names.join(seperator)
@@ -217,7 +217,7 @@ function getOrderDisplayName(order = {}) {
217
217
  }
218
218
 
219
219
  function displayOrderShippingAddress(order, isShorten) {
220
- let addr = op.get(order, 'shipping.address', {})
220
+ let addr = sb.get(order, 'shipping.address', {})
221
221
  let fulladdr = [addr.address, addr.ward, addr.district, addr.province]
222
222
  .filter((a) => a)
223
223
  .join(', ')
@@ -240,9 +240,9 @@ function calcOrderTotal(order) {
240
240
  let subtotal = 0
241
241
  let beforeTaxDiscount = 0
242
242
  let ordervalue = 0
243
- op.map(order.items, (item) => {
243
+ sb.map(order.items, (item) => {
244
244
  if (!item.product) return 0
245
- let price = op.get(item, 'product.price', 0) * (item.quantity || 0)
245
+ let price = sb.get(item, 'product.price', 0) * (item.quantity || 0)
246
246
  ordervalue += price
247
247
 
248
248
  // some products that are discounted before tax
@@ -266,7 +266,7 @@ function calcOrderTotal(order) {
266
266
 
267
267
  // TAX
268
268
  let taxM = {}
269
- op.map(order.items, (item, i) => {
269
+ sb.map(order.items, (item, i) => {
270
270
  if (!item.product) return
271
271
  let tax = item.product.tax
272
272
  if (!tax) return
@@ -277,7 +277,7 @@ function calcOrderTotal(order) {
277
277
  })
278
278
 
279
279
  let totaltax = 0
280
- op.map(taxM, (t) => (totaltax += t.taxprice))
280
+ sb.map(taxM, (t) => (totaltax += t.taxprice))
281
281
 
282
282
  let computed_discount = 0
283
283
  if (order.discount_type == 'percentage' && order.discount_percentage > 0) {
@@ -287,9 +287,9 @@ function calcOrderTotal(order) {
287
287
  }
288
288
 
289
289
  // SHIP
290
- let shippingfee = op.get(order, 'shipping.nominal_fee', 0)
290
+ let shippingfee = sb.get(order, 'shipping.nominal_fee', 0)
291
291
  if (shippingfee > 0) {
292
- if (op.get(order, 'shipping.tax.id')) {
292
+ if (sb.get(order, 'shipping.tax.id')) {
293
293
  let tax = order.shipping.tax
294
294
  let taxprice = shippingfee * (tax.percentage / 10000)
295
295
  taxM['ship'] = {tax: tax, taxprice: taxprice}
@@ -298,7 +298,7 @@ function calcOrderTotal(order) {
298
298
  }
299
299
 
300
300
  totaltax = 0
301
- op.map(taxM, (t) => (totaltax += t.taxprice))
301
+ sb.map(taxM, (t) => (totaltax += t.taxprice))
302
302
  let total = subtotal + totaltax - computed_discount + shippingfee
303
303
 
304
304
  // add adjustment
@@ -20,7 +20,7 @@ export default class QuoteMessage extends Com {
20
20
  }
21
21
 
22
22
  let att = sb.get(this.props.ev, 'data.message.attachments.0')
23
- if (att) {
23
+ if (att && !sb.get(this.props.ev, 'data.message.deleted')) {
24
24
  let isImg = sb.includes(att.mimetype, 'image')
25
25
  if (isImg) {
26
26
  let uri = att.url
@@ -58,7 +58,8 @@ export default class QuoteMessage extends Com {
58
58
  let block = sb.get(this.props.ev, 'data.message.block')
59
59
 
60
60
  let text = sb.get(this.props.ev, 'data.message.text')
61
- if (block) text = sb.blockToText(block)
61
+ if (block) text = sb.blockToText(block) || text
62
+ if (sb.get(this.props.ev, 'data.message.deleted')) text = this.props.t('recalled_message')
62
63
 
63
64
  return (
64
65
  <div class='message-quote' onClick={() => this.scrollToSourceEvent(this.props.ev)}>
@@ -3,8 +3,8 @@ import Block from './block.js'
3
3
  import {Com} from '../common.js'
4
4
  import {h} from 'preact'
5
5
  import Emoji from './Emoji.js'
6
- import Time from './time.js'
7
6
  import QuoteMessage from './QuoteMessage.js'
7
+ import {safeUrl} from '../util/url.js'
8
8
 
9
9
  export default class TextMessage extends Com {
10
10
  constructor(props) {
@@ -14,13 +14,10 @@ export default class TextMessage extends Com {
14
14
 
15
15
  renderTime() {
16
16
  return null
17
- if (!this.props.showTime && !this.props.isLast && !this.props.isFirst) return null
17
+ }
18
18
 
19
- return (
20
- <div>
21
- <Time class='inline-time' time={sb.get(this.props.ev, 'created', sb.Date.now())} t={this.props.t} />
22
- </div>
23
- )
19
+ componentWillUnmount() {
20
+ sb.clearTimeout(this.focusTimeout)
24
21
  }
25
22
 
26
23
  focusSourceEventFromQuote(ev) {
@@ -29,23 +26,30 @@ export default class TextMessage extends Com {
29
26
  let attachmentIndexField = sb.find(fields, (f) => f.key === 'attachment_index')
30
27
  let attachmentIndex = sb.get(attachmentIndexField, 'value')
31
28
  try {
32
- attachmentIndex = JSON.parse(attachmentIndex)
29
+ attachmentIndex = sb.parseJSON(attachmentIndex)
33
30
  } catch (err) {
34
31
  attachmentIndex = undefined
35
32
  }
36
33
 
37
34
  if (attachmentIndex !== undefined) return // this is a quote for image or video, not for text
38
35
  this.setState({focused: true})
39
- if (this.focusTimeout) clearTimeout(this.focusTimeout)
40
- this.focusTimeout = setTimeout(() => {
36
+ if (this.focusTimeout) sb.clearTimeout(this.focusTimeout)
37
+ this.focusTimeout = sb.setTimeout(() => {
41
38
  this.setState({focused: false})
42
39
  }, 3000)
43
40
  }
44
41
 
45
42
  render() {
46
- var format = sb.get(this.props.ev, 'data.message.format', 'plaintext')
43
+ if (sb.get(this.props.ev, 'data.message.deleted')) {
44
+ let byType = sb.get(this.props.ev, 'by.type') === 'user' ? 'user' : 'agent'
45
+ return (
46
+ <div class={'message-' + byType + ' message-text--' + byType + ' message-text--deleted'}>
47
+ {this.props.t('recalled_message')}
48
+ </div>
49
+ )
50
+ }
47
51
  let block = sb.get(this.props.ev, 'data.message.block')
48
- if (block) return this.renderBlock(block)
52
+ if (block && !sb.isEmptyBlock(block)) return this.renderBlock(block)
49
53
  return this.renderPlainText()
50
54
  }
51
55
 
@@ -68,9 +72,11 @@ export default class TextMessage extends Com {
68
72
  ) {
69
73
  url = '//' + url
70
74
  }
75
+ url = safeUrl(url)
76
+ if (!url) return tag.text
71
77
 
72
78
  return (
73
- <a class='text-message-link' href={url} target='_blank' onClick={(e) => this.props.onLink(e, url)}>
79
+ <a class='text-message-link' href={url} target='_blank' rel='noopener noreferrer' onClick={(e) => this.props.onLink && this.props.onLink(e, url)}>
74
80
  {tag.text}
75
81
  </a>
76
82
  )
@@ -91,17 +97,12 @@ export default class TextMessage extends Com {
91
97
  })
92
98
 
93
99
  if (sb.size(tags) === 0) return null
94
- let isDeleted = sb.get(this.props.ev, 'data.message.deleted')
95
100
 
96
101
  let attachments = sb.get(this.props.ev, 'data.message.attachments', [])
97
102
  let quote = sb.find(attachments, (att) => att.type === 'quote')
98
103
 
99
104
  if (byType === 'agent') {
100
105
  let cls = 'message-agent message-text--agent'
101
- if (isDeleted) {
102
- tags = this.props.t('recalled_message')
103
- cls += ' message-text--deleted'
104
- }
105
106
  if (this.state.focused) cls += ' just-focused'
106
107
 
107
108
  return (
@@ -138,7 +139,7 @@ export default class TextMessage extends Com {
138
139
  if (byType === 'agent') {
139
140
  return (
140
141
  <div class='message-agent message-text--agent' title={t}>
141
- <Block block={block} onLink={this.props.onLink} textMessage />
142
+ <Block block={block} onLink={this.props.onLink} store={this.props.store} t={this.props.t} textMessage />
142
143
  {this.renderTime()}
143
144
  </div>
144
145
  )
@@ -147,7 +148,7 @@ export default class TextMessage extends Com {
147
148
  if (this.props.ev._error) msgcls += ' message-user--error'
148
149
  return (
149
150
  <div class={msgcls} title={t}>
150
- <Block block={block} onLink={this.props.onLink} textMessage />
151
+ <Block block={block} onLink={this.props.onLink} store={this.props.store} t={this.props.t} textMessage />
151
152
  </div>
152
153
  )
153
154
  }