@botpress/webchat 0.5.0 → 0.5.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.
@@ -1,90 +1,89 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
2
21
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
23
  };
5
24
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const resize_observer_1 = require("@juggle/resize-observer");
7
25
  const difference_in_minutes_1 = __importDefault(require("date-fns/difference_in_minutes"));
8
- const debounce_1 = __importDefault(require("lodash/debounce"));
26
+ const last_1 = __importDefault(require("lodash/last"));
9
27
  const mobx_1 = require("mobx");
10
28
  const mobx_react_1 = require("mobx-react");
11
- const react_1 = __importDefault(require("react"));
29
+ const react_1 = __importStar(require("react"));
12
30
  const react_intl_1 = require("react-intl");
31
+ const react_scroll_to_bottom_1 = __importStar(require("react-scroll-to-bottom"));
13
32
  const constants_1 = __importDefault(require("../../core/constants"));
14
33
  const Avatar_1 = __importDefault(require("../common/Avatar"));
15
34
  const MessageGroup_1 = __importDefault(require("./MessageGroup"));
16
35
  class MessageList extends react_1.default.Component {
17
- constructor() {
18
- super(...arguments);
19
- this.state = { showNewMessageIndicator: false, manualScroll: false };
20
- this.shouldDisplayMessage = (m) => {
21
- return m.payload.type !== 'postback';
22
- };
23
- this.handleScroll = (0, debounce_1.default)((e) => {
24
- const scroll = this.messagesDiv.scrollHeight - this.messagesDiv.scrollTop - this.messagesDiv.clientHeight;
25
- const manualScroll = scroll >= 150;
26
- const showNewMessageIndicator = this.state.showNewMessageIndicator && manualScroll;
27
- this.setState({ manualScroll, showNewMessageIndicator });
28
- }, 50);
29
- }
30
36
  componentDidMount() {
31
- this.tryScrollToBottom(true);
32
37
  (0, mobx_1.observe)(this.props.focusedArea, (focus) => {
33
- focus.newValue === 'convo' && this.messagesDiv.focus();
38
+ var _a;
39
+ focus.newValue === 'convo' && ((_a = this.messagesDiv) === null || _a === void 0 ? void 0 : _a.focus());
34
40
  });
35
- if (this.props.currentMessages) {
36
- (0, mobx_1.observe)(this.props.currentMessages, (messages) => {
37
- if (this.state.manualScroll) {
38
- if (!this.state.showNewMessageIndicator) {
39
- this.setState({ showNewMessageIndicator: true });
40
- }
41
- return;
42
- }
43
- this.tryScrollToBottom();
44
- });
45
- }
46
- // this should account for keyboard rendering as it triggers a resize of the messagesDiv
47
- this.divSizeObserver = new resize_observer_1.ResizeObserver((0, debounce_1.default)((_divResizeEntry) => {
48
- // we don't need to do anything with the resize entry
49
- this.tryScrollToBottom();
50
- }, 200, { trailing: true }));
51
- this.divSizeObserver.observe(this.messagesDiv);
52
41
  }
53
- componentWillUnmount() {
54
- this.divSizeObserver.disconnect();
42
+ render() {
43
+ return (react_1.default.createElement(react_scroll_to_bottom_1.default, { mode: 'bottom', initialScrollBehavior: 'auto', tabIndex: 0, className: 'bpw-msg-list-scroll-container', scrollViewClassName: 'bpw-msg-list', ref: (m) => {
44
+ this.messagesDiv = m;
45
+ }, followButtonClassName: 'bpw-msg-list-follow' },
46
+ react_1.default.createElement(Content, Object.assign({}, this.props))));
55
47
  }
56
- componentDidUpdate() {
57
- if (this.state.manualScroll) {
58
- return;
48
+ }
49
+ const Content = (0, mobx_react_1.observer)((props) => {
50
+ var _a, _b;
51
+ const [state, setState] = (0, react_1.useState)({
52
+ showNewMessageIndicator: false,
53
+ messagesLength: undefined
54
+ });
55
+ const scrollToBottom = (0, react_scroll_to_bottom_1.useScrollToBottom)();
56
+ const [sticky] = (0, react_scroll_to_bottom_1.useSticky)();
57
+ (0, react_1.useEffect)(() => {
58
+ var _a, _b;
59
+ const stateUpdate = Object.assign(Object.assign({}, state), { messagesLength: (_a = props === null || props === void 0 ? void 0 : props.currentMessages) === null || _a === void 0 ? void 0 : _a.length });
60
+ if (!sticky && state.messagesLength !== ((_b = props === null || props === void 0 ? void 0 : props.currentMessages) === null || _b === void 0 ? void 0 : _b.length)) {
61
+ setState(Object.assign(Object.assign({}, stateUpdate), { showNewMessageIndicator: true }));
59
62
  }
60
- this.tryScrollToBottom();
61
- }
62
- tryScrollToBottom(delayed) {
63
- setTimeout(() => {
64
- try {
65
- this.messagesDiv.scrollTop = this.messagesDiv.scrollHeight;
66
- }
67
- catch (err) {
68
- // Discard the error
69
- }
70
- }, delayed ? 250 : 0);
71
- }
72
- renderDate(date) {
63
+ else {
64
+ setState(Object.assign(Object.assign({}, stateUpdate), { showNewMessageIndicator: false }));
65
+ }
66
+ }, [(_a = props === null || props === void 0 ? void 0 : props.currentMessages) === null || _a === void 0 ? void 0 : _a.length, sticky]);
67
+ const shouldDisplayMessage = (m) => {
68
+ return m.payload.type !== 'postback';
69
+ };
70
+ const renderDate = (date) => {
73
71
  return (react_1.default.createElement("div", { className: 'bpw-date-container' },
74
- new Intl.DateTimeFormat(this.props.intl.locale || 'en', {
72
+ new Intl.DateTimeFormat(props.intl.locale || 'en', {
75
73
  month: 'short',
76
74
  day: 'numeric',
77
75
  hour: 'numeric',
78
76
  minute: 'numeric'
79
77
  }).format(new Date(date)),
80
78
  react_1.default.createElement("div", { className: 'bpw-small-line' })));
81
- }
82
- renderAvatar(name, url) {
79
+ };
80
+ const renderAvatar = (name, url) => {
83
81
  const avatarSize = 40;
84
82
  return react_1.default.createElement(Avatar_1.default, { name: name, avatarUrl: url, height: avatarSize, width: avatarSize });
85
- }
86
- renderMessageGroups() {
87
- const messages = (this.props.currentMessages || []).filter((m) => this.shouldDisplayMessage(m));
83
+ };
84
+ const renderMessageGroups = () => {
85
+ var _a;
86
+ const messages = (props.currentMessages || []).filter((m) => shouldDisplayMessage(m));
88
87
  const groups = [];
89
88
  let lastSpeaker = undefined;
90
89
  let lastDate = undefined;
@@ -106,7 +105,7 @@ class MessageList extends react_1.default.Component {
106
105
  lastSpeaker = speaker;
107
106
  lastDate = date;
108
107
  });
109
- if (this.props.isBotTyping.get()) {
108
+ if ((_a = props === null || props === void 0 ? void 0 : props.isBotTyping) === null || _a === void 0 ? void 0 : _a.get()) {
110
109
  if (lastSpeaker !== 'bot') {
111
110
  currentGroup = [];
112
111
  groups.push(currentGroup);
@@ -123,24 +122,20 @@ class MessageList extends react_1.default.Component {
123
122
  const groupDate = group === null || group === void 0 ? void 0 : group[0].sentOn;
124
123
  const isDateNeeded = !groups[i - 1] ||
125
124
  (0, difference_in_minutes_1.default)(new Date(groupDate), new Date(lastDate)) > constants_1.default.TIME_BETWEEN_DATES;
126
- const [{ authorId }] = group;
127
- const avatar = !authorId && this.renderAvatar(this.props.botName, this.props.botAvatarUrl);
125
+ const { authorId } = (0, last_1.default)(group);
126
+ const avatar = !authorId && renderAvatar(props.botName, props.botAvatarUrl);
128
127
  return (react_1.default.createElement("div", { key: i },
129
- isDateNeeded && this.renderDate(group[0].sentOn),
128
+ isDateNeeded && renderDate(group[0].sentOn),
130
129
  react_1.default.createElement(MessageGroup_1.default, { isBot: !authorId, avatar: avatar, key: `msg-group-${i}`, isLastGroup: i >= groups.length - 1, messages: group })));
131
130
  })));
132
- }
133
- render() {
134
- return (react_1.default.createElement("div", { tabIndex: 0, className: 'bpw-msg-list', ref: (m) => {
135
- this.messagesDiv = m;
136
- }, onScroll: this.handleScroll },
137
- this.state.showNewMessageIndicator && (react_1.default.createElement("div", { className: "bpw-new-messages-indicator", onClick: (e) => this.tryScrollToBottom() },
138
- react_1.default.createElement("span", null, this.props.intl.formatMessage({
139
- id: `messages.newMessage${this.props.currentMessages.length === 1 ? '' : 's'}`
140
- })))),
141
- this.renderMessageGroups()));
142
- }
143
- }
131
+ };
132
+ return (react_1.default.createElement(react_1.default.Fragment, null,
133
+ state.showNewMessageIndicator && (react_1.default.createElement("div", { className: "bpw-new-messages-indicator", onClick: (e) => scrollToBottom() },
134
+ react_1.default.createElement("span", null, props.intl.formatMessage({
135
+ id: `messages.newMessage${((_b = props === null || props === void 0 ? void 0 : props.currentMessages) === null || _b === void 0 ? void 0 : _b.length) === 1 ? '' : 's'}`
136
+ })))),
137
+ renderMessageGroups()));
138
+ });
144
139
  exports.default = (0, mobx_react_1.inject)(({ store }) => ({
145
140
  intl: store.intl,
146
141
  botName: store.botName,
package/dist/core/api.js CHANGED
@@ -50,7 +50,7 @@ class WebchatApi {
50
50
  try {
51
51
  const conversation = yield this.socket.socket.getConversation(conversationId);
52
52
  this.socket.socket.switchConversation(conversation.id);
53
- const messages = (yield this.socket.socket.listMessages()).filter((x) => x.payload.type !== 'visit');
53
+ const messages = (yield this.socket.socket.listMessages(500)).filter((x) => x.payload.type !== 'visit');
54
54
  return Object.assign(Object.assign({}, conversation), { messages });
55
55
  }
56
56
  catch (err) {
@@ -11,4 +11,5 @@ export default class BpSocket {
11
11
  connect(): Promise<void>;
12
12
  reload(config: Config): Promise<void>;
13
13
  private getCreds;
14
+ private getUserData;
14
15
  }
@@ -31,7 +31,8 @@ class BpSocket {
31
31
  connect() {
32
32
  return __awaiter(this, void 0, void 0, function* () {
33
33
  const creds = this.getCreds();
34
- yield this.socket.connect(creds);
34
+ const userData = this.getUserData();
35
+ yield this.socket.connect(creds, userData);
35
36
  if (this.socket.userId) {
36
37
  const userId = this.socket.userId;
37
38
  window.BP_STORAGE.set('creds', this.socket.creds);
@@ -53,5 +54,8 @@ class BpSocket {
53
54
  getCreds() {
54
55
  return this.config.customUser || window.BP_STORAGE.get('creds');
55
56
  }
57
+ getUserData() {
58
+ return this.config.userData;
59
+ }
56
60
  }
57
61
  exports.default = BpSocket;
@@ -1 +1,2 @@
1
1
  declare module '*.scss';
2
+ declare module 'react-scroll-to-bottom';
package/dist/main.js CHANGED
@@ -80,6 +80,7 @@ class Web extends react_1.default.Component {
80
80
  return this.props.sendData(data.payload);
81
81
  case 'event':
82
82
  const { type, text, conversationId } = data.payload;
83
+ yield this.initialize();
83
84
  if (type === 'show') {
84
85
  this.props.showChat();
85
86
  (0, analytics_1.trackWebchatState)('show');
@@ -112,7 +113,7 @@ class Web extends react_1.default.Component {
112
113
  }
113
114
  });
114
115
  this.handleNewMessage = (event) => __awaiter(this, void 0, void 0, function* () {
115
- var _b, _c, _d;
116
+ var _b, _c, _d, _e, _f;
116
117
  if (!this.isCurrentConversation(event)) {
117
118
  // don't do anything, it's a message from another conversation
118
119
  return;
@@ -125,34 +126,70 @@ class Web extends react_1.default.Component {
125
126
  // don't do anything, it's the system message
126
127
  return;
127
128
  }
128
- if (((_c = this.props.currentConversation) === null || _c === void 0 ? void 0 : _c.userId) !== event.authorId) {
129
+ if (((_c = event.payload) === null || _c === void 0 ? void 0 : _c.type) === 'trigger') {
130
+ //
131
+ const { trigger } = event.payload;
132
+ if (typeof (trigger === null || trigger === void 0 ? void 0 : trigger.type) === 'string') {
133
+ yield this.handleTrigger(trigger);
134
+ return;
135
+ }
136
+ }
137
+ if (((_d = this.props.currentConversation) === null || _d === void 0 ? void 0 : _d.userId) !== event.authorId) {
129
138
  (0, analytics_1.trackMessage)('received');
130
- (0, webchatEvents_1.postMessageToParent)('MESSAGE.RECEIVED', event, this.props.config.chatId);
139
+ (0, webchatEvents_1.postMessageToParent)('MESSAGE.RECEIVED', event, this.config.chatId);
131
140
  // This is to handle a special case for the emulator, setting the selected css class to the last message group
132
141
  // This needs a rethinking
133
142
  if (event.id) {
134
- (_d = this.props.store) === null || _d === void 0 ? void 0 : _d.setSelectedMessage(event.id);
143
+ (_e = this.props.store) === null || _e === void 0 ? void 0 : _e.setSelectedMessage(event.id);
135
144
  }
136
145
  }
137
146
  this.props.updateLastMessage(event.conversationId, event);
138
147
  yield this.props.addEventToConversation(event);
139
148
  // there's no focus on the actual conversation
140
149
  if (!document.hasFocus() || this.props.activeView !== 'side') {
141
- yield this.playSound();
142
- this.props.incrementUnread();
150
+ if (((_f = this.props.currentConversation) === null || _f === void 0 ? void 0 : _f.userId) !== event.authorId) {
151
+ // only increment unread if the message comes from the bot
152
+ yield this.playSound();
153
+ this.props.incrementUnread();
154
+ }
143
155
  }
144
156
  this.handleResetUnreadCount();
145
157
  });
146
158
  this.handleTyping = (event) => __awaiter(this, void 0, void 0, function* () {
147
159
  yield this.props.updateTyping(event);
148
160
  });
161
+ this.handleTrigger = (trigger) => __awaiter(this, void 0, void 0, function* () {
162
+ var _g;
163
+ if (trigger.type === 'webchat-visibility') {
164
+ if (trigger.visibility === 'hide') {
165
+ this.props.hideChat();
166
+ }
167
+ else if (trigger.visibility === 'show') {
168
+ this.props.showChat();
169
+ }
170
+ else if (trigger.visibility === 'toggle') {
171
+ this.props.displayWidgetView ? this.props.showChat() : this.props.hideChat();
172
+ }
173
+ }
174
+ if (trigger.type === 'webchat-config') {
175
+ yield this.handleIframeApi({
176
+ data: { action: 'mergeConfig', payload: Object.assign(Object.assign({}, this.config), ((_g = trigger.config) !== null && _g !== void 0 ? _g : {})) }
177
+ });
178
+ }
179
+ if (trigger.type === 'custom-event') {
180
+ (0, webchatEvents_1.postMessageToParent)('TRIGGER', trigger.event, this.props.config.chatId);
181
+ }
182
+ });
149
183
  this.playSound = (0, debounce_1.default)(() => __awaiter(this, void 0, void 0, function* () {
150
- var _e;
151
- const disableNotificationSound = this.config.disableNotificationSound || ((_e = this.props.config) === null || _e === void 0 ? void 0 : _e.disableNotificationSound);
184
+ var _h;
185
+ const disableNotificationSound = this.config.disableNotificationSound || ((_h = this.props.config) === null || _h === void 0 ? void 0 : _h.disableNotificationSound);
152
186
  if (disableNotificationSound || this.audio.readyState < 2) {
153
187
  return;
154
188
  }
155
- yield this.audio.play();
189
+ try {
190
+ yield this.audio.play();
191
+ }
192
+ catch (_j) { }
156
193
  }), constants_1.default.MIN_TIME_BETWEEN_SOUNDS);
157
194
  this.handleResetUnreadCount = () => {
158
195
  if (document.hasFocus() && this.props.activeView === 'side') {
@@ -182,19 +219,24 @@ class Web extends react_1.default.Component {
182
219
  void this.initializeIfChatDisplayed();
183
220
  }
184
221
  }
185
- initializeIfChatDisplayed() {
222
+ initialize() {
186
223
  return __awaiter(this, void 0, void 0, function* () {
187
224
  if (this.hasBeenInitialized) {
188
225
  return;
189
226
  }
227
+ this.hasBeenInitialized = true;
228
+ if (this.isLazySocket() || !this.socket) {
229
+ yield this.initializeSocket();
230
+ }
231
+ yield this.socket.connect();
232
+ this.props.setSocket(this.socket);
233
+ yield this.props.initializeChat();
234
+ });
235
+ }
236
+ initializeIfChatDisplayed() {
237
+ return __awaiter(this, void 0, void 0, function* () {
190
238
  if (this.props.activeView === 'side' || this.props.isFullscreen) {
191
- this.hasBeenInitialized = true;
192
- if (this.isLazySocket() || !this.socket) {
193
- yield this.initializeSocket();
194
- }
195
- yield this.socket.connect();
196
- this.props.setSocket(this.socket);
197
- yield this.props.initializeChat();
239
+ this.initialize();
198
240
  }
199
241
  });
200
242
  }
@@ -252,7 +294,7 @@ class Web extends react_1.default.Component {
252
294
  if (this.config.lazySocket !== undefined) {
253
295
  return this.config.lazySocket;
254
296
  }
255
- return (_a = this.props.botInfo) === null || _a === void 0 ? void 0 : _a.lazySocket;
297
+ return ((_a = this.props.botInfo) === null || _a === void 0 ? void 0 : _a.lazySocket) !== false;
256
298
  }
257
299
  renderWidget() {
258
300
  if (!this.props.showWidgetButton) {
@@ -33,6 +33,13 @@ declare class RootStore {
33
33
  get botName(): string;
34
34
  get hasBotInfoDescription(): boolean;
35
35
  get botAvatarUrl(): string | undefined;
36
+ get coverPictureUrl(): string | undefined;
37
+ get description(): string | undefined;
38
+ get website(): string | undefined;
39
+ get phoneNumber(): string | undefined;
40
+ get termsConditions(): string | undefined;
41
+ get privacyPolicy(): string | undefined;
42
+ get emailAddress(): string | undefined;
36
43
  get rtl(): boolean;
37
44
  get escapeHTML(): boolean;
38
45
  get currentMessages(): Message[];
@@ -62,7 +69,9 @@ declare class RootStore {
62
69
  sendFeedback(feedback: number, messageId: string): Promise<void>;
63
70
  downloadConversation(): Promise<void>;
64
71
  /** Sends an event or a message, depending on how the backend manages those types */
65
- sendData(data: any): Promise<Message | void>;
72
+ sendData(data: any, options?: {
73
+ showBotTyping?: boolean;
74
+ }): Promise<Message | void>;
66
75
  /** Sends a message of type voice */
67
76
  sendVoiceMessage(voice: Buffer, ext: string): Promise<void>;
68
77
  /** Use this method to replace a value or add a new config */
@@ -70,6 +70,34 @@ class RootStore {
70
70
  var _a, _b, _c;
71
71
  return ((_b = (_a = this.botInfo) === null || _a === void 0 ? void 0 : _a.details) === null || _b === void 0 ? void 0 : _b.avatarUrl) || ((_c = this.config) === null || _c === void 0 ? void 0 : _c.avatarUrl) || undefined;
72
72
  }
73
+ get coverPictureUrl() {
74
+ var _a, _b, _c, _d;
75
+ return (_c = (_b = (_a = this.botInfo) === null || _a === void 0 ? void 0 : _a.details) === null || _b === void 0 ? void 0 : _b.coverPictureUrl) !== null && _c !== void 0 ? _c : (_d = this.config) === null || _d === void 0 ? void 0 : _d.coverPictureUrl;
76
+ }
77
+ get description() {
78
+ var _a;
79
+ return (_a = this.config) === null || _a === void 0 ? void 0 : _a.botConversationDescription;
80
+ }
81
+ get website() {
82
+ var _a, _b, _c, _d;
83
+ return (_c = (_b = (_a = this.botInfo) === null || _a === void 0 ? void 0 : _a.details) === null || _b === void 0 ? void 0 : _b.website) !== null && _c !== void 0 ? _c : (_d = this.config) === null || _d === void 0 ? void 0 : _d.website;
84
+ }
85
+ get phoneNumber() {
86
+ var _a, _b, _c, _d;
87
+ return (_c = (_b = (_a = this.botInfo) === null || _a === void 0 ? void 0 : _a.details) === null || _b === void 0 ? void 0 : _b.phoneNumber) !== null && _c !== void 0 ? _c : (_d = this.config) === null || _d === void 0 ? void 0 : _d.phoneNumber;
88
+ }
89
+ get termsConditions() {
90
+ var _a, _b, _c, _d;
91
+ return (_c = (_b = (_a = this.botInfo) === null || _a === void 0 ? void 0 : _a.details) === null || _b === void 0 ? void 0 : _b.termsConditions) !== null && _c !== void 0 ? _c : (_d = this.config) === null || _d === void 0 ? void 0 : _d.termsConditions;
92
+ }
93
+ get privacyPolicy() {
94
+ var _a, _b, _c, _d;
95
+ return (_c = (_b = (_a = this.botInfo) === null || _a === void 0 ? void 0 : _a.details) === null || _b === void 0 ? void 0 : _b.privacyPolicy) !== null && _c !== void 0 ? _c : (_d = this.config) === null || _d === void 0 ? void 0 : _d.privacyPolicy;
96
+ }
97
+ get emailAddress() {
98
+ var _a, _b, _c, _d;
99
+ return (_c = (_b = (_a = this.botInfo) === null || _a === void 0 ? void 0 : _a.details) === null || _b === void 0 ? void 0 : _b.emailAddress) !== null && _c !== void 0 ? _c : (_d = this.config) === null || _d === void 0 ? void 0 : _d.emailAddress;
100
+ }
73
101
  get rtl() {
74
102
  return (0, translations_1.isRTLLocale)(this.preferredLanguage);
75
103
  }
@@ -79,7 +107,7 @@ class RootStore {
79
107
  }
80
108
  get currentMessages() {
81
109
  var _a;
82
- return ((_a = this.currentConversation) === null || _a === void 0 ? void 0 : _a.messages) || [];
110
+ return (((_a = this.currentConversation) === null || _a === void 0 ? void 0 : _a.messages) || []).filter((x) => { var _a; return ((_a = x.payload) === null || _a === void 0 ? void 0 : _a.type) !== 'trigger'; });
83
111
  }
84
112
  get currentConversationId() {
85
113
  var _a;
@@ -237,7 +265,7 @@ class RootStore {
237
265
  }
238
266
  this.composer.updateMessage('');
239
267
  try {
240
- const message = yield this.sendData({ type: 'text', text: textMessage });
268
+ const message = yield this.sendData({ type: 'text', text: textMessage }, { showBotTyping: true });
241
269
  (0, analytics_1.trackMessage)('sent');
242
270
  if (message) {
243
271
  (0, webchatEvents_1.postMessageToParent)('MESSAGE.SENT', message, this.config.chatId);
@@ -321,12 +349,15 @@ class RootStore {
321
349
  });
322
350
  }
323
351
  /** Sends an event or a message, depending on how the backend manages those types */
324
- sendData(data) {
352
+ sendData(data, options) {
325
353
  return __awaiter(this, void 0, void 0, function* () {
326
354
  if (!this.isInitialized || !this.currentConversationId) {
327
355
  console.warn('[webchat] Cannot send data until the webchat is ready');
328
356
  return;
329
357
  }
358
+ if (options === null || options === void 0 ? void 0 : options.showBotTyping) {
359
+ this.isBotTyping.set(true);
360
+ }
330
361
  const message = yield this.api.sendMessage(data, this.currentConversationId);
331
362
  this.updateLastMessage(this.currentConversationId, message);
332
363
  return message;
@@ -40,7 +40,9 @@ class ViewStore {
40
40
  return (_a = this.rootStore.config) === null || _a === void 0 ? void 0 : _a.showConversationsButton;
41
41
  }
42
42
  get showBotInfoButton() {
43
- return !this.isConversationsDisplayed && this.rootStore.botInfo && this.rootStore.botInfo.showBotInfoPage;
43
+ var _a;
44
+ return (!this.isConversationsDisplayed &&
45
+ (((_a = this.rootStore.botInfo) === null || _a === void 0 ? void 0 : _a.showBotInfoPage) || !!this.rootStore.config.showBotInfoPage));
44
46
  }
45
47
  get showDownloadButton() {
46
48
  return !this.isConversationsDisplayed && !this.isBotInfoDisplayed && this.rootStore.config.enableTranscriptDownload;
@@ -49,7 +51,8 @@ class ViewStore {
49
51
  return (!this.isConversationsDisplayed && !this.isBotInfoDisplayed && this.rootStore.config.enableConversationDeletion);
50
52
  }
51
53
  get showCloseButton() {
52
- return !this.isFullscreen;
54
+ var _a, _b;
55
+ return (_b = (!this.isFullscreen && ((_a = this.rootStore.config) === null || _a === void 0 ? void 0 : _a.showCloseButton))) !== null && _b !== void 0 ? _b : true;
53
56
  }
54
57
  get showWidgetButton() {
55
58
  var _a;
@@ -62,7 +65,8 @@ class ViewStore {
62
65
  return !this._isLoading && this.activeView;
63
66
  }
64
67
  get isBotInfoDisplayed() {
65
- return this._showBotInfo && this.rootStore.botInfo && this.rootStore.botInfo.showBotInfoPage;
68
+ var _a;
69
+ return this._showBotInfo && (((_a = this.rootStore.botInfo) === null || _a === void 0 ? void 0 : _a.showBotInfoPage) || !!this.rootStore.config.showBotInfoPage);
66
70
  }
67
71
  /** Returns the active transition for the side panel, like fade in or out */
68
72
  get sideTransition() {
package/dist/typings.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /// <reference types="react" />
2
- import { UserCredentials } from '@botpress/messaging-socket';
2
+ import { UserCredentials, UserData } from '@botpress/messaging-socket';
3
3
  import { RootStore } from './store';
4
4
  import { BPStorage } from './utils/storage';
5
5
  declare global {
@@ -17,6 +17,20 @@ declare global {
17
17
  };
18
18
  }
19
19
  }
20
+ export declare namespace Trigger {
21
+ interface WebchatVisibility {
22
+ type: 'webchat-visibility';
23
+ visibility: 'show' | 'hide' | 'toggle';
24
+ }
25
+ interface WebchatConfig {
26
+ type: 'webchat-config';
27
+ config: Partial<Config>;
28
+ }
29
+ interface CustomEvent {
30
+ type: 'custom-event';
31
+ event: any;
32
+ }
33
+ }
20
34
  export declare namespace Renderer {
21
35
  interface Message {
22
36
  type?: string;
@@ -36,7 +50,9 @@ export declare namespace Renderer {
36
50
  isLastMessage?: boolean;
37
51
  sentOn?: Date;
38
52
  inlineFeedback?: any;
39
- onSendData?: (data: any) => Promise<Message | void>;
53
+ onSendData?: (data: any, options?: {
54
+ showBotTyping?: boolean;
55
+ }) => Promise<Message | void>;
40
56
  onFileUpload?: (label: string, payload: any, file: File) => Promise<void>;
41
57
  /** Allows to autoplay voice messages coming from the bot */
42
58
  onAudioEnded?: () => void;
@@ -136,7 +152,7 @@ export interface StudioConnector {
136
152
  getModuleInjector: any;
137
153
  loadModuleView: any;
138
154
  }
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';
155
+ 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' | 'TRIGGER';
140
156
  export interface WebchatEvent {
141
157
  type: WebchatEventType;
142
158
  value: any;
@@ -288,6 +304,48 @@ export interface Config {
288
304
  * Allows setting a custom user id
289
305
  */
290
306
  customUser?: UserCredentials;
307
+ /**
308
+ * Allows setting custom data to the user
309
+ */
310
+ userData?: UserData;
311
+ /**
312
+ * Displays the bot's website in the conversation page
313
+ */
314
+ website?: string;
315
+ /**
316
+ * Displays the bot's contact phone number in the conversation page
317
+ */
318
+ phoneNumber?: string;
319
+ /**
320
+ * Displays the bot's terms of service in the conversation page
321
+ */
322
+ termsConditions?: string;
323
+ /**
324
+ * Displays the bot's privacy policy in the conversation page
325
+ */
326
+ privacyPolicy?: string;
327
+ /**
328
+ * Displays the bot's email address in the conversation page
329
+ */
330
+ emailAddress?: string;
331
+ /**
332
+ * Displays the bot's cover picture in the conversation page
333
+ */
334
+ coverPictureUrl?: string;
335
+ /**
336
+ * Enables the bot's information page in the webchat
337
+ */
338
+ showBotInfoPage?: boolean;
339
+ /**
340
+ * Display's the webchat close button when the webchat is opened
341
+ */
342
+ showCloseButton?: boolean;
343
+ /**
344
+ * Replaces < and > with their HTML entities &lt; and &gt;.
345
+ * Setting it to false will let the markdown parser handle html itself
346
+ * @default true
347
+ */
348
+ escapeHtml?: boolean;
291
349
  }
292
350
  export interface BotDetails {
293
351
  website?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botpress/webchat",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "source": "src/index.tsx",
@@ -20,6 +20,7 @@
20
20
  "@types/crypto-js": "^4.1.1",
21
21
  "@types/js-cookie": "^3.0.1",
22
22
  "@types/lodash": "^4.14.178",
23
+ "@types/markdown-it": "^12.2.3",
23
24
  "@types/mime": "^2.0.3",
24
25
  "@types/node": "^16.11.13",
25
26
  "@types/react": "^17.0.38",
@@ -29,8 +30,8 @@
29
30
  },
30
31
  "dependencies": {
31
32
  "@blueprintjs/core": "^3.23.1",
32
- "@botpress/messaging-components": "0.4.2",
33
- "@botpress/messaging-socket": "1.2.0",
33
+ "@botpress/messaging-components": "0.4.3",
34
+ "@botpress/messaging-socket": "1.3.0",
34
35
  "@formatjs/intl-pluralrules": "^4.1.6",
35
36
  "@formatjs/intl-utils": "^3.8.4",
36
37
  "@juggle/resize-observer": "^3.0.2",
@@ -40,6 +41,7 @@
40
41
  "date-fns": "^1.30.1",
41
42
  "js-cookie": "^3.0.1",
42
43
  "lodash": "^4.17.21",
44
+ "markdown-it": "^13.0.1",
43
45
  "mime": "^3.0.0",
44
46
  "mobx": "5.15.7",
45
47
  "mobx-react": "^6.2.1",
@@ -48,7 +50,8 @@
48
50
  "react-dom": "^17.0.2",
49
51
  "react-ga": "^2.7.0",
50
52
  "react-intl": "^3.12.1",
53
+ "react-scroll-to-bottom": "^4.2.0",
51
54
  "snarkdown": "^2.0.0",
52
55
  "uuid": "^8.3.2"
53
56
  }
54
- }
57
+ }