@botonic/react 1.0.0-dev.1 → 1.0.0-dev.2

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 (116) hide show
  1. package/lib/components/button.js +2 -0
  2. package/lib/components/button.js.map +1 -1
  3. package/lib/components/components.js +1 -1
  4. package/lib/components/image.js +47 -5
  5. package/lib/components/image.js.map +1 -1
  6. package/lib/components/index.js +6 -6
  7. package/lib/components/markdown.js +1 -1
  8. package/lib/components/multichannel/multichannel-utils.js +8 -6
  9. package/lib/components/multichannel/multichannel-utils.js.map +1 -1
  10. package/lib/components/timestamps.js +1 -1
  11. package/lib/constants.js +2 -1
  12. package/lib/constants.js.map +1 -1
  13. package/lib/dev-app.js +7 -6
  14. package/lib/dev-app.js.map +1 -1
  15. package/lib/index.d.ts +3 -0
  16. package/lib/index.js +20 -20
  17. package/lib/message-utils.js +1 -1
  18. package/lib/util/dom.js +8 -2
  19. package/lib/util/dom.js.map +1 -1
  20. package/lib/util/environment.js +1 -1
  21. package/lib/util/objects.js +1 -1
  22. package/lib/util/react.js +1 -1
  23. package/lib/util/webchat.js +1 -1
  24. package/lib/webchat/actions.js +3 -1
  25. package/lib/webchat/actions.js.map +1 -1
  26. package/lib/webchat/devices/device-adapter.js +14 -4
  27. package/lib/webchat/devices/device-adapter.js.map +1 -1
  28. package/lib/webchat/devices/scrollbar-controller.js +5 -3
  29. package/lib/webchat/devices/scrollbar-controller.js.map +1 -1
  30. package/lib/webchat/header.js +1 -1
  31. package/lib/webchat/hooks.js +14 -4
  32. package/lib/webchat/hooks.js.map +1 -1
  33. package/lib/webchat/index.js +1 -1
  34. package/lib/webchat/webchat-reducer.js +6 -0
  35. package/lib/webchat/webchat-reducer.js.map +1 -1
  36. package/lib/webchat/webchat.js +21 -4
  37. package/lib/webchat/webchat.js.map +1 -1
  38. package/lib/webchat/webview.js +1 -1
  39. package/lib/webchat-app.js +20 -9
  40. package/lib/webchat-app.js.map +1 -1
  41. package/package.json +6 -6
  42. package/src/components/button.jsx +2 -0
  43. package/src/dev-app.jsx +8 -3
  44. package/src/experimental/components/custom-message.jsx +2 -1
  45. package/src/experimental/components/message.jsx +14 -7
  46. package/src/experimental/constants.js +189 -0
  47. package/src/experimental/contexts.jsx +36 -0
  48. package/src/experimental/dev-app.jsx +14 -7
  49. package/src/experimental/index.js +32 -20
  50. package/src/experimental/msg-to-botonic.jsx +9 -9
  51. package/src/experimental/react-bot.jsx +1 -1
  52. package/src/experimental/util/dom.js +55 -0
  53. package/src/experimental/util/objects.js +39 -0
  54. package/src/experimental/util/webchat.js +61 -0
  55. package/src/experimental/webchat/actions.jsx +24 -0
  56. package/src/experimental/webchat/hooks.js +296 -0
  57. package/src/experimental/webchat/messages-reducer.js +86 -0
  58. package/src/experimental/webchat/session-view.jsx +166 -0
  59. package/src/experimental/webchat/webchat-dev.jsx +23 -19
  60. package/src/experimental/webchat/webchat-reducer.js +68 -0
  61. package/src/experimental/webchat/webchat.jsx +107 -75
  62. package/src/experimental/webchat-app.jsx +34 -15
  63. package/src/index.d.ts +1 -0
  64. package/src/util/dom.js +5 -3
  65. package/src/webchat/devices/device-adapter.js +16 -4
  66. package/src/webchat/devices/scrollbar-controller.js +9 -4
  67. package/src/webchat/webchat.jsx +2 -1
  68. package/src/webchat-app.jsx +6 -4
  69. package/lib/experimental/components/audio.js +0 -46
  70. package/lib/experimental/components/audio.js.map +0 -1
  71. package/lib/experimental/components/carousel.js +0 -194
  72. package/lib/experimental/components/carousel.js.map +0 -1
  73. package/lib/experimental/components/custom-message.js +0 -132
  74. package/lib/experimental/components/custom-message.js.map +0 -1
  75. package/lib/experimental/components/document.js +0 -69
  76. package/lib/experimental/components/document.js.map +0 -1
  77. package/lib/experimental/components/image.js +0 -47
  78. package/lib/experimental/components/image.js.map +0 -1
  79. package/lib/experimental/components/index.js +0 -184
  80. package/lib/experimental/components/index.js.map +0 -1
  81. package/lib/experimental/components/location.js +0 -54
  82. package/lib/experimental/components/location.js.map +0 -1
  83. package/lib/experimental/components/markdown.js +0 -103
  84. package/lib/experimental/components/markdown.js.map +0 -1
  85. package/lib/experimental/components/message.js +0 -353
  86. package/lib/experimental/components/message.js.map +0 -1
  87. package/lib/experimental/components/text.js +0 -80
  88. package/lib/experimental/components/text.js.map +0 -1
  89. package/lib/experimental/components/video.js +0 -49
  90. package/lib/experimental/components/video.js.map +0 -1
  91. package/lib/experimental/components/whatsapp-template.js +0 -53
  92. package/lib/experimental/components/whatsapp-template.js.map +0 -1
  93. package/lib/experimental/dev-app.js +0 -240
  94. package/lib/experimental/dev-app.js.map +0 -1
  95. package/lib/experimental/index.js +0 -1022
  96. package/lib/experimental/index.js.map +0 -1
  97. package/lib/experimental/msg-to-botonic.js +0 -162
  98. package/lib/experimental/msg-to-botonic.js.map +0 -1
  99. package/lib/experimental/node-app.js +0 -97
  100. package/lib/experimental/node-app.js.map +0 -1
  101. package/lib/experimental/react-bot.js +0 -167
  102. package/lib/experimental/react-bot.js.map +0 -1
  103. package/lib/experimental/webchat/assets/Inter-VariableFont_slnt,wght.ttf +0 -0
  104. package/lib/experimental/webchat/assets/botonic-logo-white.svg +0 -16
  105. package/lib/experimental/webchat/assets/messenger.svg +0 -10
  106. package/lib/experimental/webchat/assets/open-new-window.svg +0 -3
  107. package/lib/experimental/webchat/assets/open.svg +0 -3
  108. package/lib/experimental/webchat/assets/telegram.svg +0 -10
  109. package/lib/experimental/webchat/assets/webchat.svg +0 -21
  110. package/lib/experimental/webchat/assets/whatsapp.svg +0 -4
  111. package/lib/experimental/webchat/webchat-dev.js +0 -300
  112. package/lib/experimental/webchat/webchat-dev.js.map +0 -1
  113. package/lib/experimental/webchat/webchat.js +0 -1051
  114. package/lib/experimental/webchat/webchat.js.map +0 -1
  115. package/lib/experimental/webchat-app.js +0 -642
  116. package/lib/experimental/webchat-app.js.map +0 -1
@@ -0,0 +1,68 @@
1
+ import {
2
+ SET_CURRENT_ATTACHMENT,
3
+ SET_ERROR,
4
+ SET_ONLINE,
5
+ TOGGLE_COVER_COMPONENT,
6
+ TOGGLE_EMOJI_PICKER,
7
+ TOGGLE_PERSISTENT_MENU,
8
+ TOGGLE_WEBCHAT,
9
+ UPDATE_BOT_STATE,
10
+ UPDATE_DEV_SETTINGS,
11
+ UPDATE_HANDOFF,
12
+ UPDATE_JWT,
13
+ UPDATE_LAST_ROUTE_PATH,
14
+ UPDATE_LATEST_INPUT,
15
+ UPDATE_SESSION,
16
+ UPDATE_THEME,
17
+ UPDATE_TYPING,
18
+ UPDATE_USER,
19
+ UPDATE_WEBVIEW,
20
+ } from './actions'
21
+ import { messagesReducer } from './messages-reducer'
22
+
23
+ // eslint-disable-next-line complexity
24
+ export function webchatReducer(state, action) {
25
+ switch (action.type) {
26
+ case UPDATE_WEBVIEW:
27
+ return { ...state, ...action.payload }
28
+ case UPDATE_TYPING:
29
+ return { ...state, typing: action.payload }
30
+ case UPDATE_THEME:
31
+ return {
32
+ ...state,
33
+ ...action.payload,
34
+ }
35
+ case UPDATE_HANDOFF:
36
+ return { ...state, handoff: action.payload }
37
+ case TOGGLE_WEBCHAT:
38
+ return { ...state, isWebchatOpen: action.payload }
39
+ case TOGGLE_EMOJI_PICKER:
40
+ return { ...state, isEmojiPickerOpen: action.payload }
41
+ case TOGGLE_PERSISTENT_MENU:
42
+ return { ...state, isPersistentMenuOpen: action.payload }
43
+ case TOGGLE_COVER_COMPONENT:
44
+ return { ...state, isCoverComponentOpen: action.payload }
45
+ case SET_ERROR:
46
+ return { ...state, error: action.payload || {} }
47
+ case SET_ONLINE:
48
+ return { ...state, online: action.payload }
49
+ case UPDATE_DEV_SETTINGS:
50
+ return { ...state, devSettings: { ...action.payload } }
51
+ case UPDATE_LATEST_INPUT:
52
+ return { ...state, latestInput: action.payload }
53
+ case UPDATE_LAST_ROUTE_PATH:
54
+ return { ...state, lastRoutePath: action.payload }
55
+ case SET_CURRENT_ATTACHMENT:
56
+ return { ...state, currentAttachment: action.payload }
57
+ case UPDATE_JWT:
58
+ return { ...state, jwt: action.payload }
59
+ case UPDATE_USER:
60
+ return { ...state, user: action.payload }
61
+ case UPDATE_SESSION:
62
+ return { ...state, session: action.payload }
63
+ case UPDATE_BOT_STATE:
64
+ return { ...state, botState: action.payload }
65
+ default:
66
+ return messagesReducer(state, action)
67
+ }
68
+ }
@@ -1,4 +1,8 @@
1
1
  import { INPUT, isMobile, params2queryString } from '@botonic/core'
2
+ import {
3
+ MessageEventAck,
4
+ MessageEventFrom,
5
+ } from '@botonic/core/src/models/events/message'
2
6
  import { motion } from 'framer-motion'
3
7
  import merge from 'lodash.merge'
4
8
  import React, {
@@ -38,12 +42,6 @@ import { scrollToBottom } from '../../util/dom'
38
42
  import { isDev, resolveImage } from '../../util/environment'
39
43
  import { ConditionalWrapper } from '../../util/react'
40
44
  import { deserializeRegex, stringifyWithRegexs } from '../../util/regexs'
41
- import {
42
- _getThemeProperty,
43
- getServerErrorMessage,
44
- initSession,
45
- shouldKeepSessionOnReload,
46
- } from '../../util/webchat'
47
45
  import { Attachment } from '../../webchat/components/attachment'
48
46
  import {
49
47
  EmojiPicker,
@@ -57,12 +55,6 @@ import { SendButton } from '../../webchat/components/send-button'
57
55
  import { TypingIndicator } from '../../webchat/components/typing-indicator'
58
56
  import { DeviceAdapter } from '../../webchat/devices/device-adapter'
59
57
  import { StyledWebchatHeader } from '../../webchat/header'
60
- import {
61
- useComponentWillMount,
62
- usePrevious,
63
- useTyping,
64
- useWebchat,
65
- } from '../../webchat/hooks'
66
58
  import { WebchatMessageList } from '../../webchat/message-list'
67
59
  import { WebchatReplies } from '../../webchat/replies'
68
60
  import { useStorageState } from '../../webchat/use-storage-state-hook'
@@ -70,6 +62,18 @@ import { WebviewContainer } from '../../webchat/webview'
70
62
  import { Audio, Document, Image, Video } from '../components'
71
63
  import { Text } from '../components/text'
72
64
  import { msgToBotonic } from '../msg-to-botonic'
65
+ import {
66
+ _getThemeProperty,
67
+ getServerErrorMessage,
68
+ initUser,
69
+ shouldKeepSessionOnReload,
70
+ } from '../util/webchat'
71
+ import {
72
+ useComponentWillMount,
73
+ usePrevious,
74
+ useTyping,
75
+ useWebchat,
76
+ } from '../webchat/hooks'
73
77
  export const getParsedAction = botonicAction => {
74
78
  const splittedAction = botonicAction.split('create_case:')
75
79
  if (splittedAction.length <= 1) return undefined
@@ -177,7 +181,6 @@ export const Webchat = forwardRef((props, ref) => {
177
181
  updateLatestInput,
178
182
  updateTyping,
179
183
  updateWebview,
180
- updateSession,
181
184
  updateLastRoutePath,
182
185
  updateHandoff,
183
186
  updateTheme,
@@ -194,14 +197,23 @@ export const Webchat = forwardRef((props, ref) => {
194
197
  updateLastMessageDate,
195
198
  setCurrentAttachment,
196
199
  updateJwt,
200
+ updateUser,
201
+ updateSession,
202
+ updateBotState,
197
203
  // eslint-disable-next-line react-hooks/rules-of-hooks
198
204
  } = props.webchatHooks || useWebchat()
199
205
 
200
206
  const firstUpdate = useRef(true)
201
- const isOnline = () => webchatState.online
207
+ const isOnline = () => webchatState.isWebchatOnline
202
208
  const currentDateString = () => new Date().toISOString()
203
209
  const theme = merge(webchatState.theme, props.theme)
204
- const { initialSession, initialDevSettings, onStateChange } = props
210
+ const {
211
+ initialUser,
212
+ initialSession,
213
+ initialBotState,
214
+ initialDevSettings,
215
+ onStateChange,
216
+ } = props
205
217
  const getThemeProperty = _getThemeProperty(theme)
206
218
 
207
219
  const storage = props.storage === undefined ? localStorage : props.storage
@@ -223,12 +235,13 @@ export const Webchat = forwardRef((props, ref) => {
223
235
  JSON.parse(
224
236
  stringifyWithRegexs({
225
237
  messages: webchatState.messagesJSON,
226
- session: webchatState.session,
227
- lastRoutePath: webchatState.lastRoutePath,
228
238
  devSettings: webchatState.devSettings,
229
239
  lastMessageUpdate: webchatState.lastMessageUpdate,
230
240
  themeUpdates: webchatState.themeUpdates,
231
241
  jwt: webchatState.jwt,
242
+ user: webchatState.user,
243
+ session: webchatState.session,
244
+ botState: webchatState.botState,
232
245
  })
233
246
  )
234
247
  )
@@ -256,10 +269,10 @@ export const Webchat = forwardRef((props, ref) => {
256
269
  const sendUserInput = async input => {
257
270
  props.onUserInput &&
258
271
  props.onUserInput({
259
- user: webchatState.session.user,
260
- input: input,
272
+ user: webchatState.user,
273
+ input,
261
274
  session: webchatState.session,
262
- lastRoutePath: webchatState.lastRoutePath,
275
+ botState: webchatState.botState,
263
276
  })
264
277
  }
265
278
 
@@ -293,16 +306,18 @@ export const Webchat = forwardRef((props, ref) => {
293
306
 
294
307
  // Load initial state from storage
295
308
  useEffect(() => {
296
- let {
309
+ const {
297
310
  messages,
298
- session,
299
311
  lastRoutePath,
300
312
  devSettings,
301
313
  lastMessageUpdate,
302
314
  themeUpdates,
315
+ user,
316
+ session,
317
+ botState,
303
318
  } = botonicState || {}
304
- session = initSession(session)
305
- updateSession(session)
319
+ updateUser(merge(initialUser, initUser(user)))
320
+
306
321
  if (shouldKeepSessionOnReload({ initialDevSettings, devSettings })) {
307
322
  if (messages) {
308
323
  messages.forEach(m => {
@@ -315,9 +330,16 @@ export const Webchat = forwardRef((props, ref) => {
315
330
  if (newComponent) addMessageComponent(newComponent)
316
331
  })
317
332
  }
318
- if (initialSession) updateSession(merge(initialSession, session))
319
- if (lastRoutePath) updateLastRoutePath(lastRoutePath)
320
- } else updateSession(merge(initialSession, session))
333
+ if (initialSession) {
334
+ updateSession(merge(initialSession, session))
335
+ }
336
+ if (initialBotState) {
337
+ updateBotState(merge(initialBotState, botState))
338
+ }
339
+ } else {
340
+ updateSession(merge(initialSession, session))
341
+ updateBotState(merge(initialBotState, botState))
342
+ }
321
343
  if (devSettings) updateDevSettings(devSettings)
322
344
  else if (initialDevSettings) updateDevSettings(initialDevSettings)
323
345
  if (lastMessageUpdate) updateLastMessageDate(lastMessageUpdate)
@@ -333,21 +355,26 @@ export const Webchat = forwardRef((props, ref) => {
333
355
  }, [webchatState.isWebchatOpen])
334
356
 
335
357
  useEffect(() => {
336
- if (onStateChange && typeof onStateChange === 'function') {
358
+ if (
359
+ onStateChange &&
360
+ typeof onStateChange === 'function' &&
361
+ webchatState.user.id
362
+ ) {
337
363
  onStateChange({ ...webchatState, updateJwt })
338
364
  }
339
365
  saveWebchatState(webchatState)
340
366
  }, [
341
367
  webchatState.messagesJSON,
342
- webchatState.session,
343
- webchatState.lastRoutePath,
344
368
  webchatState.devSettings,
345
369
  webchatState.lastMessageUpdate,
346
370
  webchatState.jwt,
371
+ webchatState.user,
372
+ webchatState.session,
373
+ webchatState.botState,
347
374
  ])
348
375
 
349
376
  useAsyncEffect(async () => {
350
- if (!webchatState.online) {
377
+ if (!webchatState.isWebchatOnline) {
351
378
  setError({
352
379
  message: getServerErrorMessage(props.server),
353
380
  })
@@ -356,7 +383,7 @@ export const Webchat = forwardRef((props, ref) => {
356
383
  setError(undefined)
357
384
  }
358
385
  }
359
- }, [webchatState.online])
386
+ }, [webchatState.isWebchatOnline])
360
387
 
361
388
  useTyping({ webchatState, updateTyping, updateMessage, host })
362
389
 
@@ -423,7 +450,7 @@ export const Webchat = forwardRef((props, ref) => {
423
450
  )
424
451
  if (!Array.isArray(blockInputs)) return false
425
452
  for (const rule of blockInputs) {
426
- if (getBlockInputs(rule, input.data)) {
453
+ if (getBlockInputs(rule, input.text)) {
427
454
  addMessageComponent(
428
455
  <Text
429
456
  id={input.id}
@@ -498,36 +525,36 @@ export const Webchat = forwardRef((props, ref) => {
498
525
 
499
526
  const messageComponentFromInput = input => {
500
527
  let messageComponent = null
528
+ let props = { ...input }
501
529
  if (isText(input)) {
502
- messageComponent = (
503
- <Text id={input.id} payload={input.payload} from={SENDERS.user}>
504
- {input.data}
505
- </Text>
506
- )
530
+ messageComponent = <Text {...props}>{input.text}</Text>
507
531
  } else if (isMedia(input)) {
508
- const temporaryDisplayUrl = URL.createObjectURL(input.data)
509
- const mediaProps = {
510
- id: input.id,
511
- from: SENDERS.user,
512
- src: temporaryDisplayUrl,
513
- }
514
- if (isImage(input)) messageComponent = <Image {...mediaProps} />
515
- else if (isAudio(input)) messageComponent = <Audio {...mediaProps} />
516
- else if (isVideo(input)) messageComponent = <Video {...mediaProps} />
517
- else if (isDocument(input))
518
- messageComponent = <Document {...mediaProps} />
532
+ const temporaryDisplayUrl = URL.createObjectURL(input.src)
533
+ props = { ...props, src: temporaryDisplayUrl }
534
+ if (isImage(input)) messageComponent = <Image {...props} />
535
+ else if (isAudio(input)) messageComponent = <Audio {...props} />
536
+ else if (isVideo(input)) messageComponent = <Video {...props} />
537
+ else if (isDocument(input)) messageComponent = <Document {...props} />
519
538
  }
520
539
  return messageComponent
521
540
  }
522
541
 
523
542
  const sendInput = async input => {
524
543
  if (!input || Object.keys(input).length == 0) return
525
- if (isText(input) && (!input.data || !input.data.trim())) return // in case trim() doesn't work in a browser we can use !/\S/.test(input.data)
544
+ if (isText(input) && (!input.text || !input.text.trim())) return // in case trim() doesn't work in a browser we can use !/\S/.test(input.text)
526
545
  if (isText(input) && checkBlockInput(input)) return
527
546
  if (!input.id) input.id = uuidv4()
547
+ const { idFromChannel, channel } = webchatState.user
548
+ input = {
549
+ idFromChannel,
550
+ channel,
551
+ ack: MessageEventAck.DRAFT,
552
+ from: MessageEventFrom.USER,
553
+ ...input,
554
+ }
528
555
  const messageComponent = messageComponentFromInput(input)
529
556
  if (messageComponent) addMessageComponent(messageComponent)
530
- if (isMedia(input)) input.data = await readDataURL(input.data)
557
+ if (isMedia(input)) input.src = await readDataURL(input.src)
531
558
  sendUserInput(input)
532
559
  updateLatestInput(input)
533
560
  isOnline() && updateLastMessageDate(currentDateString())
@@ -540,27 +567,28 @@ export const Webchat = forwardRef((props, ref) => {
540
567
  https://stackoverflow.com/questions/37949981/call-child-method-from-parent
541
568
  */
542
569
 
543
- const updateSessionWithUser = userToUpdate =>
544
- updateSession(merge(webchatState.session, { user: userToUpdate }))
570
+ const mergeAndUpdateUser = userToUpdate =>
571
+ updateUser(merge(webchatState.user, userToUpdate))
545
572
 
546
573
  useImperativeHandle(ref, () => ({
547
574
  addBotResponse: ({ response, session, lastRoutePath }) => {
548
575
  updateTyping(false)
549
576
  if (Array.isArray(response)) response.map(r => addMessageComponent(r))
550
577
  else if (response) addMessageComponent(response)
551
- if (session) {
552
- updateSession(merge(session, { user: webchatState.session.user }))
553
- const action = session._botonic_action || ''
554
- const handoff = action.startsWith('create_case')
555
- if (handoff && isDev) addMessageComponent(<Handoff />)
556
- updateHandoff(handoff)
557
- }
558
- if (lastRoutePath) updateLastRoutePath(lastRoutePath)
559
578
  updateLastMessageDate(currentDateString())
560
579
  },
561
580
  setTyping: typing => updateTyping(typing),
562
581
  addUserMessage: message => sendInput(message),
563
- updateUser: updateSessionWithUser,
582
+ updateUser: mergeAndUpdateUser,
583
+ updateBotState: botState => {
584
+ // TODO: Review handoff logic for 1.0
585
+ // const action = botState.botonicAction || ''
586
+ // // const isHandoff = action.startsWith('create_case')
587
+ if (botState.isHandoff && isDev) addMessageComponent(<Handoff />)
588
+ updateBotState(botState)
589
+ // updateHandoff(botState.isHandoff)
590
+ },
591
+ updateSession: updateSession,
564
592
  openWebchat: () => toggleWebchat(true),
565
593
  closeWebchat: () => toggleWebchat(false),
566
594
  toggleWebchat: () => toggleWebchat(!webchatState.isWebchatOpen),
@@ -593,26 +621,30 @@ export const Webchat = forwardRef((props, ref) => {
593
621
  }))
594
622
 
595
623
  const resolveCase = () => {
596
- updateHandoff(false)
597
- updateSession({ ...webchatState.session, _botonic_action: null })
624
+ // updateHandoff(false)
625
+ updateBotState({
626
+ ...webchatState.botState,
627
+ isHandoff: false,
628
+ botonicAction: null,
629
+ })
598
630
  }
599
631
 
600
- const prevSession = usePrevious(webchatState.session)
632
+ const previousBotState = usePrevious(webchatState.botState)
601
633
  useEffect(() => {
602
634
  // Resume conversation after handoff
603
635
  if (
604
- prevSession &&
605
- prevSession._botonic_action &&
606
- !webchatState.session._botonic_action
636
+ previousBotState &&
637
+ previousBotState.botonicAction &&
638
+ !webchatState.botState.botonicAction
607
639
  ) {
608
- const action = getParsedAction(prevSession._botonic_action)
640
+ const action = getParsedAction(previousBotState.botonicAction)
609
641
  if (action && action.on_finish) sendPayload(action.on_finish)
610
642
  }
611
- }, [webchatState.session._botonic_action])
643
+ }, [webchatState.botState.botonicAction])
612
644
 
613
645
  const sendText = async (text, payload) => {
614
646
  if (!text) return
615
- const input = { type: INPUT.TEXT, data: text, payload }
647
+ const input = { type: INPUT.TEXT, text, payload }
616
648
  await sendInput(input)
617
649
  }
618
650
 
@@ -628,7 +660,7 @@ export const Webchat = forwardRef((props, ref) => {
628
660
  if (!attachmentType) return
629
661
  const input = {
630
662
  type: attachmentType,
631
- data: attachment.file,
663
+ src: attachment.file,
632
664
  }
633
665
  await sendInput(input)
634
666
  setCurrentAttachment(undefined)
@@ -648,8 +680,8 @@ export const Webchat = forwardRef((props, ref) => {
648
680
  }
649
681
 
650
682
  const webviewRequestContext = {
651
- getString: stringId => props.getString(stringId, webchatState.session),
652
- setLocale: locale => props.getString(locale, webchatState.session),
683
+ getString: stringId => props.getString(stringId, webchatState.botState),
684
+ setLocale: locale => props.getString(locale, webchatState.botState),
653
685
  session: webchatState.session || {},
654
686
  params: webchatState.webviewParams || {},
655
687
  closeWebview: closeWebview,
@@ -909,7 +941,7 @@ export const Webchat = forwardRef((props, ref) => {
909
941
  updateMessage,
910
942
  updateReplies,
911
943
  updateLatestInput,
912
- updateUser: updateSessionWithUser,
944
+ updateUser: mergeAndUpdateUser,
913
945
  updateWebchatDevSettings: updateWebchatDevSettings,
914
946
  }}
915
947
  >
@@ -3,9 +3,9 @@ import merge from 'lodash.merge'
3
3
  import React, { createRef } from 'react'
4
4
  import { render } from 'react-dom'
5
5
 
6
- import { SENDERS, WEBCHAT } from '../constants'
7
- import { isShadowDOMSupported, onDOMLoaded } from '../util/dom'
6
+ import { SENDERS, WEBCHAT } from './constants'
8
7
  import { msgToBotonic } from './msg-to-botonic'
8
+ import { isShadowDOMSupported, onDOMLoaded } from './util/dom'
9
9
  import { Webchat } from './webchat/webchat'
10
10
 
11
11
  export class WebchatApp {
@@ -139,18 +139,27 @@ export class WebchatApp {
139
139
  }
140
140
 
141
141
  onServiceEvent(event) {
142
- if (event.action === 'connectionChange')
142
+ const { action, ...eventData } = event
143
+ if (action === 'connection_change')
143
144
  this.webchatRef.current.setOnline(event.online)
144
- else if (event.action === 'update_message_info')
145
- this.updateMessageInfo(event.message.id, event.message)
146
- else if (event.message.type === 'update_webchat_settings')
147
- this.updateWebchatSettings(event.message.data)
148
- else if (event.message.type === 'sender_action')
149
- this.setTyping(event.message.data === 'typing_on')
150
- else {
145
+ else if (action === 'update_message_info') {
146
+ this.updateMessageInfo(eventData.id, eventData)
147
+ } else if (action === 'update_user') {
148
+ this.updateUser(eventData)
149
+ } else if (action === 'update_session') {
150
+ this.updateSession(eventData.session)
151
+ } else if (action === 'update_bot_state') {
152
+ this.updateBotState(eventData.botState)
153
+ }
154
+ // TODO: Discuss how this updates to be done
155
+ else if (eventData.type === 'update_webchat_settings')
156
+ this.updateWebchatSettings(event.data)
157
+ else if (eventData.type === 'sender_action')
158
+ this.setTyping(event.data === 'typing_on')
159
+ else if (eventData.eventType === 'message') {
151
160
  this.onMessage &&
152
- this.onMessage(this, { from: SENDERS.bot, message: event.message })
153
- this.addBotMessage(event.message)
161
+ this.onMessage(this, { from: SENDERS.bot, message: eventData })
162
+ this.addBotMessage(eventData)
154
163
  }
155
164
  }
156
165
 
@@ -158,6 +167,14 @@ export class WebchatApp {
158
167
  this.webchatRef.current.updateUser(user)
159
168
  }
160
169
 
170
+ updateSession(session) {
171
+ this.webchatRef.current.updateSession(session)
172
+ }
173
+
174
+ updateBotState(botState) {
175
+ this.webchatRef.current.updateBotState(botState)
176
+ }
177
+
161
178
  addBotMessage(message) {
162
179
  this.webchatRef.current.addBotResponse({
163
180
  response: msgToBotonic(
@@ -240,7 +257,7 @@ export class WebchatApp {
240
257
  }
241
258
 
242
259
  // eslint-disable-next-line complexity
243
- getComponent(optionsAtRuntime = {}) {
260
+ getComponent(host, optionsAtRuntime = {}) {
244
261
  let {
245
262
  theme = {},
246
263
  persistentMenu,
@@ -258,6 +275,7 @@ export class WebchatApp {
258
275
  onOpen,
259
276
  onClose,
260
277
  onMessage,
278
+ hostId,
261
279
  appId,
262
280
  visibility,
263
281
  server,
@@ -282,6 +300,8 @@ export class WebchatApp {
282
300
  this.onMessage = onMessage || this.onMessage
283
301
  this.visibility = visibility || this.visibility
284
302
  this.appId = appId || this.appId
303
+ this.hostId = hostId || this.hostId
304
+ this.createRootElement(host)
285
305
  return (
286
306
  <Webchat
287
307
  {...webchatOptions}
@@ -342,11 +362,10 @@ export class WebchatApp {
342
362
 
343
363
  async render(dest, optionsAtRuntime = {}) {
344
364
  onDOMLoaded(async () => {
345
- this.createRootElement(dest)
346
365
  const isVisible = await this.resolveWebchatVisibility(optionsAtRuntime)
347
366
  if (isVisible)
348
367
  render(
349
- this.getComponent(optionsAtRuntime),
368
+ this.getComponent(dest, optionsAtRuntime),
350
369
  this.getReactMountNode(dest)
351
370
  )
352
371
  })
package/src/index.d.ts CHANGED
@@ -176,6 +176,7 @@ export class WebchatApp {
176
176
  close(): void
177
177
  closeCoverComponent(): void
178
178
  getComponent(
179
+ host: HTMLElement,
179
180
  optionsAtRuntime?: WebchatAppArgs
180
181
  ): React.ForwardRefExoticComponent<any>
181
182
  getLastMessageUpdate(): string
package/src/util/dom.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import { WEBCHAT } from '../constants'
2
2
 
3
+ export const getScrollableContent = webchatElement => {
4
+ return webchatElement.querySelector(WEBCHAT.SELECTORS.SCROLLABLE_CONTENT)
5
+ }
6
+
3
7
  export const getScrollableArea = webchatElement => {
4
8
  const getArea = area => {
5
- const botonicScrollableContent = webchatElement.querySelector(
6
- WEBCHAT.SELECTORS.SCROLLABLE_CONTENT
7
- )
9
+ const botonicScrollableContent = getScrollableContent(webchatElement)
8
10
  const scrollableArea =
9
11
  botonicScrollableContent && botonicScrollableContent.querySelector(area)
10
12
  return scrollableArea
@@ -4,21 +4,33 @@ import { ScrollbarController } from './scrollbar-controller'
4
4
  import { WebchatResizer } from './webchat-resizer'
5
5
 
6
6
  export class DeviceAdapter {
7
+ constructor() {
8
+ this.currentDevice = this.getCurrentDevice()
9
+ }
10
+
7
11
  init(host) {
8
- this.currentDevice = navigator.platform
9
12
  this.webchatResizer = new WebchatResizer(this.currentDevice, host)
10
13
  this.scrollbarController = new ScrollbarController(this.currentDevice, host)
11
14
  this.scrollbarController.handleScrollEvents()
12
15
  }
13
16
 
17
+ getCurrentDevice() {
18
+ // navigator.platform deprecated. Ref: (https://erikmartinjordan.com/navigator-platform-deprecated-alternative)
19
+ if (navigator.userAgentData) return navigator.userAgentData.platform
20
+ return navigator.platform
21
+ }
22
+
14
23
  onFocus(host) {
15
24
  if (this.currentDevice !== DEVICES.MOBILE.IPHONE) {
16
25
  scrollToBottom({ host, timeout: 800 })
17
26
  return
18
27
  }
19
- this.webchatResizer.onFocus(() =>
20
- this.scrollbarController.handleOnTouchMoveEvents()
21
- )
28
+ setTimeout(() => {
29
+ // Place onFocus logic to be run the last on the queue of asynchronous events to give enough time to init method to be called. Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#zero_delays
30
+ this.webchatResizer.onFocus(() =>
31
+ this.scrollbarController.handleOnTouchMoveEvents()
32
+ )
33
+ }, 0)
22
34
  }
23
35
 
24
36
  onBlur() {
@@ -1,4 +1,8 @@
1
- import { getScrollableArea, getWebchatElement } from '../../util/dom'
1
+ import {
2
+ getScrollableArea,
3
+ getScrollableContent,
4
+ getWebchatElement,
5
+ } from '../../util/dom'
2
6
  import { DEVICES, isMobileDevice } from '.'
3
7
 
4
8
  const debounced = (delay, fn) => {
@@ -28,7 +32,7 @@ export class ScrollbarController {
28
32
 
29
33
  handleScrollEvents() {
30
34
  /*
31
- It handles scroll events for Mobile/Desktop.
35
+ It handles scroll events for Mobile/Desktop.
32
36
  "ontouchmove" is the phone equivalent for "onmouseover"
33
37
  */
34
38
  if (isMobileDevice()) {
@@ -61,11 +65,12 @@ export class ScrollbarController {
61
65
  }
62
66
 
63
67
  toggleOnMouseWheelEvents() {
68
+ const scrollableContent = getScrollableContent(this.webchat)
64
69
  if (this.hasScrollbar()) {
65
- this.webchat.onmousewheel = {}
70
+ scrollableContent.onmousewheel = {}
66
71
  return
67
72
  }
68
- this.webchat.onmousewheel = e => e.preventDefault()
73
+ scrollableContent.onmousewheel = e => e.preventDefault()
69
74
  }
70
75
 
71
76
  handleOnTouchMoveEvents(e) {
@@ -212,6 +212,8 @@ export const Webchat = forwardRef((props, ref) => {
212
212
 
213
213
  const host = props.host || document.body
214
214
 
215
+ const deviceAdapter = new DeviceAdapter()
216
+
215
217
  const saveWebchatState = webchatState => {
216
218
  storage &&
217
219
  saveState(
@@ -227,7 +229,6 @@ export const Webchat = forwardRef((props, ref) => {
227
229
  )
228
230
  )
229
231
  }
230
- const deviceAdapter = new DeviceAdapter()
231
232
 
232
233
  const handleAttachment = event => {
233
234
  if (!isAllowedSize(event.target.files[0].size)) {