@botonic/react 1.0.0-beta.0 → 1.0.0-dev.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/lib/components/button.js +2 -0
  2. package/lib/components/button.js.map +1 -1
  3. package/lib/components/components.js +1 -1
  4. package/lib/components/image.js +47 -5
  5. package/lib/components/image.js.map +1 -1
  6. package/lib/components/index.js +6 -6
  7. package/lib/components/markdown.js +1 -1
  8. package/lib/components/multichannel/multichannel-utils.js +8 -6
  9. package/lib/components/multichannel/multichannel-utils.js.map +1 -1
  10. package/lib/components/timestamps.js +1 -1
  11. package/lib/constants.js +2 -1
  12. package/lib/constants.js.map +1 -1
  13. package/lib/dev-app.js +7 -6
  14. package/lib/dev-app.js.map +1 -1
  15. package/lib/index.d.ts +3 -0
  16. package/lib/index.js +20 -20
  17. package/lib/message-utils.js +1 -1
  18. package/lib/util/dom.js +8 -2
  19. package/lib/util/dom.js.map +1 -1
  20. package/lib/util/environment.js +1 -1
  21. package/lib/util/objects.js +1 -1
  22. package/lib/util/react.js +1 -1
  23. package/lib/util/webchat.js +1 -1
  24. package/lib/webchat/actions.js +5 -1
  25. package/lib/webchat/actions.js.map +1 -1
  26. package/lib/webchat/devices/device-adapter.js +14 -4
  27. package/lib/webchat/devices/device-adapter.js.map +1 -1
  28. package/lib/webchat/devices/scrollbar-controller.js +5 -3
  29. package/lib/webchat/devices/scrollbar-controller.js.map +1 -1
  30. package/lib/webchat/header.js +1 -1
  31. package/lib/webchat/hooks.js +25 -6
  32. package/lib/webchat/hooks.js.map +1 -1
  33. package/lib/webchat/index.js +1 -1
  34. package/lib/webchat/webchat-reducer.js +11 -0
  35. package/lib/webchat/webchat-reducer.js.map +1 -1
  36. package/lib/webchat/webchat.js +21 -4
  37. package/lib/webchat/webchat.js.map +1 -1
  38. package/lib/webchat/webview.js +1 -1
  39. package/lib/webchat-app.js +20 -9
  40. package/lib/webchat-app.js.map +1 -1
  41. package/package.json +6 -6
  42. package/src/components/button.jsx +2 -0
  43. package/src/dev-app.jsx +8 -3
  44. package/src/experimental/components/message.jsx +8 -2
  45. package/src/experimental/constants.js +189 -0
  46. package/src/experimental/contexts.jsx +36 -0
  47. package/src/experimental/dev-app.jsx +14 -7
  48. package/src/experimental/index.js +94 -25
  49. package/src/experimental/msg-to-botonic.jsx +8 -8
  50. package/src/experimental/react-bot.jsx +1 -1
  51. package/src/experimental/util/dom.js +55 -0
  52. package/src/experimental/util/objects.js +39 -0
  53. package/src/experimental/util/webchat.js +61 -0
  54. package/src/experimental/webchat/actions.jsx +24 -0
  55. package/src/experimental/webchat/hooks.js +296 -0
  56. package/src/experimental/webchat/messages-reducer.js +86 -0
  57. package/src/experimental/webchat/session-view.jsx +166 -0
  58. package/src/experimental/webchat/webchat-dev.jsx +23 -19
  59. package/src/experimental/webchat/webchat-reducer.js +68 -0
  60. package/src/experimental/webchat/webchat.jsx +109 -82
  61. package/src/experimental/webchat-app.jsx +39 -15
  62. package/src/index.d.ts +1 -0
  63. package/src/util/dom.js +5 -3
  64. package/src/webchat/actions.jsx +1 -0
  65. package/src/webchat/devices/device-adapter.js +16 -4
  66. package/src/webchat/devices/scrollbar-controller.js +9 -4
  67. package/src/webchat/hooks.js +10 -0
  68. package/src/webchat/webchat-reducer.js +3 -0
  69. package/src/webchat/webchat.jsx +2 -1
  70. package/src/webchat-app.jsx +6 -4
  71. package/lib/experimental/components/audio.js +0 -46
  72. package/lib/experimental/components/audio.js.map +0 -1
  73. package/lib/experimental/components/carousel.js +0 -194
  74. package/lib/experimental/components/carousel.js.map +0 -1
  75. package/lib/experimental/components/custom-message.js +0 -132
  76. package/lib/experimental/components/custom-message.js.map +0 -1
  77. package/lib/experimental/components/document.js +0 -69
  78. package/lib/experimental/components/document.js.map +0 -1
  79. package/lib/experimental/components/image.js +0 -47
  80. package/lib/experimental/components/image.js.map +0 -1
  81. package/lib/experimental/components/index.js +0 -184
  82. package/lib/experimental/components/index.js.map +0 -1
  83. package/lib/experimental/components/location.js +0 -54
  84. package/lib/experimental/components/location.js.map +0 -1
  85. package/lib/experimental/components/markdown.js +0 -103
  86. package/lib/experimental/components/markdown.js.map +0 -1
  87. package/lib/experimental/components/message.js +0 -353
  88. package/lib/experimental/components/message.js.map +0 -1
  89. package/lib/experimental/components/text.js +0 -80
  90. package/lib/experimental/components/text.js.map +0 -1
  91. package/lib/experimental/components/video.js +0 -49
  92. package/lib/experimental/components/video.js.map +0 -1
  93. package/lib/experimental/components/whatsapp-template.js +0 -53
  94. package/lib/experimental/components/whatsapp-template.js.map +0 -1
  95. package/lib/experimental/dev-app.js +0 -240
  96. package/lib/experimental/dev-app.js.map +0 -1
  97. package/lib/experimental/index.js +0 -748
  98. package/lib/experimental/index.js.map +0 -1
  99. package/lib/experimental/msg-to-botonic.js +0 -162
  100. package/lib/experimental/msg-to-botonic.js.map +0 -1
  101. package/lib/experimental/node-app.js +0 -97
  102. package/lib/experimental/node-app.js.map +0 -1
  103. package/lib/experimental/react-bot.js +0 -167
  104. package/lib/experimental/react-bot.js.map +0 -1
  105. package/lib/experimental/webchat/assets/Inter-VariableFont_slnt,wght.ttf +0 -0
  106. package/lib/experimental/webchat/assets/botonic-logo-white.svg +0 -16
  107. package/lib/experimental/webchat/assets/messenger.svg +0 -10
  108. package/lib/experimental/webchat/assets/open-new-window.svg +0 -3
  109. package/lib/experimental/webchat/assets/open.svg +0 -3
  110. package/lib/experimental/webchat/assets/telegram.svg +0 -10
  111. package/lib/experimental/webchat/assets/webchat.svg +0 -21
  112. package/lib/experimental/webchat/assets/whatsapp.svg +0 -4
  113. package/lib/experimental/webchat/webchat-dev.js +0 -300
  114. package/lib/experimental/webchat/webchat-dev.js.map +0 -1
  115. package/lib/experimental/webchat/webchat.js +0 -1044
  116. package/lib/experimental/webchat/webchat.js.map +0 -1
  117. package/lib/experimental/webchat-app.js +0 -635
  118. package/lib/experimental/webchat-app.js.map +0 -1
@@ -0,0 +1,61 @@
1
+ import { Channels } from '@botonic/core'
2
+ import merge from 'lodash.merge'
3
+ import UAParser from 'ua-parser-js'
4
+ import { v4 as uuidv4 } from 'uuid'
5
+
6
+ import { isDev } from '../../util/environment'
7
+ import { WEBCHAT } from '../constants'
8
+ import { getProperty } from './objects'
9
+
10
+ /**
11
+ * Returns the value of a property defined in bot's theme based on WEBCHAT.CUSTOM_PROPERTIES dictionary.
12
+ * It gives preference to nested defined properties (e.g.: header.style) over plain properties (e.g.: headerStyle).
13
+ * If property doesn't exist, returns the defaultValue.
14
+ */
15
+ export const _getThemeProperty = theme => (
16
+ property,
17
+ defaultValue = undefined
18
+ ) => {
19
+ for (const [k, v] of Object.entries(WEBCHAT.CUSTOM_PROPERTIES)) {
20
+ if (v == property) {
21
+ const nestedProperty = getProperty(theme, v)
22
+ if (nestedProperty !== undefined) return nestedProperty
23
+ const plainProperty = getProperty(theme, k)
24
+ if (plainProperty !== undefined) return plainProperty
25
+ return defaultValue
26
+ }
27
+ }
28
+ return undefined
29
+ }
30
+
31
+ export const createUser = () => {
32
+ const parser = new UAParser()
33
+ const ua = parser.getResult()
34
+ let name = `${ua.os.name} ${ua.browser.name}`
35
+ if (ua.device && ua.device.type) name = `${ua.device.type} ${name}`
36
+
37
+ return {
38
+ id: uuidv4(),
39
+ name,
40
+ channel: isDev ? Channels.DEV : Channels.WEBCHAT,
41
+ }
42
+ }
43
+
44
+ export const initUser = user => {
45
+ if (!user) return createUser()
46
+ if (user && !user.id) return merge(user, createUser())
47
+ return user
48
+ }
49
+
50
+ export const shouldKeepSessionOnReload = ({
51
+ initialDevSettings,
52
+ devSettings,
53
+ }) => !initialDevSettings || (devSettings && devSettings.keepSessionOnReload)
54
+
55
+ export const getServerErrorMessage = serverConfig => {
56
+ if (!serverConfig || !serverConfig.errorMessage) return 'Connection issues'
57
+ if (typeof serverConfig.errorMessage === 'function') {
58
+ return serverConfig.errorMessage()
59
+ }
60
+ return serverConfig.errorMessage
61
+ }
@@ -0,0 +1,24 @@
1
+ export const ADD_MESSAGE = 'addMessage'
2
+ export const ADD_MESSAGE_COMPONENT = 'addMessageComponent'
3
+ export const UPDATE_MESSAGE = 'updateMessage'
4
+ export const UPDATE_REPLIES = 'updateReplies'
5
+ export const UPDATE_LATEST_INPUT = 'updateLatestInput'
6
+ export const UPDATE_TYPING = 'updateTyping'
7
+ export const UPDATE_WEBVIEW = 'updateWebview'
8
+ export const UPDATE_SESSION = 'updateSession'
9
+ export const UPDATE_LAST_ROUTE_PATH = 'updateLastRoutePath'
10
+ export const UPDATE_HANDOFF = 'updateHandoff'
11
+ export const UPDATE_THEME = 'updateTheme'
12
+ export const UPDATE_DEV_SETTINGS = 'updateDevSettings'
13
+ export const TOGGLE_WEBCHAT = 'toggleWebchat'
14
+ export const TOGGLE_EMOJI_PICKER = 'toggleEmojiPicker'
15
+ export const TOGGLE_PERSISTENT_MENU = 'togglePersistentMenu'
16
+ export const TOGGLE_COVER_COMPONENT = 'toggleCoverComponent'
17
+ export const SET_ERROR = 'setError'
18
+ export const CLEAR_MESSAGES = 'clearMessages'
19
+ export const UPDATE_LAST_MESSAGE_DATE = 'updateLastMessageDate'
20
+ export const SET_CURRENT_ATTACHMENT = 'setCurrentAttachment'
21
+ export const SET_ONLINE = 'setOnline'
22
+ export const UPDATE_JWT = 'updateJwt'
23
+ export const UPDATE_USER = 'updateUser'
24
+ export const UPDATE_BOT_STATE = 'updateBotState'
@@ -0,0 +1,296 @@
1
+ import { useEffect, useMemo, useReducer, useRef, useState } from 'react'
2
+
3
+ import { COLORS, WEBCHAT } from '../constants'
4
+ import { scrollToBottom } from '../util/dom'
5
+ import {
6
+ ADD_MESSAGE,
7
+ ADD_MESSAGE_COMPONENT,
8
+ CLEAR_MESSAGES,
9
+ SET_CURRENT_ATTACHMENT,
10
+ SET_ERROR,
11
+ SET_ONLINE,
12
+ TOGGLE_COVER_COMPONENT,
13
+ TOGGLE_EMOJI_PICKER,
14
+ TOGGLE_PERSISTENT_MENU,
15
+ TOGGLE_WEBCHAT,
16
+ UPDATE_BOT_STATE,
17
+ UPDATE_DEV_SETTINGS,
18
+ UPDATE_HANDOFF,
19
+ UPDATE_JWT,
20
+ UPDATE_LAST_MESSAGE_DATE,
21
+ UPDATE_LAST_ROUTE_PATH,
22
+ UPDATE_LATEST_INPUT,
23
+ UPDATE_MESSAGE,
24
+ UPDATE_REPLIES,
25
+ UPDATE_SESSION,
26
+ UPDATE_THEME,
27
+ UPDATE_TYPING,
28
+ UPDATE_USER,
29
+ UPDATE_WEBVIEW,
30
+ } from './actions'
31
+ import { webchatReducer } from './webchat-reducer'
32
+
33
+ export const initialUser = {
34
+ id: undefined,
35
+ name: undefined,
36
+ userName: undefined,
37
+ channel: undefined,
38
+ idFromChannel: undefined,
39
+ isOnline: true,
40
+ }
41
+
42
+ const initialBotState = {
43
+ botId: undefined,
44
+ lastRoutePath: null,
45
+ isFirstInteraction: true,
46
+ retries: 0,
47
+ isHandoff: false,
48
+ isShadowing: false,
49
+ }
50
+
51
+ const initialSession = {}
52
+
53
+ export const webchatInitialState = {
54
+ width: WEBCHAT.DEFAULTS.WIDTH,
55
+ height: WEBCHAT.DEFAULTS.HEIGHT,
56
+ messagesJSON: [],
57
+ messagesComponents: [],
58
+ replies: [],
59
+ latestInput: {},
60
+ typing: false,
61
+ webview: null,
62
+ webviewParams: null,
63
+ // session: { user: null },
64
+ // lastRoutePath: null,
65
+ // handoff: false,
66
+ theme: {
67
+ headerTitle: WEBCHAT.DEFAULTS.TITLE,
68
+ brandColor: COLORS.BOTONIC_BLUE,
69
+ brandImage: WEBCHAT.DEFAULTS.LOGO,
70
+ triggerButtonImage: undefined,
71
+ textPlaceholder: WEBCHAT.DEFAULTS.PLACEHOLDER,
72
+ style: {
73
+ fontFamily: WEBCHAT.DEFAULTS.FONT_FAMILY,
74
+ },
75
+ },
76
+ themeUpdates: {},
77
+ error: {},
78
+ isWebchatOnline: true,
79
+ devSettings: { keepSessionOnReload: false },
80
+ isWebchatOpen: false,
81
+ isEmojiPickerOpen: false,
82
+ isPersistentMenuOpen: false,
83
+ isCoverComponentOpen: false,
84
+ lastMessageUpdate: undefined,
85
+ currentAttachment: undefined,
86
+ jwt: null,
87
+ user: initialUser,
88
+ session: initialSession,
89
+ botState: initialBotState,
90
+ }
91
+
92
+ export function useWebchat() {
93
+ const [webchatState, webchatDispatch] = useReducer(
94
+ webchatReducer,
95
+ webchatInitialState
96
+ )
97
+
98
+ const addMessage = message =>
99
+ webchatDispatch({ type: ADD_MESSAGE, payload: message })
100
+ const addMessageComponent = message =>
101
+ webchatDispatch({ type: ADD_MESSAGE_COMPONENT, payload: message })
102
+ const updateMessage = message =>
103
+ webchatDispatch({ type: UPDATE_MESSAGE, payload: message })
104
+ const updateReplies = replies =>
105
+ webchatDispatch({ type: UPDATE_REPLIES, payload: replies })
106
+ const updateLatestInput = input =>
107
+ webchatDispatch({ type: UPDATE_LATEST_INPUT, payload: input })
108
+ const updateTyping = typing =>
109
+ webchatDispatch({ type: UPDATE_TYPING, payload: typing })
110
+ const updateWebview = (webview, params) =>
111
+ webchatDispatch({
112
+ type: UPDATE_WEBVIEW,
113
+ payload: { webview, webviewParams: params },
114
+ })
115
+ const updateSession = session =>
116
+ webchatDispatch({
117
+ type: UPDATE_SESSION,
118
+ payload: session,
119
+ })
120
+
121
+ const updateUser = user =>
122
+ webchatDispatch({
123
+ type: UPDATE_USER,
124
+ payload: user,
125
+ })
126
+
127
+ const updateBotState = botState =>
128
+ webchatDispatch({
129
+ type: UPDATE_BOT_STATE,
130
+ payload: botState,
131
+ })
132
+
133
+ const updateLastRoutePath = path =>
134
+ webchatDispatch({
135
+ type: UPDATE_LAST_ROUTE_PATH,
136
+ payload: path,
137
+ })
138
+ const updateHandoff = handoff =>
139
+ webchatDispatch({
140
+ type: UPDATE_HANDOFF,
141
+ payload: handoff,
142
+ })
143
+ const updateTheme = (theme, themeUpdates = undefined) => {
144
+ const payload =
145
+ themeUpdates !== undefined ? { theme, themeUpdates } : { theme }
146
+ webchatDispatch({
147
+ type: UPDATE_THEME,
148
+ payload,
149
+ })
150
+ }
151
+ const updateDevSettings = settings =>
152
+ webchatDispatch({
153
+ type: UPDATE_DEV_SETTINGS,
154
+ payload: settings,
155
+ })
156
+ const toggleWebchat = toggle =>
157
+ webchatDispatch({
158
+ type: TOGGLE_WEBCHAT,
159
+ payload: toggle,
160
+ })
161
+ const toggleEmojiPicker = toggle =>
162
+ webchatDispatch({
163
+ type: TOGGLE_EMOJI_PICKER,
164
+ payload: toggle,
165
+ })
166
+ const togglePersistentMenu = toggle =>
167
+ webchatDispatch({
168
+ type: TOGGLE_PERSISTENT_MENU,
169
+ payload: toggle,
170
+ })
171
+ const toggleCoverComponent = toggle =>
172
+ webchatDispatch({
173
+ type: TOGGLE_COVER_COMPONENT,
174
+ payload: toggle,
175
+ })
176
+ const setError = error =>
177
+ webchatDispatch({
178
+ type: SET_ERROR,
179
+ payload: error,
180
+ })
181
+ const setOnline = online =>
182
+ webchatDispatch({
183
+ type: SET_ONLINE,
184
+ payload: online,
185
+ })
186
+
187
+ const clearMessages = () => {
188
+ webchatDispatch({
189
+ type: CLEAR_MESSAGES,
190
+ })
191
+ }
192
+ const updateLastMessageDate = date => {
193
+ webchatDispatch({
194
+ type: UPDATE_LAST_MESSAGE_DATE,
195
+ payload: date,
196
+ })
197
+ }
198
+ const setCurrentAttachment = attachment => {
199
+ webchatDispatch({
200
+ type: SET_CURRENT_ATTACHMENT,
201
+ payload: attachment,
202
+ })
203
+ }
204
+
205
+ const updateJwt = jwt => {
206
+ webchatDispatch({
207
+ type: UPDATE_JWT,
208
+ payload: jwt,
209
+ })
210
+ }
211
+
212
+ return {
213
+ webchatState,
214
+ webchatDispatch,
215
+ addMessage,
216
+ addMessageComponent,
217
+ updateMessage,
218
+ updateReplies,
219
+ updateLatestInput,
220
+ updateTyping,
221
+ updateWebview,
222
+ updateSession,
223
+ updateLastRoutePath,
224
+ updateHandoff,
225
+ updateTheme,
226
+ updateDevSettings,
227
+ toggleWebchat,
228
+ toggleEmojiPicker,
229
+ togglePersistentMenu,
230
+ toggleCoverComponent,
231
+ setError,
232
+ setOnline,
233
+ clearMessages,
234
+ updateLastMessageDate,
235
+ setCurrentAttachment,
236
+ updateJwt,
237
+ updateBotState,
238
+ updateUser,
239
+ }
240
+ }
241
+
242
+ export function useTyping({ webchatState, updateTyping, updateMessage, host }) {
243
+ useEffect(() => {
244
+ let delayTimeout, typingTimeout
245
+ scrollToBottom({ host })
246
+ try {
247
+ const nextMsg = webchatState.messagesJSON.filter(m => !m.display)[0]
248
+ if (nextMsg.delay && nextMsg.typing) {
249
+ delayTimeout = setTimeout(
250
+ () => updateTyping(true),
251
+ nextMsg.delay * 1000
252
+ )
253
+ } else if (nextMsg.typing) updateTyping(true)
254
+ const totalDelay = nextMsg.delay + nextMsg.typing
255
+ if (totalDelay)
256
+ typingTimeout = setTimeout(() => {
257
+ updateMessage({ ...nextMsg, display: true })
258
+ updateTyping(false)
259
+ }, totalDelay * 1000)
260
+ } catch (e) {}
261
+ return () => {
262
+ clearTimeout(delayTimeout)
263
+ clearTimeout(typingTimeout)
264
+ }
265
+ }, [webchatState.messagesJSON, webchatState.typing])
266
+ }
267
+
268
+ export function usePrevious(value) {
269
+ const ref = useRef()
270
+ useEffect(() => {
271
+ ref.current = value
272
+ })
273
+ return ref.current
274
+ }
275
+
276
+ export function useComponentVisible(initialIsVisible, onClickOutside) {
277
+ const [isComponentVisible, setIsComponentVisible] = useState(initialIsVisible)
278
+ const ref = useRef(null)
279
+ const handleClickOutside = event => {
280
+ if (ref.current && !ref.current.contains(event.target)) {
281
+ setIsComponentVisible(false)
282
+ onClickOutside()
283
+ }
284
+ }
285
+ useEffect(() => {
286
+ document.addEventListener('click', handleClickOutside, false)
287
+ return () => {
288
+ document.removeEventListener('click', handleClickOutside, false)
289
+ }
290
+ })
291
+ return { ref, isComponentVisible, setIsComponentVisible }
292
+ }
293
+
294
+ export const useComponentWillMount = func => {
295
+ useMemo(func, [])
296
+ }
@@ -0,0 +1,86 @@
1
+ import {
2
+ ADD_MESSAGE,
3
+ ADD_MESSAGE_COMPONENT,
4
+ CLEAR_MESSAGES,
5
+ UPDATE_LAST_MESSAGE_DATE,
6
+ UPDATE_MESSAGE,
7
+ UPDATE_REPLIES,
8
+ } from './actions'
9
+
10
+ export const messagesReducer = (state, action) => {
11
+ switch (action.type) {
12
+ case ADD_MESSAGE:
13
+ return addMessageReducer(state, action)
14
+ case ADD_MESSAGE_COMPONENT:
15
+ return {
16
+ ...state,
17
+ messagesComponents: [
18
+ ...(state.messagesComponents || []),
19
+ action.payload,
20
+ ],
21
+ }
22
+ case UPDATE_MESSAGE:
23
+ return updateMessageReducer(state, action)
24
+ case UPDATE_REPLIES:
25
+ return { ...state, replies: action.payload }
26
+ case CLEAR_MESSAGES:
27
+ return {
28
+ ...state,
29
+ messagesJSON: [],
30
+ messagesComponents: [],
31
+ }
32
+ case UPDATE_LAST_MESSAGE_DATE:
33
+ return {
34
+ ...state,
35
+ lastMessageUpdate: action.payload,
36
+ }
37
+ default:
38
+ throw new Error()
39
+ }
40
+ }
41
+
42
+ function updateMessageReducer(state, action) {
43
+ const msgIndex = state.messagesJSON.map(m => m.id).indexOf(action.payload.id)
44
+ if (msgIndex > -1) {
45
+ const msgComponent = state.messagesComponents[msgIndex]
46
+ let updatedMessageComponents = {}
47
+ if (msgComponent) {
48
+ const updatedMsgComponent = {
49
+ ...msgComponent,
50
+ ...{
51
+ props: { ...msgComponent.props, ack: action.payload.ack },
52
+ },
53
+ }
54
+ updatedMessageComponents = {
55
+ messagesComponents: [
56
+ ...state.messagesComponents.slice(0, msgIndex),
57
+ { ...updatedMsgComponent },
58
+ ...state.messagesComponents.slice(msgIndex + 1),
59
+ ],
60
+ }
61
+ }
62
+ return {
63
+ ...state,
64
+ messagesJSON: [
65
+ ...state.messagesJSON.slice(0, msgIndex),
66
+ { ...action.payload },
67
+ ...state.messagesJSON.slice(msgIndex + 1),
68
+ ],
69
+ ...updatedMessageComponents,
70
+ }
71
+ }
72
+
73
+ return state
74
+ }
75
+
76
+ function addMessageReducer(state, action) {
77
+ if (
78
+ state.messagesJSON &&
79
+ state.messagesJSON.find(m => m.id === action.payload.id)
80
+ )
81
+ return state
82
+ return {
83
+ ...state,
84
+ messagesJSON: [...(state.messagesJSON || []), action.payload],
85
+ }
86
+ }
@@ -0,0 +1,166 @@
1
+ import React from 'react'
2
+ import JSONTree from 'react-json-tree'
3
+ import styled from 'styled-components'
4
+
5
+ import { COLORS } from '../constants'
6
+ import { useWebchat } from './hooks'
7
+
8
+ const AttributeContainer = styled.div`
9
+ display: flex;
10
+ flex: none;
11
+ padding: 12px;
12
+ padding-bottom: 0px;
13
+ font-size: 12px;
14
+ font-weight: 600;
15
+ color: ${COLORS.SOLID_WHITE};
16
+ align-items: center;
17
+ `
18
+
19
+ const Label = styled.div`
20
+ flex: none;
21
+ `
22
+
23
+ const Value = styled.div`
24
+ flex: 1 1 auto;
25
+ max-height: 20px;
26
+ font-size: 16px;
27
+ font-weight: 400;
28
+ margin-left: 6px;
29
+ color: ${COLORS.CURIOUS_BLUE};
30
+ overflow-x: hidden;
31
+ `
32
+
33
+ const SessionViewAttribute = props => (
34
+ <AttributeContainer>
35
+ <Label>{props.label}</Label>
36
+ <Value>{props.value}</Value>
37
+ </AttributeContainer>
38
+ )
39
+
40
+ const SessionViewContent = styled.div`
41
+ position: relative;
42
+ width: ${props => (props.show ? '100%' : '0%')};
43
+ height: 100%;
44
+ display: flex;
45
+ background-color: ${COLORS.DAINTREE_BLUE};
46
+ font-family: Arial, Helvetica, sans-serif;
47
+ flex-direction: column;
48
+ z-index: 100000;
49
+ transition: all 0.2s ease-in-out;
50
+ `
51
+
52
+ const ToggleTab = styled.div`
53
+ position: absolute;
54
+ top: 10px;
55
+ right: -32px;
56
+ width: 32px;
57
+ height: 32px;
58
+ cursor: pointer;
59
+ display: flex;
60
+ align-items: center;
61
+ justify-content: center;
62
+ color: ${COLORS.SOLID_WHITE_ALPHA_0_8};
63
+ font-size: 14px;
64
+ font-weight: 600;
65
+ background-color: ${COLORS.DAINTREE_BLUE};
66
+ flex-direction: column;
67
+ z-index: 100000;
68
+ border-top-right-radius: 6px;
69
+ border-bottom-right-radius: 6px;
70
+ `
71
+
72
+ const ContentContainer = styled.div`
73
+ overflow-y: auto;
74
+ overflow-x: auto;
75
+ display: flex;
76
+ flex: 1 1 auto;
77
+ flex-direction: column;
78
+ `
79
+
80
+ const Title = styled.div`
81
+ padding: 12px;
82
+ text-align: center;
83
+ color: ${COLORS.SOLID_WHITE};
84
+ border-bottom: 1px solid ${COLORS.SOLID_WHITE_ALPHA_0_2};
85
+ `
86
+
87
+ const SessionContainer = styled.div`
88
+ flex: 1 1 auto;
89
+ height: 100%;
90
+ overflow-y: auto;
91
+ `
92
+
93
+ const KeepSessionContainer = styled.div`
94
+ flex: none;
95
+ padding: 12px;
96
+ color: ${COLORS.SOLID_WHITE_ALPHA_0_8};
97
+ font-size: 12px;
98
+ `
99
+
100
+ export const SessionView = props => {
101
+ // eslint-disable-next-line react-hooks/rules-of-hooks
102
+ const { webchatState, updateDevSettings } = props.webchatHooks || useWebchat()
103
+ const { latestInput: input, session, botState } = webchatState
104
+ const { type, id, ...latestInputData } = input
105
+
106
+ const toggleSessionView = () =>
107
+ updateDevSettings({
108
+ ...webchatState.devSettings,
109
+ showSessionView: !webchatState.devSettings.showSessionView,
110
+ })
111
+ const toggleKeepSessionOnReload = () =>
112
+ updateDevSettings({
113
+ ...webchatState.devSettings,
114
+ keepSessionOnReload: !webchatState.devSettings.keepSessionOnReload,
115
+ })
116
+ return (
117
+ <SessionViewContent show={webchatState.devSettings.showSessionView}>
118
+ <ToggleTab onClick={toggleSessionView}>
119
+ {webchatState.devSettings.showSessionView ? '⇤' : '⇥'}
120
+ </ToggleTab>
121
+ <ContentContainer>
122
+ <Title>Botonic Dev Console</Title>
123
+ <SessionViewAttribute
124
+ label='INPUT:'
125
+ value={
126
+ input && Object.keys(input).length
127
+ ? `[${type}] ${JSON.stringify(latestInputData) || ''}`
128
+ : ''
129
+ }
130
+ />
131
+ <SessionViewAttribute label='PAYLOAD:' value={input.payload} />
132
+ <SessionViewAttribute
133
+ label='INTENT:'
134
+ value={
135
+ input.intent
136
+ ? `${input.intent} (${(input.confidence * 100).toFixed(1)}%)`
137
+ : ''
138
+ }
139
+ />
140
+ <SessionViewAttribute
141
+ label='PATH:'
142
+ value={botState.lastRoutePath ? `/${botState.lastRoutePath}` : '/'}
143
+ />
144
+ <SessionViewAttribute label='BOT STATE:' />
145
+ <SessionContainer>
146
+ <JSONTree data={botState} hideRoot={true} />
147
+ </SessionContainer>
148
+ <SessionViewAttribute label='SESSION:' />
149
+ <SessionContainer>
150
+ <JSONTree data={session} hideRoot={true} />
151
+ </SessionContainer>
152
+ <KeepSessionContainer>
153
+ <label>
154
+ <input
155
+ type='checkbox'
156
+ name='toggleKeepSessionOnReload'
157
+ checked={Boolean(webchatState.devSettings.keepSessionOnReload)}
158
+ onChange={toggleKeepSessionOnReload}
159
+ />
160
+ Keep session on reload
161
+ </label>
162
+ </KeepSessionContainer>
163
+ </ContentContainer>
164
+ </SessionViewContent>
165
+ )
166
+ }
@@ -1,18 +1,18 @@
1
- import { PROVIDER } from '@botonic/core'
1
+ import { Channels } from '@botonic/core'
2
2
  import merge from 'lodash.merge'
3
3
  import QRCode from 'qrcode.react'
4
4
  import React, { forwardRef, useEffect, useState } from 'react'
5
5
  import { createPortal } from 'react-dom'
6
6
  import styled from 'styled-components'
7
7
 
8
- import { useWebchat } from '../../webchat/hooks'
9
- import { SessionView } from '../../webchat/session-view'
10
8
  import MessengerLogo from './assets/messenger.svg'
11
9
  import Open from './assets/open.svg'
12
10
  import OpenNewWindow from './assets/open-new-window.svg'
13
11
  import TelegramLogo from './assets/telegram.svg'
14
12
  import WebchatLogo from './assets/webchat.svg'
15
13
  import WhatsappLogo from './assets/whatsapp.svg'
14
+ import { useWebchat } from './hooks'
15
+ import { SessionView } from './session-view'
16
16
  import { Webchat } from './webchat'
17
17
 
18
18
  export const DebugTab = styled.div`
@@ -341,22 +341,24 @@ export const PlaygroundPortal = props =>
341
341
  document.body
342
342
  )
343
343
 
344
- const initialSession = {
345
- is_first_interaction: true,
346
- last_session: {},
347
- user: {
348
- id: '000001',
349
- username: 'johndoe',
350
- name: 'John Doe',
351
- provider: PROVIDER.DEV,
352
- provider_id: '0000000',
353
- extra_data: {},
354
- },
355
- organization: '',
356
- bot: {
357
- id: '0000000',
358
- name: 'botName',
359
- },
344
+ const initialUser = {
345
+ id: '000001',
346
+ name: 'John Doe',
347
+ username: 'johndoe',
348
+ channel: Channels.DEV,
349
+ idFromChannel: '0000000',
350
+ details: {},
351
+ }
352
+
353
+ const initialSession = {}
354
+
355
+ const initialBotState = {
356
+ botId: '0000000',
357
+ isFirstInteraction: true,
358
+ retries: 0,
359
+ lastRoutePath: null,
360
+ isHandoff: false,
361
+ isShadowing: false,
360
362
  }
361
363
 
362
364
  // eslint-disable-next-line react/display-name
@@ -377,7 +379,9 @@ export const WebchatDev = forwardRef((props, ref) => {
377
379
  {...props}
378
380
  ref={ref}
379
381
  webchatHooks={webchatHooks}
382
+ initialUser={initialUser}
380
383
  initialSession={initialSession}
384
+ initialBotState={initialBotState}
381
385
  initialDevSettings={{
382
386
  keepSessionOnReload: webchatState.devSettings.keepSessionOnReload,
383
387
  showSessionView: webchatState.devSettings.showSessionView,