@botpress/webchat 0.3.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,4 +8,4 @@ declare const _default: React.ForwardRefExoticComponent<import("react-intl").Omi
8
8
  WrappedComponent: React.ComponentType<MessageProps>;
9
9
  } & import("mobx-react").IWrappedComponent<unknown>;
10
10
  export default _default;
11
- declare type MessageProps = Renderer.Message & WrappedComponentProps & Pick<StoreDef, 'intl'>;
11
+ declare type MessageProps = Renderer.Message & WrappedComponentProps & Pick<StoreDef, 'intl' | 'selectedMessageId' | 'config'>;
@@ -27,6 +27,7 @@ const classnames_1 = __importDefault(require("classnames"));
27
27
  const mobx_react_1 = require("mobx-react");
28
28
  const react_1 = __importStar(require("react"));
29
29
  const react_intl_1 = require("react-intl");
30
+ const webchatEvents_1 = require("../../utils/webchatEvents");
30
31
  class Message extends react_1.Component {
31
32
  constructor() {
32
33
  super(...arguments);
@@ -34,6 +35,17 @@ class Message extends react_1.Component {
34
35
  hasError: false,
35
36
  showMore: false
36
37
  };
38
+ this.onMessageClick = () => {
39
+ var _a, _b;
40
+ (_a = this.props.store) === null || _a === void 0 ? void 0 : _a.setSelectedMessage(this.props.messageId);
41
+ (0, webchatEvents_1.postMessageToParent)('MESSAGE.SELECTED', {
42
+ id: this.props.messageId,
43
+ conversationId: (_b = this.props.store) === null || _b === void 0 ? void 0 : _b.currentConversationId,
44
+ sentOn: this.props.sentOn,
45
+ payload: this.props.payload,
46
+ from: this.props.isBotMessage ? 'bot' : 'user'
47
+ }, this.props.config.chatId);
48
+ };
37
49
  }
38
50
  static getDerivedStateFromError(_error) {
39
51
  return { hasError: true };
@@ -70,11 +82,14 @@ class Message extends react_1.Component {
70
82
  return null;
71
83
  }
72
84
  const additionalStyle = (this.props.payload && this.props.payload['web-style']) || {};
85
+ const messageSelectedClass = {
86
+ 'bpw-message-selected': this.props.selectedMessageId === this.props.messageId
87
+ };
73
88
  if (this.props.noBubble || ((_b = (_a = this.props.payload) === null || _a === void 0 ? void 0 : _a.wrapped) === null || _b === void 0 ? void 0 : _b.noBubble)) {
74
- return (react_1.default.createElement("div", { className: (0, classnames_1.default)(this.props.className, wrappedClass), style: additionalStyle }, rendered));
89
+ return (react_1.default.createElement("div", { className: (0, classnames_1.default)(this.props.className, wrappedClass, messageSelectedClass), style: additionalStyle, onClick: this.onMessageClick }, rendered));
75
90
  }
76
91
  return (react_1.default.createElement("div", { className: (0, classnames_1.default)(this.props.className, wrappedClass, 'bpw-chat-bubble', `bpw-bubble-${type}`), "data-from": this.props.fromLabel, tabIndex: -1, style: additionalStyle },
77
- react_1.default.createElement("div", { tabIndex: -1, className: "bpw-chat-bubble-content" },
92
+ react_1.default.createElement("div", { tabIndex: -1, className: (0, classnames_1.default)('bpw-chat-bubble-content', messageSelectedClass), onClick: this.onMessageClick },
78
93
  react_1.default.createElement("span", { className: "sr-only" }, this.props.store.intl.formatMessage({
79
94
  id: this.props.isBotMessage ? 'message.botSaid' : 'message.iSaid',
80
95
  defaultMessage: this.props.isBotMessage ? 'Virtual assistant said : ' : 'I said : '
@@ -85,5 +100,7 @@ class Message extends react_1.Component {
85
100
  }
86
101
  }
87
102
  exports.default = (0, mobx_react_1.inject)(({ store }) => ({
88
- intl: store.intl
103
+ intl: store.intl,
104
+ config: store.config,
105
+ selectedMessageId: store.selectedMessageId
89
106
  }))((0, react_intl_1.injectIntl)((0, mobx_react_1.observer)(Message)));
@@ -20,4 +20,4 @@ declare type Props = {
20
20
  messages: MessageDetails[];
21
21
  isLastGroup: boolean;
22
22
  store?: RootStore;
23
- } & Pick<StoreDef, 'sendFeedback' | 'sendData'>;
23
+ } & Pick<StoreDef, 'sendFeedback' | 'sendData' | 'selectedMessageId'>;
@@ -43,7 +43,9 @@ class MessageGroup extends react_1.default.Component {
43
43
  }) },
44
44
  avatar,
45
45
  react_1.default.createElement("div", { role: "region", className: 'bpw-message-container' },
46
- react_1.default.createElement("div", { "aria-live": "assertive", role: "log", className: 'bpw-message-group' },
46
+ react_1.default.createElement("div", { "aria-live": "assertive", role: "log", className: (0, classnames_1.default)('bpw-message-group', {
47
+ 'bpw-message-group-selected': !!this.props.messages.find((m) => m.id === this.props.selectedMessageId)
48
+ }) },
47
49
  react_1.default.createElement("span", { "data-from": fromLabel, className: "from hidden", "aria-hidden": "true" }, fromLabel),
48
50
  (0, sortBy_1.default)(messages, ['sent_on', 'eventId']).map((message, i, messages) => {
49
51
  const isLastMsg = i === messages.length - 1;
@@ -56,5 +58,6 @@ class MessageGroup extends react_1.default.Component {
56
58
  exports.default = (0, mobx_react_1.inject)(({ store }) => ({
57
59
  store,
58
60
  sendFeedback: store.sendFeedback,
59
- sendData: store.sendData
61
+ sendData: store.sendData,
62
+ selectedMessageId: store.selectedMessageId
60
63
  }))(MessageGroup);
@@ -1,12 +1,14 @@
1
1
  import { Message, MessagingSocket } from '@botpress/messaging-socket';
2
2
  import { Config } from '../typings';
3
3
  export default class BpSocket {
4
+ private config;
4
5
  socket: MessagingSocket;
5
6
  private chatId;
6
7
  onMessage: (event: any) => void;
7
8
  constructor(config: Config);
8
9
  setup(): void;
9
10
  sendPayload(payload: any): Promise<Message>;
10
- postToParent: (_type: string, payload: any) => void;
11
11
  connect(): Promise<void>;
12
+ reload(config: Config): Promise<void>;
13
+ private getCreds;
12
14
  }
@@ -10,12 +10,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const messaging_socket_1 = require("@botpress/messaging-socket");
13
+ const webchatEvents_1 = require("../utils/webchatEvents");
13
14
  class BpSocket {
14
15
  constructor(config) {
15
- this.postToParent = (_type, payload) => {
16
- var _a;
17
- (_a = window.parent) === null || _a === void 0 ? void 0 : _a.postMessage(Object.assign(Object.assign({}, payload), { chatId: this.chatId }), '*');
18
- };
16
+ this.config = config;
19
17
  this.chatId = config.chatId;
20
18
  this.socket = new messaging_socket_1.MessagingSocket({ url: config.messagingUrl, clientId: config.clientId });
21
19
  window.websocket = this.socket;
@@ -32,14 +30,28 @@ class BpSocket {
32
30
  }
33
31
  connect() {
34
32
  return __awaiter(this, void 0, void 0, function* () {
35
- const creds = window.BP_STORAGE.get('creds');
33
+ const creds = this.getCreds();
36
34
  yield this.socket.connect(creds);
37
35
  if (this.socket.userId) {
38
36
  const userId = this.socket.userId;
39
37
  window.BP_STORAGE.set('creds', this.socket.creds);
40
- this.postToParent('', { userId });
38
+ (0, webchatEvents_1.postMessageToParent)('USER.CONNECTED', { userId }, this.chatId);
41
39
  }
42
40
  });
43
41
  }
42
+ reload(config) {
43
+ var _a;
44
+ return __awaiter(this, void 0, void 0, function* () {
45
+ this.config = config;
46
+ const creds = this.getCreds();
47
+ if ((creds === null || creds === void 0 ? void 0 : creds.userId) !== this.socket.userId || (creds === null || creds === void 0 ? void 0 : creds.userToken) !== ((_a = this.socket.creds) === null || _a === void 0 ? void 0 : _a.userToken)) {
48
+ yield this.socket.disconnect();
49
+ yield this.connect();
50
+ }
51
+ });
52
+ }
53
+ getCreds() {
54
+ return this.config.customUser || window.BP_STORAGE.get('creds');
55
+ }
44
56
  }
45
57
  exports.default = BpSocket;
package/dist/main.d.ts CHANGED
@@ -10,4 +10,4 @@ declare const _default: React.ForwardRefExoticComponent<import("react-intl").Omi
10
10
  export default _default;
11
11
  declare type MainProps = {
12
12
  store?: RootStore;
13
- } & WrappedComponentProps & Pick<StoreDef, 'config' | 'initializeChat' | 'botInfo' | 'fetchBotInfo' | 'sendMessage' | 'sendData' | 'intl' | 'updateTyping' | 'updateBotUILanguage' | 'hideChat' | 'showChat' | 'toggleBotInfo' | 'widgetTransition' | 'activeView' | 'isFullscreen' | 'unreadCount' | 'hasUnreadMessages' | 'showWidgetButton' | 'addEventToConversation' | 'clearMessages' | 'updateConfig' | 'mergeConfig' | 'isWebchatReady' | 'incrementUnread' | 'displayWidgetView' | 'resetUnread' | 'setLoadingCompleted' | 'dimensions' | 'updateLastMessage' | 'fetchConversation' | 'setIntlProvider' | 'setSocket' | 'currentConversationId'>;
13
+ } & WrappedComponentProps & Pick<StoreDef, 'config' | 'initializeChat' | 'botInfo' | 'fetchBotInfo' | 'sendMessage' | 'sendData' | 'intl' | 'updateTyping' | 'updateBotUILanguage' | 'hideChat' | 'showChat' | 'toggleBotInfo' | 'widgetTransition' | 'activeView' | 'isFullscreen' | 'unreadCount' | 'hasUnreadMessages' | 'showWidgetButton' | 'addEventToConversation' | 'clearMessages' | 'updateConfig' | 'mergeConfig' | 'isWebchatReady' | 'incrementUnread' | 'displayWidgetView' | 'resetUnread' | 'setLoadingCompleted' | 'dimensions' | 'updateLastMessage' | 'fetchConversation' | 'createConversation' | 'setIntlProvider' | 'setSocket' | 'currentConversationId' | 'currentConversation' | 'resetConversation'>;
package/dist/main.js CHANGED
@@ -34,7 +34,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
34
34
  exports.DEFAULT_TYPING_DELAY = void 0;
35
35
  const classnames_1 = __importDefault(require("classnames"));
36
36
  const debounce_1 = __importDefault(require("lodash/debounce"));
37
- const set_1 = __importDefault(require("lodash/set"));
38
37
  const mobx_1 = require("mobx");
39
38
  const mobx_react_1 = require("mobx-react");
40
39
  const query_string_1 = __importDefault(require("query-string"));
@@ -46,6 +45,7 @@ const socket_1 = __importDefault(require("./core/socket"));
46
45
  const Chat_1 = __importDefault(require("./icons/Chat"));
47
46
  const utils_1 = require("./utils");
48
47
  const analytics_1 = require("./utils/analytics");
48
+ const webchatEvents_1 = require("./utils/webchatEvents");
49
49
  exports.DEFAULT_TYPING_DELAY = 1000;
50
50
  class Web extends react_1.default.Component {
51
51
  constructor(props) {
@@ -68,7 +68,14 @@ class Web extends react_1.default.Component {
68
68
  case 'configure':
69
69
  return this.props.updateConfig(Object.assign({}, constants_1.default.DEFAULT_CONFIG, data.payload));
70
70
  case 'mergeConfig':
71
- return this.props.mergeConfig(data.payload);
71
+ this.props.mergeConfig(data.payload);
72
+ const oldUserId = this.socket.socket.userId;
73
+ yield this.socket.reload(data.payload);
74
+ if (this.socket.socket.userId !== oldUserId) {
75
+ this.props.resetConversation();
76
+ yield this.props.initializeChat();
77
+ }
78
+ return;
72
79
  case 'sendPayload':
73
80
  return this.props.sendData(data.payload);
74
81
  case 'event':
@@ -86,12 +93,14 @@ class Web extends react_1.default.Component {
86
93
  (0, analytics_1.trackWebchatState)('toggle');
87
94
  }
88
95
  else if (type === 'message') {
89
- (0, analytics_1.trackMessage)('sent');
90
96
  yield this.props.sendMessage(text);
91
97
  }
92
98
  else if (type === 'loadConversation') {
93
99
  yield this.props.fetchConversation(conversationId);
94
100
  }
101
+ else if (type === 'createConversation') {
102
+ yield this.props.createConversation();
103
+ }
95
104
  else if (type === 'toggleBotInfo') {
96
105
  this.props.toggleBotInfo();
97
106
  }
@@ -103,7 +112,7 @@ class Web extends react_1.default.Component {
103
112
  }
104
113
  });
105
114
  this.handleNewMessage = (event) => __awaiter(this, void 0, void 0, function* () {
106
- var _b;
115
+ var _b, _c, _d;
107
116
  if (!this.isCurrentConversation(event)) {
108
117
  // don't do anything, it's a message from another conversation
109
118
  return;
@@ -116,7 +125,15 @@ class Web extends react_1.default.Component {
116
125
  // don't do anything, it's the system message
117
126
  return;
118
127
  }
119
- (0, analytics_1.trackMessage)('received');
128
+ if (((_c = this.props.currentConversation) === null || _c === void 0 ? void 0 : _c.userId) !== event.authorId) {
129
+ (0, analytics_1.trackMessage)('received');
130
+ (0, webchatEvents_1.postMessageToParent)('MESSAGE.RECEIVED', event, this.props.config.chatId);
131
+ // This is to handle a special case for the emulator, setting the selected css class to the last message group
132
+ // This needs a rethinking
133
+ if (event.id) {
134
+ (_d = this.props.store) === null || _d === void 0 ? void 0 : _d.setSelectedMessage(event.id);
135
+ }
136
+ }
120
137
  this.props.updateLastMessage(event.conversationId, event);
121
138
  yield this.props.addEventToConversation(event);
122
139
  // there's no focus on the actual conversation
@@ -130,8 +147,8 @@ class Web extends react_1.default.Component {
130
147
  yield this.props.updateTyping(event);
131
148
  });
132
149
  this.playSound = (0, debounce_1.default)(() => __awaiter(this, void 0, void 0, function* () {
133
- var _c;
134
- const disableNotificationSound = this.config.disableNotificationSound || ((_c = this.props.config) === null || _c === void 0 ? void 0 : _c.disableNotificationSound);
150
+ var _e;
151
+ const disableNotificationSound = this.config.disableNotificationSound || ((_e = this.props.config) === null || _e === void 0 ? void 0 : _e.disableNotificationSound);
135
152
  if (disableNotificationSound || this.audio.readyState < 2) {
136
153
  return;
137
154
  }
@@ -185,12 +202,9 @@ class Web extends react_1.default.Component {
185
202
  return __awaiter(this, void 0, void 0, function* () {
186
203
  this.config = this.extractConfig();
187
204
  this.props.updateConfig(this.config);
188
- if (this.config.exposeStore) {
189
- const storePath = this.config.chatId ? `${this.config.chatId}.webchat_store` : 'webchat_store';
190
- (0, set_1.default)(window.parent, storePath, this.props.store);
191
- }
205
+ // is this necessary ?
192
206
  if (this.config.containerWidth) {
193
- this.postMessageToParent('setWidth', this.config.containerWidth);
207
+ (0, webchatEvents_1.postMessageToParent)('UI.RESIZE', this.config.containerWidth, this.config.chatId);
194
208
  }
195
209
  yield this.props.fetchBotInfo();
196
210
  if (!this.isLazySocket()) {
@@ -199,10 +213,6 @@ class Web extends react_1.default.Component {
199
213
  this.setupObserver();
200
214
  });
201
215
  }
202
- postMessageToParent(type, value) {
203
- var _a;
204
- (_a = window.parent) === null || _a === void 0 ? void 0 : _a.postMessage({ type, value, chatId: this.config.chatId }, '*');
205
- }
206
216
  extractConfig() {
207
217
  let userConfig = Object.assign({}, constants_1.default.DEFAULT_CONFIG, this.props.config);
208
218
  const { options } = query_string_1.default.parse(location.search);
@@ -232,7 +242,8 @@ class Web extends react_1.default.Component {
232
242
  setupObserver() {
233
243
  (0, mobx_1.observe)(this.props.dimensions, 'container', (data) => {
234
244
  if (data.newValue) {
235
- this.postMessageToParent('setWidth', data.newValue);
245
+ // is this necessary ?
246
+ (0, webchatEvents_1.postMessageToParent)('UI.RESIZE', data.newValue, this.config.chatId);
236
247
  }
237
248
  });
238
249
  }
@@ -260,8 +271,8 @@ class Web extends react_1.default.Component {
260
271
  [(_a = this.props.config) === null || _a === void 0 ? void 0 : _a.className]: !!((_b = this.props.config) === null || _b === void 0 ? void 0 : _b.className)
261
272
  });
262
273
  if (this.parentClass !== parentClass) {
263
- this.postMessageToParent('setClass', parentClass);
264
274
  this.parentClass = parentClass;
275
+ (0, webchatEvents_1.postMessageToParent)('UI.SET-CLASS', parentClass, this.config.chatId);
265
276
  }
266
277
  const stylesheet = this.props.config.stylesheet;
267
278
  const extraStylesheet = (_c = this.props.botInfo) === null || _c === void 0 ? void 0 : _c.extraStylesheet;
@@ -316,7 +327,10 @@ exports.default = (0, mobx_react_1.inject)(({ store }) => ({
316
327
  sendFeedback: store.sendFeedback,
317
328
  updateLastMessage: store.updateLastMessage,
318
329
  fetchConversation: store.fetchConversation,
330
+ createConversation: store.createConversation,
319
331
  setIntlProvider: store.setIntlProvider,
320
332
  setSocket: store.setSocket,
321
- currentConversationId: store.currentConversationId
333
+ currentConversation: store.currentConversation,
334
+ currentConversationId: store.currentConversationId,
335
+ resetConversation: store.resetConversation
322
336
  }))((0, react_intl_1.injectIntl)((0, mobx_react_1.observer)(Web)));
@@ -13,6 +13,7 @@ declare class RootStore {
13
13
  private api;
14
14
  conversations: RecentConversation[];
15
15
  currentConversation?: CurrentConversation;
16
+ selectedMessageId?: string;
16
17
  botInfo: BotInfo;
17
18
  config: Config;
18
19
  preferredLanguage: string;
@@ -27,6 +28,7 @@ declare class RootStore {
27
28
  }, config?: Config);
28
29
  setIntlProvider(provider: IntlShape): void;
29
30
  setSocket(socket: BpSocket): void;
31
+ setSelectedMessage(messageId: string): void;
30
32
  get isConversationStarted(): boolean;
31
33
  get botName(): string;
32
34
  get hasBotInfoDescription(): boolean;
@@ -35,7 +37,6 @@ declare class RootStore {
35
37
  get escapeHTML(): boolean;
36
38
  get currentMessages(): Message[];
37
39
  get currentConversationId(): uuid | undefined;
38
- postMessage(name: string, payload?: any): void;
39
40
  updateMessages(messages: Message[]): void;
40
41
  updateLastMessage(conversationId: string, message?: Message): void;
41
42
  clearMessages(): void;
@@ -51,7 +52,7 @@ declare class RootStore {
51
52
  /** Fetch the specified conversation ID, or try to fetch a valid one from the list */
52
53
  fetchConversation(convoId?: uuid): Promise<uuid | undefined>;
53
54
  /** Sends the specified message, or fetch the message in the composer */
54
- sendMessage(message?: string): Promise<void>;
55
+ sendMessage(textMessage?: string): Promise<void>;
55
56
  /** Sends an event to start conversation & hide the bot info page */
56
57
  startConversation(): Promise<void>;
57
58
  /** Creates a new conversation and switches to it */
@@ -61,7 +62,7 @@ declare class RootStore {
61
62
  sendFeedback(feedback: number, messageId: string): Promise<void>;
62
63
  downloadConversation(): Promise<void>;
63
64
  /** Sends an event or a message, depending on how the backend manages those types */
64
- sendData(data: any): Promise<void>;
65
+ sendData(data: any): Promise<Message | void>;
65
66
  /** Sends a message of type voice */
66
67
  sendVoiceMessage(voice: Buffer, ext: string): Promise<void>;
67
68
  /** Use this method to replace a value or add a new config */
@@ -69,7 +70,6 @@ declare class RootStore {
69
70
  /** This replaces all the configurations by this object */
70
71
  updateConfig(config: Config): void;
71
72
  private _applyConfig;
72
- publishConfigChanged(): void;
73
73
  updatePreferredLanguage(lang: string): void;
74
74
  /** Starts a timer to remove the typing animation when it's completed */
75
75
  private _startTypingTimer;
@@ -30,6 +30,7 @@ const main_1 = require("../main");
30
30
  const translations_1 = require("../translations");
31
31
  const utils_1 = require("../utils");
32
32
  const analytics_1 = require("../utils/analytics");
33
+ const webchatEvents_1 = require("../utils/webchatEvents");
33
34
  const composer_1 = __importDefault(require("./composer"));
34
35
  const view_1 = __importDefault(require("./view"));
35
36
  class RootStore {
@@ -50,6 +51,9 @@ class RootStore {
50
51
  setSocket(socket) {
51
52
  this.api = new api_1.default(socket);
52
53
  }
54
+ setSelectedMessage(messageId) {
55
+ this.selectedMessageId = messageId;
56
+ }
53
57
  get isConversationStarted() {
54
58
  var _a;
55
59
  return !!((_a = this.currentConversation) === null || _a === void 0 ? void 0 : _a.messages.length);
@@ -81,10 +85,6 @@ class RootStore {
81
85
  var _a;
82
86
  return (_a = this.currentConversation) === null || _a === void 0 ? void 0 : _a.id;
83
87
  }
84
- postMessage(name, payload) {
85
- const chatId = this.config.chatId;
86
- window.parent.postMessage({ name, chatId, payload }, '*');
87
- }
88
88
  updateMessages(messages) {
89
89
  if (this.currentConversation) {
90
90
  this.currentConversation.messages = messages;
@@ -160,7 +160,7 @@ class RootStore {
160
160
  yield this.fetchConversation();
161
161
  (0, mobx_1.runInAction)('-> setInitialized', () => {
162
162
  this.isInitialized = true;
163
- this.postMessage('webchatReady');
163
+ (0, webchatEvents_1.postMessageToParent)('LIFECYCLE.READY', undefined, this.config.chatId);
164
164
  });
165
165
  }
166
166
  catch (err) {
@@ -229,23 +229,24 @@ class RootStore {
229
229
  });
230
230
  }
231
231
  /** Sends the specified message, or fetch the message in the composer */
232
- sendMessage(message) {
232
+ sendMessage(textMessage) {
233
233
  return __awaiter(this, void 0, void 0, function* () {
234
- if (message) {
235
- return this.sendData({ type: 'text', text: message });
236
- }
237
- const userMessage = this.composer.message;
238
- if (!userMessage || !userMessage.length) {
234
+ textMessage = textMessage || this.composer.message;
235
+ if (!textMessage) {
239
236
  return;
240
237
  }
241
238
  this.composer.updateMessage('');
242
239
  try {
243
- yield this.sendData({ type: 'text', text: userMessage });
240
+ const message = yield this.sendData({ type: 'text', text: textMessage });
244
241
  (0, analytics_1.trackMessage)('sent');
245
- this.composer.addMessageToHistory(userMessage);
242
+ if (message) {
243
+ (0, webchatEvents_1.postMessageToParent)('MESSAGE.SENT', message, this.config.chatId);
244
+ }
245
+ this.composer.addMessageToHistory(textMessage);
246
246
  }
247
247
  catch (e) {
248
- this.composer.updateMessage(userMessage);
248
+ this.composer.updateMessage(textMessage);
249
+ console.error('Webchat cloud not send message');
249
250
  throw e;
250
251
  }
251
252
  });
@@ -328,6 +329,7 @@ class RootStore {
328
329
  }
329
330
  const message = yield this.api.sendMessage(data, this.currentConversationId);
330
331
  this.updateLastMessage(this.currentConversationId, message);
332
+ return message;
331
333
  });
332
334
  }
333
335
  /** Sends a message of type voice */
@@ -364,10 +366,7 @@ class RootStore {
364
366
  const locale = (0, translations_1.getUserLocale)(this.config.locale);
365
367
  this.updateBotUILanguage(locale);
366
368
  document.documentElement.setAttribute('lang', locale);
367
- this.publishConfigChanged();
368
- }
369
- publishConfigChanged() {
370
- this.postMessage('configChanged', JSON.stringify(this.config, undefined, 2));
369
+ (0, webchatEvents_1.postMessageToParent)('CONFIG.SET', Object.assign({}, this.config), this.config.chatId);
371
370
  }
372
371
  updatePreferredLanguage(lang) {
373
372
  this.preferredLanguage = lang;
@@ -439,6 +438,9 @@ __decorate([
439
438
  __decorate([
440
439
  mobx_1.observable
441
440
  ], RootStore.prototype, "currentConversation", void 0);
441
+ __decorate([
442
+ mobx_1.observable
443
+ ], RootStore.prototype, "selectedMessageId", void 0);
442
444
  __decorate([
443
445
  mobx_1.observable
444
446
  ], RootStore.prototype, "botInfo", void 0);
@@ -463,6 +465,9 @@ __decorate([
463
465
  __decorate([
464
466
  mobx_1.action.bound
465
467
  ], RootStore.prototype, "setSocket", null);
468
+ __decorate([
469
+ mobx_1.action.bound
470
+ ], RootStore.prototype, "setSelectedMessage", null);
466
471
  __decorate([
467
472
  mobx_1.computed
468
473
  ], RootStore.prototype, "isConversationStarted", null);
@@ -487,9 +492,6 @@ __decorate([
487
492
  __decorate([
488
493
  mobx_1.computed
489
494
  ], RootStore.prototype, "currentConversationId", null);
490
- __decorate([
491
- mobx_1.action.bound
492
- ], RootStore.prototype, "postMessage", null);
493
495
  __decorate([
494
496
  mobx_1.action.bound
495
497
  ], RootStore.prototype, "updateMessages", null);
@@ -556,9 +558,6 @@ __decorate([
556
558
  __decorate([
557
559
  mobx_1.action.bound
558
560
  ], RootStore.prototype, "updateConfig", null);
559
- __decorate([
560
- mobx_1.action.bound
561
- ], RootStore.prototype, "publishConfigChanged", null);
562
561
  __decorate([
563
562
  mobx_1.action.bound
564
563
  ], RootStore.prototype, "updatePreferredLanguage", null);
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const merge_1 = __importDefault(require("lodash/merge"));
13
13
  const mobx_1 = require("mobx");
14
14
  const constants_1 = __importDefault(require("../core/constants"));
15
+ const webchatEvents_1 = require("../utils/webchatEvents");
15
16
  class ViewStore {
16
17
  constructor(rootStore, fullscreen) {
17
18
  /** If false, probably embedded on a website or on the studio */
@@ -111,7 +112,7 @@ class ViewStore {
111
112
  }
112
113
  setLoadingCompleted() {
113
114
  this._isLoading = false;
114
- this.rootStore.postMessage('webchatLoaded');
115
+ (0, webchatEvents_1.postMessageToParent)('LIFECYCLE.LOADED', undefined, this.rootStore.config.chatId);
115
116
  }
116
117
  showPoweredBy() {
117
118
  this.isPoweredByDisplayed = true;
@@ -157,7 +158,7 @@ class ViewStore {
157
158
  showChat() {
158
159
  if (this.disableAnimations) {
159
160
  this.activeView = 'side';
160
- this.rootStore.postMessage('webchatOpened');
161
+ (0, webchatEvents_1.postMessageToParent)('UI.OPENED', undefined, this.rootStore.config.chatId);
161
162
  return this._updateTransitions({ widgetTransition: undefined, sideTransition: 'none' });
162
163
  }
163
164
  this._updateTransitions({ widgetTransition: 'fadeOut' });
@@ -165,7 +166,7 @@ class ViewStore {
165
166
  this._updateTransitions({ sideTransition: 'fadeIn' });
166
167
  }, constants_1.default.ANIMATION_DURATION + 10);
167
168
  this._endAnimation('side');
168
- this.rootStore.postMessage('webchatOpened');
169
+ (0, webchatEvents_1.postMessageToParent)('UI.OPENED', undefined, this.rootStore.config.chatId);
169
170
  }
170
171
  hideChat() {
171
172
  if (this.isFullscreen) {
@@ -173,7 +174,7 @@ class ViewStore {
173
174
  }
174
175
  if (this.disableAnimations) {
175
176
  this.activeView = 'widget';
176
- this.rootStore.postMessage('webchatClosed');
177
+ (0, webchatEvents_1.postMessageToParent)('UI.CLOSED', undefined, this.rootStore.config.chatId);
177
178
  return this._updateTransitions({ widgetTransition: undefined, sideTransition: undefined });
178
179
  }
179
180
  this._updateTransitions({ sideTransition: 'fadeOut' });
@@ -183,7 +184,7 @@ class ViewStore {
183
184
  }, constants_1.default.ANIMATION_DURATION + 10);
184
185
  }
185
186
  this._endAnimation('widget');
186
- this.rootStore.postMessage('webchatClosed');
187
+ (0, webchatEvents_1.postMessageToParent)('UI.CLOSED', undefined, this.rootStore.config.chatId);
187
188
  }
188
189
  _endAnimation(finalView) {
189
190
  setTimeout(() => {
package/dist/typings.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  /// <reference types="react" />
2
+ import { UserCredentials } from '@botpress/messaging-socket';
2
3
  import { RootStore } from './store';
3
4
  import { BPStorage } from './utils/storage';
4
5
  declare global {
@@ -35,7 +36,7 @@ export declare namespace Renderer {
35
36
  isLastMessage?: boolean;
36
37
  sentOn?: Date;
37
38
  inlineFeedback?: any;
38
- onSendData?: (data: any) => Promise<void>;
39
+ onSendData?: (data: any) => Promise<Message | void>;
39
40
  onFileUpload?: (label: string, payload: any, file: File) => Promise<void>;
40
41
  /** Allows to autoplay voice messages coming from the bot */
41
42
  onAudioEnded?: () => void;
@@ -135,11 +136,27 @@ export interface StudioConnector {
135
136
  getModuleInjector: any;
136
137
  loadModuleView: any;
137
138
  }
139
+ export declare type WebchatEventType = 'LIFECYCLE.LOADED' | 'LIFECYCLE.READY' | 'UI.OPENED' | 'UI.CLOSED' | 'UI.RESIZE' | 'UI.SET-CLASS' | 'CONFIG.SET' | 'MESSAGE.SENT' | 'MESSAGE.RECEIVED' | 'MESSAGE.SELECTED' | 'USER.CONNECTED';
140
+ export interface WebchatEvent {
141
+ type: WebchatEventType;
142
+ value: any;
143
+ chatId: string;
144
+ }
138
145
  export interface Config {
139
146
  /** Url of the messaging server */
140
147
  messagingUrl: string;
141
148
  /** Id of your messaging client */
142
149
  clientId: string;
150
+ /**
151
+ * Refers to a specific webchat reference in parent window. Useful when using multiple chat window
152
+ * @default 'bp-web-widget'
153
+ */
154
+ chatId: string;
155
+ /**
156
+ * Url where the webchat bundle is hosted
157
+ * @default: '/'
158
+ */
159
+ hostUrl?: string;
143
160
  /**
144
161
  * Url of the Media File Service where we fetch the bot info
145
162
  * @default ''
@@ -250,22 +267,12 @@ export interface Config {
250
267
  * Experimental: expose the store to the parent frame for more control on the webchat's behavior
251
268
  * @default false
252
269
  */
253
- exposeStore?: boolean;
254
- /**
255
- * If true, Websocket is created when the Webchat is opened. Bot cannot be proactive.
256
- * @default false
257
- */
258
270
  lazySocket?: boolean;
259
271
  /**
260
272
  * If true, chat will no longer play the notification sound for new messages.
261
273
  * @default false
262
274
  */
263
275
  disableNotificationSound?: boolean;
264
- /**
265
- * Refers to a specific webchat reference in parent window. Useful when using multiple chat window
266
- * @default ''
267
- */
268
- chatId?: string;
269
276
  /**
270
277
  * CSS class to be applied to iframe
271
278
  * @default ''
@@ -277,6 +284,10 @@ export interface Config {
277
284
  * @default ''
278
285
  */
279
286
  googleMapsAPIKey?: string;
287
+ /**
288
+ * Allows setting a custom user id
289
+ */
290
+ customUser?: UserCredentials;
280
291
  }
281
292
  export interface BotDetails {
282
293
  website?: string;
@@ -0,0 +1,2 @@
1
+ import { WebchatEventType } from '../typings';
2
+ export declare const postMessageToParent: (type: WebchatEventType, value: any, chatId: string) => void;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.postMessageToParent = void 0;
7
+ const cloneDeep_1 = __importDefault(require("lodash/cloneDeep"));
8
+ const postMessageToParent = (type, value, chatId) => {
9
+ var _a;
10
+ //cloneDeep necessary because of potentially nested mobx proxy object isn't serializable
11
+ const evt = { type, value: (0, cloneDeep_1.default)(value), chatId };
12
+ (_a = window.parent) === null || _a === void 0 ? void 0 : _a.postMessage(evt, '*');
13
+ };
14
+ exports.postMessageToParent = postMessageToParent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botpress/webchat",
3
- "version": "0.3.3",
3
+ "version": "0.5.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "source": "src/index.tsx",
@@ -29,8 +29,8 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@blueprintjs/core": "^3.23.1",
32
- "@botpress/messaging-components": "0.3.0",
33
- "@botpress/messaging-socket": "1.1.1",
32
+ "@botpress/messaging-components": "0.4.2",
33
+ "@botpress/messaging-socket": "1.2.0",
34
34
  "@formatjs/intl-pluralrules": "^4.1.6",
35
35
  "@formatjs/intl-utils": "^3.8.4",
36
36
  "@juggle/resize-observer": "^3.0.2",