@meetelise/chat 1.3.1 → 1.4.3
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/pull.yml +0 -1
- package/.github/workflows/release.yml +1 -1
- package/.husky/commit-msg +0 -2
- package/.vscode/settings.json +2 -1
- package/declarations.d.ts +4 -0
- package/package.json +30 -24
- package/public/demo/index.html +55 -0
- package/public/dist/bundle.js +2 -0
- package/public/dist/bundle.js.LICENSE.txt +32 -0
- package/{demo → public}/index.html +1 -1
- package/public/serve.json +13 -0
- package/public/ts-loader-build/ChatBubble.d.ts +12 -0
- package/{dist/src → public/ts-loader-build}/MEChat.d.ts +3 -0
- package/{dist/src → public/ts-loader-build}/analytics.d.ts +5 -0
- package/{dist/src → public/ts-loader-build}/chatID.d.ts +0 -0
- package/{dist/src → public/ts-loader-build}/createConversation.d.ts +0 -0
- package/{dist/src → public/ts-loader-build}/fetchBuildingInfo.d.ts +0 -0
- package/{dist/src → public/ts-loader-build}/getAvatarUrl.d.ts +0 -0
- package/{dist/src → public/ts-loader-build}/getIcons.d.ts +0 -0
- package/{dist/src → public/ts-loader-build}/installTalkJSStyles.d.ts +0 -0
- package/public/ts-loader-build/launchDarklyManager.d.ts +6 -0
- package/{dist/src → public/ts-loader-build}/resolveTheme.d.ts +0 -0
- package/public/ts-loader-build/utils.d.ts +1 -0
- package/src/ChatBubble.d.ts +11 -0
- package/src/ChatBubble.module.scss +58 -0
- package/src/ChatBubble.tsx +55 -0
- package/src/DemoApp.tsx +103 -0
- package/src/MEChat.d.ts +65 -0
- package/src/MEChat.module.scss +58 -0
- package/src/MEChat.test.ts +25 -16
- package/src/MEChat.tsx +277 -0
- package/src/analytics.ts +13 -0
- package/src/getIcons.ts +26 -11
- package/src/launchDarklyManager.ts +30 -0
- package/src/utils.ts +24 -0
- package/tsconfig.json +5 -4
- package/web-test-runner.config.js +1 -1
- package/webpack.config.cjs +65 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +0 -1
- package/dist/src/MEChat.js +0 -144
- package/dist/src/MEChat.js.map +0 -1
- package/dist/src/analytics.js +0 -30
- package/dist/src/analytics.js.map +0 -1
- package/dist/src/chatID.js +0 -28
- package/dist/src/chatID.js.map +0 -1
- package/dist/src/createConversation.js +0 -30
- package/dist/src/createConversation.js.map +0 -1
- package/dist/src/fetchBuildingInfo.js +0 -15
- package/dist/src/fetchBuildingInfo.js.map +0 -1
- package/dist/src/getAvatarUrl.js +0 -36
- package/dist/src/getAvatarUrl.js.map +0 -1
- package/dist/src/getIcons.js +0 -32
- package/dist/src/getIcons.js.map +0 -1
- package/dist/src/installTalkJSStyles.js +0 -26
- package/dist/src/installTalkJSStyles.js.map +0 -1
- package/dist/src/resolveTheme.js +0 -17
- package/dist/src/resolveTheme.js.map +0 -1
- package/index.ts +0 -1
- package/src/MEChat.ts +0 -183
- package/web-dev-server.config.js +0 -10
package/src/MEChat.tsx
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import Talk from "talkjs";
|
|
2
|
+
import fetchBuildingInfo, { Building } from "./fetchBuildingInfo";
|
|
3
|
+
import { getChatID, createChatID } from "./chatID";
|
|
4
|
+
import createConversation from "./createConversation";
|
|
5
|
+
import installTalkJSStyles from "./installTalkJSStyles";
|
|
6
|
+
import resolveTheme, { Theme } from "./resolveTheme";
|
|
7
|
+
import Analytics from "./analytics";
|
|
8
|
+
import ChatBubble from "./ChatBubble";
|
|
9
|
+
import ReactDOM from "react-dom";
|
|
10
|
+
import React from "react";
|
|
11
|
+
import * as LDClient from "launchdarkly-js-client-sdk";
|
|
12
|
+
import LaunchDarkly from "./launchDarklyManager";
|
|
13
|
+
import styles from "./MEChat.module.scss";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The interface to MeetElise chat.
|
|
17
|
+
*
|
|
18
|
+
* To add meetelise chat to the screen, call its static method
|
|
19
|
+
* `start()` with your building and organization slug.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* MEChat.start({
|
|
23
|
+
* organization: 'the-jacobson-group',
|
|
24
|
+
* building: 'twin-rivers-pointe'
|
|
25
|
+
* });
|
|
26
|
+
*/
|
|
27
|
+
export default class MEChat {
|
|
28
|
+
static session: Promise<Talk.Session> = Talk.ready.then(() => {
|
|
29
|
+
const me = new Talk.User({
|
|
30
|
+
id: "anonymous",
|
|
31
|
+
name: "Me",
|
|
32
|
+
email: null,
|
|
33
|
+
role: "default",
|
|
34
|
+
});
|
|
35
|
+
return new Talk.Session({
|
|
36
|
+
appId: "ogKIvCor",
|
|
37
|
+
me,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Start an instance of MeetElise chat and add to the web page.
|
|
43
|
+
*
|
|
44
|
+
* @param opts The organization, building, and theme overrides.
|
|
45
|
+
* @returns An instance of MeetElise chat.
|
|
46
|
+
*/
|
|
47
|
+
static start(opts: Options): MEChat {
|
|
48
|
+
return new MEChat(opts);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Remove the instance from the screen.
|
|
53
|
+
*
|
|
54
|
+
* Chat will be unusable after this. If you just need to hide the
|
|
55
|
+
* chat button, use {@link MEChat#hide} instead.
|
|
56
|
+
*/
|
|
57
|
+
remove(): void {
|
|
58
|
+
this.popup.then((p) => p.destroy());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Clear all messages from the window and start a new conversation.
|
|
63
|
+
*/
|
|
64
|
+
restartConversation(): void {
|
|
65
|
+
Promise.all([MEChat.session, this.building, this.popup]).then(
|
|
66
|
+
([session, building, popup]) => {
|
|
67
|
+
this.chatId = createChatID(this.orgSlug, this.buildingSlug);
|
|
68
|
+
this.analytics.chatId = this.chatId;
|
|
69
|
+
popup.select(
|
|
70
|
+
createConversation(
|
|
71
|
+
session,
|
|
72
|
+
building,
|
|
73
|
+
resolveTheme(building, this.theme),
|
|
74
|
+
this.chatId
|
|
75
|
+
)
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Update the theme of the running chat instance.
|
|
83
|
+
*
|
|
84
|
+
* @param theme The updated theme
|
|
85
|
+
*/
|
|
86
|
+
setTheme(theme: Partial<Theme>): void {
|
|
87
|
+
const focusedElement = document.activeElement;
|
|
88
|
+
Promise.all([
|
|
89
|
+
MEChat.session,
|
|
90
|
+
this.building,
|
|
91
|
+
this.popup,
|
|
92
|
+
LaunchDarkly.isReady,
|
|
93
|
+
])
|
|
94
|
+
.then(([session, building, popup]) => {
|
|
95
|
+
const resolvedTheme = (this.theme = resolveTheme(building, {
|
|
96
|
+
...this.theme,
|
|
97
|
+
...theme,
|
|
98
|
+
}));
|
|
99
|
+
popup.select(
|
|
100
|
+
createConversation(session, building, resolvedTheme, this.chatId)
|
|
101
|
+
);
|
|
102
|
+
installTalkJSStyles(resolvedTheme);
|
|
103
|
+
return new Promise(requestAnimationFrame);
|
|
104
|
+
})
|
|
105
|
+
.then(() => {
|
|
106
|
+
if (!focusedElement) return;
|
|
107
|
+
if (focusedElement instanceof HTMLElement) focusedElement.focus();
|
|
108
|
+
else focusedElement.parentElement?.focus();
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Open the messages window */
|
|
113
|
+
open(): void {
|
|
114
|
+
this.popup.then((p) => p.show());
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Close the messages window */
|
|
118
|
+
close(): void {
|
|
119
|
+
this.popup.then((p) => p.hide());
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Show the chat button on the screen if it was previously hidden. */
|
|
123
|
+
show(): void {
|
|
124
|
+
this.launcher.then((a) => (a.style.display = ""));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Hide the chat button from the screen (but don't remove from the DOM). */
|
|
128
|
+
hide(): void {
|
|
129
|
+
this.launcher.then((a) => (a.style.display = "none"));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Show a speech bubble next to the chat button (launcher). Also adds some animations to the button. */
|
|
133
|
+
private addChatBubble(popup: Talk.Popup, launcher: HTMLAnchorElement): void {
|
|
134
|
+
const chatBubbleTarget = document.createElement("div");
|
|
135
|
+
// set up scroll listener before mounting the chat bubble component so we don't miss any scroll events
|
|
136
|
+
const closeChatBubble = (shouldFade = false) => {
|
|
137
|
+
if (shouldFade) {
|
|
138
|
+
chatBubbleTarget.classList.add(styles.fadeOut);
|
|
139
|
+
setTimeout(() => {
|
|
140
|
+
ReactDOM.unmountComponentAtNode(chatBubbleTarget);
|
|
141
|
+
}, 500);
|
|
142
|
+
} else {
|
|
143
|
+
ReactDOM.unmountComponentAtNode(chatBubbleTarget);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
// wrap the launcher and chat bubble so we can position them together but also manipulate them independently
|
|
147
|
+
const wrapper = document.createElement("div");
|
|
148
|
+
// for us, the wrapper contains the chat bubble and launcher. for consumers, we'll just call the wrapper the launcher.
|
|
149
|
+
wrapper.classList.add(styles.wrapper, "meetelise-chat", "launcher");
|
|
150
|
+
launcher.parentNode?.appendChild(wrapper);
|
|
151
|
+
wrapper.appendChild(launcher);
|
|
152
|
+
wrapper.appendChild(chatBubbleTarget);
|
|
153
|
+
// TalkJS positions the launcher, but we want to control its position ourselves
|
|
154
|
+
launcher.style.position = "unset";
|
|
155
|
+
launcher.style.top = "unset";
|
|
156
|
+
launcher.style.right = "unset";
|
|
157
|
+
// we initially hide the launcher in CSS so it doesn't visibly jump when we remove the native TalkJS positioning. Unhide it now.
|
|
158
|
+
launcher.classList.add(styles.shouldBeVisible);
|
|
159
|
+
|
|
160
|
+
popup.on("open", () => closeChatBubble());
|
|
161
|
+
const triggerBounce = () => {
|
|
162
|
+
launcher.classList.add(styles.bouncingLauncherButton);
|
|
163
|
+
launcher.addEventListener("animationend", () => {
|
|
164
|
+
launcher.classList.remove(styles.bouncingLauncherButton);
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
const bounceInterval = 3;
|
|
168
|
+
ReactDOM.render(
|
|
169
|
+
<ChatBubble
|
|
170
|
+
messages={[
|
|
171
|
+
{
|
|
172
|
+
title: "Ask us a question",
|
|
173
|
+
text: "I can also help you schedule a tour.",
|
|
174
|
+
},
|
|
175
|
+
]}
|
|
176
|
+
triggerBounce={triggerBounce}
|
|
177
|
+
bounceIntervalInSeconds={bounceInterval}
|
|
178
|
+
onClick={() => {
|
|
179
|
+
popup.show();
|
|
180
|
+
closeChatBubble();
|
|
181
|
+
}}
|
|
182
|
+
/>,
|
|
183
|
+
chatBubbleTarget
|
|
184
|
+
);
|
|
185
|
+
setTimeout(() => closeChatBubble(true), bounceInterval * 1000 + 3000);
|
|
186
|
+
// TODO: remove? it seems to be triggered immediately on https://www.simpsonpropertygroup.com/apartments/houston-texas/skyhouse-river-oaks-galleria without scrolling
|
|
187
|
+
// document.addEventListener("scroll", () => closeChatBubble(true));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private buildingSlug: string;
|
|
191
|
+
private orgSlug: string;
|
|
192
|
+
private popup: Promise<Talk.Popup>;
|
|
193
|
+
private launcher: Promise<HTMLAnchorElement>;
|
|
194
|
+
private building: Promise<Building>;
|
|
195
|
+
private theme: Partial<Theme>;
|
|
196
|
+
private chatId: string;
|
|
197
|
+
private analytics: Analytics;
|
|
198
|
+
private launchDarklyClient: LDClient.LDClient;
|
|
199
|
+
|
|
200
|
+
private constructor({ organization, building, theme = {} }: Options) {
|
|
201
|
+
this.orgSlug = organization;
|
|
202
|
+
this.buildingSlug = building;
|
|
203
|
+
this.chatId = getChatID(organization, building);
|
|
204
|
+
this.analytics = new Analytics(organization, building, this.chatId);
|
|
205
|
+
this.launchDarklyClient = LaunchDarkly.getClient(this.chatId);
|
|
206
|
+
this.analytics.ping("load");
|
|
207
|
+
this.theme = theme;
|
|
208
|
+
this.building = fetchBuildingInfo(organization, building);
|
|
209
|
+
|
|
210
|
+
this.popup = Promise.all([
|
|
211
|
+
this.building,
|
|
212
|
+
MEChat.session,
|
|
213
|
+
LaunchDarkly.isReady,
|
|
214
|
+
]).then(async ([building, session]) => {
|
|
215
|
+
const resolvedTheme = (this.theme = resolveTheme(building, theme));
|
|
216
|
+
installTalkJSStyles(resolvedTheme);
|
|
217
|
+
const p = session.createPopup(
|
|
218
|
+
createConversation(session, building, resolvedTheme, this.chatId)
|
|
219
|
+
);
|
|
220
|
+
p.on("open", () => {
|
|
221
|
+
this.analytics.ping("open");
|
|
222
|
+
});
|
|
223
|
+
if (building.conversationMaintenanceMode) {
|
|
224
|
+
return new Promise(() => {
|
|
225
|
+
// If in maintenance mode, we return an always-pending Promise
|
|
226
|
+
// eslint-disable-next-line no-console
|
|
227
|
+
console.warn(
|
|
228
|
+
"MeetElise Chat is in maintenance mode. Chat icon will not appear."
|
|
229
|
+
);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
await p.mount({ show: false });
|
|
233
|
+
const talkjsPopupElement = document.querySelector(".__talkjs_popup");
|
|
234
|
+
if (!talkjsPopupElement) throw new Error("Failed to find chat window");
|
|
235
|
+
talkjsPopupElement.classList.add("meetelise-chat", "pane");
|
|
236
|
+
return p;
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
this.launcher = Promise.all([this.popup, LaunchDarkly.isReady]).then(
|
|
240
|
+
async ([popup]) => {
|
|
241
|
+
const talkjsLauncherElement = document.querySelector<HTMLAnchorElement>(
|
|
242
|
+
"a#__talkjs_launcher"
|
|
243
|
+
);
|
|
244
|
+
if (!talkjsLauncherElement)
|
|
245
|
+
throw new Error("MeetElise Chat: Could not locate launcher.");
|
|
246
|
+
|
|
247
|
+
const webchatBubbleFlag = this.launchDarklyClient.variation(
|
|
248
|
+
"webchat-bubble",
|
|
249
|
+
false
|
|
250
|
+
);
|
|
251
|
+
this.analytics.setFeatureFlags({
|
|
252
|
+
webchatBubble: webchatBubbleFlag,
|
|
253
|
+
});
|
|
254
|
+
this.analytics.ping("receivedFeatureFlags");
|
|
255
|
+
|
|
256
|
+
if (webchatBubbleFlag) {
|
|
257
|
+
// TODO: The new icon hasn't been designed for color customization yet, so temporarily disable the background theme color
|
|
258
|
+
talkjsLauncherElement.style.backgroundColor = "white";
|
|
259
|
+
this.addChatBubble(popup, talkjsLauncherElement);
|
|
260
|
+
} else {
|
|
261
|
+
talkjsLauncherElement.classList.add(
|
|
262
|
+
"meetelise-chat",
|
|
263
|
+
"launcher",
|
|
264
|
+
styles.shouldBeVisible
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
return talkjsLauncherElement;
|
|
268
|
+
}
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export interface Options {
|
|
274
|
+
building: string;
|
|
275
|
+
organization: string;
|
|
276
|
+
theme?: Partial<Theme>;
|
|
277
|
+
}
|
package/src/analytics.ts
CHANGED
|
@@ -4,12 +4,24 @@
|
|
|
4
4
|
export default class Analytics {
|
|
5
5
|
private org: string;
|
|
6
6
|
private building: string;
|
|
7
|
+
private featureFlags?: Record<string, boolean>;
|
|
7
8
|
public chatId: string;
|
|
8
9
|
|
|
9
10
|
constructor(org: string, building: string, chatId: string) {
|
|
10
11
|
this.org = org;
|
|
11
12
|
this.building = building;
|
|
12
13
|
this.chatId = chatId;
|
|
14
|
+
this.featureFlags = {};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Store feature flag value(s) on the Analytics object to be sent with future pings. If `featureFlags` object is already present, the argument is merged into it.
|
|
19
|
+
*/
|
|
20
|
+
setFeatureFlags(featureFlags: Record<string, boolean>): void {
|
|
21
|
+
this.featureFlags = {
|
|
22
|
+
...this.featureFlags,
|
|
23
|
+
...featureFlags,
|
|
24
|
+
};
|
|
13
25
|
}
|
|
14
26
|
|
|
15
27
|
/**
|
|
@@ -28,6 +40,7 @@ export default class Analytics {
|
|
|
28
40
|
org: this.org,
|
|
29
41
|
building: this.building,
|
|
30
42
|
referrer: document.referrer,
|
|
43
|
+
featureFlags: this.featureFlags,
|
|
31
44
|
}),
|
|
32
45
|
});
|
|
33
46
|
}
|
package/src/getIcons.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Theme } from "./resolveTheme";
|
|
2
|
+
import LaunchDarkly from "./launchDarklyManager";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Get a pair of close, open icons for a building.
|
|
@@ -6,16 +7,14 @@ import { Theme } from "./resolveTheme";
|
|
|
6
7
|
* @param theme - The building, to get theme colors.
|
|
7
8
|
* @returns a pair of svg data URLs.
|
|
8
9
|
*/
|
|
9
|
-
export default function getIcons(theme: Theme): [string, string] {
|
|
10
|
-
const close = toDataUrl(`
|
|
11
|
-
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="312px" height="312px" viewBox="0 0 312 312" version="1.1">
|
|
12
|
-
<g id="Page-1" stroke="none" stroke-width="1" fill="${theme.launchButtonIconColor}" fill-rule="evenodd">
|
|
13
|
-
<path d="M178.500682,155.862614 L306.579682,28.4831591 C312.720136,22.4048864 312.720136,12.5646136 306.579682,6.50188636 C300.454773,0.423613636 290.505682,0.423613636 284.380773,6.50188636 L156.410591,133.772523 L27.2589545,4.60534091 C21.1340455,-1.53511364 11.1849545,-1.53511364 5.06004545,4.60534091 C-1.06486364,10.7613409 -1.06486364,20.7259773 5.06004545,26.8664318 L134.118409,155.940341 L4.59368182,284.749977 C-1.53122727,290.82825 -1.53122727,300.668523 4.59368182,306.73125 C10.7185909,312.809523 20.6676818,312.809523 26.7925909,306.73125 L156.2085,178.030432 L284.847136,306.684614 C290.972045,312.825068 300.921136,312.825068 307.046045,306.684614 C313.170955,300.528614 313.170955,290.563977 307.046045,284.423523 L178.500682,155.862614 L178.500682,155.862614 Z" id="Close" fill="${theme.launchButtonIconColor}"/>
|
|
14
|
-
</g>
|
|
15
|
-
</svg>`);
|
|
16
10
|
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
export default function getIcons(theme: Theme): [string, string] {
|
|
12
|
+
const useNewIcons = LaunchDarkly.getClient().variation(
|
|
13
|
+
"webchat-bubble",
|
|
14
|
+
false
|
|
15
|
+
);
|
|
16
|
+
const oldOpenIcon = `
|
|
17
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="420px" height="420px" viewBox="0 0 420 420" version="1.1">
|
|
19
18
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
20
19
|
<g id="Shape" fill="${theme.launchButtonIconColor}">
|
|
21
20
|
<path d="M187.402765,324.536547 L305.950138,417.398656 C307.890958,418.88487 309.079928,419.636719 310.233929,419.636719 C313.521083,419.636719 314.500236,417.416141 314.500236,413.726835 L314.500236,325.900367 C314.500236,319.69324 318.574209,314.762509 324.081941,314.762509 L367.182129,314.727539 C405.981041,314.727539 419.636719,295.599099 419.636719,279.757812 L419.636719,34.9697266 C419.636719,17.4848633 406.558041,0 366.937341,0 L52.2622563,0 C14.0228604,0 0,16.9428325 0,34.9697266 L0,279.757812 C0,296.700645 14.4774668,314.727539 52.4545898,314.727539 L157.36377,314.727539 C157.36377,314.727539 179.307273,315.339509 187.402765,324.536547 L187.402765,324.536547 Z" fill="${theme.launchButtonIconColor}"/>
|
|
@@ -24,8 +23,24 @@ export default function getIcons(theme: Theme): [string, string] {
|
|
|
24
23
|
<ellipse id="Oval-1" fill="${theme.launchButtonColor}" cx="306.050106" cy="162.184556" rx="28.3476608" ry="28.1845557"/>
|
|
25
24
|
</g>
|
|
26
25
|
</g>
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
</svg>
|
|
27
|
+
`;
|
|
28
|
+
const newOpenIcon = `
|
|
29
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
30
|
+
<path d="M13.2 10.8H6C5.68174 10.8 5.37652 10.9264 5.15147 11.1515C4.92643 11.3765 4.8 11.6817 4.8 12C4.8 12.3183 4.92643 12.6235 5.15147 12.8485C5.37652 13.0736 5.68174 13.2 6 13.2H13.2C13.5183 13.2 13.8235 13.0736 14.0485 12.8485C14.2736 12.6235 14.4 12.3183 14.4 12C14.4 11.6817 14.2736 11.3765 14.0485 11.1515C13.8235 10.9264 13.5183 10.8 13.2 10.8ZM18 6H6C5.68174 6 5.37652 6.12643 5.15147 6.35147C4.92643 6.57652 4.8 6.88174 4.8 7.2C4.8 7.51826 4.92643 7.82348 5.15147 8.04853C5.37652 8.27357 5.68174 8.4 6 8.4H18C18.3183 8.4 18.6235 8.27357 18.8485 8.04853C19.0736 7.82348 19.2 7.51826 19.2 7.2C19.2 6.88174 19.0736 6.57652 18.8485 6.35147C18.6235 6.12643 18.3183 6 18 6ZM20.4 0H3.6C2.64522 0 1.72955 0.379285 1.05442 1.05442C0.379285 1.72955 0 2.64522 0 3.6V15.6C0 16.5548 0.379285 17.4705 1.05442 18.1456C1.72955 18.8207 2.64522 19.2 3.6 19.2H17.508L21.948 23.652C22.0601 23.7632 22.1931 23.8512 22.3393 23.9109C22.4855 23.9706 22.6421 24.0009 22.8 24C22.9574 24.0041 23.1136 23.9712 23.256 23.904C23.4751 23.814 23.6627 23.6611 23.7951 23.4646C23.9275 23.2682 23.9988 23.0369 24 22.8V3.6C24 2.64522 23.6207 1.72955 22.9456 1.05442C22.2705 0.379285 21.3548 0 20.4 0ZM21.6 19.908L18.852 17.148C18.7399 17.0368 18.6069 16.9488 18.4607 16.8891C18.3145 16.8294 18.1579 16.7991 18 16.8H3.6C3.28174 16.8 2.97652 16.6736 2.75147 16.4485C2.52643 16.2235 2.4 15.9183 2.4 15.6V3.6C2.4 3.28174 2.52643 2.97652 2.75147 2.75147C2.97652 2.52643 3.28174 2.4 3.6 2.4H20.4C20.7183 2.4 21.0235 2.52643 21.2485 2.75147C21.4736 2.97652 21.6 3.28174 21.6 3.6V19.908Z" fill="#202020"/>
|
|
31
|
+
</svg>`;
|
|
32
|
+
const close = toDataUrl(`
|
|
33
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="312px" height="312px" viewBox="0 0 312 312" version="1.1">
|
|
34
|
+
<g id="Page-1" stroke="none" stroke-width="1" fill="${
|
|
35
|
+
theme.launchButtonIconColor
|
|
36
|
+
}" fill-rule="evenodd">
|
|
37
|
+
<path d="M178.500682,155.862614 L306.579682,28.4831591 C312.720136,22.4048864 312.720136,12.5646136 306.579682,6.50188636 C300.454773,0.423613636 290.505682,0.423613636 284.380773,6.50188636 L156.410591,133.772523 L27.2589545,4.60534091 C21.1340455,-1.53511364 11.1849545,-1.53511364 5.06004545,4.60534091 C-1.06486364,10.7613409 -1.06486364,20.7259773 5.06004545,26.8664318 L134.118409,155.940341 L4.59368182,284.749977 C-1.53122727,290.82825 -1.53122727,300.668523 4.59368182,306.73125 C10.7185909,312.809523 20.6676818,312.809523 26.7925909,306.73125 L156.2085,178.030432 L284.847136,306.684614 C290.972045,312.825068 300.921136,312.825068 307.046045,306.684614 C313.170955,300.528614 313.170955,290.563977 307.046045,284.423523 L178.500682,155.862614 L178.500682,155.862614 Z" id="Close" fill="${
|
|
38
|
+
useNewIcons ? "black" : theme.launchButtonIconColor
|
|
39
|
+
}"/>
|
|
40
|
+
</g>
|
|
41
|
+
</svg>`);
|
|
42
|
+
|
|
43
|
+
const open = toDataUrl(useNewIcons ? newOpenIcon : oldOpenIcon);
|
|
29
44
|
|
|
30
45
|
return [open, close];
|
|
31
46
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as LDClient from "launchdarkly-js-client-sdk";
|
|
2
|
+
|
|
3
|
+
export default class LaunchDarkly {
|
|
4
|
+
private static client: LDClient.LDClient;
|
|
5
|
+
static isReady: Promise<boolean>;
|
|
6
|
+
|
|
7
|
+
static getClient(chatId?: string): LDClient.LDClient {
|
|
8
|
+
if (!LaunchDarkly.client) {
|
|
9
|
+
if (!chatId) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
"chatId argument is required on first call of LaunchDarkly.getClient"
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
const user = {
|
|
15
|
+
key: chatId,
|
|
16
|
+
custom: {
|
|
17
|
+
userType: "webchatUser",
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
LaunchDarkly.client = LDClient.initialize(
|
|
21
|
+
process.env.LAUNCHDARKLY_CLIENT_ID || "",
|
|
22
|
+
user
|
|
23
|
+
);
|
|
24
|
+
LaunchDarkly.isReady = new Promise((resolve) =>
|
|
25
|
+
LaunchDarkly.client.on("ready", resolve)
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
return LaunchDarkly.client;
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useEffect, useRef, useLayoutEffect } from "react";
|
|
2
|
+
|
|
3
|
+
// https://usehooks-ts.com/react-hook/use-interval
|
|
4
|
+
export function useInterval(callback: () => void, delay: number | null): void {
|
|
5
|
+
const savedCallback = useRef(callback);
|
|
6
|
+
|
|
7
|
+
// Remember the latest callback if it changes.
|
|
8
|
+
useLayoutEffect(() => {
|
|
9
|
+
savedCallback.current = callback;
|
|
10
|
+
}, [callback]);
|
|
11
|
+
|
|
12
|
+
// Set up the interval.
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
// Don't schedule if no delay is specified.
|
|
15
|
+
// Note: 0 is a valid value for delay.
|
|
16
|
+
if (!delay && delay !== 0) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const id = setInterval(() => savedCallback.current(), delay);
|
|
21
|
+
|
|
22
|
+
return () => clearInterval(id);
|
|
23
|
+
}, [delay]);
|
|
24
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
] /* Specify library files to be included in the compilation. */,
|
|
13
13
|
// "allowJs": true, /* Allow javascript files to be compiled. */
|
|
14
14
|
// "checkJs": true, /* Report errors in .js files. */
|
|
15
|
-
|
|
15
|
+
"jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */,
|
|
16
16
|
"declaration": true /* Generates corresponding '.d.ts' file. */,
|
|
17
17
|
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
|
18
18
|
"sourceMap": true /* Generates corresponding '.map' file. */,
|
|
19
19
|
// "outFile": "./", /* Concatenate and emit output to single file. */
|
|
20
|
-
"outDir": "./
|
|
20
|
+
"outDir": "./public/ts-loader-build" /* Redirect output structure to the directory. */,
|
|
21
21
|
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
|
22
22
|
// "composite": true, /* Enable project compilation */
|
|
23
23
|
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
|
@@ -70,7 +70,8 @@
|
|
|
70
70
|
|
|
71
71
|
/* Advanced Options */
|
|
72
72
|
"skipLibCheck": true /* Skip type checking of declaration files. */,
|
|
73
|
-
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file.
|
|
73
|
+
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
|
|
74
|
+
"plugins": [{ "name": "typescript-plugin-css-modules" }]
|
|
74
75
|
},
|
|
75
|
-
"include": ["./
|
|
76
|
+
"include": ["./src/MEChat.tsx", "declarations.d.ts"]
|
|
76
77
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
|
4
|
+
const Dotenv = require("dotenv-webpack");
|
|
5
|
+
const { EnvironmentPlugin } = require("webpack");
|
|
6
|
+
|
|
7
|
+
module.exports = (env) => {
|
|
8
|
+
return {
|
|
9
|
+
devServer: {
|
|
10
|
+
static: "./public/demo",
|
|
11
|
+
hot: true,
|
|
12
|
+
port: 8000,
|
|
13
|
+
},
|
|
14
|
+
entry: env.isDemoApp ? "./src/DemoApp.tsx" : "./src/MEChat.tsx",
|
|
15
|
+
module: {
|
|
16
|
+
rules: [
|
|
17
|
+
{
|
|
18
|
+
test: /\.tsx?$/,
|
|
19
|
+
use: "ts-loader",
|
|
20
|
+
exclude: /node_modules/,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
test: /\.(c|sa|sc)ss$/,
|
|
24
|
+
use: [
|
|
25
|
+
"style-loader",
|
|
26
|
+
// Emits TypeScript declaration files matching your CSS Modules
|
|
27
|
+
"css-modules-typescript-loader",
|
|
28
|
+
"css-loader",
|
|
29
|
+
"sass-loader",
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
resolve: {
|
|
35
|
+
extensions: [".tsx", ".ts", ".js", ".css", ".scss"],
|
|
36
|
+
},
|
|
37
|
+
experiments: {
|
|
38
|
+
outputModule: !env.isDemoApp,
|
|
39
|
+
},
|
|
40
|
+
output: {
|
|
41
|
+
filename: "bundle.js",
|
|
42
|
+
sourceMapFilename: "bundle.js.map",
|
|
43
|
+
...(env.isDemoApp
|
|
44
|
+
? {
|
|
45
|
+
path: path.resolve(__dirname, "./public/demo/built"),
|
|
46
|
+
}
|
|
47
|
+
: {
|
|
48
|
+
path: path.resolve(__dirname, "public/dist"),
|
|
49
|
+
library: {
|
|
50
|
+
type: "module",
|
|
51
|
+
},
|
|
52
|
+
}),
|
|
53
|
+
},
|
|
54
|
+
plugins: [
|
|
55
|
+
...(env.isDemoApp
|
|
56
|
+
? [new HtmlWebpackPlugin({ template: "./public/demo/index.html" })]
|
|
57
|
+
: []),
|
|
58
|
+
new Dotenv(),
|
|
59
|
+
new EnvironmentPlugin({
|
|
60
|
+
IS_DEMO_APP: !!env.isDemoApp,
|
|
61
|
+
LAUNCHDARKLY_CLIENT_ID: "6119b3a2ceeceb24f45c7e66",
|
|
62
|
+
}),
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
};
|
package/dist/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "./src/MEChat";
|
package/dist/index.js
DELETED
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
|