@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.
- 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 +5 -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 +25 -6
- package/lib/webchat/hooks.js.map +1 -1
- package/lib/webchat/index.js +1 -1
- package/lib/webchat/webchat-reducer.js +11 -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/message.jsx +8 -2
- 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 +94 -25
- package/src/experimental/msg-to-botonic.jsx +8 -8
- 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 +109 -82
- package/src/experimental/webchat-app.jsx +39 -15
- package/src/index.d.ts +1 -0
- package/src/util/dom.js +5 -3
- package/src/webchat/actions.jsx +1 -0
- package/src/webchat/devices/device-adapter.js +16 -4
- package/src/webchat/devices/scrollbar-controller.js +9 -4
- package/src/webchat/hooks.js +10 -0
- package/src/webchat/webchat-reducer.js +3 -0
- 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 -748
- 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 -1044
- package/lib/experimental/webchat/webchat.js.map +0 -1
- package/lib/experimental/webchat-app.js +0 -635
- package/lib/experimental/webchat-app.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { INPUT, isBrowser } from '@botonic/core'
|
|
2
|
-
import { MessageEventAck } from '@botonic/core/
|
|
2
|
+
import { MessageEventAck } from '@botonic/core/src/models/events/message'
|
|
3
3
|
import React, { useContext, useEffect, useState } from 'react'
|
|
4
4
|
import Fade from 'react-reveal/Fade'
|
|
5
5
|
import styled from 'styled-components'
|
|
@@ -128,7 +128,11 @@ export const Message = props => {
|
|
|
128
128
|
} = resolveMessageTimestamps(getThemeProperty, enabletimestamps)
|
|
129
129
|
|
|
130
130
|
const resolveAck = ack => {
|
|
131
|
-
//
|
|
131
|
+
// Browser app version
|
|
132
|
+
// eslint-disable-next-line no-undef
|
|
133
|
+
if (typeof FULLSTACK === 'undefined' || !FULLSTACK) {
|
|
134
|
+
return MessageEventAck.RECEIVED
|
|
135
|
+
}
|
|
132
136
|
if (ack !== undefined) return ack
|
|
133
137
|
if (isFromBot) return MessageEventAck.RECEIVED
|
|
134
138
|
return MessageEventAck.DRAFT
|
|
@@ -172,6 +176,8 @@ export const Message = props => {
|
|
|
172
176
|
display: delay + typing == 0,
|
|
173
177
|
customTypeName: decomposedChildren.customTypeName,
|
|
174
178
|
ack: ack,
|
|
179
|
+
channel: props.channel,
|
|
180
|
+
idFromChannel: props.idFromChannel,
|
|
175
181
|
}
|
|
176
182
|
addMessage(message)
|
|
177
183
|
}, [])
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import BotonicLogo from '../assets/botonic_react_logo100x100.png'
|
|
2
|
+
|
|
3
|
+
export const SENDERS = {
|
|
4
|
+
bot: 'bot',
|
|
5
|
+
user: 'user',
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const COLORS = {
|
|
9
|
+
// http://chir.ag/projects/name-that-color
|
|
10
|
+
APPLE_GREEN: 'rgba(58, 156, 53, 1)',
|
|
11
|
+
BLEACHED_CEDAR_PURPLE: 'rgba(46, 32, 59, 1)',
|
|
12
|
+
BOTONIC_BLUE: 'rgba(0, 153, 255, 1)',
|
|
13
|
+
CACTUS_GREEN: 'rgba(96, 115, 94, 1)',
|
|
14
|
+
CONCRETE_WHITE: 'rgba(243, 243, 243, 1)',
|
|
15
|
+
CURIOUS_BLUE: 'rgba(38, 139, 210, 1)',
|
|
16
|
+
DAINTREE_BLUE: 'rgba(0, 43, 53, 1)',
|
|
17
|
+
ERROR_RED: 'rgba(255, 43, 94)',
|
|
18
|
+
FRINGY_FLOWER_GREEN: 'rgba(198, 231, 192, 1)',
|
|
19
|
+
GRAY: 'rgba(129, 129, 129, 1)',
|
|
20
|
+
LIGHT_GRAY: 'rgba(209, 209, 209, 1)',
|
|
21
|
+
MID_GRAY: 'rgba(105, 105, 115, 1)',
|
|
22
|
+
PIGEON_POST_BLUE_ALPHA_0_5: 'rgba(176, 196, 222, 0.5)',
|
|
23
|
+
SCORPION_GRAY: 'rgba(87, 87, 87, 1)',
|
|
24
|
+
SEASHELL_WHITE: 'rgba(241, 240, 240, 1)',
|
|
25
|
+
SILVER: 'rgba(200, 200, 200, 1)',
|
|
26
|
+
SOLID_BLACK_ALPHA_0_2: 'rgba(0, 0, 0, 0.2)',
|
|
27
|
+
SOLID_BLACK_ALPHA_0_5: 'rgba(0, 0, 0, 0.5)',
|
|
28
|
+
SOLID_BLACK: 'rgba(0, 0, 0, 1)',
|
|
29
|
+
SOLID_WHITE_ALPHA_0_2: 'rgba(255, 255, 255, 0.2)',
|
|
30
|
+
SOLID_WHITE_ALPHA_0_8: 'rgba(255, 255, 255, 0.8)',
|
|
31
|
+
SOLID_WHITE: 'rgba(255, 255, 255, 1)',
|
|
32
|
+
TASMAN_GRAY: 'rgba(209, 216, 207, 1)',
|
|
33
|
+
TRANSPARENT: 'rgba(0, 0, 0, 0)',
|
|
34
|
+
WILD_SAND_WHITE: 'rgba(244, 244, 244, 1)',
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const WEBCHAT = {
|
|
38
|
+
DEFAULTS: {
|
|
39
|
+
WIDTH: 300,
|
|
40
|
+
HEIGHT: 450,
|
|
41
|
+
TITLE: 'Botonic',
|
|
42
|
+
LOGO: BotonicLogo,
|
|
43
|
+
PLACEHOLDER: 'Ask me something...',
|
|
44
|
+
FONT_FAMILY: "'Noto Sans JP', sans-serif",
|
|
45
|
+
BORDER_RADIUS_TOP_CORNERS: '6px 6px 0px 0px',
|
|
46
|
+
ELEMENT_WIDTH: 222,
|
|
47
|
+
ELEMENT_MARGIN_RIGHT: 6,
|
|
48
|
+
STORAGE_KEY: 'botonicState',
|
|
49
|
+
HOST_ID: 'root',
|
|
50
|
+
ID: 'botonic-webchat',
|
|
51
|
+
BUTTON_AUTO_DISABLE: false,
|
|
52
|
+
BUTTON_DISABLED_STYLE: {
|
|
53
|
+
opacity: 0.5,
|
|
54
|
+
cursor: 'auto',
|
|
55
|
+
pointerEvents: 'none',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
SELECTORS: {
|
|
59
|
+
SCROLLABLE_CONTENT: '#botonic-scrollable-content',
|
|
60
|
+
SIMPLEBAR_CONTENT: '.simplebar-content',
|
|
61
|
+
SIMPLEBAR_WRAPPER: '.simplebar-content-wrapper',
|
|
62
|
+
},
|
|
63
|
+
CUSTOM_PROPERTIES: {
|
|
64
|
+
// General
|
|
65
|
+
enableAnimations: 'animations.enable',
|
|
66
|
+
markdownStyle: 'markdownStyle',
|
|
67
|
+
scrollbar: 'scrollbar',
|
|
68
|
+
// Mobile
|
|
69
|
+
mobileBreakpoint: 'mobileBreakpoint',
|
|
70
|
+
mobileStyle: 'mobileStyle',
|
|
71
|
+
// Webviews
|
|
72
|
+
webviewHeaderStyle: 'webview.header.style',
|
|
73
|
+
webviewStyle: 'webview.style',
|
|
74
|
+
// Brand
|
|
75
|
+
brandColor: 'brand.color',
|
|
76
|
+
brandImage: 'brand.image',
|
|
77
|
+
// Header
|
|
78
|
+
customHeader: 'header.custom',
|
|
79
|
+
headerImage: 'header.image',
|
|
80
|
+
headerStyle: 'header.style',
|
|
81
|
+
headerSubtitle: 'header.subtitle',
|
|
82
|
+
headerTitle: 'header.title',
|
|
83
|
+
// Bot Message
|
|
84
|
+
botMessageBackground: 'message.bot.style.background',
|
|
85
|
+
botMessageBlobTick: 'message.bot.blobTick',
|
|
86
|
+
botMessageBlobTickStyle: 'message.bot.blobTickStyle',
|
|
87
|
+
botMessageBlobWidth: 'message.bot.blobWidth',
|
|
88
|
+
botMessageBorderColor: 'message.bot.style.borderColor',
|
|
89
|
+
botMessageImage: 'message.bot.image',
|
|
90
|
+
botMessageImageStyle: 'message.bot.imageStyle',
|
|
91
|
+
botMessageStyle: 'message.bot.style',
|
|
92
|
+
// User Message
|
|
93
|
+
customMessageTypes: 'message.customTypes',
|
|
94
|
+
messageStyle: 'message.style',
|
|
95
|
+
userMessageBackground: 'message.user.style.background',
|
|
96
|
+
userMessageBlobTick: 'message.user.blobTick',
|
|
97
|
+
userMessageBlobTickStyle: 'message.user.blobTickStyle',
|
|
98
|
+
userMessageBorderColor: 'message.user.style.borderColor',
|
|
99
|
+
userMessageStyle: 'message.user.style',
|
|
100
|
+
// Timestamps
|
|
101
|
+
enableMessageTimestamps: 'message.timestamps.enable',
|
|
102
|
+
messageTimestampsFormat: 'message.timestamps.format',
|
|
103
|
+
messageTimestampsStyle: 'message.timestamps.style',
|
|
104
|
+
// Intro
|
|
105
|
+
customIntro: 'intro.custom',
|
|
106
|
+
introImage: 'intro.image',
|
|
107
|
+
introStyle: 'intro.style',
|
|
108
|
+
// Buttons
|
|
109
|
+
buttonHoverBackground: 'button.hoverBackground',
|
|
110
|
+
buttonHoverTextColor: 'button.hoverTextColor',
|
|
111
|
+
buttonMessageType: 'button.messageType',
|
|
112
|
+
buttonStyle: 'button.style',
|
|
113
|
+
buttonDisabledStyle: 'button.disabledstyle',
|
|
114
|
+
buttonAutoDisable: 'button.autodisable',
|
|
115
|
+
buttonStyleBackground: 'button.style.background',
|
|
116
|
+
buttonStyleColor: 'button.style.color',
|
|
117
|
+
customButton: 'button.custom',
|
|
118
|
+
// Replies
|
|
119
|
+
alignReplies: 'replies.align',
|
|
120
|
+
customReply: 'reply.custom',
|
|
121
|
+
replyStyle: 'reply.style',
|
|
122
|
+
wrapReplies: 'replies.wrap',
|
|
123
|
+
// TriggerButton
|
|
124
|
+
customTrigger: 'triggerButton.custom',
|
|
125
|
+
triggerButtonImage: 'triggerButton.image',
|
|
126
|
+
triggerButtonStyle: 'triggerButton.style',
|
|
127
|
+
// User Input
|
|
128
|
+
blockInputs: 'userInput.blockInputs',
|
|
129
|
+
documentDownload: 'documentDownload',
|
|
130
|
+
customMenuButton: 'userInput.menuButton.custom',
|
|
131
|
+
customPersistentMenu: 'userInput.menu.custom',
|
|
132
|
+
customSendButton: 'userInput.sendButton.custom',
|
|
133
|
+
darkBackgroundMenu: 'userInput.menu.darkBackground',
|
|
134
|
+
enableAttachments: 'userInput.attachments.enable',
|
|
135
|
+
enableEmojiPicker: 'userInput.emojiPicker.enable',
|
|
136
|
+
enableSendButton: 'userInput.sendButton.enable',
|
|
137
|
+
enableUserInput: 'userInput.enable',
|
|
138
|
+
persistentMenu: 'userInput.persistentMenu',
|
|
139
|
+
textPlaceholder: 'userInput.box.placeholder',
|
|
140
|
+
userInputBoxStyle: 'userInput.box.style',
|
|
141
|
+
userInputStyle: 'userInput.style',
|
|
142
|
+
// Cover Component
|
|
143
|
+
coverComponent: 'coverComponent.component',
|
|
144
|
+
coverComponentProps: 'coverComponent.props',
|
|
145
|
+
// Carousel
|
|
146
|
+
customCarouselLeftArrow: 'carousel.arrow.left',
|
|
147
|
+
customCarouselRightArrow: 'carousel.arrow.right',
|
|
148
|
+
enableCarouselArrows: 'carousel.enableArrows',
|
|
149
|
+
},
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export const MIME_WHITELIST = {
|
|
153
|
+
audio: ['audio/mpeg', 'audio/mp3'],
|
|
154
|
+
document: ['application/pdf'],
|
|
155
|
+
image: ['image/jpeg', 'image/png'],
|
|
156
|
+
video: ['video/mp4', 'video/quicktime'],
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export const MAX_ALLOWED_SIZE_MB = 10
|
|
160
|
+
|
|
161
|
+
export const ROLES = {
|
|
162
|
+
ATTACHMENT_ICON: 'attachment-icon',
|
|
163
|
+
EMOJI_PICKER_ICON: 'emoji-picker-icon',
|
|
164
|
+
EMOJI_PICKER: 'emoji-picker',
|
|
165
|
+
HEADER: 'header',
|
|
166
|
+
MESSAGE_LIST: 'message-list',
|
|
167
|
+
PERSISTENT_MENU_ICON: 'persistent-menu-icon',
|
|
168
|
+
PERSISTENT_MENU: 'persistent-menu',
|
|
169
|
+
SEND_BUTTON_ICON: 'send-button-icon',
|
|
170
|
+
WEBCHAT: 'webchat',
|
|
171
|
+
TRIGGER_BUTTON: 'trigger-button',
|
|
172
|
+
TYPING_INDICATOR: 'typing-indicator',
|
|
173
|
+
TEXT_BOX: 'textbox',
|
|
174
|
+
WEBVIEW: 'webview',
|
|
175
|
+
WEBVIEW_HEADER: 'webview-header',
|
|
176
|
+
MESSAGE: 'message',
|
|
177
|
+
IMAGE_MESSAGE: 'image-message',
|
|
178
|
+
AUDIO_MESSAGE: 'audio-message',
|
|
179
|
+
VIDEO_MESSAGE: 'video-message',
|
|
180
|
+
DOCUMENT_MESSAGE: 'document-message',
|
|
181
|
+
RAW_MESSAGE: 'raw-message',
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export const COMPONENT_TYPE = {
|
|
185
|
+
TEXT: 'Text',
|
|
186
|
+
BUTTON: 'Button',
|
|
187
|
+
REPLY: 'Reply',
|
|
188
|
+
CAROUSEL: 'Carousel',
|
|
189
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { webchatInitialState } from './webchat/hooks'
|
|
4
|
+
|
|
5
|
+
export const RequestContext = React.createContext({
|
|
6
|
+
getString: () => '',
|
|
7
|
+
setLocale: () => '',
|
|
8
|
+
session: {},
|
|
9
|
+
params: {},
|
|
10
|
+
input: {},
|
|
11
|
+
defaultDelay: 0,
|
|
12
|
+
defaultTyping: 0,
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
export const WebchatContext = React.createContext({
|
|
16
|
+
sendText: text => {},
|
|
17
|
+
sendAttachment: attachment => {},
|
|
18
|
+
sendPayload: payload => {},
|
|
19
|
+
sendInput: input => {},
|
|
20
|
+
setReplies: replies => {},
|
|
21
|
+
openWebview: webviewComponent => {},
|
|
22
|
+
addMessage: message => {},
|
|
23
|
+
updateMessage: message => {},
|
|
24
|
+
updateReplies: replies => {},
|
|
25
|
+
updateLatestInput: input => {},
|
|
26
|
+
closeWebview: () => {},
|
|
27
|
+
toggleWebchat: () => {},
|
|
28
|
+
getThemeProperty: property => undefined, // used to retrieve a specific property of the theme defined by the developer in his 'webchat/index.js'
|
|
29
|
+
resolveCase: () => {},
|
|
30
|
+
theme: {},
|
|
31
|
+
webchatState: webchatInitialState,
|
|
32
|
+
updateWebchatDevSettings: settings => {
|
|
33
|
+
return {}
|
|
34
|
+
},
|
|
35
|
+
updateUser: user => {},
|
|
36
|
+
})
|
|
@@ -2,9 +2,9 @@ import merge from 'lodash.merge'
|
|
|
2
2
|
import React from 'react'
|
|
3
3
|
import { render } from 'react-dom'
|
|
4
4
|
|
|
5
|
-
import { SENDERS } from '
|
|
6
|
-
import { onDOMLoaded } from '../util/dom'
|
|
5
|
+
import { SENDERS } from './constants'
|
|
7
6
|
import { ReactBot } from './react-bot'
|
|
7
|
+
import { onDOMLoaded } from './util/dom'
|
|
8
8
|
import { WebchatDev } from './webchat/webchat-dev'
|
|
9
9
|
import { WebchatApp } from './webchat-app'
|
|
10
10
|
|
|
@@ -51,7 +51,7 @@ export class DevApp extends WebchatApp {
|
|
|
51
51
|
})
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
getComponent(optionsAtRuntime = {}) {
|
|
54
|
+
getComponent(host, optionsAtRuntime = {}) {
|
|
55
55
|
let {
|
|
56
56
|
theme = {},
|
|
57
57
|
persistentMenu,
|
|
@@ -67,6 +67,7 @@ export class DevApp extends WebchatApp {
|
|
|
67
67
|
onOpen,
|
|
68
68
|
onClose,
|
|
69
69
|
onMessage,
|
|
70
|
+
hostId,
|
|
70
71
|
...webchatOptions
|
|
71
72
|
} = optionsAtRuntime
|
|
72
73
|
theme = merge(this.theme, theme)
|
|
@@ -83,6 +84,8 @@ export class DevApp extends WebchatApp {
|
|
|
83
84
|
this.onOpen = onOpen || this.onOpen
|
|
84
85
|
this.onClose = onClose || this.onClose
|
|
85
86
|
this.onMessage = onMessage || this.onMessage
|
|
87
|
+
this.hostId = hostId || this.hostId
|
|
88
|
+
this.createRootElement(host)
|
|
86
89
|
return (
|
|
87
90
|
<WebchatDev
|
|
88
91
|
{...webchatOptions}
|
|
@@ -99,8 +102,10 @@ export class DevApp extends WebchatApp {
|
|
|
99
102
|
enableAnimations={enableAnimations}
|
|
100
103
|
storage={storage}
|
|
101
104
|
storageKey={storageKey}
|
|
102
|
-
getString={(stringId,
|
|
103
|
-
|
|
105
|
+
getString={(stringId, botState) =>
|
|
106
|
+
this.bot.getString(stringId, botState)
|
|
107
|
+
}
|
|
108
|
+
setLocale={(locale, botState) => this.bot.setLocale(locale, botState)}
|
|
104
109
|
onInit={(...args) => this.onInitWebchat(...args)}
|
|
105
110
|
onOpen={(...args) => this.onOpenWebchat(...args)}
|
|
106
111
|
onClose={(...args) => this.onCloseWebchat(...args)}
|
|
@@ -111,8 +116,10 @@ export class DevApp extends WebchatApp {
|
|
|
111
116
|
|
|
112
117
|
render(dest, optionsAtRuntime = {}) {
|
|
113
118
|
onDOMLoaded(() => {
|
|
114
|
-
|
|
115
|
-
|
|
119
|
+
render(
|
|
120
|
+
this.getComponent(dest, optionsAtRuntime),
|
|
121
|
+
this.getReactMountNode(dest)
|
|
122
|
+
)
|
|
116
123
|
})
|
|
117
124
|
}
|
|
118
125
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import axios from 'axios'
|
|
1
2
|
import { decode } from 'he'
|
|
2
3
|
import merge from 'lodash.merge'
|
|
3
4
|
import React from 'react'
|
|
@@ -9,56 +10,111 @@ import { WebchatDev } from './webchat/webchat-dev'
|
|
|
9
10
|
import { WebchatApp } from './webchat-app'
|
|
10
11
|
|
|
11
12
|
class WebsocketBackendService {
|
|
12
|
-
constructor({ user, lastMessageId, onEvent }) {
|
|
13
|
+
constructor({ user, lastMessageId, onEvent, updateJwt, jwt }) {
|
|
13
14
|
this.user = user || {}
|
|
14
15
|
this.lastMessageId = lastMessageId
|
|
15
|
-
console.log(onEvent)
|
|
16
16
|
this.onEvent = onEvent
|
|
17
|
+
this.jwt = jwt
|
|
18
|
+
this.updateJwt = updateJwt
|
|
17
19
|
this.init()
|
|
18
20
|
}
|
|
19
|
-
init(user, lastMessageId) {
|
|
21
|
+
async init(user, lastMessageId) {
|
|
20
22
|
if (user) this.user = user
|
|
21
23
|
if (lastMessageId) this.lastMessageId = lastMessageId
|
|
22
24
|
if (this.wsClient || !this.user.id) return
|
|
25
|
+
if (!this.jwt) await this.doAuthAndUpdateJwt()
|
|
26
|
+
this.initWebsocket()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
initWebsocket() {
|
|
23
30
|
// Establish WebSocket Connection
|
|
24
31
|
// eslint-disable-next-line no-undef
|
|
25
32
|
this.wsClient = new ReconnectingWebSocket(WEBSOCKET_URL)
|
|
26
|
-
|
|
27
33
|
// On Connection Established...
|
|
28
|
-
this.wsClient.addEventListener('open',
|
|
29
|
-
|
|
34
|
+
this.wsClient.addEventListener('open', () => {
|
|
35
|
+
// Send JWT token to onAuth and update user with new connection ID
|
|
36
|
+
this.wsClient.send(JSON.stringify({ token: this.jwt }))
|
|
37
|
+
})
|
|
30
38
|
// On Event Received...
|
|
31
39
|
this.wsClient.addEventListener('message', event => {
|
|
32
40
|
console.log(event, this.onEvent)
|
|
33
|
-
const
|
|
41
|
+
const eventData = JSON.parse(decode(event.data))
|
|
34
42
|
if (this.onEvent && typeof this.onEvent === 'function')
|
|
35
|
-
this.onEvent(
|
|
43
|
+
this.onEvent(eventData)
|
|
36
44
|
})
|
|
37
45
|
}
|
|
38
|
-
async
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
async doAuthAndUpdateJwt() {
|
|
47
|
+
this.jwt = await this.doAuth({
|
|
48
|
+
userId: this.user.id,
|
|
49
|
+
channel: this.user.channel,
|
|
50
|
+
idFromChannel: this.user.idFromChannel,
|
|
51
|
+
})
|
|
52
|
+
this.updateJwt(this.jwt)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async doAuth({ userId, channel, idFromChannel }) {
|
|
56
|
+
try {
|
|
57
|
+
const {
|
|
58
|
+
data: { token },
|
|
59
|
+
// eslint-disable-next-line no-undef
|
|
60
|
+
} = await axios.post(`${REST_API_URL}auth/`, {
|
|
61
|
+
userId,
|
|
62
|
+
channel,
|
|
63
|
+
idFromChannel,
|
|
44
64
|
})
|
|
45
|
-
|
|
65
|
+
return token
|
|
66
|
+
} catch (e) {
|
|
67
|
+
console.error(e)
|
|
68
|
+
return null
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async postMessage(user, message) {
|
|
73
|
+
let hasErrors = false
|
|
74
|
+
try {
|
|
75
|
+
await axios
|
|
76
|
+
.post(
|
|
77
|
+
// eslint-disable-next-line no-undef
|
|
78
|
+
`${REST_API_URL}events/`,
|
|
79
|
+
{
|
|
80
|
+
message,
|
|
81
|
+
sender: user, // TODO: Really needed or we should pass user information through JWT?
|
|
82
|
+
},
|
|
83
|
+
{ headers: { Authorization: 'Bearer ' + this.jwt } } // Note: Do not use string template as it will convert the token with commas, which will be invalid
|
|
84
|
+
)
|
|
85
|
+
.catch(error => {
|
|
86
|
+
if (error.response.status === 401) hasErrors = true
|
|
87
|
+
})
|
|
88
|
+
} catch (error) {
|
|
89
|
+
if (error && error.response.status === 401) hasErrors = true
|
|
90
|
+
}
|
|
91
|
+
if (hasErrors) {
|
|
92
|
+
// TODO: Handle rest of errors
|
|
93
|
+
await this.doAuthAndUpdateJwt()
|
|
94
|
+
// await this.postMessage(user, message) // Temporary, avoid infinite events loop
|
|
95
|
+
}
|
|
46
96
|
}
|
|
47
97
|
}
|
|
48
98
|
|
|
49
99
|
export class FullstackProdApp extends WebchatApp {
|
|
50
|
-
async onUserInput({ user, input }) {
|
|
51
|
-
this.onMessage && this.onMessage(this, { from:
|
|
100
|
+
async onUserInput({ user, input, session, botState }) {
|
|
101
|
+
this.onMessage && this.onMessage(this, { from: input.from, message: input })
|
|
52
102
|
this.backendService.postMessage(user, input)
|
|
53
103
|
}
|
|
54
104
|
|
|
55
|
-
|
|
105
|
+
async doAuth(user) {
|
|
106
|
+
return await this.backendService.doAuth(user)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
onStateChange({ user, messagesJSON, jwt, updateJwt }) {
|
|
56
110
|
if (!this.backendService && user) {
|
|
57
111
|
const lastMessage = messagesJSON[messagesJSON.length - 1]
|
|
58
112
|
this.backendService = new WebsocketBackendService({
|
|
59
113
|
user,
|
|
60
114
|
lastMessageId: lastMessage && lastMessage.id,
|
|
61
115
|
onEvent: event => this.onServiceEvent(event),
|
|
116
|
+
jwt,
|
|
117
|
+
updateJwt: async token => updateJwt(token),
|
|
62
118
|
})
|
|
63
119
|
}
|
|
64
120
|
}
|
|
@@ -71,12 +127,12 @@ export class FullstackDevApp extends DevApp {
|
|
|
71
127
|
console.log('FullstackDevApp ', args.playgroundCode)
|
|
72
128
|
}
|
|
73
129
|
|
|
74
|
-
async onUserInput({ user, input }) {
|
|
75
|
-
this.onMessage && this.onMessage(this, { from:
|
|
130
|
+
async onUserInput({ user, input, session, botState }) {
|
|
131
|
+
this.onMessage && this.onMessage(this, { from: input.from, message: input })
|
|
76
132
|
this.backendService && this.backendService.postMessage(user, input)
|
|
77
133
|
}
|
|
78
134
|
|
|
79
|
-
getComponent(optionsAtRuntime = {}) {
|
|
135
|
+
getComponent(host, optionsAtRuntime = {}) {
|
|
80
136
|
let {
|
|
81
137
|
theme = {},
|
|
82
138
|
persistentMenu,
|
|
@@ -92,6 +148,7 @@ export class FullstackDevApp extends DevApp {
|
|
|
92
148
|
onOpen,
|
|
93
149
|
onClose,
|
|
94
150
|
onMessage,
|
|
151
|
+
hostId,
|
|
95
152
|
...webchatOptions
|
|
96
153
|
} = optionsAtRuntime
|
|
97
154
|
theme = merge(this.theme, theme)
|
|
@@ -108,6 +165,8 @@ export class FullstackDevApp extends DevApp {
|
|
|
108
165
|
this.onOpen = onOpen || this.onOpen
|
|
109
166
|
this.onClose = onClose || this.onClose
|
|
110
167
|
this.onMessage = onMessage || this.onMessage
|
|
168
|
+
this.hostId = hostId || this.hostId
|
|
169
|
+
this.createRootElement(host)
|
|
111
170
|
return (
|
|
112
171
|
<WebchatDev
|
|
113
172
|
{...webchatOptions}
|
|
@@ -126,23 +185,32 @@ export class FullstackDevApp extends DevApp {
|
|
|
126
185
|
storageKey={storageKey}
|
|
127
186
|
playgroundCode={this.playgroundCode}
|
|
128
187
|
onStateChange={webchatState => this.onStateChange(webchatState)}
|
|
129
|
-
getString={(stringId,
|
|
130
|
-
|
|
188
|
+
getString={(stringId, botState) =>
|
|
189
|
+
this.bot.getString(stringId, botState)
|
|
190
|
+
}
|
|
191
|
+
setLocale={(locale, botState) => this.bot.setLocale(locale, botState)}
|
|
131
192
|
onInit={(...args) => this.onInitWebchat(...args)}
|
|
132
193
|
onOpen={(...args) => this.onOpenWebchat(...args)}
|
|
133
194
|
onClose={(...args) => this.onCloseWebchat(...args)}
|
|
134
195
|
onUserInput={(...args) => this.onUserInput(...args)}
|
|
196
|
+
doAuth={(...args) => this.doAuth(...args)}
|
|
135
197
|
/>
|
|
136
198
|
)
|
|
137
199
|
}
|
|
138
200
|
|
|
139
|
-
|
|
201
|
+
async doAuth(user) {
|
|
202
|
+
return await this.backendService.doAuth(user)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
onStateChange({ user, messagesJSON, jwt, updateJwt }) {
|
|
140
206
|
if (!this.backendService && user) {
|
|
141
207
|
const lastMessage = messagesJSON[messagesJSON.length - 1]
|
|
142
208
|
this.backendService = new WebsocketBackendService({
|
|
143
209
|
user,
|
|
144
210
|
lastMessageId: lastMessage && lastMessage.id,
|
|
145
211
|
onEvent: event => this.onServiceEvent(event),
|
|
212
|
+
updateJwt: async token => updateJwt(token),
|
|
213
|
+
jwt,
|
|
146
214
|
})
|
|
147
215
|
}
|
|
148
216
|
}
|
|
@@ -200,6 +268,7 @@ export class BrowserProdApp extends WebchatApp {
|
|
|
200
268
|
...botOptions,
|
|
201
269
|
})
|
|
202
270
|
}
|
|
271
|
+
// TODO: Review how this be done for only browser versions
|
|
203
272
|
async onUserInput({ input, session, lastRoutePath }) {
|
|
204
273
|
this.onMessage && this.onMessage(this, { from: 'user', message: input })
|
|
205
274
|
const resp = await this.bot.input({ input, session, lastRoutePath })
|
|
@@ -220,10 +289,10 @@ export { ShareButton } from '../components/share-button'
|
|
|
220
289
|
export { Subtitle } from '../components/subtitle'
|
|
221
290
|
export { Title } from '../components/title'
|
|
222
291
|
export { WebchatSettings } from '../components/webchat-settings'
|
|
223
|
-
export { RequestContext, WebchatContext } from '../contexts'
|
|
224
292
|
export { staticAsset } from '../util/environment'
|
|
225
293
|
export { getBotonicApp } from '../webchat'
|
|
226
294
|
export { WebviewApp } from '../webview'
|
|
295
|
+
export { RequestContext, WebchatContext } from './contexts'
|
|
227
296
|
// Experimental
|
|
228
297
|
export { Audio } from './components/audio'
|
|
229
298
|
export { Carousel } from './components/carousel'
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
|
+
import { Button } from '../components/button'
|
|
4
|
+
import { ButtonsDisabler } from '../components/buttons-disabler'
|
|
5
|
+
import { Element } from '../components/element'
|
|
6
|
+
import { Pic } from '../components/pic'
|
|
7
|
+
import { Reply } from '../components/reply'
|
|
8
|
+
import { Subtitle } from '../components/subtitle'
|
|
9
|
+
import { Title } from '../components/title'
|
|
3
10
|
import {
|
|
4
11
|
isAudio,
|
|
5
12
|
isCarousel,
|
|
@@ -9,14 +16,7 @@ import {
|
|
|
9
16
|
isLocation,
|
|
10
17
|
isText,
|
|
11
18
|
isVideo,
|
|
12
|
-
} from '
|
|
13
|
-
import { Button } from '../components/button'
|
|
14
|
-
import { ButtonsDisabler } from '../components/buttons-disabler'
|
|
15
|
-
import { Element } from '../components/element'
|
|
16
|
-
import { Pic } from '../components/pic'
|
|
17
|
-
import { Reply } from '../components/reply'
|
|
18
|
-
import { Subtitle } from '../components/subtitle'
|
|
19
|
-
import { Title } from '../components/title'
|
|
19
|
+
} from '../message-utils'
|
|
20
20
|
// Experimental
|
|
21
21
|
import { Audio } from './components/audio'
|
|
22
22
|
import { Carousel } from './components/carousel'
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { CoreBot } from '@botonic/core'
|
|
2
2
|
import React from 'react'
|
|
3
3
|
|
|
4
|
-
import { RequestContext } from '../contexts'
|
|
5
4
|
import { Text } from './components/text'
|
|
5
|
+
import { RequestContext } from './contexts'
|
|
6
6
|
|
|
7
7
|
export class ReactBot extends CoreBot {
|
|
8
8
|
constructor(options) {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { WEBCHAT } from '../constants'
|
|
2
|
+
|
|
3
|
+
export const getScrollableArea = webchatElement => {
|
|
4
|
+
const getArea = area => {
|
|
5
|
+
const botonicScrollableContent = webchatElement.querySelector(
|
|
6
|
+
WEBCHAT.SELECTORS.SCROLLABLE_CONTENT
|
|
7
|
+
)
|
|
8
|
+
const scrollableArea =
|
|
9
|
+
botonicScrollableContent && botonicScrollableContent.querySelector(area)
|
|
10
|
+
return scrollableArea
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
full: getArea(WEBCHAT.SELECTORS.SIMPLEBAR_CONTENT),
|
|
14
|
+
visible: getArea(WEBCHAT.SELECTORS.SIMPLEBAR_WRAPPER),
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const scrollToBottom = ({
|
|
19
|
+
timeout = 200,
|
|
20
|
+
behavior = 'smooth',
|
|
21
|
+
host,
|
|
22
|
+
} = {}) => {
|
|
23
|
+
const webchatElement = getWebchatElement(host)
|
|
24
|
+
if (!webchatElement) return
|
|
25
|
+
const frame = getScrollableArea(webchatElement).visible
|
|
26
|
+
if (frame) {
|
|
27
|
+
setTimeout(
|
|
28
|
+
() => frame.scrollTo({ top: frame.scrollHeight, behavior: behavior }),
|
|
29
|
+
timeout
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const getWebchatElement = host =>
|
|
35
|
+
host && host.querySelector(`#${WEBCHAT.DEFAULTS.ID}`)
|
|
36
|
+
|
|
37
|
+
// https://stackoverflow.com/questions/9457891/how-to-detect-if-domcontentloaded-was-fired
|
|
38
|
+
export const onDOMLoaded = callback => {
|
|
39
|
+
if (/complete|interactive|loaded/.test(document.readyState)) {
|
|
40
|
+
// In case the document has finished parsing, document's readyState will
|
|
41
|
+
// be one of "complete", "interactive" or (non-standard) "loaded".
|
|
42
|
+
callback()
|
|
43
|
+
} else {
|
|
44
|
+
// The document is not ready yet, so wait for the DOMContentLoaded event
|
|
45
|
+
document.addEventListener('DOMContentLoaded', callback, false)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const isShadowDOMSupported = () => {
|
|
50
|
+
try {
|
|
51
|
+
return document.head.createShadowRoot || document.head.attachShadow
|
|
52
|
+
} catch (e) {
|
|
53
|
+
return false
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* given an object and a property, returns the property if exists (recursively), else undefined
|
|
3
|
+
* ex:
|
|
4
|
+
* let obj = { a: { b: { c: 5 } } }
|
|
5
|
+
* getProperty(obj, 'a.b.c'), returns 5
|
|
6
|
+
* getProperty(obj, 'a.b.z'), returns undefined
|
|
7
|
+
*/
|
|
8
|
+
export const getProperty = (obj, property) => {
|
|
9
|
+
if (!property) return undefined
|
|
10
|
+
const properties = property.split('.')
|
|
11
|
+
for (let i = 0; i < properties.length; i++) {
|
|
12
|
+
const prop = properties[i]
|
|
13
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
14
|
+
if (!obj || !obj.hasOwnProperty(prop)) {
|
|
15
|
+
return undefined
|
|
16
|
+
} else {
|
|
17
|
+
obj = obj[prop]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return obj
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function strToBool(string) {
|
|
24
|
+
const regex = /^\s*(true|1|on)\s*$/i
|
|
25
|
+
string = String(string)
|
|
26
|
+
return regex.test(string)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const mapObject = (obj, conversion = ([key, value]) => [key, value]) => {
|
|
30
|
+
return (
|
|
31
|
+
obj &&
|
|
32
|
+
Object.entries(obj)
|
|
33
|
+
.map(conversion)
|
|
34
|
+
.reduce(function (prev, curr) {
|
|
35
|
+
prev[curr[0]] = curr[1]
|
|
36
|
+
return prev
|
|
37
|
+
}, {})
|
|
38
|
+
)
|
|
39
|
+
}
|