@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
@@ -1,5 +1,5 @@
1
1
  import { INPUT, isBrowser } from '@botonic/core'
2
- import { MessageEventAck } from '@botonic/core/lib/models/events/message'
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
- // TODO: Resolution for browser app?
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 '../constants'
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, session) => this.bot.getString(stringId, session)}
103
- setLocale={(locale, session) => this.bot.setLocale(locale, session)}
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
- this.createRootElement(dest)
115
- render(this.getComponent(optionsAtRuntime), this.getReactMountNode(dest))
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', event => {})
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 message = JSON.parse(decode(event.data))
41
+ const eventData = JSON.parse(decode(event.data))
34
42
  if (this.onEvent && typeof this.onEvent === 'function')
35
- this.onEvent({ message })
43
+ this.onEvent(eventData)
36
44
  })
37
45
  }
38
- async postMessage(user, message) {
39
- if (this.wsClient.readyState > 1) this.wsClient.reconnect()
40
- this.wsClient.send(
41
- JSON.stringify({
42
- sender: user,
43
- message,
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: 'user', message: input })
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
- onStateChange({ session: { user }, messagesJSON }) {
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: 'user', message: input })
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, session) => this.bot.getString(stringId, session)}
130
- setLocale={(locale, session) => this.bot.setLocale(locale, session)}
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
- onStateChange({ session: { user }, messagesJSON }) {
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 '../../src/message-utils'
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
+ }