@botpress/webchat 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  | File | ✅ | |
14
14
  | Audio | ✅ | |
15
15
  | Video | ✅ | |
16
- | Location | | |
16
+ | Location | | |
17
17
 
18
18
  ### Receiving
19
19
 
@@ -23,9 +23,19 @@
23
23
  | Quick Reply | ✅ | |
24
24
  | Postback | ✅ | |
25
25
  | Say Something | ✅ | |
26
- | Voice | | |
26
+ | Voice | | |
27
27
  | Image | ❌ | |
28
28
  | File | ❌ | |
29
29
  | Audio | ❌ | |
30
30
  | Video | ❌ | |
31
31
  | Location | ❌ | |
32
+
33
+ ## Development
34
+
35
+ When working on the webchat, to see changes made to the codebase, you will need to follow the steps detailed [here](../inject/README.md). The inject script is the part that links any web page to a webchat. This is the easiest way to manually test the webchat.
36
+
37
+ ## Tests
38
+
39
+ To run automated E2E tests, simply run the command `yarn test:chat [--browser <chromium | firefox | edge | electron>]` (for the list of supported browsers see: https://docs.cypress.io/guides/guides/launching-browsers#Browsers).
40
+
41
+ Tests can be found in `tests/e2e` and uses Cypress as the test framework. Configurations for Cypress can be found in the `cypress.json` file at the root of the repository.
@@ -2,8 +2,8 @@
2
2
  declare const Avatar: ({ name, avatarUrl, height, width }: AvatarProps) => JSX.Element;
3
3
  export default Avatar;
4
4
  export interface AvatarProps {
5
- name: string;
6
- avatarUrl: string;
5
+ name?: string;
6
+ avatarUrl?: string;
7
7
  height: number;
8
8
  width: number;
9
9
  }
@@ -7,4 +7,4 @@ declare const _default: React.ForwardRefExoticComponent<import("react-intl").Omi
7
7
  WrappedComponent: React.ComponentType<BotInfoProps>;
8
8
  } & import("mobx-react").IWrappedComponent<unknown>;
9
9
  export default _default;
10
- declare type BotInfoProps = WrappedComponentProps & Pick<StoreDef, 'botInfo' | 'botName' | 'avatarUrl' | 'toggleBotInfo' | 'startConversation' | 'isConversationStarted' | 'updatePreferredLanguage' | 'preferredLanguage' | 'escapeHTML' | 'rtl'>;
10
+ declare type BotInfoProps = WrappedComponentProps & Pick<StoreDef, 'coverPictureUrl' | 'description' | 'phoneNumber' | 'website' | 'emailAddress' | 'termsConditions' | 'privacyPolicy' | 'botInfo' | 'botName' | 'avatarUrl' | 'toggleBotInfo' | 'updatePreferredLanguage' | 'preferredLanguage' | 'escapeHTML' | 'rtl'>;
@@ -31,8 +31,8 @@ const Phone_1 = __importDefault(require("../../../icons/Phone"));
31
31
  const Website_1 = __importDefault(require("../../../icons/Website"));
32
32
  const utils_1 = require("../../../utils");
33
33
  const Avatar_1 = __importDefault(require("../Avatar"));
34
- const CoverPicture = ({ botInfo }) => (react_1.default.createElement("div", { className: 'bpw-botinfo-cover-picture-wrapper' },
35
- react_1.default.createElement("img", { className: 'bpw-botinfo-cover-picture', src: (botInfo === null || botInfo === void 0 ? void 0 : botInfo.details.coverPictureUrl) || `https://via.placeholder.com/400x175?text=${(botInfo === null || botInfo === void 0 ? void 0 : botInfo.name) || ''}` })));
34
+ const CoverPicture = ({ coverPictureUrl }) => (react_1.default.createElement("div", { className: 'bpw-botinfo-cover-picture-wrapper' },
35
+ react_1.default.createElement("img", { className: 'bpw-botinfo-cover-picture', src: coverPictureUrl })));
36
36
  class BotInfoPage extends react_1.default.Component {
37
37
  constructor() {
38
38
  super(...arguments);
@@ -50,51 +50,56 @@ class BotInfoPage extends react_1.default.Component {
50
50
  return react_1.default.createElement("div", { className: 'bpw-botinfo-description', dangerouslySetInnerHTML: { __html: html } });
51
51
  }
52
52
  render() {
53
- const { botInfo, botName, avatarUrl } = this.props;
54
- const onDismiss = this.props.isConversationStarted ? this.props.toggleBotInfo : this.props.startConversation;
53
+ const { botName, avatarUrl, coverPictureUrl, description, phoneNumber, website, emailAddress, termsConditions, privacyPolicy, botInfo } = this.props;
55
54
  return (react_1.default.createElement(react_1.Fragment, null,
56
55
  react_1.default.createElement("link", { rel: "stylesheet", href: "style.scss" }),
57
56
  react_1.default.createElement("div", { className: (0, classnames_1.default)('bpw-botinfo-container', {
58
57
  'bpw-rtl': this.props.rtl
59
58
  }) },
60
- react_1.default.createElement(CoverPicture, { botInfo: botInfo }),
59
+ coverPictureUrl ? react_1.default.createElement(CoverPicture, { coverPictureUrl: coverPictureUrl }) : react_1.default.createElement("div", { style: { height: '42px' } }),
61
60
  react_1.default.createElement("div", { className: 'bpw-botinfo-summary' },
62
61
  react_1.default.createElement(Avatar_1.default, { name: botName, avatarUrl: avatarUrl, height: 64, width: 64 }),
63
- react_1.default.createElement("h3", null, botName),
64
- this.renderDescription(botInfo.description)),
65
- botInfo.details && (react_1.default.createElement(react_1.default.Fragment, null,
62
+ react_1.default.createElement("h3", { style: { marginBottom: '10px' } }, botName),
63
+ description && this.renderDescription(description)),
64
+ react_1.default.createElement(react_1.default.Fragment, null,
66
65
  react_1.default.createElement("div", { className: 'bpw-botinfo-links' },
67
- botInfo.details.phoneNumber && (react_1.default.createElement("div", { className: 'bpw-botinfo-link' },
66
+ phoneNumber && (react_1.default.createElement("div", { className: 'bpw-botinfo-link' },
68
67
  react_1.default.createElement("i", null,
69
68
  react_1.default.createElement(Phone_1.default, null)),
70
- react_1.default.createElement("a", { target: '_blank', href: `tel:${botInfo.details.phoneNumber}` }, botInfo.details.phoneNumber))),
71
- botInfo.details.website && (react_1.default.createElement("div", { className: 'bpw-botinfo-link' },
69
+ react_1.default.createElement("a", { target: '_blank', rel: "noopener noreferrer", href: `tel:${phoneNumber}` }, phoneNumber))),
70
+ website && (react_1.default.createElement("div", { className: 'bpw-botinfo-link' },
72
71
  react_1.default.createElement("i", null,
73
72
  react_1.default.createElement(Website_1.default, null)),
74
- react_1.default.createElement("a", { target: '_blank', href: botInfo.details.website }, botInfo.details.website))),
75
- botInfo.details.emailAddress && (react_1.default.createElement("div", { className: 'bpw-botinfo-link' },
73
+ react_1.default.createElement("a", { target: '_blank', rel: "noopener noreferrer", href: website }, website))),
74
+ emailAddress && (react_1.default.createElement("div", { className: 'bpw-botinfo-link' },
76
75
  react_1.default.createElement("i", null,
77
76
  react_1.default.createElement(Email_1.default, null)),
78
- react_1.default.createElement("a", { target: '_blank', href: `mailto:${botInfo.details.emailAddress}` }, botInfo.details.emailAddress)))),
79
- botInfo.details.termsConditions && (react_1.default.createElement("div", { className: 'bpw-botinfo-terms' },
80
- react_1.default.createElement("a", { target: '_blank', href: botInfo.details.termsConditions },
77
+ react_1.default.createElement("a", { target: '_blank', rel: "noopener noreferrer", href: `mailto:${emailAddress}` }, emailAddress)))),
78
+ termsConditions && (react_1.default.createElement("div", { className: 'bpw-botinfo-terms' },
79
+ react_1.default.createElement("a", { target: '_blank', rel: "noopener noreferrer", href: termsConditions },
81
80
  react_1.default.createElement(react_intl_1.FormattedMessage, { id: 'botInfo.termsAndConditions' })))),
82
- botInfo.details.privacyPolicy && (react_1.default.createElement("div", { className: 'bpw-botinfo-terms' },
83
- react_1.default.createElement("a", { target: '_blank', href: botInfo.details.privacyPolicy },
84
- react_1.default.createElement(react_intl_1.FormattedMessage, { id: 'botInfo.privacyPolicy' })))))),
85
- botInfo.languages.length > 1 && (react_1.default.createElement("div", { className: 'bpw-botinfo-preferred-language' },
81
+ privacyPolicy && (react_1.default.createElement("div", { className: 'bpw-botinfo-terms' },
82
+ react_1.default.createElement("a", { target: '_blank', rel: "noopener noreferrer", href: privacyPolicy },
83
+ react_1.default.createElement(react_intl_1.FormattedMessage, { id: 'botInfo.privacyPolicy' }))))),
84
+ (botInfo === null || botInfo === void 0 ? void 0 : botInfo.languages) && botInfo.languages.length > 1 && (react_1.default.createElement("div", { className: 'bpw-botinfo-preferred-language' },
86
85
  react_1.default.createElement(react_intl_1.FormattedMessage, { id: 'botInfo.preferredLanguage' }),
87
86
  react_1.default.createElement("select", { value: this.props.preferredLanguage, onChange: this.changeLanguage }, botInfo.languages.map((lang) => (react_1.default.createElement("option", { key: lang, value: lang }, lang.toUpperCase())))))),
88
- react_1.default.createElement("button", { tabIndex: 1, ref: (el) => (this.btnEl = el), className: 'bpw-botinfo-start-button', onClick: onDismiss.bind(this, undefined) }, this.props.isConversationStarted ? (react_1.default.createElement(react_intl_1.FormattedMessage, { id: 'botInfo.backToConversation' })) : (react_1.default.createElement(react_intl_1.FormattedMessage, { id: 'botInfo.startConversation' }))))));
87
+ react_1.default.createElement("button", { tabIndex: 1, ref: (el) => (this.btnEl = el), className: 'bpw-botinfo-start-button', onClick: this.props.toggleBotInfo.bind(this, undefined) },
88
+ react_1.default.createElement(react_intl_1.FormattedMessage, { id: 'botInfo.backToConversation' })))));
89
89
  }
90
90
  }
91
91
  exports.default = (0, mobx_react_1.inject)(({ store }) => ({
92
+ coverPictureUrl: store.coverPictureUrl,
93
+ description: store.description,
94
+ phoneNumber: store.phoneNumber,
95
+ website: store.website,
96
+ emailAddress: store.emailAddress,
97
+ termsConditions: store.termsConditions,
98
+ privacyPolicy: store.privacyPolicy,
92
99
  botName: store.botName,
93
100
  botInfo: store.botInfo,
94
101
  avatarUrl: store.botAvatarUrl,
95
- startConversation: store.startConversation,
96
102
  toggleBotInfo: store.view.toggleBotInfo,
97
- isConversationStarted: store.isConversationStarted,
98
103
  updatePreferredLanguage: store.updatePreferredLanguage,
99
104
  preferredLanguage: store.preferredLanguage,
100
105
  escapeHTML: store.escapeHTML,
@@ -1,90 +1,88 @@
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
38
  focus.newValue === 'convo' && this.messagesDiv.focus();
34
39
  });
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
40
  }
53
- componentWillUnmount() {
54
- this.divSizeObserver.disconnect();
41
+ render() {
42
+ 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) => {
43
+ this.messagesDiv = m;
44
+ }, followButtonClassName: 'bpw-msg-list-follow' },
45
+ react_1.default.createElement(Content, Object.assign({}, this.props))));
55
46
  }
56
- componentDidUpdate() {
57
- if (this.state.manualScroll) {
58
- return;
47
+ }
48
+ const Content = (0, mobx_react_1.observer)((props) => {
49
+ var _a, _b;
50
+ const [state, setState] = (0, react_1.useState)({
51
+ showNewMessageIndicator: false,
52
+ messagesLength: undefined
53
+ });
54
+ const scrollToBottom = (0, react_scroll_to_bottom_1.useScrollToBottom)();
55
+ const [sticky] = (0, react_scroll_to_bottom_1.useSticky)();
56
+ (0, react_1.useEffect)(() => {
57
+ var _a, _b;
58
+ 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 });
59
+ if (!sticky && state.messagesLength !== ((_b = props === null || props === void 0 ? void 0 : props.currentMessages) === null || _b === void 0 ? void 0 : _b.length)) {
60
+ setState(Object.assign(Object.assign({}, stateUpdate), { showNewMessageIndicator: true }));
59
61
  }
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) {
62
+ else {
63
+ setState(Object.assign(Object.assign({}, stateUpdate), { showNewMessageIndicator: false }));
64
+ }
65
+ }, [(_a = props === null || props === void 0 ? void 0 : props.currentMessages) === null || _a === void 0 ? void 0 : _a.length, sticky]);
66
+ const shouldDisplayMessage = (m) => {
67
+ return m.payload.type !== 'postback';
68
+ };
69
+ const renderDate = (date) => {
73
70
  return (react_1.default.createElement("div", { className: 'bpw-date-container' },
74
- new Intl.DateTimeFormat(this.props.intl.locale || 'en', {
71
+ new Intl.DateTimeFormat(props.intl.locale || 'en', {
75
72
  month: 'short',
76
73
  day: 'numeric',
77
74
  hour: 'numeric',
78
75
  minute: 'numeric'
79
76
  }).format(new Date(date)),
80
77
  react_1.default.createElement("div", { className: 'bpw-small-line' })));
81
- }
82
- renderAvatar(name, url) {
78
+ };
79
+ const renderAvatar = (name, url) => {
83
80
  const avatarSize = 40;
84
81
  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));
82
+ };
83
+ const renderMessageGroups = () => {
84
+ var _a;
85
+ const messages = (props.currentMessages || []).filter((m) => shouldDisplayMessage(m));
88
86
  const groups = [];
89
87
  let lastSpeaker = undefined;
90
88
  let lastDate = undefined;
@@ -106,7 +104,7 @@ class MessageList extends react_1.default.Component {
106
104
  lastSpeaker = speaker;
107
105
  lastDate = date;
108
106
  });
109
- if (this.props.isBotTyping.get()) {
107
+ if ((_a = props === null || props === void 0 ? void 0 : props.isBotTyping) === null || _a === void 0 ? void 0 : _a.get()) {
110
108
  if (lastSpeaker !== 'bot') {
111
109
  currentGroup = [];
112
110
  groups.push(currentGroup);
@@ -123,24 +121,20 @@ class MessageList extends react_1.default.Component {
123
121
  const groupDate = group === null || group === void 0 ? void 0 : group[0].sentOn;
124
122
  const isDateNeeded = !groups[i - 1] ||
125
123
  (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);
124
+ const { authorId } = (0, last_1.default)(group);
125
+ const avatar = !authorId && renderAvatar(props.botName, props.botAvatarUrl);
128
126
  return (react_1.default.createElement("div", { key: i },
129
- isDateNeeded && this.renderDate(group[0].sentOn),
127
+ isDateNeeded && renderDate(group[0].sentOn),
130
128
  react_1.default.createElement(MessageGroup_1.default, { isBot: !authorId, avatar: avatar, key: `msg-group-${i}`, isLastGroup: i >= groups.length - 1, messages: group })));
131
129
  })));
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
- }
130
+ };
131
+ return (react_1.default.createElement(react_1.default.Fragment, null,
132
+ state.showNewMessageIndicator && (react_1.default.createElement("div", { className: "bpw-new-messages-indicator", onClick: (e) => scrollToBottom() },
133
+ react_1.default.createElement("span", null, props.intl.formatMessage({
134
+ id: `messages.newMessage${((_b = props === null || props === void 0 ? void 0 : props.currentMessages) === null || _b === void 0 ? void 0 : _b.length) === 1 ? '' : 's'}`
135
+ })))),
136
+ renderMessageGroups()));
137
+ });
144
138
  exports.default = (0, mobx_react_1.inject)(({ store }) => ({
145
139
  intl: store.intl,
146
140
  botName: store.botName,
@@ -1 +1,2 @@
1
1
  declare module '*.scss';
2
+ declare module 'react-scroll-to-bottom';
@@ -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[];
@@ -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
  }
@@ -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
@@ -288,6 +288,38 @@ export interface Config {
288
288
  * Allows setting a custom user id
289
289
  */
290
290
  customUser?: UserCredentials;
291
+ /**
292
+ * Displays the bot's website in the conversation page
293
+ */
294
+ website?: string;
295
+ /**
296
+ * Displays the bot's contact phone number in the conversation page
297
+ */
298
+ phoneNumber?: string;
299
+ /**
300
+ * Displays the bot's terms of service in the conversation page
301
+ */
302
+ termsConditions?: string;
303
+ /**
304
+ * Displays the bot's privacy policy in the conversation page
305
+ */
306
+ privacyPolicy?: string;
307
+ /**
308
+ * Displays the bot's email address in the conversation page
309
+ */
310
+ emailAddress?: string;
311
+ /**
312
+ * Displays the bot's cover picture in the conversation page
313
+ */
314
+ coverPictureUrl?: string;
315
+ /**
316
+ * Enables the bot's information page in the webchat
317
+ */
318
+ showBotInfoPage?: boolean;
319
+ /**
320
+ * Display's the webchat close button when the webchat is opened
321
+ */
322
+ showCloseButton?: boolean;
291
323
  }
292
324
  export interface BotDetails {
293
325
  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.1",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "source": "src/index.tsx",
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@blueprintjs/core": "^3.23.1",
32
- "@botpress/messaging-components": "0.4.2",
32
+ "@botpress/messaging-components": "0.4.3",
33
33
  "@botpress/messaging-socket": "1.2.0",
34
34
  "@formatjs/intl-pluralrules": "^4.1.6",
35
35
  "@formatjs/intl-utils": "^3.8.4",
@@ -48,6 +48,7 @@
48
48
  "react-dom": "^17.0.2",
49
49
  "react-ga": "^2.7.0",
50
50
  "react-intl": "^3.12.1",
51
+ "react-scroll-to-bottom": "^4.2.0",
51
52
  "snarkdown": "^2.0.0",
52
53
  "uuid": "^8.3.2"
53
54
  }