@meetelise/chat 1.9.1 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +1 -0
- package/.github/workflows/release.yml +1 -0
- package/.vscode/settings.json +6 -1
- package/CONTRIBUTING.md +8 -0
- package/package.json +9 -10
- package/public/demo/index.html +78 -11
- package/public/dist/index.js +1714 -1
- package/public/dist/index.js.LICENSE.txt +26 -14
- package/public/index.html +2 -1
- package/src/MEChat.test.ts +5 -5
- package/src/MEChat.ts +53 -0
- package/src/WebComponent/InHouseLauncher.ts +446 -0
- package/src/WebComponent/MEChat.css +5 -0
- package/src/WebComponent/MEChat.ts +282 -0
- package/src/WebComponent/OfficeHours.ts +73 -0
- package/src/WebComponent/Scheduler/date-picker.ts +320 -0
- package/src/WebComponent/Scheduler/me-select.ts +244 -0
- package/src/WebComponent/Scheduler/time-picker.ts +101 -0
- package/src/WebComponent/Scheduler/tour-scheduler.ts +383 -0
- package/src/WebComponent/Scheduler/tour-type-option.ts +92 -0
- package/src/WebComponent/actions/ActionConfirmButton.ts +94 -0
- package/src/WebComponent/actions/CallUsWindow.ts +110 -0
- package/src/WebComponent/actions/DetailsWindow.ts +109 -0
- package/src/WebComponent/actions/EmailUsWindow.ts +432 -0
- package/src/WebComponent/actions/InputStyles.ts +31 -0
- package/src/WebComponent/actions/TextUsWindow.ts +226 -0
- package/src/WebComponent/actions/formatPhoneNumber.ts +42 -0
- package/src/WebComponent/inHouseLauncherStyles.ts +300 -0
- package/src/WebComponent/index.ts +2 -0
- package/src/WebComponent/utils.ts +82 -0
- package/src/analytics.ts +48 -15
- package/src/assetUrls.ts +4 -0
- package/src/fetchBuildingInfo.ts +1 -0
- package/src/getAvailabilities.ts +71 -0
- package/src/themes.ts +5 -3
- package/tsconfig.json +9 -3
- package/web-test-runner.config.js +0 -6
- package/webpack.config.cjs +8 -25
- package/public/dist/index.d.ts +0 -1
- package/public/dist/src/ChatButton.d.ts +0 -9
- package/public/dist/src/ChatIcon.d.ts +0 -6
- package/public/dist/src/InHouseLauncher.d.ts +0 -11
- package/public/dist/src/MEChat.d.ts +0 -73
- package/public/dist/src/analytics.d.ts +0 -18
- package/public/dist/src/chatID.d.ts +0 -11
- package/public/dist/src/createConversation.d.ts +0 -4
- package/public/dist/src/fetchBuildingInfo.d.ts +0 -25
- package/public/dist/src/themes.d.ts +0 -52
- package/public/dist/src/utils.d.ts +0 -2
- package/src/ChatButton.module.scss +0 -52
- package/src/ChatButton.tsx +0 -26
- package/src/ChatIcon.tsx +0 -26
- package/src/DemoApp.tsx +0 -113
- package/src/InHouseLauncher.module.scss +0 -140
- package/src/InHouseLauncher.tsx +0 -65
- package/src/MEChat.module.scss +0 -22
- package/src/MEChat.tsx +0 -293
package/src/DemoApp.tsx
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useState } from "react";
|
|
2
|
-
import ReactDOM from "react-dom";
|
|
3
|
-
import { debounce } from "lodash";
|
|
4
|
-
import MEChat from "./MEChat";
|
|
5
|
-
|
|
6
|
-
declare global {
|
|
7
|
-
interface Window {
|
|
8
|
-
chat: MEChat;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const DemoApp = () => {
|
|
13
|
-
useEffect(() => {
|
|
14
|
-
const chat = MEChat.start({
|
|
15
|
-
organization: "test-company",
|
|
16
|
-
building: "e2e-test-yardi-building",
|
|
17
|
-
// mini: true
|
|
18
|
-
});
|
|
19
|
-
//////////////////////////////////////////////////
|
|
20
|
-
// The above code is all a client would need. //
|
|
21
|
-
// Below is some code to make local dev easier. //
|
|
22
|
-
//////////////////////////////////////////////////
|
|
23
|
-
|
|
24
|
-
// Put the chat instance on window so it's available in the console.
|
|
25
|
-
|
|
26
|
-
window.chat = chat;
|
|
27
|
-
|
|
28
|
-
// Reset on button click
|
|
29
|
-
document.getElementById("reset")?.addEventListener("click", () => {
|
|
30
|
-
chat.restartConversation();
|
|
31
|
-
});
|
|
32
|
-
const onChange = debounce((e) => {
|
|
33
|
-
chat.setTheme({ [e.target.name]: e.target.value });
|
|
34
|
-
}, 300);
|
|
35
|
-
document.querySelectorAll("#theme input").forEach((input) => {
|
|
36
|
-
input.addEventListener("input", onChange);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// Open the chat window shortly after the page loads/refreshes.
|
|
40
|
-
// setTimeout(() => chat.open(), 1000);
|
|
41
|
-
|
|
42
|
-
// Every x seconds, cycle through ui states. Close the window and
|
|
43
|
-
// remove the button from the screen, then show it and open the
|
|
44
|
-
// window. Set the DEBUG_cycle_ui_states boolean to false to disable.
|
|
45
|
-
const DEBUG_cycle_ui_states = false;
|
|
46
|
-
const period_seconds = 30;
|
|
47
|
-
if (DEBUG_cycle_ui_states) {
|
|
48
|
-
let seconds = 0;
|
|
49
|
-
setInterval(() => {
|
|
50
|
-
seconds = (seconds + 1) % period_seconds;
|
|
51
|
-
if (seconds === period_seconds - 4) chat.close();
|
|
52
|
-
if (seconds === period_seconds - 3) chat.hide();
|
|
53
|
-
if (seconds === period_seconds - 2) chat.show();
|
|
54
|
-
if (seconds === period_seconds - 1) chat.open();
|
|
55
|
-
}, 1000);
|
|
56
|
-
}
|
|
57
|
-
}, []);
|
|
58
|
-
|
|
59
|
-
const [showCookieBanner, setShowCookieBanner] = useState(true);
|
|
60
|
-
return (
|
|
61
|
-
<>
|
|
62
|
-
<h1>Example Page</h1>
|
|
63
|
-
<p>This is an example page for serving the chat widget locally.</p>
|
|
64
|
-
<button id="reset" type="button">
|
|
65
|
-
Restart conversation
|
|
66
|
-
</button>
|
|
67
|
-
<form id="theme" style={{ display: "flex", flexDirection: "column" }}>
|
|
68
|
-
<label htmlFor="chatTitle">Title</label>
|
|
69
|
-
<input type="text" id="chatTitle" name="chatTitle" />
|
|
70
|
-
<label htmlFor="chatSubtitle">Subtitle</label>
|
|
71
|
-
<input type="text" id="chatSubtitle" name="chatSubtitle" />
|
|
72
|
-
<label htmlFor="bannerColor">Banner Color</label>
|
|
73
|
-
<input type="text" id="bannerColor" name="bannerColor" />
|
|
74
|
-
<label htmlFor="bannerTextColor">Banner Text Color</label>
|
|
75
|
-
<input type="text" id="bannerTextColor" name="bannerTextColor" />
|
|
76
|
-
<label htmlFor="launchButtonColor">Launch Button Color</label>
|
|
77
|
-
<input type="text" id="launchButtonColor" name="launchButtonColor" />
|
|
78
|
-
<label htmlFor="launchButtonIconColor">Launch Button Icon Color</label>
|
|
79
|
-
<input
|
|
80
|
-
type="text"
|
|
81
|
-
id="launchButtonIconColor"
|
|
82
|
-
name="launchButtonIconColor"
|
|
83
|
-
/>
|
|
84
|
-
</form>
|
|
85
|
-
{showCookieBanner && (
|
|
86
|
-
<div
|
|
87
|
-
id="bottomBanner"
|
|
88
|
-
style={{
|
|
89
|
-
position: "absolute",
|
|
90
|
-
bottom: 0,
|
|
91
|
-
left: 0,
|
|
92
|
-
width: "100%",
|
|
93
|
-
height: "20vh",
|
|
94
|
-
backgroundColor: "lightpink",
|
|
95
|
-
textAlign: "center",
|
|
96
|
-
}}
|
|
97
|
-
>
|
|
98
|
-
<p style={{ marginTop: "5em" }}>
|
|
99
|
-
Hi, I'm a banner that appears at the bottom of the screen! I eat
|
|
100
|
-
chat widgets for breakfast and I'll eat yours if you aren't careful!
|
|
101
|
-
</p>
|
|
102
|
-
<div
|
|
103
|
-
style={{ position: "absolute", top: "1rem", right: "1rem" }}
|
|
104
|
-
onClick={() => setShowCookieBanner(false)}
|
|
105
|
-
>
|
|
106
|
-
✕
|
|
107
|
-
</div>
|
|
108
|
-
</div>
|
|
109
|
-
)}
|
|
110
|
-
</>
|
|
111
|
-
);
|
|
112
|
-
};
|
|
113
|
-
ReactDOM.render(<DemoApp />, document.getElementById("root"));
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700;900&display=swap");
|
|
2
|
-
|
|
3
|
-
$glowBarHeight: 11.2px;
|
|
4
|
-
$enterAnimationDuration: 0.5s;
|
|
5
|
-
$glowBarGif: url("https://s3.us-west-2.amazonaws.com/meetelise.com/HorizontalBar-Shadow.gif");
|
|
6
|
-
$desktopZIndex: 100000; // on desktop, since the widget is small, it's ok to appear above a cookie banner
|
|
7
|
-
|
|
8
|
-
@keyframes slideInFromRight {
|
|
9
|
-
from {
|
|
10
|
-
transform: translateX(100%);
|
|
11
|
-
}
|
|
12
|
-
to {
|
|
13
|
-
transform: translateX(0);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
.launcher {
|
|
18
|
-
// CSS reset
|
|
19
|
-
all: initial;
|
|
20
|
-
// we want to isolate the app from the host page's styles, but not undo our own subcomponents' styles
|
|
21
|
-
*:not(svg, svg *, :global(.excludeFromReset)) {
|
|
22
|
-
all: revert;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
&:not(.miniLauncher) {
|
|
26
|
-
display: flex;
|
|
27
|
-
justify-content: space-evenly;
|
|
28
|
-
align-items: center;
|
|
29
|
-
background-color: rgba(white, 0.8);
|
|
30
|
-
color: #202020;
|
|
31
|
-
backdrop-filter: blur(10px);
|
|
32
|
-
box-shadow: 0px 8px 8px 4px rgba(0, 0, 0, 0.25);
|
|
33
|
-
|
|
34
|
-
&.mobile {
|
|
35
|
-
width: 100%;
|
|
36
|
-
bottom: 0px;
|
|
37
|
-
padding: 5px;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
&.desktop {
|
|
41
|
-
width: 272px;
|
|
42
|
-
height: 112px;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
font-family: Poppins;
|
|
47
|
-
user-select: none;
|
|
48
|
-
position: fixed;
|
|
49
|
-
|
|
50
|
-
&:hover {
|
|
51
|
-
background: radial-gradient(
|
|
52
|
-
36.85% 65.32% at 50% 106.31%,
|
|
53
|
-
#03ecc4 0%,
|
|
54
|
-
rgba(131, 129, 142, 1) 100%
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
&.desktop {
|
|
59
|
-
right: 0px;
|
|
60
|
-
overflow: hidden;
|
|
61
|
-
border-radius: 10px 0px 0px 10px;
|
|
62
|
-
bottom: 40px;
|
|
63
|
-
z-index: $desktopZIndex;
|
|
64
|
-
|
|
65
|
-
&.firstMount {
|
|
66
|
-
animation: slideInFromRight $enterAnimationDuration;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.glowBar {
|
|
71
|
-
overflow: hidden;
|
|
72
|
-
background-image: $glowBarGif;
|
|
73
|
-
background-position: center;
|
|
74
|
-
position: absolute;
|
|
75
|
-
top: 0;
|
|
76
|
-
left: 0;
|
|
77
|
-
height: $glowBarHeight;
|
|
78
|
-
width: 100%;
|
|
79
|
-
|
|
80
|
-
& + * {
|
|
81
|
-
margin-top: calc($glowBarHeight + 8px);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
.content {
|
|
86
|
-
display: flex;
|
|
87
|
-
flex-direction: column;
|
|
88
|
-
align-items: center;
|
|
89
|
-
gap: 10px;
|
|
90
|
-
margin-bottom: 6px;
|
|
91
|
-
|
|
92
|
-
.header {
|
|
93
|
-
display: flex;
|
|
94
|
-
align-items: center;
|
|
95
|
-
|
|
96
|
-
.headerText {
|
|
97
|
-
font-weight: 600;
|
|
98
|
-
font-size: 20px;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
.subtitle {
|
|
103
|
-
font-size: 12px;
|
|
104
|
-
font-weight: 600;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
.miniLauncher {
|
|
110
|
-
display: flex;
|
|
111
|
-
align-items: center;
|
|
112
|
-
background-color: #202020;
|
|
113
|
-
position: fixed;
|
|
114
|
-
height: 35px;
|
|
115
|
-
|
|
116
|
-
&:hover {
|
|
117
|
-
background: radial-gradient(
|
|
118
|
-
36.85% 65.32% at 50% 106.31%,
|
|
119
|
-
#03ecc4 0%,
|
|
120
|
-
rgba(131, 129, 142, 1) 100%
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
&.firstMount {
|
|
125
|
-
animation: slideInFromRight $enterAnimationDuration;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
&.desktop {
|
|
129
|
-
padding-right: 20px;
|
|
130
|
-
right: 0px;
|
|
131
|
-
overflow: hidden;
|
|
132
|
-
bottom: 40px;
|
|
133
|
-
z-index: $desktopZIndex;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
&.mobile {
|
|
137
|
-
right: 10px;
|
|
138
|
-
bottom: 20px;
|
|
139
|
-
}
|
|
140
|
-
}
|
package/src/InHouseLauncher.tsx
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import classnames from "classnames";
|
|
3
|
-
import ChatButton from "./ChatButton";
|
|
4
|
-
import styles from "./InHouseLauncher.module.scss";
|
|
5
|
-
|
|
6
|
-
interface InHouseLauncherProps {
|
|
7
|
-
onChatTapped: () => void;
|
|
8
|
-
mobile: boolean;
|
|
9
|
-
firstMount: boolean;
|
|
10
|
-
backgroundColor: string;
|
|
11
|
-
textColor: string;
|
|
12
|
-
mini: boolean;
|
|
13
|
-
}
|
|
14
|
-
const InHouseLauncher: React.FunctionComponent<InHouseLauncherProps> = ({
|
|
15
|
-
onChatTapped,
|
|
16
|
-
mobile,
|
|
17
|
-
firstMount,
|
|
18
|
-
backgroundColor,
|
|
19
|
-
textColor,
|
|
20
|
-
mini,
|
|
21
|
-
}) => {
|
|
22
|
-
if (mini) {
|
|
23
|
-
return (
|
|
24
|
-
<ChatButton
|
|
25
|
-
className={classnames(
|
|
26
|
-
styles.miniLauncher,
|
|
27
|
-
"excludeFromReset",
|
|
28
|
-
mobile ? styles.mobile : styles.desktop,
|
|
29
|
-
{ [styles.firstMount]: firstMount }
|
|
30
|
-
)}
|
|
31
|
-
text="Ask a question"
|
|
32
|
-
onClick={onChatTapped}
|
|
33
|
-
/>
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return (
|
|
38
|
-
<div
|
|
39
|
-
className={classnames(
|
|
40
|
-
styles.launcher,
|
|
41
|
-
mobile ? styles.mobile : styles.desktop,
|
|
42
|
-
{ [styles.firstMount]: firstMount }
|
|
43
|
-
)}
|
|
44
|
-
style={{ backgroundColor, color: textColor }}
|
|
45
|
-
onClick={onChatTapped}
|
|
46
|
-
>
|
|
47
|
-
<div className={styles.glowBar}></div>
|
|
48
|
-
<div className={styles.content}>
|
|
49
|
-
<div className={styles.header}>
|
|
50
|
-
<ChatButton
|
|
51
|
-
text="Ask"
|
|
52
|
-
useGlowingBorder
|
|
53
|
-
className={"excludeFromReset"}
|
|
54
|
-
/>
|
|
55
|
-
<span className={styles.headerText}>a question</span>
|
|
56
|
-
</div>
|
|
57
|
-
<span className={styles.subtitle}>
|
|
58
|
-
I can also help you schedule a tour.
|
|
59
|
-
</span>
|
|
60
|
-
</div>
|
|
61
|
-
</div>
|
|
62
|
-
);
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export default InHouseLauncher;
|
package/src/MEChat.module.scss
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
:global(#__talkjs_launcher):not(.shouldBeVisible) {
|
|
2
|
-
display: none;
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
// This docks the chat panel at the right edge of the screen.
|
|
6
|
-
// The 25px is because .ChatBox is styled by TalkJS with `width: calc(100% - 25px)`.
|
|
7
|
-
:global(.meetelise-chat.pane).desktop {
|
|
8
|
-
right: -25px;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
.inHouseLauncherContainer {
|
|
12
|
-
&.desktop {
|
|
13
|
-
width: 100px;
|
|
14
|
-
height: 100px;
|
|
15
|
-
position: relative;
|
|
16
|
-
overflow: hidden;
|
|
17
|
-
}
|
|
18
|
-
&.mobile {
|
|
19
|
-
width: 100%;
|
|
20
|
-
height: 100px;
|
|
21
|
-
}
|
|
22
|
-
}
|
package/src/MEChat.tsx
DELETED
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import ReactDOM from "react-dom";
|
|
3
|
-
import Talk from "talkjs";
|
|
4
|
-
|
|
5
|
-
import fetchBuildingInfo, { Building } from "./fetchBuildingInfo";
|
|
6
|
-
import { getChatID, createChatID } from "./chatID";
|
|
7
|
-
import createConversation from "./createConversation";
|
|
8
|
-
import { Theme, ThemeIdString } from "./themes";
|
|
9
|
-
import Analytics from "./analytics";
|
|
10
|
-
import { isMobile } from "./utils";
|
|
11
|
-
import InHouseLauncher from "./InHouseLauncher";
|
|
12
|
-
import styles from "./MEChat.module.scss";
|
|
13
|
-
import { getTheme } from "./themes";
|
|
14
|
-
|
|
15
|
-
const LAUNCHER_CLASSES = ["meetelise-chat", "launcher"];
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* The interface to MeetElise chat.
|
|
19
|
-
*
|
|
20
|
-
* To add meetelise chat to the screen, call its static method
|
|
21
|
-
* `start()` with your building and organization slug.
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* MEChat.start({
|
|
25
|
-
* organization: 'the-jacobson-group',
|
|
26
|
-
* building: 'twin-rivers-pointe'
|
|
27
|
-
* });
|
|
28
|
-
*/
|
|
29
|
-
export default class MEChat {
|
|
30
|
-
static session: Promise<Talk.Session> = Talk.ready.then(() => {
|
|
31
|
-
const me = new Talk.User({
|
|
32
|
-
id: "anonymous",
|
|
33
|
-
name: "Me",
|
|
34
|
-
email: null,
|
|
35
|
-
role: "Default",
|
|
36
|
-
});
|
|
37
|
-
return new Talk.Session({
|
|
38
|
-
appId: "ogKIvCor",
|
|
39
|
-
me,
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Start an instance of MeetElise chat and add to the web page.
|
|
45
|
-
*
|
|
46
|
-
* @param opts The organization, building, and theme overrides.
|
|
47
|
-
* @returns An instance of MeetElise chat.
|
|
48
|
-
*/
|
|
49
|
-
static start(opts: Options): MEChat {
|
|
50
|
-
return new MEChat(opts);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Remove the instance from the screen.
|
|
55
|
-
*
|
|
56
|
-
* Chat will be unusable after this. If you just need to hide the
|
|
57
|
-
* chat button, use {@link MEChat#hide} instead.
|
|
58
|
-
*/
|
|
59
|
-
remove(): void {
|
|
60
|
-
this.popup.then((p) => p.destroy());
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Clear all messages from the window and start a new conversation.
|
|
65
|
-
*/
|
|
66
|
-
restartConversation(): void {
|
|
67
|
-
Promise.all([
|
|
68
|
-
MEChat.session,
|
|
69
|
-
this.building,
|
|
70
|
-
this.theme,
|
|
71
|
-
this.avatarSrc,
|
|
72
|
-
this.popup,
|
|
73
|
-
]).then(([session, building, theme, avatarSrc, popup]) => {
|
|
74
|
-
this.chatId = createChatID(this.orgSlug, this.buildingSlug);
|
|
75
|
-
this.analytics.chatId = this.chatId;
|
|
76
|
-
popup.select(
|
|
77
|
-
createConversation(
|
|
78
|
-
session,
|
|
79
|
-
building,
|
|
80
|
-
theme,
|
|
81
|
-
avatarSrc || building.avatarSrc,
|
|
82
|
-
this.chatId,
|
|
83
|
-
this.isMobile
|
|
84
|
-
)
|
|
85
|
-
);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Update the theme of the running chat instance.
|
|
91
|
-
*
|
|
92
|
-
* @param theme The updated theme
|
|
93
|
-
*/
|
|
94
|
-
// TODO: probably want to replace theme argument with themeId, avatarSrc
|
|
95
|
-
setTheme(newTheme: Partial<Theme>): void {
|
|
96
|
-
const focusedElement = document.activeElement;
|
|
97
|
-
Promise.all([
|
|
98
|
-
MEChat.session,
|
|
99
|
-
this.building,
|
|
100
|
-
this.theme,
|
|
101
|
-
this.avatarSrc,
|
|
102
|
-
this.popup,
|
|
103
|
-
])
|
|
104
|
-
.then(([session, building, theme, avatarSrc, popup]) => {
|
|
105
|
-
popup.select(
|
|
106
|
-
createConversation(
|
|
107
|
-
session,
|
|
108
|
-
building,
|
|
109
|
-
{ ...theme, ...newTheme },
|
|
110
|
-
avatarSrc || building.avatarSrc,
|
|
111
|
-
this.chatId,
|
|
112
|
-
this.isMobile
|
|
113
|
-
)
|
|
114
|
-
);
|
|
115
|
-
return new Promise(requestAnimationFrame);
|
|
116
|
-
})
|
|
117
|
-
.then(() => {
|
|
118
|
-
if (!focusedElement) return;
|
|
119
|
-
if (focusedElement instanceof HTMLElement) focusedElement.focus();
|
|
120
|
-
else focusedElement.parentElement?.focus();
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/** Open the messages window */
|
|
125
|
-
open(): void {
|
|
126
|
-
this.popup.then((p) => p.show());
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/** Close the messages window */
|
|
130
|
-
close(): void {
|
|
131
|
-
this.popup.then((p) => p.hide());
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/** Show the chat button on the screen if it was previously hidden. */
|
|
135
|
-
// TODO: will this work with the new launcher? it needs to be display flex? will this just change the inline style and leave the stylesheet/style tag alone?
|
|
136
|
-
show(): void {
|
|
137
|
-
this.launcher.then((a) => (a.style.display = ""));
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/** Hide the chat button from the screen (but don't remove from the DOM). */
|
|
141
|
-
hide(): void {
|
|
142
|
-
this.launcher.then((a) => (a.style.display = "none"));
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** Show a custom launcher designed in-house instead of the default TalkJS launcher. */
|
|
146
|
-
private async mountInHouseLauncher(
|
|
147
|
-
targetElement: HTMLElement,
|
|
148
|
-
firstMount: boolean
|
|
149
|
-
): Promise<void> {
|
|
150
|
-
const chatTappedHandler = async () => {
|
|
151
|
-
ReactDOM.unmountComponentAtNode(targetElement);
|
|
152
|
-
(await this.popup).show();
|
|
153
|
-
this.analytics.ping("open");
|
|
154
|
-
};
|
|
155
|
-
ReactDOM.render(
|
|
156
|
-
<InHouseLauncher
|
|
157
|
-
onChatTapped={chatTappedHandler}
|
|
158
|
-
mobile={this.isMobile}
|
|
159
|
-
firstMount={firstMount}
|
|
160
|
-
backgroundColor={(await this.theme).chatPaneBackgroundColor}
|
|
161
|
-
textColor={(await this.theme).chatHeader.textColor}
|
|
162
|
-
mini={this.useMiniWidget}
|
|
163
|
-
/>,
|
|
164
|
-
targetElement
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
private async getInHouseLauncher(): Promise<HTMLElement> {
|
|
169
|
-
const inHouseLauncherTarget = document.createElement("div");
|
|
170
|
-
inHouseLauncherTarget.classList.add(
|
|
171
|
-
styles.inHouseLauncherContainer,
|
|
172
|
-
this.isMobile ? styles.mobile : styles.desktop,
|
|
173
|
-
...LAUNCHER_CLASSES,
|
|
174
|
-
styles.shouldBeVisible
|
|
175
|
-
);
|
|
176
|
-
document.body.appendChild(inHouseLauncherTarget);
|
|
177
|
-
this.mountInHouseLauncher(inHouseLauncherTarget, true);
|
|
178
|
-
(await this.popup).on("close", () => {
|
|
179
|
-
this.mountInHouseLauncher(inHouseLauncherTarget, false);
|
|
180
|
-
});
|
|
181
|
-
return inHouseLauncherTarget;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
private buildingSlug: string;
|
|
185
|
-
private orgSlug: string;
|
|
186
|
-
private popup: Promise<Talk.Popup>;
|
|
187
|
-
private launcher: Promise<HTMLElement>;
|
|
188
|
-
private building: Promise<Building>;
|
|
189
|
-
private themeId?: ThemeIdString;
|
|
190
|
-
private theme: Promise<Theme>;
|
|
191
|
-
private avatarSrc?: Promise<null | string>;
|
|
192
|
-
private chatId: string;
|
|
193
|
-
private analytics: Analytics;
|
|
194
|
-
private useInHouseLauncher: boolean;
|
|
195
|
-
private useMiniWidget: boolean;
|
|
196
|
-
private isMobile: boolean;
|
|
197
|
-
|
|
198
|
-
private constructor({
|
|
199
|
-
organization,
|
|
200
|
-
building,
|
|
201
|
-
themeId,
|
|
202
|
-
avatarSrc,
|
|
203
|
-
mini = false,
|
|
204
|
-
}: Options) {
|
|
205
|
-
this.orgSlug = organization;
|
|
206
|
-
this.buildingSlug = building;
|
|
207
|
-
this.chatId = getChatID(organization, building);
|
|
208
|
-
this.analytics = new Analytics(organization, building, this.chatId);
|
|
209
|
-
this.analytics.ping("load");
|
|
210
|
-
this.building = fetchBuildingInfo(organization, building);
|
|
211
|
-
this.themeId = themeId;
|
|
212
|
-
this.theme = this.building.then((building) =>
|
|
213
|
-
getTheme(themeId || building.themeId)
|
|
214
|
-
);
|
|
215
|
-
this.avatarSrc = this.building.then(
|
|
216
|
-
(building) => avatarSrc || building.avatarSrc
|
|
217
|
-
);
|
|
218
|
-
this.useInHouseLauncher = true;
|
|
219
|
-
this.useMiniWidget = mini;
|
|
220
|
-
this.isMobile = isMobile();
|
|
221
|
-
|
|
222
|
-
this.popup = Promise.all([
|
|
223
|
-
this.building,
|
|
224
|
-
this.theme,
|
|
225
|
-
this.avatarSrc,
|
|
226
|
-
MEChat.session,
|
|
227
|
-
]).then(async ([building, theme, avatarSrc, session]) => {
|
|
228
|
-
const popup = session.createPopup(
|
|
229
|
-
createConversation(
|
|
230
|
-
session,
|
|
231
|
-
building,
|
|
232
|
-
theme,
|
|
233
|
-
avatarSrc || building.avatarSrc,
|
|
234
|
-
this.chatId,
|
|
235
|
-
this.isMobile
|
|
236
|
-
),
|
|
237
|
-
{
|
|
238
|
-
launcher: this.useInHouseLauncher ? "never" : "always",
|
|
239
|
-
showCloseInHeader: true,
|
|
240
|
-
messageField: { placeholder: "Ask a question..." },
|
|
241
|
-
}
|
|
242
|
-
);
|
|
243
|
-
if (building.conversationMaintenanceMode) {
|
|
244
|
-
return new Promise(() => {
|
|
245
|
-
// If in maintenance mode, we return an always-pending Promise
|
|
246
|
-
// eslint-disable-next-line no-console
|
|
247
|
-
console.warn(
|
|
248
|
-
"MeetElise Chat is in maintenance mode. Chat icon will not appear."
|
|
249
|
-
);
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
await popup.mount({ show: false });
|
|
253
|
-
const talkjsPopupElement = document.querySelector(".__talkjs_popup");
|
|
254
|
-
if (!talkjsPopupElement) throw new Error("Failed to find chat window");
|
|
255
|
-
talkjsPopupElement.classList.add("meetelise-chat", "pane");
|
|
256
|
-
if (!this.isMobile) {
|
|
257
|
-
talkjsPopupElement.classList.add(styles.desktop);
|
|
258
|
-
}
|
|
259
|
-
(talkjsPopupElement as HTMLElement).style.zIndex = "99999999999";
|
|
260
|
-
return popup;
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
this.launcher = Promise.all([this.popup]).then(async () => {
|
|
264
|
-
let launcherElement: HTMLElement;
|
|
265
|
-
|
|
266
|
-
if (this.useInHouseLauncher) {
|
|
267
|
-
// TODO: there's a big delay between page load and the launcher getting added, maybe 2s. Maybe put it earlier even if it has to wait for TalkJS to load to be functional?
|
|
268
|
-
launcherElement = await this.getInHouseLauncher();
|
|
269
|
-
} else {
|
|
270
|
-
const talkjsLauncherElement = document.querySelector<HTMLElement>(
|
|
271
|
-
"a#__talkjs_launcher"
|
|
272
|
-
);
|
|
273
|
-
if (!talkjsLauncherElement)
|
|
274
|
-
throw new Error("MeetElise Chat: Could not locate launcher.");
|
|
275
|
-
launcherElement = talkjsLauncherElement;
|
|
276
|
-
launcherElement.classList.add(
|
|
277
|
-
...LAUNCHER_CLASSES,
|
|
278
|
-
styles.shouldBeVisible
|
|
279
|
-
);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
return launcherElement;
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
export interface Options {
|
|
288
|
-
building: string;
|
|
289
|
-
organization: string;
|
|
290
|
-
themeId?: ThemeIdString;
|
|
291
|
-
avatarSrc?: string;
|
|
292
|
-
mini?: boolean;
|
|
293
|
-
}
|