@botonic/react 0.31.0-alpha.1 → 0.31.0-alpha.3

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 (172) hide show
  1. package/lib/cjs/components/element.js +6 -6
  2. package/lib/cjs/components/element.js.map +1 -1
  3. package/lib/cjs/components/index-types.d.ts +2 -0
  4. package/lib/cjs/components/multichannel/multichannel-button.js +2 -2
  5. package/lib/cjs/components/multichannel/multichannel-button.js.map +1 -1
  6. package/lib/cjs/components/multichannel/multichannel-carousel.js +4 -4
  7. package/lib/cjs/components/multichannel/multichannel-carousel.js.map +1 -1
  8. package/lib/cjs/components/multichannel/multichannel-text.js +12 -11
  9. package/lib/cjs/components/multichannel/multichannel-text.js.map +1 -1
  10. package/lib/cjs/components/multichannel/multichannel-utils.d.ts +9 -15
  11. package/lib/cjs/components/multichannel/multichannel-utils.js +48 -27
  12. package/lib/cjs/components/multichannel/multichannel-utils.js.map +1 -1
  13. package/lib/cjs/components/multichannel/multichannel.js +7 -7
  14. package/lib/cjs/components/multichannel/multichannel.js.map +1 -1
  15. package/lib/cjs/components/multichannel/whatsapp/constants.d.ts +11 -0
  16. package/lib/cjs/components/multichannel/whatsapp/constants.js +13 -0
  17. package/lib/cjs/components/multichannel/whatsapp/constants.js.map +1 -0
  18. package/lib/cjs/components/whatsapp-button-list.js +2 -2
  19. package/lib/cjs/components/whatsapp-button-list.js.map +1 -1
  20. package/lib/cjs/components/whatsapp-cta-url-button.js +5 -5
  21. package/lib/cjs/components/whatsapp-cta-url-button.js.map +1 -1
  22. package/lib/cjs/constants.d.ts +0 -6
  23. package/lib/cjs/constants.js +1 -7
  24. package/lib/cjs/constants.js.map +1 -1
  25. package/lib/cjs/contexts.js +6 -0
  26. package/lib/cjs/contexts.js.map +1 -1
  27. package/lib/cjs/dev-app.d.ts +7 -0
  28. package/lib/cjs/dev-app.js +5 -2
  29. package/lib/cjs/dev-app.js.map +1 -1
  30. package/lib/cjs/index-types.d.ts +4 -7
  31. package/lib/cjs/index-types.js.map +1 -1
  32. package/lib/cjs/index.d.ts +1 -1
  33. package/lib/cjs/index.js.map +1 -1
  34. package/lib/cjs/webchat/actions.d.ts +2 -1
  35. package/lib/cjs/webchat/actions.js +1 -0
  36. package/lib/cjs/webchat/actions.js.map +1 -1
  37. package/lib/cjs/webchat/cover-component/index.d.ts +6 -0
  38. package/lib/cjs/webchat/cover-component/index.js +21 -0
  39. package/lib/cjs/webchat/cover-component/index.js.map +1 -0
  40. package/lib/cjs/webchat/hooks/use-scroll-to-bottom.d.ts +6 -4
  41. package/lib/cjs/webchat/hooks/use-scroll-to-bottom.js.map +1 -1
  42. package/lib/cjs/webchat/hooks/use-webchat.d.ts +1 -0
  43. package/lib/cjs/webchat/hooks/use-webchat.js +8 -0
  44. package/lib/cjs/webchat/hooks/use-webchat.js.map +1 -1
  45. package/lib/cjs/webchat/index-types.d.ts +1 -0
  46. package/lib/cjs/webchat/input-panel/textarea.js +8 -2
  47. package/lib/cjs/webchat/input-panel/textarea.js.map +1 -1
  48. package/lib/cjs/webchat/message-list/index.js +63 -35
  49. package/lib/cjs/webchat/message-list/index.js.map +1 -1
  50. package/lib/cjs/webchat/message-list/styles.js +3 -3
  51. package/lib/cjs/webchat/typing-indicator/index.d.ts +3 -1
  52. package/lib/cjs/webchat/typing-indicator/index.js +4 -3
  53. package/lib/cjs/webchat/typing-indicator/index.js.map +1 -1
  54. package/lib/cjs/webchat/typing-indicator/styles.d.ts +3 -2
  55. package/lib/cjs/webchat/typing-indicator/styles.js +6 -3
  56. package/lib/cjs/webchat/typing-indicator/styles.js.map +1 -1
  57. package/lib/cjs/webchat/webchat-reducer.js +2 -0
  58. package/lib/cjs/webchat/webchat-reducer.js.map +1 -1
  59. package/lib/cjs/webchat/webchat-typed/styles.d.ts +7 -0
  60. package/lib/cjs/webchat/webchat-typed/styles.js +54 -0
  61. package/lib/cjs/webchat/webchat-typed/styles.js.map +1 -0
  62. package/lib/cjs/webchat/webchat-typed/webchat-typed.d.ts +0 -0
  63. package/lib/cjs/webchat/webchat-typed/webchat-typed.js +663 -0
  64. package/lib/cjs/webchat/webchat-typed/webchat-typed.js.map +1 -0
  65. package/lib/cjs/webchat/webchat.js +16 -18
  66. package/lib/cjs/webchat/webchat.js.map +1 -1
  67. package/lib/cjs/webchat-app.d.ts +119 -72
  68. package/lib/cjs/webchat-app.js +98 -53
  69. package/lib/cjs/webchat-app.js.map +1 -1
  70. package/lib/cjs/webview-app.js +5 -2
  71. package/lib/cjs/webview-app.js.map +1 -1
  72. package/lib/esm/components/element.js +6 -6
  73. package/lib/esm/components/element.js.map +1 -1
  74. package/lib/esm/components/index-types.d.ts +2 -0
  75. package/lib/esm/components/multichannel/multichannel-button.js +1 -1
  76. package/lib/esm/components/multichannel/multichannel-carousel.js +5 -5
  77. package/lib/esm/components/multichannel/multichannel-carousel.js.map +1 -1
  78. package/lib/esm/components/multichannel/multichannel-text.js +2 -1
  79. package/lib/esm/components/multichannel/multichannel-text.js.map +1 -1
  80. package/lib/esm/components/multichannel/multichannel-utils.d.ts +9 -15
  81. package/lib/esm/components/multichannel/multichannel-utils.js +40 -24
  82. package/lib/esm/components/multichannel/multichannel-utils.js.map +1 -1
  83. package/lib/esm/components/multichannel/multichannel.js +7 -7
  84. package/lib/esm/components/multichannel/multichannel.js.map +1 -1
  85. package/lib/esm/components/multichannel/whatsapp/constants.d.ts +11 -0
  86. package/lib/esm/components/multichannel/whatsapp/constants.js +10 -0
  87. package/lib/esm/components/multichannel/whatsapp/constants.js.map +1 -0
  88. package/lib/esm/components/whatsapp-button-list.js +1 -1
  89. package/lib/esm/components/whatsapp-cta-url-button.js +1 -1
  90. package/lib/esm/constants.d.ts +0 -6
  91. package/lib/esm/constants.js +0 -6
  92. package/lib/esm/constants.js.map +1 -1
  93. package/lib/esm/contexts.js +6 -0
  94. package/lib/esm/contexts.js.map +1 -1
  95. package/lib/esm/dev-app.d.ts +7 -0
  96. package/lib/esm/dev-app.js +5 -2
  97. package/lib/esm/dev-app.js.map +1 -1
  98. package/lib/esm/index-types.d.ts +4 -7
  99. package/lib/esm/index-types.js.map +1 -1
  100. package/lib/esm/index.d.ts +1 -1
  101. package/lib/esm/index.js.map +1 -1
  102. package/lib/esm/webchat/actions.d.ts +2 -1
  103. package/lib/esm/webchat/actions.js +1 -0
  104. package/lib/esm/webchat/actions.js.map +1 -1
  105. package/lib/esm/webchat/cover-component/index.d.ts +6 -0
  106. package/lib/esm/webchat/cover-component/index.js +17 -0
  107. package/lib/esm/webchat/cover-component/index.js.map +1 -0
  108. package/lib/esm/webchat/hooks/use-scroll-to-bottom.d.ts +6 -4
  109. package/lib/esm/webchat/hooks/use-scroll-to-bottom.js.map +1 -1
  110. package/lib/esm/webchat/hooks/use-webchat.d.ts +1 -0
  111. package/lib/esm/webchat/hooks/use-webchat.js +8 -0
  112. package/lib/esm/webchat/hooks/use-webchat.js.map +1 -1
  113. package/lib/esm/webchat/index-types.d.ts +1 -0
  114. package/lib/esm/webchat/input-panel/textarea.js +8 -2
  115. package/lib/esm/webchat/input-panel/textarea.js.map +1 -1
  116. package/lib/esm/webchat/message-list/index.js +62 -35
  117. package/lib/esm/webchat/message-list/index.js.map +1 -1
  118. package/lib/esm/webchat/message-list/styles.js +3 -3
  119. package/lib/esm/webchat/typing-indicator/index.d.ts +3 -1
  120. package/lib/esm/webchat/typing-indicator/index.js +5 -2
  121. package/lib/esm/webchat/typing-indicator/index.js.map +1 -1
  122. package/lib/esm/webchat/typing-indicator/styles.d.ts +3 -2
  123. package/lib/esm/webchat/typing-indicator/styles.js +5 -2
  124. package/lib/esm/webchat/typing-indicator/styles.js.map +1 -1
  125. package/lib/esm/webchat/webchat-reducer.js +2 -0
  126. package/lib/esm/webchat/webchat-reducer.js.map +1 -1
  127. package/lib/esm/webchat/webchat-typed/styles.d.ts +7 -0
  128. package/lib/esm/webchat/webchat-typed/styles.js +50 -0
  129. package/lib/esm/webchat/webchat-typed/styles.js.map +1 -0
  130. package/lib/esm/webchat/webchat-typed/webchat-typed.d.ts +0 -0
  131. package/lib/esm/webchat/webchat-typed/webchat-typed.js +663 -0
  132. package/lib/esm/webchat/webchat-typed/webchat-typed.js.map +1 -0
  133. package/lib/esm/webchat/webchat.js +16 -18
  134. package/lib/esm/webchat/webchat.js.map +1 -1
  135. package/lib/esm/webchat-app.d.ts +119 -72
  136. package/lib/esm/webchat-app.js +99 -54
  137. package/lib/esm/webchat-app.js.map +1 -1
  138. package/lib/esm/webview-app.js +5 -2
  139. package/lib/esm/webview-app.js.map +1 -1
  140. package/package.json +3 -2
  141. package/src/components/element.jsx +4 -11
  142. package/src/components/index-types.ts +4 -0
  143. package/src/components/multichannel/multichannel-button.jsx +1 -1
  144. package/src/components/multichannel/multichannel-carousel.jsx +7 -5
  145. package/src/components/multichannel/multichannel-text.jsx +4 -2
  146. package/src/components/multichannel/multichannel-utils.js +45 -27
  147. package/src/components/multichannel/multichannel.jsx +12 -7
  148. package/src/components/multichannel/whatsapp/constants.ts +10 -0
  149. package/src/components/whatsapp-button-list.tsx +1 -1
  150. package/src/components/whatsapp-cta-url-button.tsx +1 -1
  151. package/src/constants.js +0 -7
  152. package/src/contexts.tsx +6 -0
  153. package/src/dev-app.jsx +5 -5
  154. package/src/index-types.ts +4 -7
  155. package/src/index.ts +1 -1
  156. package/src/webchat/actions.ts +1 -0
  157. package/src/webchat/cover-component/index.tsx +31 -0
  158. package/src/webchat/hooks/use-scroll-to-bottom.ts +8 -2
  159. package/src/webchat/hooks/use-webchat.ts +9 -0
  160. package/src/webchat/index-types.ts +1 -0
  161. package/src/webchat/input-panel/textarea.tsx +12 -1
  162. package/src/webchat/message-list/index.tsx +79 -48
  163. package/src/webchat/message-list/styles.ts +3 -3
  164. package/src/webchat/typing-indicator/index.tsx +20 -12
  165. package/src/webchat/typing-indicator/styles.ts +7 -3
  166. package/src/webchat/webchat-reducer.ts +2 -0
  167. package/src/webchat/webchat-typed/styles.ts +54 -0
  168. package/src/webchat/webchat-typed/webchat-typed.tsx +728 -0
  169. package/src/webchat/webchat.jsx +48 -48
  170. package/src/webchat-app.tsx +546 -0
  171. package/src/webview-app.tsx +6 -4
  172. package/src/webchat-app.jsx +0 -389
@@ -44,6 +44,7 @@ import {
44
44
  import { ChatArea } from './chat-area'
45
45
  import { OpenedPersistentMenu } from './components/opened-persistent-menu'
46
46
  import { BotonicContainerId } from './constants'
47
+ import { CoverComponent } from './cover-component'
47
48
  import { WebchatHeader } from './header'
48
49
  import {
49
50
  useComponentWillMount,
@@ -120,6 +121,7 @@ export const Webchat = forwardRef((props, ref) => {
120
121
  resetUnreadMessages,
121
122
  setCurrentAttachment,
122
123
  setError,
124
+ setIsInputFocused,
123
125
  setLastMessageVisible,
124
126
  setOnline,
125
127
  toggleCoverComponent,
@@ -203,7 +205,7 @@ export const Webchat = forwardRef((props, ref) => {
203
205
  const sendUserInput = async input => {
204
206
  if (props.onUserInput) {
205
207
  resetUnreadMessages()
206
- scrollToBottom({ host })
208
+ scrollToBottom({ host }) // TODO: Remove param {host}
207
209
  props.onUserInput({
208
210
  user: webchatState.session.user,
209
211
  input: input,
@@ -367,7 +369,7 @@ export const Webchat = forwardRef((props, ref) => {
367
369
  if (getBlockInputs(rule, input.data)) {
368
370
  addMessageComponent(
369
371
  <Text
370
- id={input.id}
372
+ id={input.id} //TODO: Remove id from Text component
371
373
  sentBy={SENDERS.user}
372
374
  blob={false}
373
375
  style={{
@@ -407,41 +409,23 @@ export const Webchat = forwardRef((props, ref) => {
407
409
  (props.coverComponent.component || props.coverComponent)
408
410
  )
409
411
  }
410
- const CoverComponent = getCoverComponent()
411
-
412
- const closeCoverComponent = () => {
413
- toggleCoverComponent(false)
414
- }
412
+ const coverComponent = getCoverComponent()
413
+ const coverComponentProps = props.coverComponent?.props
415
414
 
416
415
  useEffect(() => {
417
- if (!CoverComponent) return
416
+ if (!coverComponent) return
418
417
  if (
419
418
  !botonicState ||
420
- (botonicState.messages && botonicState.messages.length == 0)
419
+ (botonicState.messages && botonicState.messages.length === 0)
421
420
  )
422
421
  toggleCoverComponent(true)
423
422
  }, [])
424
423
 
425
- const coverComponent = () => {
426
- const coverComponentProps = getThemeProperty(
427
- WEBCHAT.CUSTOM_PROPERTIES.coverComponentProps,
428
- props.coverComponent && props.coverComponent.props
429
- )
430
-
431
- if (CoverComponent && webchatState.isCoverComponentOpen)
432
- return (
433
- <CoverComponent
434
- closeComponent={closeCoverComponent}
435
- {...coverComponentProps}
436
- />
437
- )
438
- return null
439
- }
440
-
441
424
  const messageComponentFromInput = input => {
442
425
  let messageComponent = null
443
426
  if (isText(input)) {
444
427
  messageComponent = (
428
+ //TODO: Remove id and payload from Text component
445
429
  <Text id={input.id} payload={input.payload} sentBy={SENDERS.user}>
446
430
  {input.data}
447
431
  </Text>
@@ -700,6 +684,7 @@ export const Webchat = forwardRef((props, ref) => {
700
684
  openWebview,
701
685
  resolveCase,
702
686
  resetUnreadMessages,
687
+ setIsInputFocused,
703
688
  setLastMessageVisible,
704
689
  sendAttachment,
705
690
  sendInput,
@@ -708,6 +693,7 @@ export const Webchat = forwardRef((props, ref) => {
708
693
  toggleWebchat,
709
694
  toggleEmojiPicker,
710
695
  togglePersistentMenu,
696
+ toggleCoverComponent,
711
697
  updateLatestInput,
712
698
  updateMessage,
713
699
  updateReplies,
@@ -744,36 +730,50 @@ export const Webchat = forwardRef((props, ref) => {
744
730
  toggleWebchat(false)
745
731
  }}
746
732
  />
747
- {webchatState.error.message && (
748
- <ErrorMessageContainer>
749
- <ErrorMessage>{webchatState.error.message}</ErrorMessage>
750
- </ErrorMessageContainer>
751
- )}
752
- <ChatArea />
753
733
 
754
- {webchatState.isPersistentMenuOpen && (
755
- <DarkenBackground component={persistentMenu()} />
756
- )}
757
- {!webchatState.handoff && userInputEnabled && (
758
- <InputPanel
759
- persistentMenu={props.persistentMenu}
760
- enableEmojiPicker={props.enableEmojiPicker}
761
- enableAttachments={props.enableAttachments}
762
- handleAttachment={handleAttachment}
763
- textareaRef={textareaRef}
764
- host={host}
765
- onUserInput={props.onUserInput}
734
+ {webchatState.isCoverComponentOpen ? (
735
+ <CoverComponent
736
+ component={coverComponent}
737
+ componentProps={coverComponentProps}
766
738
  />
739
+ ) : (
740
+ <>
741
+ {webchatState.error.message && (
742
+ <ErrorMessageContainer>
743
+ <ErrorMessage>{webchatState.error.message}</ErrorMessage>
744
+ </ErrorMessageContainer>
745
+ )}
746
+
747
+ <ChatArea />
748
+
749
+ {webchatState.isPersistentMenuOpen && (
750
+ <DarkenBackground component={persistentMenu()} />
751
+ )}
752
+
753
+ {!webchatState.handoff && userInputEnabled && (
754
+ <InputPanel
755
+ persistentMenu={props.persistentMenu}
756
+ enableEmojiPicker={props.enableEmojiPicker}
757
+ enableAttachments={props.enableAttachments}
758
+ handleAttachment={handleAttachment}
759
+ textareaRef={textareaRef}
760
+ host={host}
761
+ onUserInput={props.onUserInput}
762
+ />
763
+ )}
764
+
765
+ {webchatState.webview && webchatWebview()}
766
+
767
+ {webchatState.isCustomComponentRendered &&
768
+ customComponent &&
769
+ _renderCustomComponent()}
770
+ </>
767
771
  )}
768
- {webchatState.webview && webchatWebview()}
769
- {webchatState.isCoverComponentOpen && coverComponent()}
770
- {webchatState.isCustomComponentRendered &&
771
- customComponent &&
772
- _renderCustomComponent()}
773
772
  </StyledWebchat>
774
773
  )}
775
774
  </WebchatContext.Provider>
776
775
  )
776
+
777
777
  return props.shadowDOM ? (
778
778
  <StyleSheetManager target={host}>{WebchatComponent}</StyleSheetManager>
779
779
  ) : (
@@ -0,0 +1,546 @@
1
+ import { HubtypeService, INPUT, Input, ServerConfig } from '@botonic/core'
2
+ import merge from 'lodash.merge'
3
+ import React, { createRef } from 'react'
4
+ import { createRoot, Root } from 'react-dom/client'
5
+
6
+ import {
7
+ BlockInputOption,
8
+ CoverComponentOptions,
9
+ PersistentMenuTheme,
10
+ ThemeProps,
11
+ WebchatSettingsProps,
12
+ } from './components/index-types'
13
+ import { WEBCHAT } from './constants'
14
+ import { CloseWebviewOptions } from './contexts'
15
+ import {
16
+ ActionRequest,
17
+ EventArgs,
18
+ SENDERS,
19
+ Typing,
20
+ WebchatMessage,
21
+ } from './index-types'
22
+ import { msgToBotonic } from './msg-to-botonic'
23
+ import { isShadowDOMSupported, onDOMLoaded } from './util/dom'
24
+ import { ErrorMessage } from './webchat/index-types'
25
+ import { Webchat } from './webchat/webchat'
26
+
27
+ export interface WebchatAppProps {
28
+ theme?: ThemeProps
29
+ persistentMenu?: PersistentMenuTheme
30
+ coverComponent?: CoverComponentOptions
31
+ blockInputs?: BlockInputOption[]
32
+ enableEmojiPicker?: boolean
33
+ enableAttachments?: boolean
34
+ enableUserInput?: boolean
35
+ enableAnimations?: boolean
36
+ hostId?: string
37
+ shadowDOM?: boolean | (() => boolean)
38
+ defaultDelay?: number
39
+ defaultTyping?: number
40
+ storage?: Storage | null
41
+ storageKey?: string
42
+ onInit?: (app: WebchatApp, args: any) => void
43
+ onOpen?: (app: WebchatApp, args: any) => void
44
+ onClose?: (app: WebchatApp, args: any) => void
45
+ onMessage?: (app: WebchatApp, message: WebchatMessage) => void
46
+ onTrackEvent?: (
47
+ request: ActionRequest,
48
+ eventName: string,
49
+ args?: EventArgs
50
+ ) => Promise<void>
51
+ onConnectionChange?: (app: WebchatApp, isOnline: boolean) => void
52
+ appId?: string
53
+ visibility?: boolean | (() => boolean) | 'dynamic'
54
+ server?: ServerConfig
55
+ }
56
+
57
+ interface WebchatRef {
58
+ addBotResponse: ({
59
+ response,
60
+ session,
61
+ lastRoutePath,
62
+ }: AddBotResponseArgs) => void
63
+ setTyping: (typing: boolean) => void
64
+ addUserMessage: (message: any) => Promise<void>
65
+ updateUser: (userToUpdate: any) => void
66
+ openWebchat: () => void
67
+ closeWebchat: () => void
68
+ toggleWebchat: () => void
69
+ openCoverComponent: () => void
70
+ closeCoverComponent: () => void
71
+ renderCustomComponent: (customComponent: any) => void
72
+ unmountCustomComponent: () => void
73
+ toggleCoverComponent: () => void
74
+ openWebviewApi: (component: any) => void
75
+ setError: (error: ErrorMessage) => void
76
+ setOnline: (online: boolean) => void
77
+ getMessages: () => { id: string; ack: number; unsentInput: Input }[] // TODO: define MessagesJSON
78
+ isOnline: () => boolean
79
+ clearMessages: () => void
80
+ getLastMessageUpdate: () => string
81
+ updateMessageInfo: (msgId: string, messageInfo: any) => void
82
+ updateWebchatSettings: (settings: WebchatSettingsProps) => void
83
+ closeWebview: (options?: CloseWebviewOptions) => Promise<void>
84
+ }
85
+
86
+ interface AddBotResponseArgs {
87
+ response: any
88
+ session?: any
89
+ lastRoutePath?: any
90
+ }
91
+
92
+ export class WebchatApp {
93
+ public theme?: ThemeProps
94
+ public persistentMenu?: PersistentMenuTheme
95
+ public coverComponent?: CoverComponentOptions
96
+ public blockInputs?: BlockInputOption[]
97
+ public enableEmojiPicker?: boolean
98
+ public enableAttachments?: boolean
99
+ public enableUserInput?: boolean
100
+ public enableAnimations?: boolean
101
+ public hostId?: string
102
+ public shadowDOM?: boolean | (() => boolean)
103
+ public defaultDelay?: number
104
+ public defaultTyping?: number
105
+ public storage?: Storage | null
106
+ public storageKey: string
107
+ public onInit?: (app: WebchatApp, args: any) => void
108
+ public onOpen?: (app: WebchatApp, args: any) => void
109
+ public onClose?: (app: WebchatApp, args: any) => void
110
+ public onMessage?: (app: WebchatApp, message: WebchatMessage) => void
111
+ public onTrackEvent?: (
112
+ request: ActionRequest,
113
+ eventName: string,
114
+ args?: EventArgs
115
+ ) => Promise<void>
116
+ public onConnectionChange?: (app: WebchatApp, isOnline: boolean) => void
117
+ public appId?: string
118
+ public visibility?: boolean | (() => boolean) | 'dynamic'
119
+ public server?: ServerConfig
120
+ public webchatRef: React.RefObject<WebchatRef | null>
121
+
122
+ private reactRoot: Root | null = null
123
+ private host: (HTMLElement | null) | ShadowRoot = null
124
+ private hubtypeService: HubtypeService
125
+
126
+ constructor({
127
+ theme = {},
128
+ persistentMenu,
129
+ coverComponent,
130
+ blockInputs,
131
+ enableEmojiPicker,
132
+ enableAttachments,
133
+ enableUserInput,
134
+ enableAnimations,
135
+ hostId = 'root',
136
+ shadowDOM = false,
137
+ defaultDelay,
138
+ defaultTyping,
139
+ storage,
140
+ storageKey,
141
+ onInit,
142
+ onOpen,
143
+ onClose,
144
+ onMessage,
145
+ onTrackEvent,
146
+ onConnectionChange,
147
+ appId,
148
+ visibility,
149
+ server,
150
+ }: WebchatAppProps) {
151
+ this.theme = theme
152
+ this.persistentMenu = persistentMenu
153
+ this.coverComponent = coverComponent
154
+ this.blockInputs = blockInputs
155
+ this.enableEmojiPicker = enableEmojiPicker
156
+ this.enableAttachments = enableAttachments
157
+ this.enableUserInput = enableUserInput
158
+ this.enableAnimations = enableAnimations
159
+
160
+ this.shadowDOM = Boolean(
161
+ typeof shadowDOM === 'function' ? shadowDOM() : shadowDOM
162
+ )
163
+ if (this.shadowDOM && !isShadowDOMSupported()) {
164
+ console.warn('[botonic] ShadowDOM not supported on this browser')
165
+ this.shadowDOM = false
166
+ } // Review this
167
+
168
+ this.hostId = hostId || WEBCHAT.DEFAULTS.HOST_ID
169
+ this.defaultDelay = defaultDelay
170
+ this.defaultTyping = defaultTyping
171
+ this.storage = storage === undefined ? localStorage : storage
172
+ this.storageKey = storageKey || WEBCHAT.DEFAULTS.STORAGE_KEY
173
+ this.onInit = onInit
174
+ this.onOpen = onOpen
175
+ this.onClose = onClose
176
+ this.onMessage = onMessage
177
+ this.onTrackEvent = onTrackEvent
178
+ this.onConnectionChange = onConnectionChange
179
+ this.visibility = visibility
180
+ this.server = server
181
+ this.webchatRef = createRef<WebchatRef>()
182
+ this.appId = appId
183
+
184
+ this.host = null
185
+ this.reactRoot = null
186
+ }
187
+
188
+ createRootElement(host: HTMLElement | null) {
189
+ // Create root element <div id='root'> if not exists
190
+ // Create shadowDOM to root element if needed
191
+ if (host) {
192
+ if (host.id && this.hostId) {
193
+ if (host.id != this.hostId) {
194
+ console.warn(
195
+ `[botonic] Host ID "${host.id}" don't match 'hostId' option: ${this.hostId}. Using value: ${host.id}.`
196
+ )
197
+ this.hostId = host.id
198
+ }
199
+ } else if (host.id) {
200
+ this.hostId = host.id
201
+ } else if (this.hostId) {
202
+ host.id = this.hostId
203
+ }
204
+ } else {
205
+ host = this.hostId ? document.getElementById(this.hostId) : null
206
+ }
207
+
208
+ if (!host) {
209
+ host = document.createElement('div')
210
+ host.id = this.hostId!
211
+ if (document.body.firstChild) {
212
+ document.body.insertBefore(host, document.body.firstChild)
213
+ } else {
214
+ document.body.appendChild(host)
215
+ }
216
+ }
217
+ this.host = this.shadowDOM ? host.attachShadow({ mode: 'open' }) : host
218
+ }
219
+
220
+ getReactMountNode(
221
+ node?: (HTMLElement | null) | ShadowRoot
222
+ ): Element | DocumentFragment {
223
+ if (!node) {
224
+ node = this.host
225
+ }
226
+
227
+ if (node === null) {
228
+ throw new Error('Host element not found')
229
+ }
230
+
231
+ // TODO: Review logic of ShadowRoot
232
+ if ('shadowRoot' in node && node.shadowRoot !== null) {
233
+ return node.shadowRoot
234
+ }
235
+
236
+ return node
237
+ }
238
+
239
+ onInitWebchat(...args: [any]) {
240
+ this.onInit && this.onInit(this, ...args)
241
+ }
242
+
243
+ onOpenWebchat(...args: [any]) {
244
+ this.onOpen && this.onOpen(this, ...args)
245
+ }
246
+
247
+ onCloseWebchat(...args: [any]) {
248
+ this.onClose && this.onClose(this, ...args)
249
+ }
250
+
251
+ async onUserInput({ user, input }) {
252
+ this.onMessage &&
253
+ this.onMessage(this, {
254
+ ...input,
255
+ sentBy: SENDERS.user,
256
+ isUnread: false,
257
+ })
258
+ return this.hubtypeService.postMessage(user, input)
259
+ }
260
+
261
+ async onConnectionRegained() {
262
+ return this.hubtypeService.onConnectionRegained()
263
+ }
264
+
265
+ onStateChange({ session: { user }, messagesJSON }) {
266
+ const lastMessage = messagesJSON[messagesJSON.length - 1]
267
+ const lastMessageId = lastMessage && lastMessage.id
268
+ const lastMessageUpdateDate = this.getLastMessageUpdate()
269
+ if (this.hubtypeService) {
270
+ this.hubtypeService.lastMessageId = lastMessageId
271
+ this.hubtypeService.lastMessageUpdateDate = lastMessageUpdateDate!
272
+ } else if (!this.hubtypeService && user) {
273
+ this.hubtypeService = new HubtypeService({
274
+ appId: this.appId!,
275
+ user,
276
+ lastMessageId,
277
+ lastMessageUpdateDate: lastMessageUpdateDate!,
278
+ onEvent: (event: any) => this.onServiceEvent(event),
279
+ unsentInputs: () =>
280
+ this.webchatRef.current
281
+ ?.getMessages()
282
+ .filter(msg => msg.ack === 0 && msg.unsentInput) || [],
283
+ server: this.server!,
284
+ })
285
+ }
286
+ }
287
+
288
+ onServiceEvent(event) {
289
+ if (event.action === 'connectionChange') {
290
+ this.onConnectionChange && this.onConnectionChange(this, event.online)
291
+ this.webchatRef.current?.setOnline(event.online)
292
+ } else if (event.action === 'update_message_info') {
293
+ this.updateMessageInfo(event.message.id, event.message)
294
+ } else if (event.message?.type === 'update_webchat_settings') {
295
+ this.updateWebchatSettings(event.message.data)
296
+ } else if (event.message?.type === 'sender_action') {
297
+ this.setTyping(event.message.data === Typing.On)
298
+ } else {
299
+ this.onMessage &&
300
+ this.onMessage(this, {
301
+ sentBy: SENDERS.bot,
302
+ ...event.message,
303
+ } as WebchatMessage)
304
+ this.addBotMessage(event.message)
305
+ }
306
+ }
307
+
308
+ updateUser(user: any) {
309
+ this.webchatRef.current?.updateUser(user)
310
+ }
311
+
312
+ addBotMessage(message: any) {
313
+ message.ack = 0
314
+ message.isUnread = true
315
+ message.sentBy = message.sent_by?.split('message_sent_by_')[1]
316
+ delete message.sent_by
317
+ const response = msgToBotonic(
318
+ message,
319
+ // TODO: Review if is neded allow declar customTypes inseide and ouside theme
320
+ // @ts-ignore
321
+ this.theme?.message?.customTypes || this.theme?.customMessageTypes || []
322
+ )
323
+
324
+ this.webchatRef.current?.addBotResponse({
325
+ response,
326
+ })
327
+ }
328
+
329
+ addBotText(text: string) {
330
+ this.addBotMessage({ type: INPUT.TEXT, data: text })
331
+ }
332
+
333
+ addUserMessage(message: any) {
334
+ this.webchatRef.current?.addUserMessage(message)
335
+ }
336
+
337
+ addUserText(text: string) {
338
+ this.addUserMessage({ type: INPUT.TEXT, data: text })
339
+ }
340
+
341
+ addUserPayload(payload: string) {
342
+ this.addUserMessage({ type: INPUT.POSTBACK, payload })
343
+ }
344
+
345
+ setTyping(typing: boolean) {
346
+ this.webchatRef.current?.setTyping(typing)
347
+ }
348
+
349
+ open() {
350
+ this.webchatRef.current?.openWebchat()
351
+ }
352
+
353
+ close() {
354
+ this.webchatRef.current?.closeWebchat()
355
+ }
356
+
357
+ async closeWebview(options?: CloseWebviewOptions) {
358
+ await this.webchatRef.current?.closeWebview(options)
359
+ }
360
+
361
+ // TODO: Remove this function because we have open and close functions
362
+ toggle() {
363
+ this.webchatRef.current?.toggleWebchat()
364
+ }
365
+
366
+ openCoverComponent() {
367
+ this.webchatRef.current?.openCoverComponent()
368
+ }
369
+
370
+ closeCoverComponent() {
371
+ this.webchatRef.current?.closeCoverComponent()
372
+ }
373
+
374
+ renderCustomComponent(_customComponent: any) {
375
+ this.webchatRef.current?.renderCustomComponent(_customComponent)
376
+ }
377
+
378
+ unmountCustomComponent() {
379
+ this.webchatRef.current?.unmountCustomComponent()
380
+ }
381
+
382
+ // TODO: Remove this function because we have openCoverComponent and closeCoverComponent functions
383
+ toggleCoverComponent() {
384
+ this.webchatRef.current?.toggleCoverComponent()
385
+ }
386
+
387
+ getMessages() {
388
+ return this.webchatRef.current?.getMessages()
389
+ }
390
+
391
+ clearMessages() {
392
+ this.webchatRef.current?.clearMessages()
393
+ }
394
+
395
+ async getVisibility() {
396
+ return this.resolveWebchatVisibility({
397
+ appId: this.appId!,
398
+ visibility: this.visibility,
399
+ })
400
+ }
401
+
402
+ getLastMessageUpdate() {
403
+ return this.webchatRef.current?.getLastMessageUpdate()
404
+ }
405
+
406
+ updateMessageInfo(msgId: string, messageInfo: any) {
407
+ return this.webchatRef.current?.updateMessageInfo(msgId, messageInfo)
408
+ }
409
+
410
+ updateWebchatSettings(settings: WebchatSettingsProps) {
411
+ return this.webchatRef.current?.updateWebchatSettings(settings)
412
+ }
413
+
414
+ // eslint-disable-next-line complexity
415
+ getComponent(host, optionsAtRuntime: WebchatAppProps = {}) {
416
+ let {
417
+ theme = {},
418
+ persistentMenu,
419
+ coverComponent,
420
+ blockInputs,
421
+ enableAttachments,
422
+ enableUserInput,
423
+ enableAnimations,
424
+ enableEmojiPicker,
425
+ defaultDelay,
426
+ defaultTyping,
427
+ storage,
428
+ storageKey,
429
+ onInit,
430
+ onOpen,
431
+ onClose,
432
+ onMessage,
433
+ onConnectionChange,
434
+ onTrackEvent,
435
+ appId,
436
+ visibility,
437
+ server,
438
+ hostId,
439
+ ...webchatOptions
440
+ } = optionsAtRuntime
441
+ theme = merge(this.theme, theme)
442
+ persistentMenu = persistentMenu || this.persistentMenu
443
+ coverComponent = coverComponent || this.coverComponent
444
+ blockInputs = blockInputs || this.blockInputs
445
+ enableEmojiPicker = enableEmojiPicker || this.enableEmojiPicker
446
+ enableAttachments = enableAttachments || this.enableAttachments
447
+ enableUserInput = enableUserInput || this.enableUserInput
448
+ enableAnimations = enableAnimations || this.enableAnimations
449
+ defaultDelay = defaultDelay || this.defaultDelay
450
+ defaultTyping = defaultTyping || this.defaultTyping
451
+ server = server || this.server
452
+ this.storage = storage || this.storage
453
+ this.storageKey = storageKey || this.storageKey
454
+ this.onInit = onInit || this.onInit
455
+ this.onOpen = onOpen || this.onOpen
456
+ this.onClose = onClose || this.onClose
457
+ this.onMessage = onMessage || this.onMessage
458
+ this.onTrackEvent = onTrackEvent || this.onTrackEvent
459
+ this.onConnectionChange = onConnectionChange || this.onConnectionChange
460
+ this.visibility = visibility || this.visibility
461
+ this.appId = appId || this.appId
462
+ this.hostId = hostId || this.hostId
463
+ this.createRootElement(host)
464
+
465
+ return (
466
+ <Webchat
467
+ {...webchatOptions}
468
+ ref={this.webchatRef}
469
+ // @ts-ignore
470
+ host={this.host}
471
+ shadowDOM={this.shadowDOM}
472
+ theme={theme}
473
+ persistentMenu={persistentMenu}
474
+ coverComponent={coverComponent}
475
+ blockInputs={blockInputs}
476
+ enableEmojiPicker={enableEmojiPicker}
477
+ enableAttachments={enableAttachments}
478
+ enableUserInput={enableUserInput}
479
+ enableAnimations={enableAnimations}
480
+ storage={this.storage}
481
+ storageKey={this.storageKey}
482
+ defaultDelay={defaultDelay}
483
+ defaultTyping={defaultTyping}
484
+ onInit={(...args: [any]) => this.onInitWebchat(...args)}
485
+ onOpen={(...args: [any]) => this.onOpenWebchat(...args)}
486
+ onClose={(...args: [any]) => this.onCloseWebchat(...args)}
487
+ onUserInput={(...args: [any]) => this.onUserInput(...args)}
488
+ onStateChange={webchatState => this.onStateChange(webchatState)}
489
+ onTrackEvent={(
490
+ request: ActionRequest,
491
+ eventName: string,
492
+ args?: EventArgs
493
+ ) => this.onTrackEvent && this.onTrackEvent(request, eventName, args)}
494
+ server={server}
495
+ />
496
+ )
497
+ }
498
+
499
+ async isWebchatVisible({ appId }) {
500
+ try {
501
+ const { status } = await HubtypeService.getWebchatVisibility({
502
+ appId,
503
+ })
504
+ return status === 200
505
+ } catch (e) {
506
+ return false
507
+ }
508
+ }
509
+
510
+ isOnline() {
511
+ return this.webchatRef.current?.isOnline()
512
+ }
513
+
514
+ async resolveWebchatVisibility(optionsAtRuntime?: {
515
+ appId: string
516
+ visibility: boolean | (() => boolean) | 'dynamic' | undefined
517
+ }) {
518
+ if (!optionsAtRuntime) return true // If optionsAtRuntime is not provided, always render the webchat
519
+
520
+ let { appId, visibility } = optionsAtRuntime
521
+ visibility = visibility || this.visibility
522
+ if (visibility === undefined || visibility === true) return true
523
+ if (typeof visibility === 'function' && visibility()) return true
524
+ if (visibility === 'dynamic' && (await this.isWebchatVisible({ appId })))
525
+ return true
526
+ return false
527
+ }
528
+
529
+ destroy() {
530
+ if (this.hubtypeService) this.hubtypeService.destroyPusher()
531
+ this.reactRoot?.unmount()
532
+ if (this.storage) this.storage.removeItem(this.storageKey)
533
+ }
534
+
535
+ async render(dest: HTMLDivElement, optionsAtRuntime: any = undefined) {
536
+ onDOMLoaded(async () => {
537
+ const isVisible = await this.resolveWebchatVisibility(optionsAtRuntime)
538
+ if (isVisible) {
539
+ const webchatComponent = this.getComponent(dest, optionsAtRuntime)
540
+ const container = this.getReactMountNode(dest)
541
+ this.reactRoot = createRoot(container) // createRoot(container!) if you use TypeScript
542
+ this.reactRoot.render(webchatComponent)
543
+ }
544
+ })
545
+ }
546
+ }