@botonic/react 1.0.0-beta.0 → 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 (118) 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 +5 -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 +25 -6
  32. package/lib/webchat/hooks.js.map +1 -1
  33. package/lib/webchat/index.js +1 -1
  34. package/lib/webchat/webchat-reducer.js +11 -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/message.jsx +8 -2
  45. package/src/experimental/constants.js +189 -0
  46. package/src/experimental/contexts.jsx +36 -0
  47. package/src/experimental/dev-app.jsx +14 -7
  48. package/src/experimental/index.js +94 -25
  49. package/src/experimental/msg-to-botonic.jsx +8 -8
  50. package/src/experimental/react-bot.jsx +1 -1
  51. package/src/experimental/util/dom.js +55 -0
  52. package/src/experimental/util/objects.js +39 -0
  53. package/src/experimental/util/webchat.js +61 -0
  54. package/src/experimental/webchat/actions.jsx +24 -0
  55. package/src/experimental/webchat/hooks.js +296 -0
  56. package/src/experimental/webchat/messages-reducer.js +86 -0
  57. package/src/experimental/webchat/session-view.jsx +166 -0
  58. package/src/experimental/webchat/webchat-dev.jsx +23 -19
  59. package/src/experimental/webchat/webchat-reducer.js +68 -0
  60. package/src/experimental/webchat/webchat.jsx +109 -82
  61. package/src/experimental/webchat-app.jsx +39 -15
  62. package/src/index.d.ts +1 -0
  63. package/src/util/dom.js +5 -3
  64. package/src/webchat/actions.jsx +1 -0
  65. package/src/webchat/devices/device-adapter.js +16 -4
  66. package/src/webchat/devices/scrollbar-controller.js +9 -4
  67. package/src/webchat/hooks.js +10 -0
  68. package/src/webchat/webchat-reducer.js +3 -0
  69. package/src/webchat/webchat.jsx +2 -1
  70. package/src/webchat-app.jsx +6 -4
  71. package/lib/experimental/components/audio.js +0 -46
  72. package/lib/experimental/components/audio.js.map +0 -1
  73. package/lib/experimental/components/carousel.js +0 -194
  74. package/lib/experimental/components/carousel.js.map +0 -1
  75. package/lib/experimental/components/custom-message.js +0 -132
  76. package/lib/experimental/components/custom-message.js.map +0 -1
  77. package/lib/experimental/components/document.js +0 -69
  78. package/lib/experimental/components/document.js.map +0 -1
  79. package/lib/experimental/components/image.js +0 -47
  80. package/lib/experimental/components/image.js.map +0 -1
  81. package/lib/experimental/components/index.js +0 -184
  82. package/lib/experimental/components/index.js.map +0 -1
  83. package/lib/experimental/components/location.js +0 -54
  84. package/lib/experimental/components/location.js.map +0 -1
  85. package/lib/experimental/components/markdown.js +0 -103
  86. package/lib/experimental/components/markdown.js.map +0 -1
  87. package/lib/experimental/components/message.js +0 -353
  88. package/lib/experimental/components/message.js.map +0 -1
  89. package/lib/experimental/components/text.js +0 -80
  90. package/lib/experimental/components/text.js.map +0 -1
  91. package/lib/experimental/components/video.js +0 -49
  92. package/lib/experimental/components/video.js.map +0 -1
  93. package/lib/experimental/components/whatsapp-template.js +0 -53
  94. package/lib/experimental/components/whatsapp-template.js.map +0 -1
  95. package/lib/experimental/dev-app.js +0 -240
  96. package/lib/experimental/dev-app.js.map +0 -1
  97. package/lib/experimental/index.js +0 -748
  98. package/lib/experimental/index.js.map +0 -1
  99. package/lib/experimental/msg-to-botonic.js +0 -162
  100. package/lib/experimental/msg-to-botonic.js.map +0 -1
  101. package/lib/experimental/node-app.js +0 -97
  102. package/lib/experimental/node-app.js.map +0 -1
  103. package/lib/experimental/react-bot.js +0 -167
  104. package/lib/experimental/react-bot.js.map +0 -1
  105. package/lib/experimental/webchat/assets/Inter-VariableFont_slnt,wght.ttf +0 -0
  106. package/lib/experimental/webchat/assets/botonic-logo-white.svg +0 -16
  107. package/lib/experimental/webchat/assets/messenger.svg +0 -10
  108. package/lib/experimental/webchat/assets/open-new-window.svg +0 -3
  109. package/lib/experimental/webchat/assets/open.svg +0 -3
  110. package/lib/experimental/webchat/assets/telegram.svg +0 -10
  111. package/lib/experimental/webchat/assets/webchat.svg +0 -21
  112. package/lib/experimental/webchat/assets/whatsapp.svg +0 -4
  113. package/lib/experimental/webchat/webchat-dev.js +0 -300
  114. package/lib/experimental/webchat/webchat-dev.js.map +0 -1
  115. package/lib/experimental/webchat/webchat.js +0 -1044
  116. package/lib/experimental/webchat/webchat.js.map +0 -1
  117. package/lib/experimental/webchat-app.js +0 -635
  118. 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
+ }
@@ -2,7 +2,7 @@ import { INPUT, isMobile, params2queryString } from '@botonic/core'
2
2
  import {
3
3
  MessageEventAck,
4
4
  MessageEventFrom,
5
- } from '@botonic/core/lib/models/events/message'
5
+ } from '@botonic/core/src/models/events/message'
6
6
  import { motion } from 'framer-motion'
7
7
  import merge from 'lodash.merge'
8
8
  import React, {
@@ -42,12 +42,6 @@ import { scrollToBottom } from '../../util/dom'
42
42
  import { isDev, resolveImage } from '../../util/environment'
43
43
  import { ConditionalWrapper } from '../../util/react'
44
44
  import { deserializeRegex, stringifyWithRegexs } from '../../util/regexs'
45
- import {
46
- _getThemeProperty,
47
- getServerErrorMessage,
48
- initSession,
49
- shouldKeepSessionOnReload,
50
- } from '../../util/webchat'
51
45
  import { Attachment } from '../../webchat/components/attachment'
52
46
  import {
53
47
  EmojiPicker,
@@ -61,12 +55,6 @@ import { SendButton } from '../../webchat/components/send-button'
61
55
  import { TypingIndicator } from '../../webchat/components/typing-indicator'
62
56
  import { DeviceAdapter } from '../../webchat/devices/device-adapter'
63
57
  import { StyledWebchatHeader } from '../../webchat/header'
64
- import {
65
- useComponentWillMount,
66
- usePrevious,
67
- useTyping,
68
- useWebchat,
69
- } from '../../webchat/hooks'
70
58
  import { WebchatMessageList } from '../../webchat/message-list'
71
59
  import { WebchatReplies } from '../../webchat/replies'
72
60
  import { useStorageState } from '../../webchat/use-storage-state-hook'
@@ -74,7 +62,18 @@ import { WebviewContainer } from '../../webchat/webview'
74
62
  import { Audio, Document, Image, Video } from '../components'
75
63
  import { Text } from '../components/text'
76
64
  import { msgToBotonic } from '../msg-to-botonic'
77
-
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'
78
77
  export const getParsedAction = botonicAction => {
79
78
  const splittedAction = botonicAction.split('create_case:')
80
79
  if (splittedAction.length <= 1) return undefined
@@ -182,7 +181,6 @@ export const Webchat = forwardRef((props, ref) => {
182
181
  updateLatestInput,
183
182
  updateTyping,
184
183
  updateWebview,
185
- updateSession,
186
184
  updateLastRoutePath,
187
185
  updateHandoff,
188
186
  updateTheme,
@@ -198,13 +196,24 @@ export const Webchat = forwardRef((props, ref) => {
198
196
  closeWebviewT,
199
197
  updateLastMessageDate,
200
198
  setCurrentAttachment,
199
+ updateJwt,
200
+ updateUser,
201
+ updateSession,
202
+ updateBotState,
201
203
  // eslint-disable-next-line react-hooks/rules-of-hooks
202
204
  } = props.webchatHooks || useWebchat()
205
+
203
206
  const firstUpdate = useRef(true)
204
- const isOnline = () => webchatState.online
207
+ const isOnline = () => webchatState.isWebchatOnline
205
208
  const currentDateString = () => new Date().toISOString()
206
209
  const theme = merge(webchatState.theme, props.theme)
207
- const { initialSession, initialDevSettings, onStateChange } = props
210
+ const {
211
+ initialUser,
212
+ initialSession,
213
+ initialBotState,
214
+ initialDevSettings,
215
+ onStateChange,
216
+ } = props
208
217
  const getThemeProperty = _getThemeProperty(theme)
209
218
 
210
219
  const storage = props.storage === undefined ? localStorage : props.storage
@@ -226,11 +235,13 @@ export const Webchat = forwardRef((props, ref) => {
226
235
  JSON.parse(
227
236
  stringifyWithRegexs({
228
237
  messages: webchatState.messagesJSON,
229
- session: webchatState.session,
230
- lastRoutePath: webchatState.lastRoutePath,
231
238
  devSettings: webchatState.devSettings,
232
239
  lastMessageUpdate: webchatState.lastMessageUpdate,
233
240
  themeUpdates: webchatState.themeUpdates,
241
+ jwt: webchatState.jwt,
242
+ user: webchatState.user,
243
+ session: webchatState.session,
244
+ botState: webchatState.botState,
234
245
  })
235
246
  )
236
247
  )
@@ -256,17 +267,12 @@ export const Webchat = forwardRef((props, ref) => {
256
267
  }, [webchatState.currentAttachment])
257
268
 
258
269
  const sendUserInput = async input => {
259
- input = {
260
- ...input,
261
- ack: MessageEventAck.DRAFT,
262
- from: MessageEventFrom.USER,
263
- }
264
270
  props.onUserInput &&
265
271
  props.onUserInput({
266
- user: webchatState.session.user,
272
+ user: webchatState.user,
267
273
  input,
268
274
  session: webchatState.session,
269
- lastRoutePath: webchatState.lastRoutePath,
275
+ botState: webchatState.botState,
270
276
  })
271
277
  }
272
278
 
@@ -300,16 +306,18 @@ export const Webchat = forwardRef((props, ref) => {
300
306
 
301
307
  // Load initial state from storage
302
308
  useEffect(() => {
303
- let {
309
+ const {
304
310
  messages,
305
- session,
306
311
  lastRoutePath,
307
312
  devSettings,
308
313
  lastMessageUpdate,
309
314
  themeUpdates,
315
+ user,
316
+ session,
317
+ botState,
310
318
  } = botonicState || {}
311
- session = initSession(session)
312
- updateSession(session)
319
+ updateUser(merge(initialUser, initUser(user)))
320
+
313
321
  if (shouldKeepSessionOnReload({ initialDevSettings, devSettings })) {
314
322
  if (messages) {
315
323
  messages.forEach(m => {
@@ -322,9 +330,16 @@ export const Webchat = forwardRef((props, ref) => {
322
330
  if (newComponent) addMessageComponent(newComponent)
323
331
  })
324
332
  }
325
- if (initialSession) updateSession(merge(initialSession, session))
326
- if (lastRoutePath) updateLastRoutePath(lastRoutePath)
327
- } 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
+ }
328
343
  if (devSettings) updateDevSettings(devSettings)
329
344
  else if (initialDevSettings) updateDevSettings(initialDevSettings)
330
345
  if (lastMessageUpdate) updateLastMessageDate(lastMessageUpdate)
@@ -340,19 +355,26 @@ export const Webchat = forwardRef((props, ref) => {
340
355
  }, [webchatState.isWebchatOpen])
341
356
 
342
357
  useEffect(() => {
343
- if (onStateChange && typeof onStateChange === 'function')
344
- onStateChange(webchatState)
358
+ if (
359
+ onStateChange &&
360
+ typeof onStateChange === 'function' &&
361
+ webchatState.user.id
362
+ ) {
363
+ onStateChange({ ...webchatState, updateJwt })
364
+ }
345
365
  saveWebchatState(webchatState)
346
366
  }, [
347
367
  webchatState.messagesJSON,
348
- webchatState.session,
349
- webchatState.lastRoutePath,
350
368
  webchatState.devSettings,
351
369
  webchatState.lastMessageUpdate,
370
+ webchatState.jwt,
371
+ webchatState.user,
372
+ webchatState.session,
373
+ webchatState.botState,
352
374
  ])
353
375
 
354
376
  useAsyncEffect(async () => {
355
- if (!webchatState.online) {
377
+ if (!webchatState.isWebchatOnline) {
356
378
  setError({
357
379
  message: getServerErrorMessage(props.server),
358
380
  })
@@ -361,7 +383,7 @@ export const Webchat = forwardRef((props, ref) => {
361
383
  setError(undefined)
362
384
  }
363
385
  }
364
- }, [webchatState.online])
386
+ }, [webchatState.isWebchatOnline])
365
387
 
366
388
  useTyping({ webchatState, updateTyping, updateMessage, host })
367
389
 
@@ -428,7 +450,7 @@ export const Webchat = forwardRef((props, ref) => {
428
450
  )
429
451
  if (!Array.isArray(blockInputs)) return false
430
452
  for (const rule of blockInputs) {
431
- if (getBlockInputs(rule, input.data)) {
453
+ if (getBlockInputs(rule, input.text)) {
432
454
  addMessageComponent(
433
455
  <Text
434
456
  id={input.id}
@@ -503,36 +525,36 @@ export const Webchat = forwardRef((props, ref) => {
503
525
 
504
526
  const messageComponentFromInput = input => {
505
527
  let messageComponent = null
528
+ let props = { ...input }
506
529
  if (isText(input)) {
507
- messageComponent = (
508
- <Text id={input.id} payload={input.payload} from={SENDERS.user}>
509
- {input.data}
510
- </Text>
511
- )
530
+ messageComponent = <Text {...props}>{input.text}</Text>
512
531
  } else if (isMedia(input)) {
513
- const temporaryDisplayUrl = URL.createObjectURL(input.data)
514
- const mediaProps = {
515
- id: input.id,
516
- from: SENDERS.user,
517
- src: temporaryDisplayUrl,
518
- }
519
- if (isImage(input)) messageComponent = <Image {...mediaProps} />
520
- else if (isAudio(input)) messageComponent = <Audio {...mediaProps} />
521
- else if (isVideo(input)) messageComponent = <Video {...mediaProps} />
522
- else if (isDocument(input))
523
- 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} />
524
538
  }
525
539
  return messageComponent
526
540
  }
527
541
 
528
542
  const sendInput = async input => {
529
543
  if (!input || Object.keys(input).length == 0) return
530
- 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)
531
545
  if (isText(input) && checkBlockInput(input)) return
532
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
+ }
533
555
  const messageComponent = messageComponentFromInput(input)
534
556
  if (messageComponent) addMessageComponent(messageComponent)
535
- if (isMedia(input)) input.data = await readDataURL(input.data)
557
+ if (isMedia(input)) input.src = await readDataURL(input.src)
536
558
  sendUserInput(input)
537
559
  updateLatestInput(input)
538
560
  isOnline() && updateLastMessageDate(currentDateString())
@@ -545,27 +567,28 @@ export const Webchat = forwardRef((props, ref) => {
545
567
  https://stackoverflow.com/questions/37949981/call-child-method-from-parent
546
568
  */
547
569
 
548
- const updateSessionWithUser = userToUpdate =>
549
- updateSession(merge(webchatState.session, { user: userToUpdate }))
570
+ const mergeAndUpdateUser = userToUpdate =>
571
+ updateUser(merge(webchatState.user, userToUpdate))
550
572
 
551
573
  useImperativeHandle(ref, () => ({
552
574
  addBotResponse: ({ response, session, lastRoutePath }) => {
553
575
  updateTyping(false)
554
576
  if (Array.isArray(response)) response.map(r => addMessageComponent(r))
555
577
  else if (response) addMessageComponent(response)
556
- if (session) {
557
- updateSession(merge(session, { user: webchatState.session.user }))
558
- const action = session._botonic_action || ''
559
- const handoff = action.startsWith('create_case')
560
- if (handoff && isDev) addMessageComponent(<Handoff />)
561
- updateHandoff(handoff)
562
- }
563
- if (lastRoutePath) updateLastRoutePath(lastRoutePath)
564
578
  updateLastMessageDate(currentDateString())
565
579
  },
566
580
  setTyping: typing => updateTyping(typing),
567
581
  addUserMessage: message => sendInput(message),
568
- 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,
569
592
  openWebchat: () => toggleWebchat(true),
570
593
  closeWebchat: () => toggleWebchat(false),
571
594
  toggleWebchat: () => toggleWebchat(!webchatState.isWebchatOpen),
@@ -598,26 +621,30 @@ export const Webchat = forwardRef((props, ref) => {
598
621
  }))
599
622
 
600
623
  const resolveCase = () => {
601
- updateHandoff(false)
602
- updateSession({ ...webchatState.session, _botonic_action: null })
624
+ // updateHandoff(false)
625
+ updateBotState({
626
+ ...webchatState.botState,
627
+ isHandoff: false,
628
+ botonicAction: null,
629
+ })
603
630
  }
604
631
 
605
- const prevSession = usePrevious(webchatState.session)
632
+ const previousBotState = usePrevious(webchatState.botState)
606
633
  useEffect(() => {
607
634
  // Resume conversation after handoff
608
635
  if (
609
- prevSession &&
610
- prevSession._botonic_action &&
611
- !webchatState.session._botonic_action
636
+ previousBotState &&
637
+ previousBotState.botonicAction &&
638
+ !webchatState.botState.botonicAction
612
639
  ) {
613
- const action = getParsedAction(prevSession._botonic_action)
640
+ const action = getParsedAction(previousBotState.botonicAction)
614
641
  if (action && action.on_finish) sendPayload(action.on_finish)
615
642
  }
616
- }, [webchatState.session._botonic_action])
643
+ }, [webchatState.botState.botonicAction])
617
644
 
618
645
  const sendText = async (text, payload) => {
619
646
  if (!text) return
620
- const input = { type: INPUT.TEXT, data: text, payload }
647
+ const input = { type: INPUT.TEXT, text, payload }
621
648
  await sendInput(input)
622
649
  }
623
650
 
@@ -633,7 +660,7 @@ export const Webchat = forwardRef((props, ref) => {
633
660
  if (!attachmentType) return
634
661
  const input = {
635
662
  type: attachmentType,
636
- data: attachment.file,
663
+ src: attachment.file,
637
664
  }
638
665
  await sendInput(input)
639
666
  setCurrentAttachment(undefined)
@@ -653,8 +680,8 @@ export const Webchat = forwardRef((props, ref) => {
653
680
  }
654
681
 
655
682
  const webviewRequestContext = {
656
- getString: stringId => props.getString(stringId, webchatState.session),
657
- setLocale: locale => props.getString(locale, webchatState.session),
683
+ getString: stringId => props.getString(stringId, webchatState.botState),
684
+ setLocale: locale => props.getString(locale, webchatState.botState),
658
685
  session: webchatState.session || {},
659
686
  params: webchatState.webviewParams || {},
660
687
  closeWebview: closeWebview,
@@ -914,7 +941,7 @@ export const Webchat = forwardRef((props, ref) => {
914
941
  updateMessage,
915
942
  updateReplies,
916
943
  updateLatestInput,
917
- updateUser: updateSessionWithUser,
944
+ updateUser: mergeAndUpdateUser,
918
945
  updateWebchatDevSettings: updateWebchatDevSettings,
919
946
  }}
920
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.message.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}
@@ -306,10 +326,15 @@ export class WebchatApp {
306
326
  onUserInput={(...args) => this.onUserInput(...args)}
307
327
  onStateChange={webchatState => this.onStateChange(webchatState)}
308
328
  server={server}
329
+ doAuth={(...args) => this.doAuth(...args)}
309
330
  />
310
331
  )
311
332
  }
312
333
 
334
+ doAuth() {
335
+ // TODO: we should inject the service in question to WebchatApp and do call this one
336
+ }
337
+
313
338
  async isWebchatVisible({ appId }) {
314
339
  try {
315
340
  const { status } = await HubtypeService.getWebchatVisibility({
@@ -337,11 +362,10 @@ export class WebchatApp {
337
362
 
338
363
  async render(dest, optionsAtRuntime = {}) {
339
364
  onDOMLoaded(async () => {
340
- this.createRootElement(dest)
341
365
  const isVisible = await this.resolveWebchatVisibility(optionsAtRuntime)
342
366
  if (isVisible)
343
367
  render(
344
- this.getComponent(optionsAtRuntime),
368
+ this.getComponent(dest, optionsAtRuntime),
345
369
  this.getReactMountNode(dest)
346
370
  )
347
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
@@ -19,3 +19,4 @@ export const CLEAR_MESSAGES = 'clearMessages'
19
19
  export const UPDATE_LAST_MESSAGE_DATE = 'updateLastMessageDate'
20
20
  export const SET_CURRENT_ATTACHMENT = 'setCurrentAttachment'
21
21
  export const SET_ONLINE = 'setOnline'
22
+ export const UPDATE_JWT = 'updateJwt'
@@ -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) {