@subiz/widget-v4 4.1.116 → 4.1.119

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 (62) hide show
  1. package/assets/less/base.less +1 -0
  2. package/assets/less/chatbox.less +2 -0
  3. package/assets/less/message.less +42 -22
  4. package/assets/less/message_input.less +2 -0
  5. package/assets/less/order.less +1 -1
  6. package/assets/less/widget.less +33 -16
  7. package/assets/less/widget_header.less +35 -36
  8. package/compiled_src/chatbox/AgentsAvatar.js +1 -1
  9. package/compiled_src/chatbox/Event.js +70 -15
  10. package/compiled_src/chatbox/Events.js +108 -19
  11. package/compiled_src/chatbox/Header.js +5 -4
  12. package/compiled_src/chatbox/InfoMessage.js +2 -13
  13. package/compiled_src/chatbox/MessageInput.js +25 -9
  14. package/compiled_src/chatbox/TextMessage.js +11 -2
  15. package/compiled_src/chatbox/Widget.js +29 -22
  16. package/compiled_src/chatbox/main.js +3 -0
  17. package/compiled_src/chatbox/time.js +1 -0
  18. package/compiled_src/lib.js +4 -0
  19. package/compiled_src/plugin.js +4 -0
  20. package/compiled_src/util/common.js +89 -0
  21. package/dist/185.js +19 -19
  22. package/dist/19641cfa41e.278.js +1 -0
  23. package/dist/19641cfa41e.app.js +1 -0
  24. package/dist/{195ec5442e3.index.html → 19641cfa41e.index.html} +1 -1
  25. package/dist/{195ec5442e3.library.html → 19641cfa41e.library.html} +1 -1
  26. package/dist/19641cfa41e.library.js +1 -0
  27. package/dist/{195ec5442e3.preview.html → 19641cfa41e.preview.html} +1 -1
  28. package/dist/19641cfa41e.preview.js +1 -0
  29. package/dist/19641cfa41e.webview.js +1 -0
  30. package/dist/278.js +1 -1
  31. package/dist/HASH +1 -1
  32. package/dist/screenshoot.js +562 -185
  33. package/package.json +4 -4
  34. package/src/chatbox/AgentsAvatar.js +1 -1
  35. package/src/chatbox/Event.js +76 -15
  36. package/src/chatbox/Events.js +95 -14
  37. package/src/chatbox/Header.js +3 -2
  38. package/src/chatbox/InfoMessage.js +0 -14
  39. package/src/chatbox/MessageInput.js +23 -7
  40. package/src/chatbox/TextMessage.js +13 -0
  41. package/src/chatbox/Widget.js +22 -19
  42. package/src/chatbox/main.js +4 -0
  43. package/src/chatbox/time.js +12 -8
  44. package/src/lib.js +5 -0
  45. package/src/plugin.js +5 -0
  46. package/src/util/common.js +121 -0
  47. package/up +33 -21
  48. package/compiled_src/chatbox/Typing.js +0 -77
  49. package/dist/195ec5442e3.278.js +0 -1
  50. package/dist/195ec5442e3.app.js +0 -1
  51. package/dist/195ec5442e3.library.js +0 -1
  52. package/dist/195ec5442e3.preview.js +0 -1
  53. package/dist/195ec5442e3.webview.js +0 -1
  54. package/src/chatbox/Typing.js +0 -72
  55. /package/dist/{195ec5442e3.127.js → 19641cfa41e.127.js} +0 -0
  56. /package/dist/{195ec5442e3.132.js → 19641cfa41e.132.js} +0 -0
  57. /package/dist/{195ec5442e3.185.js → 19641cfa41e.185.js} +0 -0
  58. /package/dist/{195ec5442e3.27.js → 19641cfa41e.27.js} +0 -0
  59. /package/dist/{195ec5442e3.459.js → 19641cfa41e.459.js} +0 -0
  60. /package/dist/{195ec5442e3.490.js → 19641cfa41e.490.js} +0 -0
  61. /package/dist/{195ec5442e3.74.js → 19641cfa41e.74.js} +0 -0
  62. /package/dist/{195ec5442e3.997.js → 19641cfa41e.997.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@subiz/widget-v4",
3
- "version": "4.1.116",
3
+ "version": "4.1.119",
4
4
  "main": "./compiled_src/lib.js",
5
5
  "scripts": {
6
6
  "dev": "npx webpack serve --config webpack.dev.js",
@@ -17,7 +17,7 @@
17
17
  "core-js": "^3.41.0",
18
18
  "css-loader": "5.2.7",
19
19
  "file-loader": "^6.2.0",
20
- "less": "^4.2.2",
20
+ "less": "^4.3.0",
21
21
  "less-loader": "^12.2.0",
22
22
  "preact": "10.19.3"
23
23
  },
@@ -30,8 +30,8 @@
30
30
  "babel-loader": "^10.0.0",
31
31
  "babel-plugin-syntax-jsx": "^6.18.0",
32
32
  "html-webpack-plugin": "^5.6.3",
33
- "puppeteer": "^24.6.0",
34
- "webpack": "^5.99.1",
33
+ "puppeteer": "^24.6.1",
34
+ "webpack": "^5.99.5",
35
35
  "webpack-bundle-analyzer": "^4.10.2",
36
36
  "webpack-cli": "^6.0.1",
37
37
  "webpack-dev-server": "^5.2.1"
@@ -38,7 +38,7 @@ export default function AgentsAvatar(props) {
38
38
  }
39
39
 
40
40
  agents = sb.filter(agents, (ag) => !!ag)
41
- if (agents.length === 0) {
41
+ if (agents.length === 0 || props.agent_ids === undefined || sb.size(props.agent_ids) === 0) {
42
42
  // use account avatar
43
43
  return (
44
44
  <div class={`group-avatar group-avatar--${mode}`}>
@@ -33,10 +33,13 @@ export default class Event extends Com {
33
33
  render() {
34
34
  var ev = sb.get(this.props.event, 'ev', {})
35
35
  switch (ev.type) {
36
+ case 'conversation_typing':
37
+ return this.renderTyping()
36
38
  case 'conversation_joined':
37
39
  // return this.renderMessageChatJoin()
38
40
  break
39
41
  case 'message_sent':
42
+ if (this.props.event.is_typing) return this.renderTyping()
40
43
  return this.renderMessageSent()
41
44
  case 'conversation_state_updated':
42
45
  case 'conversation_rating_requested':
@@ -149,19 +152,74 @@ export default class Event extends Com {
149
152
  })
150
153
  }
151
154
 
155
+ renderTyping() {
156
+ let innercls = 'agent-convo-event-inner'
157
+ var html = `
158
+ <html>
159
+ <head>
160
+ <style>
161
+ .typing-container {
162
+ width: 100vw;
163
+ height: 100vh;
164
+ display: flex;
165
+ align-items: center;
166
+ gap: 4px;
167
+ justify-content: center;
168
+ }
169
+ .typing-dot {
170
+ display: inline-block;
171
+ vertical-align: middle;
172
+ width: 8px;
173
+ height: 8px;
174
+ background: black;
175
+ border-radius: 50%;
176
+ animation: typing 0.5s infinite alternate;
177
+ }
178
+ .typing-dot:nth-of-type(2) {
179
+ animation-delay: 0.2s;
180
+ }
181
+ .typing-dot:nth-of-type(3) {
182
+ animation-delay: 0.6s;
183
+ }
184
+ @keyframes typing {
185
+ 0% {
186
+ opacity: 0.9;
187
+ }
188
+ 100% {
189
+ opacity: 0.4;
190
+ }
191
+ }
192
+ </style>
193
+ </head>
194
+ <body style="padding: 0; margin: 0">
195
+ <div class="typing-container">
196
+ <span class="typing-dot"></span>
197
+ <span class="typing-dot"></span>
198
+ <span class="typing-dot"></span>
199
+ </div>
200
+ </body>
201
+ </html>
202
+ `
203
+ let src = 'data:text/html,' + encodeURIComponent(html)
204
+ return (
205
+ <div class='convo-event agent-message'>
206
+ <div class='agent-convo-event-wrapper'>
207
+ <div class={innercls}>
208
+ <div class='message-agent message-text--agent' style='opacity: 0.6 !important'>
209
+ <iframe
210
+ src={src}
211
+ style='width: 35px !important; height: 20px !important; display: block !important; overflow: hidden !important; padding: 0 !important '
212
+ ></iframe>
213
+ </div>
214
+ </div>
215
+ </div>
216
+ </div>
217
+ )
218
+ }
219
+
152
220
  renderMessageSent() {
153
221
  let ev = this.props.event.ev
154
222
  this.tryLoadOrder(ev)
155
- var $time = null
156
- if (this.props.event.show_time) {
157
- $time = (
158
- <div class='convo-event-time'>
159
- <Time time={ev.created} t={this.props.t} />
160
- </div>
161
- )
162
- }
163
- if (this.props.store.isAIDevMode()) $time = null
164
-
165
223
  let t = new sb.Date(ev.created).toLocaleString()
166
224
  // message of agent
167
225
  if (sb.get(ev, 'by.type') !== 'user') {
@@ -171,11 +229,16 @@ export default class Event extends Com {
171
229
  if (this.props.isLast) innercls += ' last'
172
230
  return (
173
231
  <div class='convo-event agent-message'>
174
- {$time}
175
232
  {$sender}
176
233
  <div class='agent-convo-event-wrapper'>
177
234
  <div class={innercls}>
178
- <TextMessage store={this.props.store} ev={ev} onLink={this.props.onLink} />
235
+ <TextMessage
236
+ showTime={this.props.event.show_time}
237
+ isLast={this.props.isLast}
238
+ store={this.props.store}
239
+ ev={ev}
240
+ onLink={this.props.onLink}
241
+ />
179
242
  <ImageAttachments store={this.props.store} ev={ev} />
180
243
  <AudioAttachments store={this.props.store} ev={ev} />
181
244
  <VideoAttachments store={this.props.store} ev={ev} />
@@ -223,7 +286,7 @@ export default class Event extends Com {
223
286
  }
224
287
 
225
288
  let $sender = null
226
- if (this.props.event.mode === 'lead') $sender = <div class='pt-3'></div>
289
+ // if (this.props.event.mode === 'lead') $sender = <div class='pt-3'></div>
227
290
 
228
291
  let $error = null
229
292
  if (ev._error && ev.type === 'message_sent') {
@@ -239,7 +302,6 @@ export default class Event extends Com {
239
302
 
240
303
  return (
241
304
  <div class='convo-event user-message'>
242
- {$time}
243
305
  {$sender}
244
306
  <div class='message-user__container'>
245
307
  {$dot}
@@ -307,7 +369,6 @@ export default class Event extends Com {
307
369
 
308
370
  renderReactions() {
309
371
  if (this.props.store.isAIDevMode()) return null
310
-
311
372
  let ev = sb.get(this.props.event, 'ev') || {}
312
373
  let pongs = sb.get(ev, 'data.message.pongs') || []
313
374
  let hasLike = this.hasLike()
@@ -29,10 +29,66 @@ export default class Events extends Com {
29
29
  this.checkAndSeen()
30
30
  }
31
31
 
32
+ componentDidMount() {
33
+ this.setState({
34
+ isShowTyping: false,
35
+ typing_event: {},
36
+ })
37
+
38
+ this.props.store.onConvoEvent(this, (ev) => {
39
+ if (sb.convoOfEv(ev) !== this.props.cid) return
40
+ // if (sb.get(ev, 'by.id') === user.id) return
41
+ // fixwidget in dashboard show typing of user
42
+ if (sb.get(ev, 'by.type') === 'user') return
43
+
44
+ let oldby = sb.get(this.state.typing_event, 'by.id')
45
+ if (oldby && sb.get(ev, 'by.id') !== oldby) return
46
+
47
+ if (sb.get(ev, 'type') === 'message_sent') {
48
+ // stop the typing
49
+ if (this.timer) sb.clearTimeout(this.timer)
50
+ this.timer = null
51
+ this.setState({isShowTyping: false})
52
+ return
53
+ }
54
+ if (sb.get(ev, 'type') !== 'conversation_typing') return
55
+
56
+ // dont show typing of agent, some agent dont like this
57
+ let ag = this.props.store.matchAgent(sb.get(ev, 'by.id')) || {}
58
+ if (ag.type == 'agent' && !sb.get(this.props.plugin, 'chatbox.show_agent_typing')) return
59
+ this.setState({typing_event: ev, isShowTyping: true})
60
+ if (this.timer) sb.clearTimeout(this.timer)
61
+ this.timer = sb.setTimeout(() => {
62
+ if (sb.convoOfEv(ev) !== this.props.cid) return
63
+ this.timer = null
64
+ this.setState({isShowTyping: false})
65
+ }, 5000)
66
+ })
67
+ }
68
+
32
69
  componentDidUpdate(prevProps) {
33
70
  if (prevProps.cid !== this.props.cid) this.loadConvo()
34
71
  }
35
72
 
73
+ ShowTyping() {
74
+ this.setState({
75
+ isShowTyping: true,
76
+ typing_event: {
77
+ id: 'evzz' + sb.Date.now(),
78
+ created: sb.Date.now(),
79
+ by: {
80
+ type: 'agent',
81
+ },
82
+ type: 'conversation_typing',
83
+ },
84
+ })
85
+ if (this.timer) sb.clearTimeout(this.timer)
86
+ this.timer = sb.setTimeout(() => {
87
+ this.timer = null
88
+ this.setState({isShowTyping: false})
89
+ }, 5000)
90
+ }
91
+
36
92
  render() {
37
93
  if (this.state.error) return this.renderError()
38
94
  let events = sb.map(this.props.store.listEvents(this.props.cid))
@@ -53,20 +109,29 @@ export default class Events extends Com {
53
109
 
54
110
  let convo = this.props.store.matchConvo(this.props.cid)
55
111
  events = sb.map(events, (ev) => ({ev: ev}))
56
- processEvents(convo, events)
57
112
 
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
113
+ // console.log('-----------', this.state.isShowTyping, this.state.typing_event)
114
+ if (this.state.isShowTyping) {
115
+ let ev = this.state.typing_event || {id: 'evzz' + sb.Date.now(), created: sb.Date.now(), by: {type: 'agent'}}
116
+ let fakeev = {
117
+ is_typing: true,
118
+ id: ev.id,
119
+ ev: {
120
+ id: ev.id,
121
+ created: ev.created,
122
+ by: ev.by,
123
+ type: 'message_sent',
124
+ data: {message: {text: '...', conversation_id: this.props.cid}},
125
+ },
65
126
  }
127
+ console.log('FA', fakeev)
128
+ events.push(fakeev)
66
129
  }
67
130
 
131
+ processEvents(convo, events)
132
+
68
133
  let $events = sb.map(events, (ev) => {
69
- let isLast = ev.ev.id === lastAgentMessageSentId
134
+ let isLast = ev.ev.id === sb.get(convo, 'last_message_sent.id')
70
135
  return (
71
136
  <Event
72
137
  isLast={isLast}
@@ -85,11 +150,18 @@ export default class Events extends Com {
85
150
 
86
151
  let $loading = null
87
152
  let style = 'opacity: 1 !important'
88
- if (this.state.loading || this.props.creating) {
153
+ if (this.props.creating) {
89
154
  $loading = <Spinner static mode={sb.get(this.props.chatbox, 'primary_color', 'dark')} size='120' center />
90
155
  style = 'opacity: 0 !important'
91
156
  }
92
157
 
158
+ if (this.state.loading) {
159
+ if (sb.Date.now() - this.state.loading > 500) {
160
+ $loading = <Spinner static mode={sb.get(this.props.chatbox, 'primary_color', 'dark')} size='120' center />
161
+ style = 'opacity: 0 !important'
162
+ }
163
+ }
164
+
93
165
  return (
94
166
  <Fragment>
95
167
  {$loading}
@@ -171,9 +243,13 @@ export default class Events extends Com {
171
243
  if (!this.props.cid) return
172
244
  // load msg
173
245
  this.lastContentOffsetY = 0
174
- this.setState({loading: true})
246
+ this.setState({loading: false})
175
247
  let events = this.props.store.listEvents(this.props.cid)
176
248
  if (sb.size(events) === 0) {
249
+ this.setState({loading: sb.Date.now()})
250
+ sb.setTimeout(() => {
251
+ this.forceUpdate()
252
+ }, 800)
177
253
  let cid = this.props.cid
178
254
 
179
255
  let {error} = await this.props.store.fetchMoreEvents(cid)
@@ -184,11 +260,10 @@ export default class Events extends Com {
184
260
  return
185
261
  }
186
262
  }
187
- this.setState({loading: true})
188
263
  this.props.store.fetchMoreEvents(this.props.cid)
189
264
  this.setState({error: false})
190
265
  this.scrollToBottom()
191
- sb.setTimeout(() => this.setState({loading: false}), 50)
266
+ // sb.setTimeout(() => this.setState({loading: false}), 50)
192
267
  }
193
268
 
194
269
  isBottom() {
@@ -205,7 +280,7 @@ export default class Events extends Com {
205
280
  sb.setTimeout(() => {
206
281
  const $div = this.refBody
207
282
  if ($div) $div.scrollTop = $div.scrollHeight
208
- }, 200)
283
+ }, 100)
209
284
 
210
285
  if (this.props.isShow) {
211
286
  if (this.props.store.browserVisibility === 'visible' && document.hasFocus()) {
@@ -224,9 +299,13 @@ function processEvents(convo, events) {
224
299
 
225
300
  // show time for the first msg sent
226
301
  // insert time
302
+ var lastAgentEvent
227
303
  for (var i = 1; i < events.length; i++) {
228
304
  let curEvent = events[i]
229
305
  let lastEvent = events[i - 1]
306
+ if (sb.get(curEvent, 'ev.by.type') != 'user' && !curEvent.is_typing) {
307
+ lastAgentEvent = curEvent
308
+ }
230
309
 
231
310
  if (visibleEvent(curEvent.ev)) {
232
311
  let elapsedSec = sb.unixSec(curEvent.ev.created) - sb.unixSec(lastEvent.ev.created)
@@ -234,6 +313,8 @@ function processEvents(convo, events) {
234
313
  }
235
314
  }
236
315
 
316
+ if (lastAgentEvent) lastAgentEvent.show_time = true
317
+
237
318
  for (var i = 0; i < events.length; i++) {
238
319
  let curEvent = events[i]
239
320
  if (sb.get(curEvent, 'ev.type') === 'message_sent') {
@@ -54,7 +54,8 @@ export default class Header extends Com {
54
54
  return (
55
55
  <div class={cls} style={style}>
56
56
  {$back}
57
- <AgentsAvatar plugin={this.props.plugin} store={this.props.store} mode='secondary' agent_ids={agentIdsJoined} />
57
+ <div class="mr-2"></div>
58
+ <AgentsAvatar plugin={this.props.plugin} store={this.props.store} mode='secondary' />
58
59
  <div class='widget-header--title-container'>
59
60
  <div class='widget-header--title'>{this.getTitle()}</div>
60
61
  <div class='widget-header--description' title={desc}>
@@ -201,7 +202,7 @@ Vui lòng hỏi chúng tôi bất cứ điều gì hoặc chia sẻ phản hồi
201
202
  if (accSetting.hide_close_button) return null
202
203
  return (
203
204
  <div
204
- class='widget-header--button widget-header--button-close'
205
+ class='widget-header--button-close'
205
206
  data-tooltip={this.props.t('close')}
206
207
  data-tooltip-position='bottom'
207
208
  onClick={(e) => this.onCloseClick(e)}
@@ -7,24 +7,12 @@ function getRateLang(rating) {
7
7
  return 'great'
8
8
  }
9
9
 
10
- import Time from './time.js'
11
10
  import {Com} from '../common.js'
12
11
  import {h} from 'preact'
13
12
  // [ 'ev'],
14
13
  export default class InfoMessage extends Com {
15
14
  render() {
16
15
  let ev = this.props.ev.ev
17
- var $time = null
18
-
19
- if (this.props.ev.show_time) {
20
- $time = (
21
- <div class='convo-event-time'>
22
- <Time time={ev.created} t={this.props.t} />
23
- </div>
24
- )
25
- }
26
-
27
- if (this.props.store.isAIDevMode()) $time = null
28
16
 
29
17
  // if (this.ev.type === 'conversation_rating_requested') return <div class="info-message"></div>
30
18
  if (ev.type === 'conversation_rated') {
@@ -32,7 +20,6 @@ export default class InfoMessage extends Com {
32
20
  if (rating.no_rating) return null
33
21
  return (
34
22
  <div class='convo-event'>
35
- {$time}
36
23
  <div class='info-message'>
37
24
  {this.props.t('rated_as') + ' ' + this.props.t(getRateLang(rating.rating)).toLowerCase()}
38
25
  </div>
@@ -42,7 +29,6 @@ export default class InfoMessage extends Com {
42
29
  if (ev.type === 'conversation_state_updated' && sb.get(ev, 'data.conversation.state') === 'ended') {
43
30
  return (
44
31
  <div class='convo-event'>
45
- {$time}
46
32
  <div class='info-message'>{this.props.t('conversation_ended')}</div>
47
33
  </div>
48
34
  )
@@ -1,8 +1,9 @@
1
1
  import sb from '../util/index.js'
2
2
  import {Com} from '../common.js'
3
3
  import {h, createRef} from 'preact'
4
+ import Spinner from './spinner.js'
4
5
  import Emoji from './Emoji.js'
5
- // props: ['disabled', 'cid'],
6
+ // props: ['disabled', 'cid', 'is_loading'],
6
7
  export default class MessageInput extends Com {
7
8
  constructor(props) {
8
9
  super(props)
@@ -37,7 +38,7 @@ export default class MessageInput extends Com {
37
38
  }
38
39
 
39
40
  render() {
40
- var buttons = (
41
+ var $buttons = (
41
42
  <div class='d-flex flex-row align-self-end'>
42
43
  {this.renderEmoji()}
43
44
  <div
@@ -49,7 +50,7 @@ export default class MessageInput extends Com {
49
50
  )
50
51
  var msg = sb.trim(this.state.msgValue)
51
52
  if (msg.length > 0) {
52
- buttons = (
53
+ $buttons = (
53
54
  <div class='d-flex flex-row align-self-end'>
54
55
  {this.renderEmoji()}
55
56
  <div
@@ -61,9 +62,17 @@ export default class MessageInput extends Com {
61
62
  )
62
63
  }
63
64
 
65
+ if (this.props.is_loading) {
66
+ $buttons = (
67
+ <div class='d-flex flex-row align-self-end'>
68
+ <Spinner size='32' />
69
+ </div>
70
+ )
71
+ }
72
+
64
73
  var containercls = ''
65
74
  var disabled = this.hasUnfinishRequiredFormOrLock() || this.props.disabled
66
- if (disabled) containercls = 'message-input--disabled'
75
+ if (disabled || this.props.is_loading) containercls = 'message-input--disabled'
67
76
 
68
77
  return (
69
78
  <div
@@ -77,7 +86,7 @@ export default class MessageInput extends Com {
77
86
  disabled={disabled}
78
87
  class='message-input--input'
79
88
  maxlength='1024'
80
- placeholder={this.props.t('message_input_help')}
89
+ placeholder={this.props.t('message_input_help').substr(0, 25)}
81
90
  ref={this.refMessageInput}
82
91
  rows='1'
83
92
  value={this.state.msgValue}
@@ -86,7 +95,7 @@ export default class MessageInput extends Com {
86
95
  data-emojiable='true'
87
96
  onKeydown={(e) => this.onKeydown(e)}
88
97
  />
89
- {buttons}
98
+ {$buttons}
90
99
  </div>
91
100
  )
92
101
  }
@@ -226,6 +235,11 @@ export default class MessageInput extends Com {
226
235
  )
227
236
  }
228
237
 
238
+ applyMessage(text) {
239
+ this.setState({msgValue: text})
240
+ this.resizeInput()
241
+ }
242
+
229
243
  focus() {
230
244
  if (this.props.disabled) return
231
245
  this.refMessageInput.current && this.refMessageInput.current.focus()
@@ -319,9 +333,11 @@ export default class MessageInput extends Com {
319
333
  msg = msg || sb.trim(this.state.msgValue) || ''
320
334
  if (!msg) return
321
335
  if (this.props.disabled) return
336
+ if (this.props.onSend) {
337
+ await this.props.onSend({text: msg, format: 'plaintext'})
338
+ }
322
339
  this.setState({openEmoji: false, msgValue: ''})
323
340
  this.resizeInput()
324
- this.props.onSend && this.props.onSend({text: msg, format: 'plaintext'})
325
341
  }
326
342
 
327
343
  sendTyping() {
@@ -3,8 +3,19 @@ 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'
6
7
 
7
8
  export default class TextMessage extends Com {
9
+ renderTime() {
10
+ if (!this.props.showTime && !this.props.isLast && !this.props.isFirst) return null
11
+
12
+ return (
13
+ <div>
14
+ <Time class='inline-time' time={sb.get(this.props.ev, 'created', sb.Date.now())} t={this.props.t} />
15
+ </div>
16
+ )
17
+ }
18
+
8
19
  render() {
9
20
  var format = sb.get(this.props.ev, 'data.message.format', 'plaintext')
10
21
  let block = sb.get(this.props.ev, 'data.message.block')
@@ -48,6 +59,7 @@ export default class TextMessage extends Com {
48
59
  return (
49
60
  <div class='message-agent message-text--agent' title={t}>
50
61
  {tags}
62
+ {this.renderTime()}
51
63
  </div>
52
64
  )
53
65
  return (
@@ -66,6 +78,7 @@ export default class TextMessage extends Com {
66
78
  return (
67
79
  <div class='message-agent message-text--agent' title={t}>
68
80
  <Block block={block} onLink={this.props.onLink} />
81
+ {this.renderTime()}
69
82
  </div>
70
83
  )
71
84
  }
@@ -8,7 +8,7 @@ var checkedimg = require('../../assets/img/done_gray.svg')
8
8
  var empty = require('../../assets/img/empty.svg')
9
9
  import MessageInput from './MessageInput.js'
10
10
  import Events from './Events.js'
11
- import Typing from './Typing.js'
11
+ // import Typing from './Typing.js'
12
12
  import Rating from './Rating.js'
13
13
  // import Order from './Order.js'
14
14
  let Order = false
@@ -17,7 +17,6 @@ import ListField from './ListField.js'
17
17
 
18
18
  const defaultAvatar = 'https://vcdn.subiz-cdn.com/file/fiqoahedjbgwclpouoml-avatar_default_baa8a1d7.png'
19
19
 
20
- import Spinner from './spinner.js'
21
20
  import Time from './time.js'
22
21
  import {Com} from '../common.js'
23
22
  import {h, createRef} from 'preact'
@@ -49,6 +48,10 @@ export default class Widget extends Com {
49
48
  this.updateFormDataFields()
50
49
  }
51
50
 
51
+ applyMessage(text) {
52
+ this.focusInput(text)
53
+ }
54
+
52
55
  componentDidUpdate(prevProp) {
53
56
  if (prevProp.isShow !== this.props.isShow) {
54
57
  if (this.props.isShow && !this.props.store.isMobile()) this.focusInput()
@@ -64,7 +67,7 @@ export default class Widget extends Com {
64
67
  // focus Events then focus MessageInput
65
68
  // if there is any pending form, MessageInput will be disabled
66
69
  // and dont steal focus from Events
67
- this.events && this.events.focus()
70
+ this.$events && this.$events.focus()
68
71
  if (!this.props.store.isMobile()) this.focusInput()
69
72
  }, 100)
70
73
  this.updateFormDataFields()
@@ -73,7 +76,7 @@ export default class Widget extends Com {
73
76
  }
74
77
 
75
78
  ShowTyping() {
76
- this.refTyping.current && this.refTyping.current.ShowTyping()
79
+ this.$events && this.$events.ShowTyping()
77
80
  }
78
81
 
79
82
  shouldShowForm() {
@@ -130,7 +133,7 @@ export default class Widget extends Com {
130
133
  let position = sb.get(accountSetting, 'desktop_button.position') || 'right'
131
134
  if (this.props.store.isMobile()) position = sb.get(accountSetting, 'mobile_button.position', position)
132
135
  let cls = 'widget-container widget-container--' + position
133
- if ( !this.props.store.isMobile() && sb.get(accountSetting, 'window_mode') === 'full')
136
+ if (!this.props.store.isMobile() && sb.get(accountSetting, 'window_mode') === 'full')
134
137
  cls += ' widget-container--full'
135
138
  if (!this.props.isShow) cls += ' widget-container--hide'
136
139
  let hideheader = sb.get(accountSetting, 'header.hidden', false)
@@ -709,9 +712,8 @@ export default class Widget extends Com {
709
712
  sb.setTimeout(() => $input && $input.blur(), 300) // force lose focus
710
713
  }
711
714
 
712
- focusInput() {
715
+ focusInput(text) {
713
716
  if (this.props.store.isPreview) return
714
-
715
717
  if (this.shouldShowForm()) {
716
718
  // focus on the element
717
719
  let firstemptykey = ''
@@ -733,6 +735,9 @@ export default class Widget extends Com {
733
735
 
734
736
  var $input = this.msgInput.current
735
737
  if (!$input) return
738
+ if (text != undefined && typeof text === 'string') {
739
+ $input.applyMessage(text)
740
+ }
736
741
  sb.setTimeout(() => $input.focus())
737
742
  sb.setTimeout(() => $input.focus(), 300) // force focus
738
743
  }
@@ -770,17 +775,21 @@ export default class Widget extends Com {
770
775
  })
771
776
  if (_creating != this._creating) return // outdated
772
777
  if (error) {
778
+ this.setState({creating: false})
779
+ return
773
780
  // show error and try again
774
781
  }
775
782
  await this.props.store.sendConvoEvent(convo.id, 'message_sent', {message})
776
783
  if (_creating != this._creating) return // outdated
777
- this.setState({creating: false})
778
-
779
784
  this.props.onGo({view: 'chat', cid: convo.id})
785
+ this.setState({creating: false})
780
786
  return
781
787
  }
782
- await this.props.store.sendConvoEvent(this.props.cid, 'message_sent', {message})
783
- this.setState({creating: false})
788
+ // dont let text-input wait, since we want to clear out the message as soon as posible
789
+ sb.setTimeout(async () => {
790
+ await this.props.store.sendConvoEvent(this.props.cid, 'message_sent', {message})
791
+ this.setState({creating: false})
792
+ }, 1)
784
793
  }
785
794
 
786
795
  createPostback(postback) {
@@ -798,6 +807,7 @@ export default class Widget extends Com {
798
807
  ref={this.msgInput}
799
808
  cid={this.props.cid}
800
809
  onChange={this.onChangeMessageInput}
810
+ is_loading={this.state.creating}
801
811
  onSend={this.sendMessage}
802
812
  t={this.props.t}
803
813
  />
@@ -812,7 +822,6 @@ export default class Widget extends Com {
812
822
  </div>
813
823
  )
814
824
  }
815
-
816
825
  return (
817
826
  <div class='widget-body'>
818
827
  <div class='widget-body--content' onClick={(e) => this.onContainerClick(e)}>
@@ -822,19 +831,13 @@ export default class Widget extends Com {
822
831
  store={this.props.store}
823
832
  chatbox={this.props.plugin.chatbox || {}}
824
833
  onFormSubmit={(e) => this.onEventFormSubmit(e)}
825
- ref={(ref) => (this.events = ref)}
834
+ ref={(ref) => (this.$events = ref)}
826
835
  cid={this.props.cid}
827
836
  creating={this.creating}
828
837
  onSend={(e) => this.sendMessage(e)}
829
838
  onPostback={(e) => this.createPostback(e)}
830
839
  t={this.props.t}
831
- />
832
- <Typing
833
- store={this.props.store}
834
- cid={this.props.cid}
835
- t={this.props.t}
836
840
  plugin={this.props.plugin}
837
- ref={this.refTyping}
838
841
  />
839
842
  </div>
840
843
  <Rating
@@ -168,6 +168,10 @@ export default class Main extends Com {
168
168
  this.props.store.setLastview(p.view, {cid: p.cid})
169
169
  }
170
170
 
171
+ applyMessage(text) {
172
+ this.refWidget.current && this.refWidget.current.applyMessage(text)
173
+ }
174
+
171
175
  showGreeting() {
172
176
  this.refButton.current && this.refButton.current.ShowGreeting()
173
177
  }