@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
@@ -1,45 +1,63 @@
1
- /**
2
- *
3
- * Whatsapp does not support Markdown
4
- * (its markup syntax is different)
5
- */
6
- export const MULTICHANNEL_WHATSAPP_PROPS = { markdown: false }
7
-
8
- export const WHATSAPP_MAX_BUTTONS = 3
9
- export const WHATSAPP_LIST_MAX_BUTTONS = 10
10
- export const WHATSAPP_MAX_BUTTON_CHARS = 20
11
- export const WHATSAPP_MAX_HEADER_CHARS = 60
12
- export const WHATSAPP_MAX_BODY_CHARS = 1024
13
- export const WHATSAPP_MAX_FOOTER_CHARS = 60
14
- export const DEFAULT_WHATSAPP_MAX_BUTTON_SEPARATOR = 'More options:'
15
- export const MENU_BUTTON_WHATSAPP_BUTTON_LIST = 'Show options'
1
+ import { Button } from '../button'
2
+ import { Carousel } from '../carousel'
3
+ import { Pic } from '../pic'
4
+ import { Reply } from '../reply'
5
+ import { Subtitle } from '../subtitle'
6
+ import { Text } from '../text'
7
+ import { Title } from '../title'
8
+ import { MultichannelButton } from './multichannel-button'
9
+ import { MultichannelReply } from './multichannel-reply'
10
+
11
+ function isNodeKind(node, kind) {
12
+ return node?.type?.name === kind
13
+ }
16
14
 
17
15
  export function isMultichannelButton(node) {
18
- return isNodeKind(node, 'MultichannelButton')
16
+ return isNodeKind(node, MultichannelButton.name)
19
17
  }
20
18
 
21
19
  export function isMultichannelReply(node) {
22
- return isNodeKind(node, 'MultichannelReply')
20
+ return isNodeKind(node, MultichannelReply.name)
21
+ }
22
+
23
+ export function isNodeText(node) {
24
+ return isNodeKind(node, Text.name)
25
+ }
26
+
27
+ export function isNodeButton(node) {
28
+ return isNodeKind(node, Button.name)
29
+ }
30
+
31
+ export function isNodeCarousel(node) {
32
+ return isNodeKind(node, Carousel.name)
23
33
  }
24
34
 
25
- export function isButton(node) {
26
- return isNodeKind(node, 'Button')
35
+ export function isNodeReply(node) {
36
+ return isNodeKind(node, Reply.name)
27
37
  }
28
38
 
29
- export function isNodeKind(node, kind) {
30
- return node.type && node.type.name == kind
39
+ export function isNodePic(node) {
40
+ return isNodeKind(node, Pic.name)
31
41
  }
42
+
43
+ export function isNodeTitle(node) {
44
+ return isNodeKind(node, Title.name)
45
+ }
46
+
47
+ export function isNodeSubtitle(node) {
48
+ return isNodeKind(node, Subtitle.name)
49
+ }
50
+
32
51
  export function elementHasUrl(element) {
33
- return element.props && element.props.url
52
+ return element?.props?.url
34
53
  }
54
+
35
55
  export function elementHasPostback(element) {
36
- return (
37
- (element.props && element.props.payload) ||
38
- (element.props && element.props.path)
39
- )
56
+ return element?.props?.payload || element?.props?.path
40
57
  }
58
+
41
59
  export function elementHasWebview(element) {
42
- return element.props && element.props.webview
60
+ return element?.props?.webview
43
61
  }
44
62
 
45
63
  export const buttonTypes = {
@@ -1,7 +1,6 @@
1
1
  import { isFacebook, isWhatsapp } from '@botonic/core'
2
2
  import React, { useContext } from 'react'
3
3
 
4
- import { COMPONENT_TYPE } from '../../constants'
5
4
  import { RequestContext } from '../../contexts'
6
5
  import { deepMapWithIndex } from '../../util/react'
7
6
  import { Text } from '../text'
@@ -10,7 +9,13 @@ import { MultichannelCarousel } from './multichannel-carousel'
10
9
  import { MultichannelContext } from './multichannel-context'
11
10
  import { MultichannelReply } from './multichannel-reply'
12
11
  import { MultichannelText } from './multichannel-text'
13
- import { MULTICHANNEL_WHATSAPP_PROPS } from './multichannel-utils'
12
+ import {
13
+ isNodeButton,
14
+ isNodeCarousel,
15
+ isNodeReply,
16
+ isNodeText,
17
+ } from './multichannel-utils'
18
+ import { MULTICHANNEL_WHATSAPP_PROPS } from './whatsapp/constants'
14
19
 
15
20
  export const Multichannel = props => {
16
21
  const requestContext = useContext(RequestContext)
@@ -22,7 +27,7 @@ export const Multichannel = props => {
22
27
  }
23
28
  if (isFacebook(requestContext.session)) {
24
29
  const newChildren = deepMapWithIndex(props.children, child => {
25
- if (child && child.type && child.type.name === COMPONENT_TYPE.TEXT) {
30
+ if (isNodeText(child)) {
26
31
  return (
27
32
  <MultichannelText {...child.props} key={child.key}>
28
33
  {child.props.children}
@@ -35,21 +40,21 @@ export const Multichannel = props => {
35
40
  }
36
41
 
37
42
  let newChildren = deepMapWithIndex(props.children, (child, index) => {
38
- if (child && child.type && child.type.name === COMPONENT_TYPE.BUTTON) {
43
+ if (isNodeButton(child)) {
39
44
  return (
40
45
  <MultichannelButton {...child.props} key={child.key}>
41
46
  {child.props.children}
42
47
  </MultichannelButton>
43
48
  )
44
49
  }
45
- if (child && child.type && child.type.name === COMPONENT_TYPE.REPLY) {
50
+ if (isNodeReply(child)) {
46
51
  return (
47
52
  <MultichannelReply {...child.props} key={child.key}>
48
53
  {child.props.children}
49
54
  </MultichannelReply>
50
55
  )
51
56
  }
52
- if (child && child.type && child.type.name === COMPONENT_TYPE.TEXT) {
57
+ if (isNodeText(child)) {
53
58
  return (
54
59
  <MultichannelText
55
60
  {...child.props}
@@ -62,7 +67,7 @@ export const Multichannel = props => {
62
67
  </MultichannelText>
63
68
  )
64
69
  }
65
- if (child && child.type && child.type.name === COMPONENT_TYPE.CAROUSEL) {
70
+ if (isNodeCarousel(child)) {
66
71
  return (
67
72
  <MultichannelCarousel
68
73
  {...child.props}
@@ -0,0 +1,10 @@
1
+ export const MULTICHANNEL_WHATSAPP_PROPS = { markdown: false }
2
+
3
+ export const WHATSAPP_MAX_BUTTONS = 3
4
+ export const WHATSAPP_LIST_MAX_BUTTONS = 10
5
+ export const WHATSAPP_MAX_BUTTON_CHARS = 20
6
+ export const WHATSAPP_MAX_HEADER_CHARS = 60
7
+ export const WHATSAPP_MAX_BODY_CHARS = 1024
8
+ export const WHATSAPP_MAX_FOOTER_CHARS = 60
9
+ export const DEFAULT_WHATSAPP_MAX_BUTTON_SEPARATOR = 'More options:'
10
+ export const MENU_BUTTON_WHATSAPP_BUTTON_LIST = 'Show options'
@@ -4,7 +4,7 @@ import React from 'react'
4
4
  import { truncateText } from '../util'
5
5
  import { renderComponent } from '../util/react'
6
6
  import { Message } from './message'
7
- import { WHATSAPP_MAX_BUTTON_CHARS } from './multichannel/multichannel-utils'
7
+ import { WHATSAPP_MAX_BUTTON_CHARS } from './multichannel/whatsapp/constants'
8
8
  import { convertToMarkdownMeta } from './multichannel/whatsapp/markdown-meta'
9
9
 
10
10
  // TODO: Add validation in component
@@ -10,7 +10,7 @@ import {
10
10
  WHATSAPP_MAX_BUTTON_CHARS,
11
11
  WHATSAPP_MAX_FOOTER_CHARS,
12
12
  WHATSAPP_MAX_HEADER_CHARS,
13
- } from './multichannel/multichannel-utils'
13
+ } from './multichannel/whatsapp/constants'
14
14
  import { convertToMarkdownMeta } from './multichannel/whatsapp/markdown-meta'
15
15
 
16
16
  export interface WhatsappCTAUrlButtonCommonProps {
package/src/constants.js CHANGED
@@ -189,10 +189,3 @@ export const ROLES = {
189
189
  DOCUMENT_MESSAGE: 'document-message',
190
190
  RAW_MESSAGE: 'raw-message',
191
191
  }
192
-
193
- export const COMPONENT_TYPE = {
194
- TEXT: 'Text',
195
- BUTTON: 'Button',
196
- REPLY: 'Reply',
197
- CAROUSEL: 'Carousel',
198
- }
package/src/contexts.tsx CHANGED
@@ -56,6 +56,9 @@ export const WebchatContext = createContext<WebchatContextProps>({
56
56
  resetUnreadMessages: () => {
57
57
  return
58
58
  },
59
+ setIsInputFocused: () => {
60
+ return
61
+ },
59
62
  setLastMessageVisible: () => {
60
63
  return
61
64
  },
@@ -72,6 +75,9 @@ export const WebchatContext = createContext<WebchatContextProps>({
72
75
  return
73
76
  },
74
77
  theme: {},
78
+ toggleCoverComponent: () => {
79
+ return
80
+ },
75
81
  toggleWebchat: () => {
76
82
  return
77
83
  },
package/src/dev-app.jsx CHANGED
@@ -1,6 +1,6 @@
1
1
  import merge from 'lodash.merge'
2
2
  import React from 'react'
3
- import { render } from 'react-dom'
3
+ import { createRoot } from 'react-dom/client'
4
4
 
5
5
  import { SENDERS } from './index-types'
6
6
  import { ReactBot } from './react-bot'
@@ -119,10 +119,10 @@ export class DevApp extends WebchatApp {
119
119
 
120
120
  render(dest, optionsAtRuntime = {}) {
121
121
  onDOMLoaded(() => {
122
- render(
123
- this.getComponent(dest, optionsAtRuntime),
124
- this.getReactMountNode(dest)
125
- )
122
+ const devAppComponent = this.getComponent(dest, optionsAtRuntime)
123
+ const container = this.getReactMountNode(dest)
124
+ const reactRoot = createRoot(container)
125
+ reactRoot.render(devAppComponent)
126
126
  })
127
127
  }
128
128
 
@@ -79,18 +79,13 @@ export interface WebchatArgs {
79
79
  theme?: ThemeProps
80
80
  }
81
81
 
82
- type EventArgs = { [key: string]: any }
83
- type TrackEventFunction = (
82
+ export type EventArgs = { [key: string]: any }
83
+ export type TrackEventFunction = (
84
84
  request: ActionRequest,
85
85
  eventName: string,
86
86
  args?: EventArgs
87
87
  ) => Promise<void>
88
88
 
89
- export interface WebchatAppArgs {
90
- appId?: string
91
- visibility?: () => boolean
92
- }
93
-
94
89
  export enum SENDERS {
95
90
  bot = 'bot',
96
91
  user = 'user',
@@ -162,11 +157,13 @@ export interface WebchatContextProps {
162
157
  sendInput: (input: ClientInput) => Promise<void>
163
158
  sendPayload: (payload: string) => Promise<void>
164
159
  sendText: (text: string, payload?: string) => Promise<void>
160
+ setIsInputFocused: (isInputFocused: boolean) => void
165
161
  setLastMessageVisible: (isLastMessageVisible: boolean) => void
166
162
  theme: ThemeProps
167
163
  toggleWebchat: (toggle: boolean) => void
168
164
  toggleEmojiPicker: (toggle: boolean) => void
169
165
  togglePersistentMenu: (toggle: boolean) => void
166
+ toggleCoverComponent: (toggle: boolean) => void
170
167
  updateLatestInput: (input: ClientInput) => void
171
168
  updateMessage: (message: WebchatMessage) => void
172
169
  updateReplies: (replies: boolean) => void
package/src/index.ts CHANGED
@@ -11,5 +11,5 @@ export { msgsToBotonic, msgToBotonic } from './msg-to-botonic'
11
11
  export { NodeApp } from './node-app'
12
12
  export * from './util'
13
13
  export * from './webchat'
14
- export { WebchatApp } from './webchat-app'
14
+ export { WebchatApp, WebchatAppProps } from './webchat-app'
15
15
  export { WebviewApp } from './webview-app'
@@ -23,4 +23,5 @@ export enum WebchatAction {
23
23
  UPDATE_THEME = 'updateTheme',
24
24
  UPDATE_TYPING = 'updateTyping',
25
25
  UPDATE_WEBVIEW = 'updateWebview',
26
+ SET_IS_INPUT_FOCUSED = 'setIsInputFocused',
26
27
  }
@@ -0,0 +1,31 @@
1
+ import React, { useContext } from 'react'
2
+
3
+ import { WEBCHAT } from '../../constants'
4
+ import { WebchatContext } from '../../contexts'
5
+
6
+ interface Props {
7
+ component: any
8
+ componentProps: any
9
+ }
10
+
11
+ export const CoverComponent = ({ component, componentProps }: Props) => {
12
+ const { getThemeProperty, toggleCoverComponent, webchatState } =
13
+ useContext(WebchatContext)
14
+
15
+ const Cover = component
16
+
17
+ const coverComponentProps = getThemeProperty(
18
+ WEBCHAT.CUSTOM_PROPERTIES.coverComponentProps,
19
+ componentProps
20
+ )
21
+
22
+ const closeCoverComponent = () => {
23
+ toggleCoverComponent(false)
24
+ }
25
+
26
+ if (!Cover || !webchatState.isCoverComponentOpen) {
27
+ return null
28
+ }
29
+
30
+ return <Cover closeComponent={closeCoverComponent} {...coverComponentProps} />
31
+ }
@@ -3,11 +3,17 @@ import {
3
3
  getWebchatElement,
4
4
  } from '../../util/dom'
5
5
 
6
+ interface UseScrollToBottom {
7
+ host: any
8
+ behavior?: ScrollBehavior
9
+ timeout?: number
10
+ }
11
+
6
12
  export const useScrollToBottom = ({
7
13
  host,
8
14
  behavior = 'smooth',
9
15
  timeout = 200,
10
- }) => {
16
+ }: UseScrollToBottom) => {
11
17
  const scrollToBottom = () => {
12
18
  const webchatElement = getWebchatElement(host)
13
19
  if (!webchatElement) return
@@ -17,7 +23,7 @@ export const useScrollToBottom = ({
17
23
  setTimeout(() => {
18
24
  scrollableMessagesListElement.scrollTo({
19
25
  top: scrollableMessagesListElement.scrollHeight,
20
- behavior: behavior as ScrollBehavior,
26
+ behavior: behavior,
21
27
  })
22
28
  }, timeout)
23
29
  }
@@ -44,6 +44,7 @@ export const webchatInitialState: WebchatState = {
44
44
  currentAttachment: undefined,
45
45
  numUnreadMessages: 0,
46
46
  isLastMessageVisible: true,
47
+ isInputFocused: false,
47
48
  }
48
49
 
49
50
  export function useWebchat() {
@@ -196,6 +197,13 @@ export function useWebchat() {
196
197
  })
197
198
  }
198
199
 
200
+ const setIsInputFocused = (isInputFocused: boolean) => {
201
+ webchatDispatch({
202
+ type: WebchatAction.SET_IS_INPUT_FOCUSED,
203
+ payload: isInputFocused,
204
+ })
205
+ }
206
+
199
207
  return {
200
208
  addMessage,
201
209
  addMessageComponent,
@@ -204,6 +212,7 @@ export function useWebchat() {
204
212
  resetUnreadMessages,
205
213
  setCurrentAttachment,
206
214
  setError,
215
+ setIsInputFocused,
207
216
  setLastMessageVisible,
208
217
  setOnline,
209
218
  toggleCoverComponent,
@@ -49,6 +49,7 @@ export interface WebchatState {
49
49
  currentAttachment?: File
50
50
  numUnreadMessages: number
51
51
  isLastMessageVisible: boolean
52
+ isInputFocused: boolean
52
53
  }
53
54
 
54
55
  // export interface WebchatProps extends WebchatArgs {
@@ -23,7 +23,8 @@ export const Textarea = ({
23
23
  sendChatEvent,
24
24
  sendTextAreaText,
25
25
  }: TextareaProps) => {
26
- const { getThemeProperty, webchatState } = useContext(WebchatContext)
26
+ const { getThemeProperty, webchatState, setIsInputFocused } =
27
+ useContext(WebchatContext)
27
28
 
28
29
  useDeviceAdapter(host, webchatState.isWebchatOpen)
29
30
 
@@ -71,10 +72,20 @@ export const Textarea = ({
71
72
  sendChatEvent(Typing.Off)
72
73
  }
73
74
 
75
+ const handleFocus = () => {
76
+ setIsInputFocused(true)
77
+ }
78
+
79
+ const handleBlur = () => {
80
+ setIsInputFocused(false)
81
+ }
82
+
74
83
  return (
75
84
  <TextAreaContainer>
76
85
  <TextareaAutosize
77
86
  ref={(ref: HTMLTextAreaElement) => (textareaRef.current = ref)}
87
+ onFocus={handleFocus}
88
+ onBlur={handleBlur}
78
89
  name='text'
79
90
  maxRows={4}
80
91
  wrap='soft'
@@ -3,13 +3,23 @@ import React, { useContext, useEffect, useRef, useState } from 'react'
3
3
  import { ROLES } from '../../constants'
4
4
  import { WebchatContext } from '../../contexts'
5
5
  import { BotonicContainerId } from '../constants'
6
- import { TypingIndicator } from '../typing-indicator'
6
+ import TypingIndicator from '../typing-indicator'
7
7
  import { IntroMessage } from './intro-message'
8
8
  import { ScrollButton } from './scroll-button'
9
9
  import { ContainerMessage, ScrollableMessageList } from './styles'
10
10
  import { UnreadMessagesBanner } from './unread-messages-banner'
11
11
  import { useNotifications } from './use-notifications'
12
12
 
13
+ const SCROLL_TIMEOUT = 200
14
+ const scrollOptionsEnd: ScrollIntoViewOptions = {
15
+ behavior: 'smooth',
16
+ block: 'end',
17
+ }
18
+ const scrollOptionsCenter: ScrollIntoViewOptions = {
19
+ behavior: 'smooth',
20
+ block: 'center',
21
+ }
22
+
13
23
  export const WebchatMessageList = () => {
14
24
  const {
15
25
  webchatState,
@@ -18,39 +28,50 @@ export const WebchatMessageList = () => {
18
28
  scrollableMessagesListRef,
19
29
  } = useContext(WebchatContext)
20
30
 
21
- const [firstUnreadMessageId, setFirstUnreadMessageId] = useState()
31
+ const { notificationsEnabled } = useNotifications()
22
32
 
23
- const lastMessageBottomRef = useRef<HTMLDivElement>(null)
33
+ const [firstUnreadMessageId, setFirstUnreadMessageId] = useState<string>()
24
34
 
25
- const scrollToBottom = () => {
35
+ const lastMessageRef = useRef<HTMLDivElement>(null)
36
+ const typingRef = useRef<HTMLDivElement>(null)
37
+ const unreadMessagesBannerRef = useRef<HTMLDivElement>(null)
38
+
39
+ const scrollToTyping = () => {
26
40
  setTimeout(() => {
27
- lastMessageBottomRef.current?.scrollIntoView({
28
- behavior: 'smooth',
29
- block: 'end',
30
- })
31
- }, 100)
41
+ typingRef.current?.scrollIntoView(scrollOptionsEnd)
42
+ }, SCROLL_TIMEOUT)
32
43
  }
33
44
 
34
- const handleScrollToBottom = () => {
35
- resetUnreadMessages()
36
- scrollToBottom()
45
+ const scrollToLastMessage = () => {
46
+ setTimeout(() => {
47
+ lastMessageRef.current?.scrollIntoView(scrollOptionsEnd)
48
+ }, SCROLL_TIMEOUT)
37
49
  }
38
50
 
39
- const unreadMessagesBannerRef = useRef<HTMLDivElement>(null)
40
-
41
51
  const scrollToBanner = () => {
42
52
  setTimeout(() => {
43
- unreadMessagesBannerRef.current?.scrollIntoView({
44
- behavior: 'smooth',
45
- block: 'center',
46
- })
47
- }, 100)
53
+ unreadMessagesBannerRef.current?.scrollIntoView(scrollOptionsCenter)
54
+ }, SCROLL_TIMEOUT)
48
55
  }
49
56
 
50
- const showUnreadMessagesBanner = (messageComponentId: string) =>
51
- firstUnreadMessageId &&
52
- messageComponentId === firstUnreadMessageId &&
53
- webchatState.numUnreadMessages > 0
57
+ const handleScrollToBottom = () => {
58
+ resetUnreadMessages()
59
+ if (webchatState.typing) {
60
+ scrollToTyping()
61
+ return
62
+ }
63
+
64
+ scrollToLastMessage()
65
+ }
66
+
67
+ const showUnreadMessagesBanner = (messageComponentId: string) => {
68
+ return (
69
+ !webchatState.isInputFocused &&
70
+ firstUnreadMessageId &&
71
+ messageComponentId === firstUnreadMessageId &&
72
+ webchatState.numUnreadMessages > 0
73
+ )
74
+ }
54
75
 
55
76
  useEffect(() => {
56
77
  const firstUnreadMessage = webchatState.messagesComponents.find(
@@ -60,39 +81,47 @@ export const WebchatMessageList = () => {
60
81
  }, [webchatState.messagesComponents])
61
82
 
62
83
  useEffect(() => {
63
- if (
64
- webchatState.messagesComponents.length > 0 &&
65
- lastMessageBottomRef.current
66
- ) {
84
+ if (webchatState.messagesComponents.length > 0 && lastMessageRef.current) {
67
85
  const observer = new IntersectionObserver(entries => {
68
86
  entries.forEach(entry => {
69
87
  setLastMessageVisible(entry.isIntersecting)
70
88
  })
71
89
  })
72
- observer.observe(lastMessageBottomRef.current)
90
+ observer.observe(lastMessageRef.current)
73
91
  }
74
92
  }, [webchatState.messagesComponents])
75
93
 
76
- const { notificationsEnabled } = useNotifications()
77
-
78
94
  useEffect(() => {
79
95
  if (!notificationsEnabled) {
80
- scrollToBottom()
81
- return
96
+ if (webchatState.typing) {
97
+ scrollToTyping()
98
+ return
99
+ }
100
+
101
+ scrollToLastMessage()
82
102
  }
83
- }, [webchatState.typing])
103
+ }, [webchatState.typing, webchatState.messagesComponents])
84
104
 
85
105
  useEffect(() => {
86
- if (notificationsEnabled) {
87
- if (webchatState.isWebchatOpen && unreadMessagesBannerRef.current) {
106
+ if (webchatState.isWebchatOpen && notificationsEnabled) {
107
+ if (unreadMessagesBannerRef.current) {
88
108
  scrollToBanner()
89
109
  return
90
110
  }
91
111
 
92
- scrollToBottom()
93
- return
112
+ if (webchatState.typing) {
113
+ scrollToTyping()
114
+ return
115
+ }
116
+
117
+ scrollToLastMessage()
94
118
  }
95
- }, [firstUnreadMessageId, webchatState.isWebchatOpen, webchatState.typing])
119
+ }, [
120
+ firstUnreadMessageId,
121
+ webchatState.isWebchatOpen,
122
+ webchatState.typing,
123
+ webchatState.messagesComponents,
124
+ ])
96
125
 
97
126
  const showScrollButton =
98
127
  webchatState.numUnreadMessages > 0 && !webchatState.isLastMessageVisible
@@ -107,25 +136,27 @@ export const WebchatMessageList = () => {
107
136
  <IntroMessage />
108
137
  {webchatState.messagesComponents.map((messageComponent, index) => {
109
138
  return (
110
- <ContainerMessage role={ROLES.MESSAGE} key={index}>
111
- {showUnreadMessagesBanner(messageComponent.props.id) && (
112
- <UnreadMessagesBanner
113
- unreadMessagesBannerRef={unreadMessagesBannerRef}
114
- />
115
- )}
116
- {messageComponent}
139
+ <>
140
+ <ContainerMessage role={ROLES.MESSAGE} key={index}>
141
+ {showUnreadMessagesBanner(messageComponent.props.id) && (
142
+ <UnreadMessagesBanner
143
+ unreadMessagesBannerRef={unreadMessagesBannerRef}
144
+ />
145
+ )}
146
+ {messageComponent}
147
+ </ContainerMessage>
117
148
  {index === webchatState.messagesComponents.length - 1 && (
118
149
  <div
119
- ref={lastMessageBottomRef}
150
+ ref={lastMessageRef}
120
151
  style={{
121
152
  content: '',
122
153
  }}
123
154
  ></div>
124
155
  )}
125
- </ContainerMessage>
156
+ </>
126
157
  )
127
158
  })}
128
- {webchatState.typing && <TypingIndicator />}
159
+ {webchatState.typing && <TypingIndicator ref={typingRef} />}
129
160
  </ScrollableMessageList>
130
161
  {showScrollButton && <ScrollButton handleClick={handleScrollToBottom} />}
131
162
  </>
@@ -15,9 +15,9 @@ export const DefaultIntroImage = styled.img`
15
15
  `
16
16
 
17
17
  export const ContainerScrollButton = styled.div`
18
- position: absolute;
19
- right: 10px;
20
- bottom: 65px;
18
+ position: sticky;
19
+ left: 85%;
20
+ bottom: 15px;
21
21
 
22
22
  background-color: #6d6a78;
23
23
  cursor: pointer;