@botonic/react 0.23.3 → 0.24.0-alpha.0

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 (170) hide show
  1. package/index.d.ts +1 -1
  2. package/lib/cjs/assets/arrow-down.svg +3 -0
  3. package/lib/cjs/assets/arrow-scroll-down.svg +3 -0
  4. package/lib/cjs/assets/index-types.d.ts +4 -0
  5. package/lib/cjs/assets/index-types.js +2 -0
  6. package/lib/cjs/assets/index-types.js.map +1 -0
  7. package/lib/cjs/components/custom-message.js +2 -2
  8. package/lib/cjs/components/custom-message.js.map +1 -1
  9. package/lib/cjs/components/index-types.d.ts +10 -4
  10. package/lib/cjs/components/index-types.js.map +1 -1
  11. package/lib/cjs/components/message.js +21 -17
  12. package/lib/cjs/components/message.js.map +1 -1
  13. package/lib/cjs/components/timestamps.d.ts +2 -2
  14. package/lib/cjs/components/timestamps.js +5 -5
  15. package/lib/cjs/components/timestamps.js.map +1 -1
  16. package/lib/cjs/constants.d.ts +7 -5
  17. package/lib/cjs/constants.js +10 -6
  18. package/lib/cjs/constants.js.map +1 -1
  19. package/lib/cjs/contexts.js +6 -0
  20. package/lib/cjs/contexts.js.map +1 -1
  21. package/lib/cjs/dev-app.js +9 -3
  22. package/lib/cjs/dev-app.js.map +1 -1
  23. package/lib/cjs/index-types.d.ts +16 -2
  24. package/lib/cjs/index-types.js +7 -0
  25. package/lib/cjs/index-types.js.map +1 -1
  26. package/lib/cjs/msg-to-botonic.js +5 -2
  27. package/lib/cjs/msg-to-botonic.js.map +1 -1
  28. package/lib/cjs/webchat/actions.d.ts +19 -17
  29. package/lib/cjs/webchat/actions.js +19 -17
  30. package/lib/cjs/webchat/actions.js.map +1 -1
  31. package/lib/cjs/webchat/components/styled-scrollbar.js +1 -1
  32. package/lib/cjs/webchat/devices/device-adapter.js +1 -1
  33. package/lib/cjs/webchat/devices/device-adapter.js.map +1 -1
  34. package/lib/cjs/webchat/devices/webchat-resizer.js +1 -1
  35. package/lib/cjs/webchat/devices/webchat-resizer.js.map +1 -1
  36. package/lib/cjs/webchat/hooks/use-typing.d.ts +1 -1
  37. package/lib/cjs/webchat/hooks/use-typing.js +2 -3
  38. package/lib/cjs/webchat/hooks/use-typing.js.map +1 -1
  39. package/lib/cjs/webchat/hooks/use-webchat.d.ts +5 -1
  40. package/lib/cjs/webchat/hooks/use-webchat.js +15 -1
  41. package/lib/cjs/webchat/hooks/use-webchat.js.map +1 -1
  42. package/lib/cjs/webchat/index-types.d.ts +2 -1
  43. package/lib/cjs/webchat/message-list/index.d.ts +1 -0
  44. package/lib/cjs/webchat/message-list/index.js +58 -0
  45. package/lib/cjs/webchat/message-list/index.js.map +1 -0
  46. package/lib/cjs/webchat/message-list/intro-message.d.ts +1 -0
  47. package/lib/cjs/webchat/message-list/intro-message.js +23 -0
  48. package/lib/cjs/webchat/message-list/intro-message.js.map +1 -0
  49. package/lib/cjs/webchat/message-list/scroll-button.d.ts +5 -0
  50. package/lib/cjs/webchat/message-list/scroll-button.js +19 -0
  51. package/lib/cjs/webchat/message-list/scroll-button.js.map +1 -0
  52. package/lib/cjs/webchat/message-list/styles.d.ts +4 -0
  53. package/lib/cjs/webchat/message-list/styles.js +48 -0
  54. package/lib/cjs/webchat/message-list/styles.js.map +1 -0
  55. package/lib/cjs/webchat/message-list/unread-messages-banner.d.ts +5 -0
  56. package/lib/cjs/webchat/message-list/unread-messages-banner.js +30 -0
  57. package/lib/cjs/webchat/message-list/unread-messages-banner.js.map +1 -0
  58. package/lib/cjs/webchat/messages-reducer.js +31 -9
  59. package/lib/cjs/webchat/messages-reducer.js.map +1 -1
  60. package/lib/cjs/webchat/trigger-button/index.js +3 -2
  61. package/lib/cjs/webchat/trigger-button/index.js.map +1 -1
  62. package/lib/cjs/webchat/webchat-reducer.js +1 -1
  63. package/lib/cjs/webchat/webchat-reducer.js.map +1 -1
  64. package/lib/cjs/webchat/webchat.js +39 -21
  65. package/lib/cjs/webchat/webchat.js.map +1 -1
  66. package/lib/cjs/webchat-app.js +24 -9
  67. package/lib/cjs/webchat-app.js.map +1 -1
  68. package/lib/esm/assets/arrow-down.svg +3 -0
  69. package/lib/esm/assets/arrow-scroll-down.svg +3 -0
  70. package/lib/esm/assets/index-types.d.ts +4 -0
  71. package/lib/esm/assets/index-types.js +2 -0
  72. package/lib/esm/assets/index-types.js.map +1 -0
  73. package/lib/esm/components/custom-message.js +2 -2
  74. package/lib/esm/components/custom-message.js.map +1 -1
  75. package/lib/esm/components/index-types.d.ts +10 -4
  76. package/lib/esm/components/index-types.js.map +1 -1
  77. package/lib/esm/components/message.js +22 -18
  78. package/lib/esm/components/message.js.map +1 -1
  79. package/lib/esm/components/timestamps.d.ts +2 -2
  80. package/lib/esm/components/timestamps.js +5 -5
  81. package/lib/esm/components/timestamps.js.map +1 -1
  82. package/lib/esm/constants.d.ts +7 -5
  83. package/lib/esm/constants.js +9 -5
  84. package/lib/esm/constants.js.map +1 -1
  85. package/lib/esm/contexts.js +6 -0
  86. package/lib/esm/contexts.js.map +1 -1
  87. package/lib/esm/dev-app.js +9 -3
  88. package/lib/esm/dev-app.js.map +1 -1
  89. package/lib/esm/index-types.d.ts +16 -2
  90. package/lib/esm/index-types.js +6 -1
  91. package/lib/esm/index-types.js.map +1 -1
  92. package/lib/esm/msg-to-botonic.js +6 -3
  93. package/lib/esm/msg-to-botonic.js.map +1 -1
  94. package/lib/esm/webchat/actions.d.ts +19 -17
  95. package/lib/esm/webchat/actions.js +19 -17
  96. package/lib/esm/webchat/actions.js.map +1 -1
  97. package/lib/esm/webchat/components/styled-scrollbar.js +1 -1
  98. package/lib/esm/webchat/devices/device-adapter.js +1 -1
  99. package/lib/esm/webchat/devices/device-adapter.js.map +1 -1
  100. package/lib/esm/webchat/devices/webchat-resizer.js +1 -1
  101. package/lib/esm/webchat/devices/webchat-resizer.js.map +1 -1
  102. package/lib/esm/webchat/hooks/use-typing.d.ts +1 -1
  103. package/lib/esm/webchat/hooks/use-typing.js +2 -3
  104. package/lib/esm/webchat/hooks/use-typing.js.map +1 -1
  105. package/lib/esm/webchat/hooks/use-webchat.d.ts +5 -1
  106. package/lib/esm/webchat/hooks/use-webchat.js +15 -1
  107. package/lib/esm/webchat/hooks/use-webchat.js.map +1 -1
  108. package/lib/esm/webchat/index-types.d.ts +2 -1
  109. package/lib/esm/webchat/message-list/index.d.ts +1 -0
  110. package/lib/esm/webchat/message-list/index.js +54 -0
  111. package/lib/esm/webchat/message-list/index.js.map +1 -0
  112. package/lib/esm/webchat/message-list/intro-message.d.ts +1 -0
  113. package/lib/esm/webchat/message-list/intro-message.js +18 -0
  114. package/lib/esm/webchat/message-list/intro-message.js.map +1 -0
  115. package/lib/esm/webchat/message-list/scroll-button.d.ts +5 -0
  116. package/lib/esm/webchat/message-list/scroll-button.js +14 -0
  117. package/lib/esm/webchat/message-list/scroll-button.js.map +1 -0
  118. package/lib/esm/webchat/message-list/styles.d.ts +4 -0
  119. package/lib/esm/webchat/message-list/styles.js +44 -0
  120. package/lib/esm/webchat/message-list/styles.js.map +1 -0
  121. package/lib/esm/webchat/message-list/unread-messages-banner.d.ts +5 -0
  122. package/lib/esm/webchat/message-list/unread-messages-banner.js +25 -0
  123. package/lib/esm/webchat/message-list/unread-messages-banner.js.map +1 -0
  124. package/lib/esm/webchat/messages-reducer.js +31 -9
  125. package/lib/esm/webchat/messages-reducer.js.map +1 -1
  126. package/lib/esm/webchat/trigger-button/index.js +3 -2
  127. package/lib/esm/webchat/trigger-button/index.js.map +1 -1
  128. package/lib/esm/webchat/webchat-reducer.js +1 -1
  129. package/lib/esm/webchat/webchat-reducer.js.map +1 -1
  130. package/lib/esm/webchat/webchat.js +40 -22
  131. package/lib/esm/webchat/webchat.js.map +1 -1
  132. package/lib/esm/webchat-app.js +25 -10
  133. package/lib/esm/webchat-app.js.map +1 -1
  134. package/package.json +7 -5
  135. package/src/assets/arrow-down.svg +3 -0
  136. package/src/assets/arrow-scroll-down.svg +3 -0
  137. package/src/assets/index-types.ts +4 -0
  138. package/src/components/custom-message.jsx +2 -2
  139. package/src/components/index-types.ts +8 -4
  140. package/src/components/message.jsx +29 -21
  141. package/src/components/timestamps.jsx +5 -5
  142. package/src/constants.js +9 -6
  143. package/src/contexts.tsx +6 -0
  144. package/src/dev-app.jsx +9 -3
  145. package/src/index-types.ts +18 -3
  146. package/src/msg-to-botonic.jsx +2 -3
  147. package/src/webchat/actions.ts +19 -17
  148. package/src/webchat/components/styled-scrollbar.jsx +1 -1
  149. package/src/webchat/devices/device-adapter.js +1 -1
  150. package/src/webchat/devices/webchat-resizer.js +1 -1
  151. package/src/webchat/hooks/use-typing.ts +3 -3
  152. package/src/webchat/hooks/use-webchat.ts +18 -2
  153. package/src/webchat/index-types.ts +2 -1
  154. package/src/webchat/message-list/index.tsx +103 -0
  155. package/src/webchat/message-list/intro-message.tsx +38 -0
  156. package/src/webchat/message-list/scroll-button.tsx +41 -0
  157. package/src/webchat/message-list/styles.ts +47 -0
  158. package/src/webchat/message-list/unread-messages-banner.tsx +62 -0
  159. package/src/webchat/messages-reducer.ts +42 -5
  160. package/src/webchat/trigger-button/index.tsx +9 -4
  161. package/src/webchat/webchat-reducer.ts +0 -1
  162. package/src/webchat/webchat.jsx +71 -49
  163. package/src/webchat-app.jsx +25 -14
  164. package/lib/cjs/webchat/message-list.d.ts +0 -1
  165. package/lib/cjs/webchat/message-list.js +0 -39
  166. package/lib/cjs/webchat/message-list.js.map +0 -1
  167. package/lib/esm/webchat/message-list.d.ts +0 -1
  168. package/lib/esm/webchat/message-list.js +0 -34
  169. package/lib/esm/webchat/message-list.js.map +0 -1
  170. package/src/webchat/message-list.jsx +0 -77
@@ -4,8 +4,9 @@ import Fade from 'react-reveal/Fade'
4
4
  import styled from 'styled-components'
5
5
  import { v4 as uuidv4 } from 'uuid'
6
6
 
7
- import { COLORS, SENDERS, WEBCHAT } from '../constants'
7
+ import { COLORS, WEBCHAT } from '../constants'
8
8
  import { RequestContext, WebchatContext } from '../contexts'
9
+ import { SENDERS } from '../index-types'
9
10
  import { isDev, resolveImage } from '../util/environment'
10
11
  import { ConditionalWrapper, renderComponent } from '../util/react'
11
12
  import { Button } from './button'
@@ -16,7 +17,7 @@ import { MessageTimestamp, resolveMessageTimestamps } from './timestamps'
16
17
 
17
18
  const MessageContainer = styled.div`
18
19
  display: flex;
19
- justify-content: ${props => (props.isfromuser ? 'flex-end' : 'flex-start')};
20
+ justify-content: ${props => (props.isSentByUser ? 'flex-end' : 'flex-start')};
20
21
  position: relative;
21
22
  padding: 0px 6px;
22
23
  `
@@ -72,7 +73,7 @@ export const Message = props => {
72
73
  let {
73
74
  type = '',
74
75
  blob = true,
75
- from = SENDERS.bot,
76
+ sentBy,
76
77
  delay = defaultDelay,
77
78
  typing = defaultTyping,
78
79
  children,
@@ -80,10 +81,13 @@ export const Message = props => {
80
81
  json,
81
82
  style,
82
83
  imagestyle = props.imagestyle || props.imageStyle,
84
+ isUnread = true,
83
85
  ...otherProps
84
86
  } = props
85
- const isFromUser = from === SENDERS.user
86
- const isFromBot = from === SENDERS.bot
87
+
88
+ const isSentByUser = sentBy === SENDERS.user
89
+ const isSentByBot = sentBy === SENDERS.bot
90
+ const isSentByAgent = sentBy === SENDERS.agent
87
91
  const markdown = props.markdown
88
92
  const { webchatState, addMessage, updateReplies, getThemeProperty } =
89
93
  useContext(WebchatContext)
@@ -105,7 +109,7 @@ export const Message = props => {
105
109
  let textChildren = React.Children.toArray(children).filter(
106
110
  e => ![Button, Reply].includes(e.type)
107
111
  )
108
- if (isFromUser)
112
+ if (isSentByUser)
109
113
  textChildren = textChildren.map(e =>
110
114
  typeof e === 'string' ? renderLinks(e) : e
111
115
  )
@@ -115,7 +119,7 @@ export const Message = props => {
115
119
 
116
120
  const getEnvAck = () => {
117
121
  if (isDev) return 1
118
- if (!isFromUser) return 1
122
+ if (!isSentByUser) return 1
119
123
  if (props.ack !== undefined) return props.ack
120
124
  return 0
121
125
  }
@@ -131,7 +135,7 @@ export const Message = props => {
131
135
  data: decomposedChildren ? decomposedChildren : textChildren,
132
136
  timestamp: props.timestamp || getFormattedTimestamp,
133
137
  markdown,
134
- from,
138
+ sentBy,
135
139
  buttons: buttons.map(b => ({
136
140
  parentId: b.props.parentId,
137
141
  payload: b.props.payload,
@@ -153,6 +157,7 @@ export const Message = props => {
153
157
  display: delay + typing == 0,
154
158
  customTypeName: decomposedChildren.customTypeName,
155
159
  ack: ack,
160
+ isUnread: isUnread === 1 || isUnread === true,
156
161
  }
157
162
  addMessage(message)
158
163
  }
@@ -178,7 +183,7 @@ export const Message = props => {
178
183
 
179
184
  const getBgColor = () => {
180
185
  if (!blob) return COLORS.TRANSPARENT
181
- if (isFromUser) {
186
+ if (isSentByUser) {
182
187
  return getThemeProperty(
183
188
  WEBCHAT.CUSTOM_PROPERTIES.userMessageBackground,
184
189
  brandColor
@@ -191,11 +196,13 @@ export const Message = props => {
191
196
  }
192
197
 
193
198
  const getMessageStyle = () =>
194
- isFromBot
199
+ isSentByBot
195
200
  ? getThemeProperty(WEBCHAT.CUSTOM_PROPERTIES.botMessageStyle)
196
201
  : getThemeProperty(WEBCHAT.CUSTOM_PROPERTIES.userMessageStyle)
197
202
 
198
- const hasBlobTick = () => getThemeProperty(`message.${from}.blobTick`, true)
203
+ const userOrBotMessage = isSentByUser ? SENDERS.user : SENDERS.bot
204
+ const hasBlobTick = () =>
205
+ getThemeProperty(`message.${userOrBotMessage}.blobTick`, true)
199
206
 
200
207
  const renderBrowser = () => {
201
208
  const m = webchatState.messagesJSON.find(m => m.id === state.id)
@@ -209,14 +216,14 @@ export const Message = props => {
209
216
  pointerSize == 5
210
217
  ? getBgColor()
211
218
  : getThemeProperty(
212
- `message.${from}.style.borderColor`,
219
+ `message.${userOrBotMessage}.style.borderColor`,
213
220
  COLORS.TRANSPARENT
214
221
  )
215
222
  const containerStyle = {
216
- ...getThemeProperty(`message.${from}.blobTickStyle`),
223
+ ...getThemeProperty(`message.${userOrBotMessage}.blobTickStyle`),
217
224
  }
218
225
  const blobTickStyle = {}
219
- if (isFromUser) {
226
+ if (isSentByUser) {
220
227
  containerStyle.right = 0
221
228
  containerStyle.marginRight = -pointerSize
222
229
  blobTickStyle.borderRight = 0
@@ -247,9 +254,10 @@ export const Message = props => {
247
254
  )
248
255
 
249
256
  const resolveCustomTypeName = () =>
250
- isFromBot && type === INPUT.CUSTOM ? ` ${m.customTypeName}` : ''
257
+ isSentByBot && type === INPUT.CUSTOM ? ` ${m.customTypeName}` : ''
258
+
259
+ const className = `${type}-${userOrBotMessage}${resolveCustomTypeName()}`
251
260
 
252
- const className = `${type}-${from}${resolveCustomTypeName()}`
253
261
  return (
254
262
  <ConditionalWrapper
255
263
  condition={animationsEnabled}
@@ -257,12 +265,12 @@ export const Message = props => {
257
265
  >
258
266
  <>
259
267
  <MessageContainer
260
- isfromuser={isFromUser}
268
+ isSentByUser={isSentByUser}
261
269
  style={{
262
270
  ...getThemeProperty(WEBCHAT.CUSTOM_PROPERTIES.messageStyle),
263
271
  }}
264
272
  >
265
- {isFromBot && BotMessageImage && (
273
+ {isSentByBot && BotMessageImage && (
266
274
  <BotMessageImageContainer
267
275
  style={{
268
276
  ...getThemeProperty(
@@ -280,7 +288,7 @@ export const Message = props => {
280
288
  <Blob
281
289
  className={className}
282
290
  bgcolor={getBgColor()}
283
- color={isFromUser ? COLORS.SOLID_WHITE : COLORS.SOLID_BLACK}
291
+ color={isSentByUser ? COLORS.SOLID_WHITE : COLORS.SOLID_BLACK}
284
292
  blobwidth={getThemeProperty(
285
293
  WEBCHAT.CUSTOM_PROPERTIES.botMessageBlobWidth
286
294
  )}
@@ -300,7 +308,7 @@ export const Message = props => {
300
308
  }}
301
309
  markdownstyle={getMarkdownStyle(
302
310
  getThemeProperty,
303
- isFromUser ? COLORS.SEASHELL_WHITE : brandColor
311
+ isSentByUser ? COLORS.SEASHELL_WHITE : brandColor
304
312
  )}
305
313
  />
306
314
  ) : (
@@ -317,7 +325,7 @@ export const Message = props => {
317
325
  <MessageTimestamp
318
326
  timestamp={m.timestamp}
319
327
  style={timestampStyle}
320
- isfromuser={isFromUser}
328
+ isSentByUser={isSentByUser}
321
329
  />
322
330
  )}
323
331
  </>
@@ -46,17 +46,17 @@ const TimestampText = styled.div`
46
46
  font-size: 10px;
47
47
  color: ${COLORS.SOLID_BLACK};
48
48
  width: 100%;
49
- text-align: ${props => (props.isfromuser ? 'right' : 'left')};
50
- padding: ${props => (props.isfromuser ? '0px 15px' : '0px 50px')};
49
+ text-align: ${props => (props.isSentByUser ? 'right' : 'left')};
50
+ padding: ${props => (props.isSentByUser ? '0px 15px' : '0px 50px')};
51
51
  margin-bottom: 5px;
52
52
  `
53
53
 
54
- export const MessageTimestamp = ({ timestamp, style, isfromuser }) => {
55
- const classSufix = isfromuser ? 'user' : 'bot'
54
+ export const MessageTimestamp = ({ timestamp, style, isSentByUser }) => {
55
+ const classSufix = isSentByUser ? 'user' : 'bot'
56
56
  return (
57
57
  <TimestampContainer className={`botonic-timestamp-${classSufix}`}>
58
58
  <TimestampText
59
- isfromuser={isfromuser}
59
+ isSentByUser={isSentByUser}
60
60
  style={{
61
61
  ...style,
62
62
  }}
package/src/constants.js CHANGED
@@ -1,10 +1,5 @@
1
1
  import BotonicLogo from './assets/botonic_react_logo100x100.png'
2
2
 
3
- export const SENDERS = {
4
- bot: 'bot',
5
- user: 'user',
6
- }
7
-
8
3
  export const COLORS = {
9
4
  // http://chir.ag/projects/name-that-color
10
5
  APPLE_GREEN: 'rgba(58, 156, 53, 1)',
@@ -125,8 +120,16 @@ export const WEBCHAT = {
125
120
  // TriggerButton
126
121
  customTrigger: 'triggerButton.custom',
127
122
  triggerButtonImage: 'triggerButton.image',
128
- triggerButtonNotificationsEnabled: 'triggerButton.notificationsEnabled',
129
123
  triggerButtonStyle: 'triggerButton.style',
124
+ // Notifications
125
+ notificationsEnabled: 'notifications.enable',
126
+ notificationsBannerEnabled: 'notifications.banner.enable',
127
+ notificationsBannerCustom: 'notifications.banner.custom',
128
+ notificationsBannerText: 'notifications.banner.text',
129
+ notificationsTriggerButtonEnabled: 'notifications.triggerButton.enable',
130
+ // Scroll Button
131
+ scrollButtonEnabled: 'scrollButton.enable',
132
+ scrollButtonCustom: 'scrollButton.custom',
130
133
  // User Input
131
134
  blockInputs: 'userInput.blockInputs',
132
135
  documentDownload: 'documentDownload',
package/src/contexts.tsx CHANGED
@@ -29,6 +29,12 @@ export const WebchatContext = createContext<WebchatContextProps>({
29
29
  resolveCase: () => {
30
30
  return
31
31
  },
32
+ resetUnreadMessages: () => {
33
+ return
34
+ },
35
+ setLastMessageVisible: () => {
36
+ return
37
+ },
32
38
  sendAttachment: () => {
33
39
  return
34
40
  },
package/src/dev-app.jsx CHANGED
@@ -2,7 +2,7 @@ import merge from 'lodash.merge'
2
2
  import React from 'react'
3
3
  import { render } from 'react-dom'
4
4
 
5
- import { SENDERS } from './constants'
5
+ import { SENDERS } from './index-types'
6
6
  import { ReactBot } from './react-bot'
7
7
  import { onDOMLoaded } from './util/dom'
8
8
  import { WebchatDev } from './webchat/webchat-dev'
@@ -123,11 +123,17 @@ export class DevApp extends WebchatApp {
123
123
 
124
124
  async onUserInput({ input, session, lastRoutePath }) {
125
125
  this.onMessage &&
126
- this.onMessage(this, { from: SENDERS.user, message: input })
126
+ this.onMessage(this, {
127
+ sentBy: SENDERS.user,
128
+ message: input,
129
+ })
127
130
  const resp = await this.bot.input({ input, session, lastRoutePath })
128
131
  this.onMessage &&
129
132
  resp.response.map(r =>
130
- this.onMessage(this, { from: SENDERS.bot, message: r })
133
+ this.onMessage(this, {
134
+ sentBy: SENDERS.bot,
135
+ message: r,
136
+ })
131
137
  )
132
138
  this.webchatRef.current.addBotResponse(resp)
133
139
  }
@@ -1,4 +1,4 @@
1
- import type {
1
+ import {
2
2
  BotRequest as CoreBotRequest,
3
3
  Input as CoreInput,
4
4
  InputType as CoreInputType,
@@ -7,7 +7,7 @@ import type {
7
7
  Routes as CoreRoutes,
8
8
  Session as CoreSession,
9
9
  SessionUser as CoreSessionUser,
10
- } from '@botonic/core/lib/esm/models/legacy-types'
10
+ } from '@botonic/core/src'
11
11
  import React from 'react'
12
12
 
13
13
  import {
@@ -129,16 +129,29 @@ export interface WebchatAppArgs {
129
129
  visibility?: () => boolean
130
130
  }
131
131
 
132
+ export enum SENDERS {
133
+ bot = 'bot',
134
+ user = 'user',
135
+ agent = 'agent',
136
+ }
137
+
132
138
  export interface WebchatMessage {
133
139
  ack: 0 | 1
140
+ blob: boolean
134
141
  buttons: ButtonProps[]
142
+ children: any
135
143
  data: any
136
144
  delay: number
137
145
  display: boolean
138
- from: 'user' | 'bot'
146
+ enabletimestamps: boolean
139
147
  id: string
148
+ imagestyle: any
149
+ isUnread: boolean
150
+ json: any
140
151
  markdown: boolean
141
152
  replies: ReplyProps[]
153
+ sentBy: SENDERS
154
+ style: any
142
155
  timestamp: string
143
156
  type: CoreInputType
144
157
  typing: number
@@ -173,11 +186,13 @@ export interface WebchatContextProps {
173
186
  closeWebview: () => void
174
187
  getThemeProperty: (property: string, defaultValue?: string | boolean) => any
175
188
  openWebview: (webviewComponent: Webview) => void
189
+ resetUnreadMessages: () => void
176
190
  resolveCase: () => void
177
191
  sendAttachment: (attachment: File) => void
178
192
  sendInput: (input: CoreInput) => void
179
193
  sendPayload: (payload: string) => void
180
194
  sendText: (text: string, payload?: string) => void
195
+ setLastMessageVisible: (isLastMessageVisible: boolean) => void
181
196
  theme: ThemeProps
182
197
  toggleWebchat: (toggle: boolean) => void
183
198
  updateLatestInput: (input: CoreInput) => void
@@ -88,7 +88,7 @@ export function msgToBotonic(msg, customMessageTypes) {
88
88
  const buttons = buttonsParse(msg.buttons)
89
89
  return (
90
90
  <>
91
- <Text {...msg}>
91
+ <Text {...msg} key={msg.key}>
92
92
  {msg.text}
93
93
  {buttons}
94
94
  </Text>
@@ -125,8 +125,7 @@ export function msgsToBotonic(msgs, customMessageTypes) {
125
125
  }
126
126
 
127
127
  function textToBotonic(msg) {
128
- const txt =
129
- msg.data && msg.data.text != undefined ? msg.data.text : String(msg.data)
128
+ const txt = msg.data?.text ?? String(msg.data)
130
129
  if (
131
130
  (msg.replies && msg.replies.length) ||
132
131
  (msg.keyboard && msg.keyboard.length)
@@ -1,25 +1,27 @@
1
1
  export enum WebchatAction {
2
2
  ADD_MESSAGE = 'addMessage',
3
3
  ADD_MESSAGE_COMPONENT = 'addMessageComponent',
4
- UPDATE_MESSAGE = 'updateMessage',
5
- UPDATE_REPLIES = 'updateReplies',
6
- UPDATE_LATEST_INPUT = 'updateLatestInput',
7
- UPDATE_TYPING = 'updateTyping',
8
- UPDATE_WEBVIEW = 'updateWebview',
9
- UPDATE_SESSION = 'updateSession',
10
- UPDATE_LAST_ROUTE_PATH = 'updateLastRoutePath',
11
- UPDATE_HANDOFF = 'updateHandoff',
12
- UPDATE_THEME = 'updateTheme',
13
- UPDATE_DEV_SETTINGS = 'updateDevSettings',
14
- TOGGLE_WEBCHAT = 'toggleWebchat',
15
- TOGGLE_EMOJI_PICKER = 'toggleEmojiPicker',
16
- TOGGLE_PERSISTENT_MENU = 'togglePersistentMenu',
17
- TOGGLE_COVER_COMPONENT = 'toggleCoverComponent',
18
- DO_RENDER_CUSTOM_COMPONENT = 'doRenderCustomComponent',
19
- SET_ERROR = 'setError',
20
4
  CLEAR_MESSAGES = 'clearMessages',
21
- UPDATE_LAST_MESSAGE_DATE = 'updateLastMessageDate',
5
+ DO_RENDER_CUSTOM_COMPONENT = 'doRenderCustomComponent',
6
+ RESET_UNREAD_MESSAGES = 'resetUnreadMessages',
22
7
  SET_CURRENT_ATTACHMENT = 'setCurrentAttachment',
8
+ SET_ERROR = 'setError',
9
+ SET_LAST_MESSAGE_VISIBLE = 'setlastMessageVisible',
23
10
  SET_ONLINE = 'setOnline',
11
+ TOGGLE_COVER_COMPONENT = 'toggleCoverComponent',
12
+ TOGGLE_EMOJI_PICKER = 'toggleEmojiPicker',
13
+ TOGGLE_PERSISTENT_MENU = 'togglePersistentMenu',
14
+ TOGGLE_WEBCHAT = 'toggleWebchat',
15
+ UPDATE_DEV_SETTINGS = 'updateDevSettings',
16
+ UPDATE_HANDOFF = 'updateHandoff',
24
17
  UPDATE_JWT = 'updateJwt',
18
+ UPDATE_LAST_MESSAGE_DATE = 'updateLastMessageDate',
19
+ UPDATE_LAST_ROUTE_PATH = 'updateLastRoutePath',
20
+ UPDATE_LATEST_INPUT = 'updateLatestInput',
21
+ UPDATE_MESSAGE = 'updateMessage',
22
+ UPDATE_REPLIES = 'updateReplies',
23
+ UPDATE_SESSION = 'updateSession',
24
+ UPDATE_THEME = 'updateTheme',
25
+ UPDATE_TYPING = 'updateTyping',
26
+ UPDATE_WEBVIEW = 'updateWebview',
25
27
  }
@@ -8,7 +8,7 @@ import { COLORS } from '../../constants'
8
8
 
9
9
  export const StyledScrollbar = styled(SimpleBar)`
10
10
  ${props =>
11
- props.ismessagescontainer &&
11
+ props.isMessagesContainer &&
12
12
  css`
13
13
  display: flex;
14
14
  flex-direction: column;
@@ -22,7 +22,7 @@ export class DeviceAdapter {
22
22
 
23
23
  onFocus(host) {
24
24
  if (this.currentDevice !== DEVICES.MOBILE.IPHONE) {
25
- scrollToBottom({ host, timeout: 800 })
25
+ // scrollToBottom({ host, timeout: 800 })
26
26
  return
27
27
  }
28
28
  setTimeout(() => {
@@ -29,7 +29,7 @@ export class WebchatResizer {
29
29
  }
30
30
  setTimeout(() => {
31
31
  this.setWebchatElementHeight(`${calculateNewWebchatElementHeight()}%`)
32
- scrollToBottom(this.host)
32
+ // scrollToBottom(this.host)
33
33
  onKeyboardShownFn()
34
34
  }, waitUntilKeyboardIsShown)
35
35
  }
@@ -15,11 +15,11 @@ export function useTyping({
15
15
  webchatState,
16
16
  updateTyping,
17
17
  updateMessage,
18
- host,
19
- }: UseTyping): void {
18
+ }: // host,
19
+ UseTyping): void {
20
20
  useEffect(() => {
21
21
  let delayTimeout, typingTimeout
22
- scrollToBottom({ host })
22
+ // scrollToBottom({ host })
23
23
  try {
24
24
  const nextMsg = webchatState.messagesJSON.filter(m => !m.display)[0]
25
25
  if (nextMsg.delay && nextMsg.typing) {
@@ -43,7 +43,8 @@ export const webchatInitialState: WebchatState = {
43
43
  lastMessageUpdate: undefined,
44
44
  currentAttachment: undefined,
45
45
  jwt: undefined,
46
- unreadMessages: 0,
46
+ numUnreadMessages: 0,
47
+ isLastMessageVisible: true,
47
48
  }
48
49
 
49
50
  export function useWebchat() {
@@ -55,7 +56,7 @@ export function useWebchat() {
55
56
  const addMessage = (message: WebchatMessage) =>
56
57
  webchatDispatch({ type: WebchatAction.ADD_MESSAGE, payload: message })
57
58
 
58
- const addMessageComponent = (message: WebchatMessage) =>
59
+ const addMessageComponent = (message: { props: WebchatMessage }) =>
59
60
  webchatDispatch({
60
61
  type: WebchatAction.ADD_MESSAGE_COMPONENT,
61
62
  payload: message,
@@ -183,13 +184,28 @@ export function useWebchat() {
183
184
  })
184
185
  }
185
186
 
187
+ const resetUnreadMessages = () => {
188
+ webchatDispatch({
189
+ type: WebchatAction.RESET_UNREAD_MESSAGES,
190
+ })
191
+ }
192
+
193
+ const setLastMessageVisible = (isLastMessageVisible: boolean) => {
194
+ webchatDispatch({
195
+ type: WebchatAction.SET_LAST_MESSAGE_VISIBLE,
196
+ payload: isLastMessageVisible,
197
+ })
198
+ }
199
+
186
200
  return {
187
201
  addMessage,
188
202
  addMessageComponent,
189
203
  clearMessages,
190
204
  doRenderCustomComponent,
205
+ resetUnreadMessages,
191
206
  setCurrentAttachment,
192
207
  setError,
208
+ setLastMessageVisible,
193
209
  setOnline,
194
210
  toggleCoverComponent,
195
211
  toggleEmojiPicker,
@@ -48,7 +48,8 @@ export interface WebchatState {
48
48
  lastMessageUpdate?: string
49
49
  currentAttachment?: File
50
50
  jwt?: string
51
- unreadMessages: number
51
+ numUnreadMessages: number
52
+ isLastMessageVisible: boolean
52
53
  }
53
54
 
54
55
  // export interface WebchatProps extends WebchatArgs {
@@ -0,0 +1,103 @@
1
+ import React, { useContext, useEffect, useRef, useState } from 'react'
2
+
3
+ import { ROLES, WEBCHAT } from '../../constants'
4
+ import { WebchatContext } from '../../contexts'
5
+ import { scrollToBottom } from '../../util'
6
+ import { StyledScrollbar } from '../components/styled-scrollbar'
7
+ import { TypingIndicator } from '../components/typing-indicator'
8
+ import { IntroMessage } from './intro-message'
9
+ import { ScrollButton } from './scroll-button'
10
+ import { ContainerMessage } from './styles'
11
+ import { UnreadMessagesBanner } from './unread-messages-banner'
12
+
13
+ export const WebchatMessageList = props => {
14
+ const {
15
+ webchatState,
16
+ getThemeProperty,
17
+ resetUnreadMessages,
18
+ setLastMessageVisible,
19
+ } = useContext(WebchatContext)
20
+
21
+ const scrollbarOptions = {
22
+ ...{ enable: true, autoHide: true },
23
+ ...getThemeProperty(WEBCHAT.CUSTOM_PROPERTIES.scrollbar),
24
+ }
25
+
26
+ const [firstUnreadMessageId, setFirstUnreadMessageId] = useState()
27
+ useEffect(() => {
28
+ const firstUnreadMessage = webchatState.messagesComponents.find(
29
+ message => message.props.isUnread
30
+ )
31
+ setFirstUnreadMessageId(firstUnreadMessage?.props?.id)
32
+ }, [webchatState.messagesComponents])
33
+
34
+ const lastMessageRef = useRef(null)
35
+ useEffect(() => {
36
+ if (webchatState.messagesComponents.length > 0 && lastMessageRef.current) {
37
+ const observer = new IntersectionObserver(entries => {
38
+ entries.forEach(entry => {
39
+ setLastMessageVisible(entry.isIntersecting)
40
+ })
41
+ })
42
+ observer.observe(lastMessageRef.current)
43
+ }
44
+ }, [webchatState.messagesComponents])
45
+
46
+ useEffect(() => {
47
+ if (webchatState.isLastMessageVisible && webchatState.typing) {
48
+ scrollToBottom({ host: props.host })
49
+ }
50
+ if (webchatState.isLastMessageVisible) {
51
+ scrollToBottom({ host: props.host })
52
+ }
53
+ }, [webchatState.typing, webchatState.isLastMessageVisible])
54
+
55
+ const handleScrollToBottom = () => {
56
+ resetUnreadMessages()
57
+ scrollToBottom({ host: props.host })
58
+ }
59
+
60
+ const showUnreadMessagesBanner = (messageComponentId: string) =>
61
+ firstUnreadMessageId &&
62
+ messageComponentId === firstUnreadMessageId &&
63
+ webchatState.numUnreadMessages > 0
64
+
65
+ const showScrollButton =
66
+ webchatState.numUnreadMessages > 0 && !webchatState.isLastMessageVisible
67
+
68
+ return (
69
+ <>
70
+ <StyledScrollbar
71
+ role={ROLES.MESSAGE_LIST}
72
+ // TODO: Distinguis between multiple instances of webchat, e.g. `${uniqueId}-botonic-scrollable`
73
+ id='botonic-scrollable-content'
74
+ scrollbar={scrollbarOptions}
75
+ autoHide={scrollbarOptions.autoHide}
76
+ isMessagesContainer={true}
77
+ style={{
78
+ ...props.style,
79
+ }}
80
+ >
81
+ <IntroMessage />
82
+ {webchatState.messagesComponents.map((messageComponent, index) => {
83
+ return (
84
+ <ContainerMessage role={ROLES.MESSAGE} key={index}>
85
+ {showUnreadMessagesBanner(messageComponent.props.id) && (
86
+ <UnreadMessagesBanner
87
+ numUnreadMessages={webchatState.numUnreadMessages}
88
+ />
89
+ )}
90
+
91
+ {index === webchatState.messagesComponents.length - 1 && (
92
+ <div ref={lastMessageRef} style={{ content: '' }}></div>
93
+ )}
94
+ {messageComponent}
95
+ </ContainerMessage>
96
+ )
97
+ })}
98
+ {webchatState.typing && <TypingIndicator />}
99
+ </StyledScrollbar>
100
+ {showScrollButton && <ScrollButton handleClick={handleScrollToBottom} />}
101
+ </>
102
+ )
103
+ }
@@ -0,0 +1,38 @@
1
+ import React, { useContext } from 'react'
2
+ import Fade from 'react-reveal/Fade'
3
+
4
+ import { WEBCHAT } from '../../constants'
5
+ import { WebchatContext } from '../../contexts'
6
+ import { resolveImage } from '../../util/environment'
7
+ import { ConditionalWrapper } from '../../util/react'
8
+ import { DefaultIntroImage } from './styles'
9
+
10
+ export const IntroMessage = () => {
11
+ const { getThemeProperty } = useContext(WebchatContext)
12
+
13
+ const animationsEnabled = getThemeProperty(
14
+ WEBCHAT.CUSTOM_PROPERTIES.enableAnimations,
15
+ true
16
+ )
17
+ const CustomIntro = getThemeProperty(WEBCHAT.CUSTOM_PROPERTIES.customIntro)
18
+ const introImage = getThemeProperty(WEBCHAT.CUSTOM_PROPERTIES.introImage)
19
+ const introStyle = getThemeProperty(WEBCHAT.CUSTOM_PROPERTIES.introStyle)
20
+
21
+ const DefaultIntro = introImage && (
22
+ <DefaultIntroImage
23
+ style={{
24
+ ...introStyle,
25
+ }}
26
+ src={resolveImage(introImage)}
27
+ />
28
+ )
29
+
30
+ return CustomIntro || DefaultIntro ? (
31
+ <ConditionalWrapper
32
+ condition={animationsEnabled}
33
+ wrapper={children => <Fade>{children}</Fade>}
34
+ >
35
+ {CustomIntro ? <CustomIntro /> : DefaultIntro}
36
+ </ConditionalWrapper>
37
+ ) : null
38
+ }
@@ -0,0 +1,41 @@
1
+ import React, { useContext, useEffect, useState } from 'react'
2
+
3
+ import ArrowScrollDown from '../../assets/arrow-scroll-down.svg'
4
+ import { WEBCHAT } from '../../constants'
5
+ import { WebchatContext } from '../../contexts'
6
+ import { resolveImage } from '../../util/environment'
7
+ import { ContainerScrollButton } from './styles'
8
+
9
+ interface ScrollButtonProps {
10
+ handleClick: () => void
11
+ }
12
+
13
+ export const ScrollButton = ({
14
+ handleClick,
15
+ }: ScrollButtonProps): JSX.Element => {
16
+ const { getThemeProperty } = useContext(WebchatContext)
17
+
18
+ const CustomScrollButton = getThemeProperty(
19
+ WEBCHAT.CUSTOM_PROPERTIES.scrollButtonCustom,
20
+ undefined
21
+ )
22
+
23
+ const scrollButtonEnabled = getThemeProperty(
24
+ WEBCHAT.CUSTOM_PROPERTIES.scrollButtonEnabled,
25
+ false
26
+ )
27
+
28
+ return (
29
+ scrollButtonEnabled && (
30
+ <>
31
+ {CustomScrollButton ? (
32
+ <CustomScrollButton />
33
+ ) : (
34
+ <ContainerScrollButton onClick={handleClick}>
35
+ <img src={resolveImage(ArrowScrollDown)} />
36
+ </ContainerScrollButton>
37
+ )}
38
+ </>
39
+ )
40
+ )
41
+ }