@botonic/react 1.0.0-dev.1 → 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.
- package/lib/components/button.js +2 -0
- package/lib/components/button.js.map +1 -1
- package/lib/components/components.js +1 -1
- package/lib/components/image.js +47 -5
- package/lib/components/image.js.map +1 -1
- package/lib/components/index.js +6 -6
- package/lib/components/markdown.js +1 -1
- package/lib/components/multichannel/multichannel-utils.js +8 -6
- package/lib/components/multichannel/multichannel-utils.js.map +1 -1
- package/lib/components/timestamps.js +1 -1
- package/lib/constants.js +2 -1
- package/lib/constants.js.map +1 -1
- package/lib/dev-app.js +7 -6
- package/lib/dev-app.js.map +1 -1
- package/lib/index.d.ts +3 -0
- package/lib/index.js +20 -20
- package/lib/message-utils.js +1 -1
- package/lib/util/dom.js +8 -2
- package/lib/util/dom.js.map +1 -1
- package/lib/util/environment.js +1 -1
- package/lib/util/objects.js +1 -1
- package/lib/util/react.js +1 -1
- package/lib/util/webchat.js +1 -1
- package/lib/webchat/actions.js +3 -1
- package/lib/webchat/actions.js.map +1 -1
- package/lib/webchat/devices/device-adapter.js +14 -4
- package/lib/webchat/devices/device-adapter.js.map +1 -1
- package/lib/webchat/devices/scrollbar-controller.js +5 -3
- package/lib/webchat/devices/scrollbar-controller.js.map +1 -1
- package/lib/webchat/header.js +1 -1
- package/lib/webchat/hooks.js +14 -4
- package/lib/webchat/hooks.js.map +1 -1
- package/lib/webchat/index.js +1 -1
- package/lib/webchat/webchat-reducer.js +6 -0
- package/lib/webchat/webchat-reducer.js.map +1 -1
- package/lib/webchat/webchat.js +21 -4
- package/lib/webchat/webchat.js.map +1 -1
- package/lib/webchat/webview.js +1 -1
- package/lib/webchat-app.js +20 -9
- package/lib/webchat-app.js.map +1 -1
- package/package.json +6 -6
- package/src/components/button.jsx +2 -0
- package/src/dev-app.jsx +8 -3
- package/src/experimental/components/custom-message.jsx +2 -1
- package/src/experimental/components/message.jsx +14 -7
- package/src/experimental/constants.js +189 -0
- package/src/experimental/contexts.jsx +36 -0
- package/src/experimental/dev-app.jsx +14 -7
- package/src/experimental/index.js +32 -20
- package/src/experimental/msg-to-botonic.jsx +9 -9
- package/src/experimental/react-bot.jsx +1 -1
- package/src/experimental/util/dom.js +55 -0
- package/src/experimental/util/objects.js +39 -0
- package/src/experimental/util/webchat.js +61 -0
- package/src/experimental/webchat/actions.jsx +24 -0
- package/src/experimental/webchat/hooks.js +296 -0
- package/src/experimental/webchat/messages-reducer.js +86 -0
- package/src/experimental/webchat/session-view.jsx +166 -0
- package/src/experimental/webchat/webchat-dev.jsx +23 -19
- package/src/experimental/webchat/webchat-reducer.js +68 -0
- package/src/experimental/webchat/webchat.jsx +107 -75
- package/src/experimental/webchat-app.jsx +34 -15
- package/src/index.d.ts +1 -0
- package/src/util/dom.js +5 -3
- package/src/webchat/devices/device-adapter.js +16 -4
- package/src/webchat/devices/scrollbar-controller.js +9 -4
- package/src/webchat/webchat.jsx +2 -1
- package/src/webchat-app.jsx +6 -4
- package/lib/experimental/components/audio.js +0 -46
- package/lib/experimental/components/audio.js.map +0 -1
- package/lib/experimental/components/carousel.js +0 -194
- package/lib/experimental/components/carousel.js.map +0 -1
- package/lib/experimental/components/custom-message.js +0 -132
- package/lib/experimental/components/custom-message.js.map +0 -1
- package/lib/experimental/components/document.js +0 -69
- package/lib/experimental/components/document.js.map +0 -1
- package/lib/experimental/components/image.js +0 -47
- package/lib/experimental/components/image.js.map +0 -1
- package/lib/experimental/components/index.js +0 -184
- package/lib/experimental/components/index.js.map +0 -1
- package/lib/experimental/components/location.js +0 -54
- package/lib/experimental/components/location.js.map +0 -1
- package/lib/experimental/components/markdown.js +0 -103
- package/lib/experimental/components/markdown.js.map +0 -1
- package/lib/experimental/components/message.js +0 -353
- package/lib/experimental/components/message.js.map +0 -1
- package/lib/experimental/components/text.js +0 -80
- package/lib/experimental/components/text.js.map +0 -1
- package/lib/experimental/components/video.js +0 -49
- package/lib/experimental/components/video.js.map +0 -1
- package/lib/experimental/components/whatsapp-template.js +0 -53
- package/lib/experimental/components/whatsapp-template.js.map +0 -1
- package/lib/experimental/dev-app.js +0 -240
- package/lib/experimental/dev-app.js.map +0 -1
- package/lib/experimental/index.js +0 -1022
- package/lib/experimental/index.js.map +0 -1
- package/lib/experimental/msg-to-botonic.js +0 -162
- package/lib/experimental/msg-to-botonic.js.map +0 -1
- package/lib/experimental/node-app.js +0 -97
- package/lib/experimental/node-app.js.map +0 -1
- package/lib/experimental/react-bot.js +0 -167
- package/lib/experimental/react-bot.js.map +0 -1
- package/lib/experimental/webchat/assets/Inter-VariableFont_slnt,wght.ttf +0 -0
- package/lib/experimental/webchat/assets/botonic-logo-white.svg +0 -16
- package/lib/experimental/webchat/assets/messenger.svg +0 -10
- package/lib/experimental/webchat/assets/open-new-window.svg +0 -3
- package/lib/experimental/webchat/assets/open.svg +0 -3
- package/lib/experimental/webchat/assets/telegram.svg +0 -10
- package/lib/experimental/webchat/assets/webchat.svg +0 -21
- package/lib/experimental/webchat/assets/whatsapp.svg +0 -4
- package/lib/experimental/webchat/webchat-dev.js +0 -300
- package/lib/experimental/webchat/webchat-dev.js.map +0 -1
- package/lib/experimental/webchat/webchat.js +0 -1051
- package/lib/experimental/webchat/webchat.js.map +0 -1
- package/lib/experimental/webchat-app.js +0 -642
- 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 {
|
|
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
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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,
|