@botpress/webchat 0.4.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 +12 -2
- package/dist/components/common/Avatar/index.d.ts +2 -2
- package/dist/components/common/BotInfo/index.d.ts +1 -1
- package/dist/components/common/BotInfo/index.js +28 -23
- package/dist/components/messages/Message.d.ts +1 -1
- package/dist/components/messages/Message.js +20 -3
- package/dist/components/messages/MessageGroup.d.ts +1 -1
- package/dist/components/messages/MessageGroup.js +5 -2
- package/dist/components/messages/MessageList.js +68 -74
- package/dist/core/socket.d.ts +0 -1
- package/dist/core/socket.js +2 -5
- package/dist/declaration.d.ts +1 -0
- package/dist/main.d.ts +1 -1
- package/dist/main.js +23 -17
- package/dist/store/index.d.ts +11 -4
- package/dist/store/index.js +51 -24
- package/dist/store/view.js +13 -8
- package/dist/typings.d.ts +51 -15
- package/dist/utils/webchatEvents.d.ts +2 -0
- package/dist/utils/webchatEvents.js +14 -0
- package/package.json +3 -2
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.
|
|
@@ -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, '
|
|
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 = ({
|
|
35
|
-
react_1.default.createElement("img", { className: 'bpw-botinfo-cover-picture', src:
|
|
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 {
|
|
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, {
|
|
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",
|
|
64
|
-
this.renderDescription(
|
|
65
|
-
|
|
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
|
-
|
|
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:${
|
|
71
|
-
|
|
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:
|
|
75
|
-
|
|
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:${
|
|
79
|
-
|
|
80
|
-
react_1.default.createElement("a", { target: '_blank', href:
|
|
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
|
-
|
|
83
|
-
react_1.default.createElement("a", { target: '_blank', href:
|
|
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:
|
|
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,
|
|
@@ -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:
|
|
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)));
|
|
@@ -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,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
|
|
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 =
|
|
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
|
-
|
|
54
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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(
|
|
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
|
-
|
|
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 (
|
|
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
|
|
127
|
-
const avatar = !authorId &&
|
|
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 &&
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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,
|
package/dist/core/socket.d.ts
CHANGED
|
@@ -8,7 +8,6 @@ export default class BpSocket {
|
|
|
8
8
|
constructor(config: Config);
|
|
9
9
|
setup(): void;
|
|
10
10
|
sendPayload(payload: any): Promise<Message>;
|
|
11
|
-
postToParent: (_type: string, payload: any) => void;
|
|
12
11
|
connect(): Promise<void>;
|
|
13
12
|
reload(config: Config): Promise<void>;
|
|
14
13
|
private getCreds;
|
package/dist/core/socket.js
CHANGED
|
@@ -10,13 +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
16
|
this.config = config;
|
|
16
|
-
this.postToParent = (_type, payload) => {
|
|
17
|
-
var _a;
|
|
18
|
-
(_a = window.parent) === null || _a === void 0 ? void 0 : _a.postMessage(Object.assign(Object.assign({}, payload), { chatId: this.chatId }), '*');
|
|
19
|
-
};
|
|
20
17
|
this.chatId = config.chatId;
|
|
21
18
|
this.socket = new messaging_socket_1.MessagingSocket({ url: config.messagingUrl, clientId: config.clientId });
|
|
22
19
|
window.websocket = this.socket;
|
|
@@ -38,7 +35,7 @@ class BpSocket {
|
|
|
38
35
|
if (this.socket.userId) {
|
|
39
36
|
const userId = this.socket.userId;
|
|
40
37
|
window.BP_STORAGE.set('creds', this.socket.creds);
|
|
41
|
-
|
|
38
|
+
(0, webchatEvents_1.postMessageToParent)('USER.CONNECTED', { userId }, this.chatId);
|
|
42
39
|
}
|
|
43
40
|
});
|
|
44
41
|
}
|
package/dist/declaration.d.ts
CHANGED
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' | 'resetConversation'>;
|
|
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) {
|
|
@@ -93,12 +93,14 @@ class Web extends react_1.default.Component {
|
|
|
93
93
|
(0, analytics_1.trackWebchatState)('toggle');
|
|
94
94
|
}
|
|
95
95
|
else if (type === 'message') {
|
|
96
|
-
(0, analytics_1.trackMessage)('sent');
|
|
97
96
|
yield this.props.sendMessage(text);
|
|
98
97
|
}
|
|
99
98
|
else if (type === 'loadConversation') {
|
|
100
99
|
yield this.props.fetchConversation(conversationId);
|
|
101
100
|
}
|
|
101
|
+
else if (type === 'createConversation') {
|
|
102
|
+
yield this.props.createConversation();
|
|
103
|
+
}
|
|
102
104
|
else if (type === 'toggleBotInfo') {
|
|
103
105
|
this.props.toggleBotInfo();
|
|
104
106
|
}
|
|
@@ -110,7 +112,7 @@ class Web extends react_1.default.Component {
|
|
|
110
112
|
}
|
|
111
113
|
});
|
|
112
114
|
this.handleNewMessage = (event) => __awaiter(this, void 0, void 0, function* () {
|
|
113
|
-
var _b;
|
|
115
|
+
var _b, _c, _d;
|
|
114
116
|
if (!this.isCurrentConversation(event)) {
|
|
115
117
|
// don't do anything, it's a message from another conversation
|
|
116
118
|
return;
|
|
@@ -123,7 +125,15 @@ class Web extends react_1.default.Component {
|
|
|
123
125
|
// don't do anything, it's the system message
|
|
124
126
|
return;
|
|
125
127
|
}
|
|
126
|
-
(0
|
|
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
|
+
}
|
|
127
137
|
this.props.updateLastMessage(event.conversationId, event);
|
|
128
138
|
yield this.props.addEventToConversation(event);
|
|
129
139
|
// there's no focus on the actual conversation
|
|
@@ -137,8 +147,8 @@ class Web extends react_1.default.Component {
|
|
|
137
147
|
yield this.props.updateTyping(event);
|
|
138
148
|
});
|
|
139
149
|
this.playSound = (0, debounce_1.default)(() => __awaiter(this, void 0, void 0, function* () {
|
|
140
|
-
var
|
|
141
|
-
const disableNotificationSound = this.config.disableNotificationSound || ((
|
|
150
|
+
var _e;
|
|
151
|
+
const disableNotificationSound = this.config.disableNotificationSound || ((_e = this.props.config) === null || _e === void 0 ? void 0 : _e.disableNotificationSound);
|
|
142
152
|
if (disableNotificationSound || this.audio.readyState < 2) {
|
|
143
153
|
return;
|
|
144
154
|
}
|
|
@@ -192,12 +202,9 @@ class Web extends react_1.default.Component {
|
|
|
192
202
|
return __awaiter(this, void 0, void 0, function* () {
|
|
193
203
|
this.config = this.extractConfig();
|
|
194
204
|
this.props.updateConfig(this.config);
|
|
195
|
-
|
|
196
|
-
const storePath = this.config.chatId ? `${this.config.chatId}.webchat_store` : 'webchat_store';
|
|
197
|
-
(0, set_1.default)(window.parent, storePath, this.props.store);
|
|
198
|
-
}
|
|
205
|
+
// is this necessary ?
|
|
199
206
|
if (this.config.containerWidth) {
|
|
200
|
-
|
|
207
|
+
(0, webchatEvents_1.postMessageToParent)('UI.RESIZE', this.config.containerWidth, this.config.chatId);
|
|
201
208
|
}
|
|
202
209
|
yield this.props.fetchBotInfo();
|
|
203
210
|
if (!this.isLazySocket()) {
|
|
@@ -206,10 +213,6 @@ class Web extends react_1.default.Component {
|
|
|
206
213
|
this.setupObserver();
|
|
207
214
|
});
|
|
208
215
|
}
|
|
209
|
-
postMessageToParent(type, value) {
|
|
210
|
-
var _a;
|
|
211
|
-
(_a = window.parent) === null || _a === void 0 ? void 0 : _a.postMessage({ type, value, chatId: this.config.chatId }, '*');
|
|
212
|
-
}
|
|
213
216
|
extractConfig() {
|
|
214
217
|
let userConfig = Object.assign({}, constants_1.default.DEFAULT_CONFIG, this.props.config);
|
|
215
218
|
const { options } = query_string_1.default.parse(location.search);
|
|
@@ -239,7 +242,8 @@ class Web extends react_1.default.Component {
|
|
|
239
242
|
setupObserver() {
|
|
240
243
|
(0, mobx_1.observe)(this.props.dimensions, 'container', (data) => {
|
|
241
244
|
if (data.newValue) {
|
|
242
|
-
this
|
|
245
|
+
// is this necessary ?
|
|
246
|
+
(0, webchatEvents_1.postMessageToParent)('UI.RESIZE', data.newValue, this.config.chatId);
|
|
243
247
|
}
|
|
244
248
|
});
|
|
245
249
|
}
|
|
@@ -267,8 +271,8 @@ class Web extends react_1.default.Component {
|
|
|
267
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)
|
|
268
272
|
});
|
|
269
273
|
if (this.parentClass !== parentClass) {
|
|
270
|
-
this.postMessageToParent('setClass', parentClass);
|
|
271
274
|
this.parentClass = parentClass;
|
|
275
|
+
(0, webchatEvents_1.postMessageToParent)('UI.SET-CLASS', parentClass, this.config.chatId);
|
|
272
276
|
}
|
|
273
277
|
const stylesheet = this.props.config.stylesheet;
|
|
274
278
|
const extraStylesheet = (_c = this.props.botInfo) === null || _c === void 0 ? void 0 : _c.extraStylesheet;
|
|
@@ -323,8 +327,10 @@ exports.default = (0, mobx_react_1.inject)(({ store }) => ({
|
|
|
323
327
|
sendFeedback: store.sendFeedback,
|
|
324
328
|
updateLastMessage: store.updateLastMessage,
|
|
325
329
|
fetchConversation: store.fetchConversation,
|
|
330
|
+
createConversation: store.createConversation,
|
|
326
331
|
setIntlProvider: store.setIntlProvider,
|
|
327
332
|
setSocket: store.setSocket,
|
|
333
|
+
currentConversation: store.currentConversation,
|
|
328
334
|
currentConversationId: store.currentConversationId,
|
|
329
335
|
resetConversation: store.resetConversation
|
|
330
336
|
}))((0, react_intl_1.injectIntl)((0, mobx_react_1.observer)(Web)));
|
package/dist/store/index.d.ts
CHANGED
|
@@ -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,15 +28,22 @@ 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;
|
|
33
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;
|
|
34
43
|
get rtl(): boolean;
|
|
35
44
|
get escapeHTML(): boolean;
|
|
36
45
|
get currentMessages(): Message[];
|
|
37
46
|
get currentConversationId(): uuid | undefined;
|
|
38
|
-
postMessage(name: string, payload?: any): void;
|
|
39
47
|
updateMessages(messages: Message[]): void;
|
|
40
48
|
updateLastMessage(conversationId: string, message?: Message): void;
|
|
41
49
|
clearMessages(): void;
|
|
@@ -51,7 +59,7 @@ declare class RootStore {
|
|
|
51
59
|
/** Fetch the specified conversation ID, or try to fetch a valid one from the list */
|
|
52
60
|
fetchConversation(convoId?: uuid): Promise<uuid | undefined>;
|
|
53
61
|
/** Sends the specified message, or fetch the message in the composer */
|
|
54
|
-
sendMessage(
|
|
62
|
+
sendMessage(textMessage?: string): Promise<void>;
|
|
55
63
|
/** Sends an event to start conversation & hide the bot info page */
|
|
56
64
|
startConversation(): Promise<void>;
|
|
57
65
|
/** Creates a new conversation and switches to it */
|
|
@@ -61,7 +69,7 @@ declare class RootStore {
|
|
|
61
69
|
sendFeedback(feedback: number, messageId: string): Promise<void>;
|
|
62
70
|
downloadConversation(): Promise<void>;
|
|
63
71
|
/** Sends an event or a message, depending on how the backend manages those types */
|
|
64
|
-
sendData(data: any): Promise<void>;
|
|
72
|
+
sendData(data: any): Promise<Message | void>;
|
|
65
73
|
/** Sends a message of type voice */
|
|
66
74
|
sendVoiceMessage(voice: Buffer, ext: string): Promise<void>;
|
|
67
75
|
/** Use this method to replace a value or add a new config */
|
|
@@ -69,7 +77,6 @@ declare class RootStore {
|
|
|
69
77
|
/** This replaces all the configurations by this object */
|
|
70
78
|
updateConfig(config: Config): void;
|
|
71
79
|
private _applyConfig;
|
|
72
|
-
publishConfigChanged(): void;
|
|
73
80
|
updatePreferredLanguage(lang: string): void;
|
|
74
81
|
/** Starts a timer to remove the typing animation when it's completed */
|
|
75
82
|
private _startTypingTimer;
|
package/dist/store/index.js
CHANGED
|
@@ -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);
|
|
@@ -66,6 +70,34 @@ class RootStore {
|
|
|
66
70
|
var _a, _b, _c;
|
|
67
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;
|
|
68
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
|
+
}
|
|
69
101
|
get rtl() {
|
|
70
102
|
return (0, translations_1.isRTLLocale)(this.preferredLanguage);
|
|
71
103
|
}
|
|
@@ -81,10 +113,6 @@ class RootStore {
|
|
|
81
113
|
var _a;
|
|
82
114
|
return (_a = this.currentConversation) === null || _a === void 0 ? void 0 : _a.id;
|
|
83
115
|
}
|
|
84
|
-
postMessage(name, payload) {
|
|
85
|
-
const chatId = this.config.chatId;
|
|
86
|
-
window.parent.postMessage({ name, chatId, payload }, '*');
|
|
87
|
-
}
|
|
88
116
|
updateMessages(messages) {
|
|
89
117
|
if (this.currentConversation) {
|
|
90
118
|
this.currentConversation.messages = messages;
|
|
@@ -160,7 +188,7 @@ class RootStore {
|
|
|
160
188
|
yield this.fetchConversation();
|
|
161
189
|
(0, mobx_1.runInAction)('-> setInitialized', () => {
|
|
162
190
|
this.isInitialized = true;
|
|
163
|
-
|
|
191
|
+
(0, webchatEvents_1.postMessageToParent)('LIFECYCLE.READY', undefined, this.config.chatId);
|
|
164
192
|
});
|
|
165
193
|
}
|
|
166
194
|
catch (err) {
|
|
@@ -229,23 +257,24 @@ class RootStore {
|
|
|
229
257
|
});
|
|
230
258
|
}
|
|
231
259
|
/** Sends the specified message, or fetch the message in the composer */
|
|
232
|
-
sendMessage(
|
|
260
|
+
sendMessage(textMessage) {
|
|
233
261
|
return __awaiter(this, void 0, void 0, function* () {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
237
|
-
const userMessage = this.composer.message;
|
|
238
|
-
if (!userMessage || !userMessage.length) {
|
|
262
|
+
textMessage = textMessage || this.composer.message;
|
|
263
|
+
if (!textMessage) {
|
|
239
264
|
return;
|
|
240
265
|
}
|
|
241
266
|
this.composer.updateMessage('');
|
|
242
267
|
try {
|
|
243
|
-
yield this.sendData({ type: 'text', text:
|
|
268
|
+
const message = yield this.sendData({ type: 'text', text: textMessage });
|
|
244
269
|
(0, analytics_1.trackMessage)('sent');
|
|
245
|
-
|
|
270
|
+
if (message) {
|
|
271
|
+
(0, webchatEvents_1.postMessageToParent)('MESSAGE.SENT', message, this.config.chatId);
|
|
272
|
+
}
|
|
273
|
+
this.composer.addMessageToHistory(textMessage);
|
|
246
274
|
}
|
|
247
275
|
catch (e) {
|
|
248
|
-
this.composer.updateMessage(
|
|
276
|
+
this.composer.updateMessage(textMessage);
|
|
277
|
+
console.error('Webchat cloud not send message');
|
|
249
278
|
throw e;
|
|
250
279
|
}
|
|
251
280
|
});
|
|
@@ -328,6 +357,7 @@ class RootStore {
|
|
|
328
357
|
}
|
|
329
358
|
const message = yield this.api.sendMessage(data, this.currentConversationId);
|
|
330
359
|
this.updateLastMessage(this.currentConversationId, message);
|
|
360
|
+
return message;
|
|
331
361
|
});
|
|
332
362
|
}
|
|
333
363
|
/** Sends a message of type voice */
|
|
@@ -364,10 +394,7 @@ class RootStore {
|
|
|
364
394
|
const locale = (0, translations_1.getUserLocale)(this.config.locale);
|
|
365
395
|
this.updateBotUILanguage(locale);
|
|
366
396
|
document.documentElement.setAttribute('lang', locale);
|
|
367
|
-
this.
|
|
368
|
-
}
|
|
369
|
-
publishConfigChanged() {
|
|
370
|
-
this.postMessage('configChanged', JSON.stringify(this.config, undefined, 2));
|
|
397
|
+
(0, webchatEvents_1.postMessageToParent)('CONFIG.SET', Object.assign({}, this.config), this.config.chatId);
|
|
371
398
|
}
|
|
372
399
|
updatePreferredLanguage(lang) {
|
|
373
400
|
this.preferredLanguage = lang;
|
|
@@ -439,6 +466,9 @@ __decorate([
|
|
|
439
466
|
__decorate([
|
|
440
467
|
mobx_1.observable
|
|
441
468
|
], RootStore.prototype, "currentConversation", void 0);
|
|
469
|
+
__decorate([
|
|
470
|
+
mobx_1.observable
|
|
471
|
+
], RootStore.prototype, "selectedMessageId", void 0);
|
|
442
472
|
__decorate([
|
|
443
473
|
mobx_1.observable
|
|
444
474
|
], RootStore.prototype, "botInfo", void 0);
|
|
@@ -463,6 +493,9 @@ __decorate([
|
|
|
463
493
|
__decorate([
|
|
464
494
|
mobx_1.action.bound
|
|
465
495
|
], RootStore.prototype, "setSocket", null);
|
|
496
|
+
__decorate([
|
|
497
|
+
mobx_1.action.bound
|
|
498
|
+
], RootStore.prototype, "setSelectedMessage", null);
|
|
466
499
|
__decorate([
|
|
467
500
|
mobx_1.computed
|
|
468
501
|
], RootStore.prototype, "isConversationStarted", null);
|
|
@@ -487,9 +520,6 @@ __decorate([
|
|
|
487
520
|
__decorate([
|
|
488
521
|
mobx_1.computed
|
|
489
522
|
], RootStore.prototype, "currentConversationId", null);
|
|
490
|
-
__decorate([
|
|
491
|
-
mobx_1.action.bound
|
|
492
|
-
], RootStore.prototype, "postMessage", null);
|
|
493
523
|
__decorate([
|
|
494
524
|
mobx_1.action.bound
|
|
495
525
|
], RootStore.prototype, "updateMessages", null);
|
|
@@ -556,9 +586,6 @@ __decorate([
|
|
|
556
586
|
__decorate([
|
|
557
587
|
mobx_1.action.bound
|
|
558
588
|
], RootStore.prototype, "updateConfig", null);
|
|
559
|
-
__decorate([
|
|
560
|
-
mobx_1.action.bound
|
|
561
|
-
], RootStore.prototype, "publishConfigChanged", null);
|
|
562
589
|
__decorate([
|
|
563
590
|
mobx_1.action.bound
|
|
564
591
|
], RootStore.prototype, "updatePreferredLanguage", null);
|
package/dist/store/view.js
CHANGED
|
@@ -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 */
|
|
@@ -39,7 +40,9 @@ class ViewStore {
|
|
|
39
40
|
return (_a = this.rootStore.config) === null || _a === void 0 ? void 0 : _a.showConversationsButton;
|
|
40
41
|
}
|
|
41
42
|
get showBotInfoButton() {
|
|
42
|
-
|
|
43
|
+
var _a;
|
|
44
|
+
return (!this.isConversationsDisplayed &&
|
|
45
|
+
(((_a = this.rootStore.botInfo) === null || _a === void 0 ? void 0 : _a.showBotInfoPage) || !!this.rootStore.config.showBotInfoPage));
|
|
43
46
|
}
|
|
44
47
|
get showDownloadButton() {
|
|
45
48
|
return !this.isConversationsDisplayed && !this.isBotInfoDisplayed && this.rootStore.config.enableTranscriptDownload;
|
|
@@ -48,7 +51,8 @@ class ViewStore {
|
|
|
48
51
|
return (!this.isConversationsDisplayed && !this.isBotInfoDisplayed && this.rootStore.config.enableConversationDeletion);
|
|
49
52
|
}
|
|
50
53
|
get showCloseButton() {
|
|
51
|
-
|
|
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;
|
|
52
56
|
}
|
|
53
57
|
get showWidgetButton() {
|
|
54
58
|
var _a;
|
|
@@ -61,7 +65,8 @@ class ViewStore {
|
|
|
61
65
|
return !this._isLoading && this.activeView;
|
|
62
66
|
}
|
|
63
67
|
get isBotInfoDisplayed() {
|
|
64
|
-
|
|
68
|
+
var _a;
|
|
69
|
+
return this._showBotInfo && (((_a = this.rootStore.botInfo) === null || _a === void 0 ? void 0 : _a.showBotInfoPage) || !!this.rootStore.config.showBotInfoPage);
|
|
65
70
|
}
|
|
66
71
|
/** Returns the active transition for the side panel, like fade in or out */
|
|
67
72
|
get sideTransition() {
|
|
@@ -111,7 +116,7 @@ class ViewStore {
|
|
|
111
116
|
}
|
|
112
117
|
setLoadingCompleted() {
|
|
113
118
|
this._isLoading = false;
|
|
114
|
-
this.rootStore.
|
|
119
|
+
(0, webchatEvents_1.postMessageToParent)('LIFECYCLE.LOADED', undefined, this.rootStore.config.chatId);
|
|
115
120
|
}
|
|
116
121
|
showPoweredBy() {
|
|
117
122
|
this.isPoweredByDisplayed = true;
|
|
@@ -157,7 +162,7 @@ class ViewStore {
|
|
|
157
162
|
showChat() {
|
|
158
163
|
if (this.disableAnimations) {
|
|
159
164
|
this.activeView = 'side';
|
|
160
|
-
this.rootStore.
|
|
165
|
+
(0, webchatEvents_1.postMessageToParent)('UI.OPENED', undefined, this.rootStore.config.chatId);
|
|
161
166
|
return this._updateTransitions({ widgetTransition: undefined, sideTransition: 'none' });
|
|
162
167
|
}
|
|
163
168
|
this._updateTransitions({ widgetTransition: 'fadeOut' });
|
|
@@ -165,7 +170,7 @@ class ViewStore {
|
|
|
165
170
|
this._updateTransitions({ sideTransition: 'fadeIn' });
|
|
166
171
|
}, constants_1.default.ANIMATION_DURATION + 10);
|
|
167
172
|
this._endAnimation('side');
|
|
168
|
-
this.rootStore.
|
|
173
|
+
(0, webchatEvents_1.postMessageToParent)('UI.OPENED', undefined, this.rootStore.config.chatId);
|
|
169
174
|
}
|
|
170
175
|
hideChat() {
|
|
171
176
|
if (this.isFullscreen) {
|
|
@@ -173,7 +178,7 @@ class ViewStore {
|
|
|
173
178
|
}
|
|
174
179
|
if (this.disableAnimations) {
|
|
175
180
|
this.activeView = 'widget';
|
|
176
|
-
this.rootStore.
|
|
181
|
+
(0, webchatEvents_1.postMessageToParent)('UI.CLOSED', undefined, this.rootStore.config.chatId);
|
|
177
182
|
return this._updateTransitions({ widgetTransition: undefined, sideTransition: undefined });
|
|
178
183
|
}
|
|
179
184
|
this._updateTransitions({ sideTransition: 'fadeOut' });
|
|
@@ -183,7 +188,7 @@ class ViewStore {
|
|
|
183
188
|
}, constants_1.default.ANIMATION_DURATION + 10);
|
|
184
189
|
}
|
|
185
190
|
this._endAnimation('widget');
|
|
186
|
-
this.rootStore.
|
|
191
|
+
(0, webchatEvents_1.postMessageToParent)('UI.CLOSED', undefined, this.rootStore.config.chatId);
|
|
187
192
|
}
|
|
188
193
|
_endAnimation(finalView) {
|
|
189
194
|
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 ''
|
|
@@ -280,10 +287,39 @@ export interface Config {
|
|
|
280
287
|
/**
|
|
281
288
|
* Allows setting a custom user id
|
|
282
289
|
*/
|
|
283
|
-
customUser?:
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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;
|
|
287
323
|
}
|
|
288
324
|
export interface BotDetails {
|
|
289
325
|
website?: string;
|
|
@@ -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
|
+
"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.
|
|
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
|
}
|