@meetelise/chat 1.20.85 → 1.20.87
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/package.json +1 -1
- package/public/demo/index.html +8 -4
- package/public/dist/index.js +67 -45
- package/src/MyPubnub.ts +96 -13
- package/src/WebComponent/me-chat.ts +29 -31
- package/src/WebComponent/pubnub-chat-styles.ts +23 -10
- package/src/WebComponent/pubnub-chat.ts +18 -8
package/src/MyPubnub.ts
CHANGED
|
@@ -3,6 +3,12 @@ import Pubnub, { ListenerParameters, MessageEvent } from "pubnub";
|
|
|
3
3
|
|
|
4
4
|
import axios from "axios";
|
|
5
5
|
import { Building } from "./fetchBuildingInfo";
|
|
6
|
+
import parseISO from "date-fns/parseISO";
|
|
7
|
+
import { v4 as uuid } from "uuid";
|
|
8
|
+
import addHours from "date-fns/addHours";
|
|
9
|
+
import isAfter from "date-fns/isAfter";
|
|
10
|
+
import formatISO from "date-fns/formatISO";
|
|
11
|
+
import isBefore from "date-fns/isBefore";
|
|
6
12
|
|
|
7
13
|
interface TokenResponse {
|
|
8
14
|
auth: {
|
|
@@ -27,12 +33,18 @@ export interface ChatMessage {
|
|
|
27
33
|
timetoken: number;
|
|
28
34
|
}
|
|
29
35
|
|
|
36
|
+
interface ChatInfo {
|
|
37
|
+
leadId: string | null;
|
|
38
|
+
timestamp: Date | null;
|
|
39
|
+
buildingSlug: string | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
30
42
|
class MyPubnub {
|
|
31
43
|
private apiHost = "https://app.meetelise.com";
|
|
32
44
|
|
|
33
45
|
private building: Building | null = null;
|
|
34
46
|
private buildingSlug: string;
|
|
35
|
-
private
|
|
47
|
+
private ttlHours = 24;
|
|
36
48
|
|
|
37
49
|
pubnub: Pubnub | null = null;
|
|
38
50
|
leadUserId = "";
|
|
@@ -65,12 +77,7 @@ class MyPubnub {
|
|
|
65
77
|
};
|
|
66
78
|
isLoadingMessages = false;
|
|
67
79
|
|
|
68
|
-
constructor(
|
|
69
|
-
orgSlug: string,
|
|
70
|
-
buildingSlug: string,
|
|
71
|
-
buildingDetails: Building
|
|
72
|
-
) {
|
|
73
|
-
this.orgSlug = orgSlug;
|
|
80
|
+
constructor(buildingSlug: string, buildingDetails: Building) {
|
|
74
81
|
this.buildingSlug = buildingSlug;
|
|
75
82
|
this.building = buildingDetails;
|
|
76
83
|
}
|
|
@@ -85,14 +92,28 @@ class MyPubnub {
|
|
|
85
92
|
}
|
|
86
93
|
|
|
87
94
|
async initializePubnub(): Promise<Pubnub | undefined> {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
95
|
+
const storedChatKeyValues = this.getChatStorageKey();
|
|
96
|
+
if (!storedChatKeyValues.leadId) {
|
|
97
|
+
// eslint-disable-next-line no-console
|
|
98
|
+
console.error("Error getting chat storage key...");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
92
101
|
|
|
93
|
-
this.leadUserId =
|
|
102
|
+
this.leadUserId = storedChatKeyValues.leadId;
|
|
94
103
|
this.channel = `webchat_${this.leadUserId}`;
|
|
95
104
|
|
|
105
|
+
// If the user comes back to the page after closing it out, we get a new token to persist
|
|
106
|
+
// the chat for another this.ttlHours hours.
|
|
107
|
+
if (
|
|
108
|
+
storedChatKeyValues.timestamp &&
|
|
109
|
+
isAfter(
|
|
110
|
+
storedChatKeyValues.timestamp,
|
|
111
|
+
addHours(new Date(), this.ttlHours)
|
|
112
|
+
)
|
|
113
|
+
) {
|
|
114
|
+
this.createChatStorageKey(this.leadUserId);
|
|
115
|
+
}
|
|
116
|
+
|
|
96
117
|
const pubnubToken = await this.fetchToken(this.leadUserId, this.channel);
|
|
97
118
|
|
|
98
119
|
// These keys are OK to expose live, the authKey generated by the BE is what
|
|
@@ -221,7 +242,6 @@ class MyPubnub {
|
|
|
221
242
|
buildingId: this.building?.id,
|
|
222
243
|
buildingSlug: this.buildingSlug,
|
|
223
244
|
userId: this.building?.userId, // this userid is actually the AI user!
|
|
224
|
-
// leadSource: DEFAULT_LEAD_SOURCE,
|
|
225
245
|
},
|
|
226
246
|
});
|
|
227
247
|
});
|
|
@@ -231,6 +251,69 @@ class MyPubnub {
|
|
|
231
251
|
isLeadMessage = (message: ChatMessage): boolean =>
|
|
232
252
|
message.publisher.includes("lead_") &&
|
|
233
253
|
message.message.customType === "lead_message";
|
|
254
|
+
|
|
255
|
+
createChatStorageKey = (existingUserId?: string): ChatInfo => {
|
|
256
|
+
const storageTimestamp = formatISO(new Date());
|
|
257
|
+
const leadUserId = existingUserId ?? `lead_${uuid()}_${this.buildingSlug}`;
|
|
258
|
+
localStorage.setItem(
|
|
259
|
+
"com.eliseai.webchat.slug=" + this.buildingSlug,
|
|
260
|
+
JSON.stringify({
|
|
261
|
+
buildingSlug: this.buildingSlug,
|
|
262
|
+
leadId: leadUserId,
|
|
263
|
+
timestamp: storageTimestamp,
|
|
264
|
+
})
|
|
265
|
+
);
|
|
266
|
+
return {
|
|
267
|
+
leadId: leadUserId,
|
|
268
|
+
timestamp: parseISO(storageTimestamp),
|
|
269
|
+
buildingSlug: this.buildingSlug,
|
|
270
|
+
};
|
|
271
|
+
};
|
|
272
|
+
getChatStorageKey = (): ChatInfo => {
|
|
273
|
+
const eliseaiLocalStorageValue = localStorage.getItem(
|
|
274
|
+
"com.eliseai.webchat.slug=" + this.buildingSlug
|
|
275
|
+
);
|
|
276
|
+
if (eliseaiLocalStorageValue) {
|
|
277
|
+
try {
|
|
278
|
+
const eliseaiLocalStorageValueParsed = JSON.parse(
|
|
279
|
+
eliseaiLocalStorageValue
|
|
280
|
+
);
|
|
281
|
+
const lsBuildingSlug = eliseaiLocalStorageValueParsed.buildingSlug;
|
|
282
|
+
const lsLeadId = eliseaiLocalStorageValueParsed.leadId;
|
|
283
|
+
const lsExpiration = new Date(eliseaiLocalStorageValueParsed.timestamp);
|
|
284
|
+
|
|
285
|
+
if (
|
|
286
|
+
this.isChatKeyValid({
|
|
287
|
+
leadId: lsLeadId,
|
|
288
|
+
timestamp: lsExpiration,
|
|
289
|
+
buildingSlug: lsBuildingSlug,
|
|
290
|
+
})
|
|
291
|
+
)
|
|
292
|
+
return {
|
|
293
|
+
leadId: lsLeadId,
|
|
294
|
+
timestamp: lsExpiration,
|
|
295
|
+
buildingSlug: lsBuildingSlug,
|
|
296
|
+
};
|
|
297
|
+
} catch (_) {
|
|
298
|
+
// eslint-disable-next-line no-console
|
|
299
|
+
console.warn("Error getting chat storage key");
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return this.createChatStorageKey();
|
|
304
|
+
};
|
|
305
|
+
isChatKeyValid = (storageValueDeconstructed: ChatInfo): boolean => {
|
|
306
|
+
if (
|
|
307
|
+
storageValueDeconstructed.buildingSlug !== this.buildingSlug ||
|
|
308
|
+
!storageValueDeconstructed.leadId ||
|
|
309
|
+
!storageValueDeconstructed.timestamp
|
|
310
|
+
) {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const expirationDate = addHours(new Date(), this.ttlHours);
|
|
315
|
+
return isBefore(storageValueDeconstructed.timestamp, expirationDate);
|
|
316
|
+
};
|
|
234
317
|
}
|
|
235
318
|
|
|
236
319
|
export default MyPubnub;
|
|
@@ -400,30 +400,35 @@ export class MEChat extends LitElement {
|
|
|
400
400
|
(talkjsPopupElement as HTMLElement).style.zIndex = "99999999999";
|
|
401
401
|
this.popup = popup;
|
|
402
402
|
|
|
403
|
-
if (this.
|
|
403
|
+
if (this.shouldAutoOpenChat(building.autoOpenChatWidget)) {
|
|
404
404
|
this.popup.show();
|
|
405
405
|
this.hideLauncher = true;
|
|
406
406
|
this.hasMounted = true;
|
|
407
|
-
|
|
408
|
-
"autoOpenedTimestamp",
|
|
409
|
-
formatISO(addMinutes(new Date(), 15))
|
|
410
|
-
);
|
|
407
|
+
this.updateAlreadyAutoOpenedTimestamp();
|
|
411
408
|
this.analytics?.ping("autoOpen");
|
|
412
409
|
}
|
|
413
410
|
};
|
|
414
411
|
|
|
415
|
-
private
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
412
|
+
private shouldAutoOpenChat = (buildingHasAutoOpen: boolean): boolean => {
|
|
413
|
+
const alreadyAutoOpenedTimestamp = sessionStorage.getItem(
|
|
414
|
+
"alreadyAutoOpenedTimestamp"
|
|
415
|
+
); // we dont want to autoopen on EVERY single page load, so we'll only do it once every 15 minutes max
|
|
419
416
|
const shouldAutoOpen =
|
|
420
|
-
!
|
|
421
|
-
(
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
return
|
|
417
|
+
!alreadyAutoOpenedTimestamp ||
|
|
418
|
+
!(
|
|
419
|
+
alreadyAutoOpenedTimestamp &&
|
|
420
|
+
!isPast(parseISO(alreadyAutoOpenedTimestamp))
|
|
421
|
+
);
|
|
422
|
+
return !!buildingHasAutoOpen && !!shouldAutoOpen && !isMobile();
|
|
426
423
|
};
|
|
424
|
+
|
|
425
|
+
private updateAlreadyAutoOpenedTimestamp = (): void => {
|
|
426
|
+
sessionStorage.setItem(
|
|
427
|
+
"alreadyAutoOpenedTimestamp",
|
|
428
|
+
formatISO(addMinutes(new Date(), 15))
|
|
429
|
+
);
|
|
430
|
+
};
|
|
431
|
+
|
|
427
432
|
/**
|
|
428
433
|
* Remove the instance from the screen.
|
|
429
434
|
*
|
|
@@ -479,22 +484,16 @@ export class MEChat extends LitElement {
|
|
|
479
484
|
await this.setBuildingDerivedInfo();
|
|
480
485
|
if (!this.building) return;
|
|
481
486
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
this.
|
|
485
|
-
|
|
486
|
-
);
|
|
487
|
-
await pubnubRaw.initializePubnub();
|
|
488
|
-
this.myPubnub = pubnubRaw;
|
|
487
|
+
this.myPubnub = new MyPubnub(this.buildingSlug, this.building);
|
|
488
|
+
if (this.myPubnub.isChatKeyValid(this.myPubnub.getChatStorageKey())) {
|
|
489
|
+
await this.myPubnub.initializePubnub();
|
|
490
|
+
}
|
|
489
491
|
this.attachOnClickToLauncher();
|
|
490
|
-
if (this.
|
|
492
|
+
if (this.shouldAutoOpenChat(this.building.autoOpenChatWidget)) {
|
|
491
493
|
this.displayPubnubChat = true;
|
|
492
494
|
this.hideLauncher = true;
|
|
493
495
|
this.hasMounted = true;
|
|
494
|
-
|
|
495
|
-
"autoOpenedTimestamp",
|
|
496
|
-
formatISO(addMinutes(new Date(), 15))
|
|
497
|
-
);
|
|
496
|
+
this.updateAlreadyAutoOpenedTimestamp();
|
|
498
497
|
}
|
|
499
498
|
this.isLoading = false;
|
|
500
499
|
};
|
|
@@ -589,13 +588,11 @@ export class MEChat extends LitElement {
|
|
|
589
588
|
</div>
|
|
590
589
|
|
|
591
590
|
${
|
|
592
|
-
this.chatProvider === ChatProviders.PUBNUB &&
|
|
593
|
-
this.displayPubnubChat &&
|
|
594
|
-
this.myPubnub
|
|
591
|
+
this.chatProvider === ChatProviders.PUBNUB && this.displayPubnubChat
|
|
595
592
|
? html`
|
|
596
593
|
<pubnub-chat
|
|
597
594
|
id="pubnub-chat"
|
|
598
|
-
.channel=${this.myPubnub
|
|
595
|
+
.channel=${this.myPubnub?.channel}
|
|
599
596
|
.myPubnub=${this.myPubnub}
|
|
600
597
|
.buildingSlug=${this.buildingSlug}
|
|
601
598
|
.building=${this.building}
|
|
@@ -607,6 +604,7 @@ export class MEChat extends LitElement {
|
|
|
607
604
|
.onMount=${() => {
|
|
608
605
|
this.adjustTopHeaderContactCoords();
|
|
609
606
|
}}
|
|
607
|
+
.isMobile=${this.isMobile}
|
|
610
608
|
></pubnub-chat>
|
|
611
609
|
${this.renderChatAdditionalActions(
|
|
612
610
|
"chatAdditionalActionsPubnub",
|
|
@@ -3,22 +3,33 @@ import { css } from "lit";
|
|
|
3
3
|
export const pubnubChatStyles = css`
|
|
4
4
|
#pubnub-chat-container {
|
|
5
5
|
position: fixed;
|
|
6
|
-
|
|
7
|
-
bottom: 110px;
|
|
6
|
+
|
|
8
7
|
z-index: 100000;
|
|
9
8
|
display: flex;
|
|
10
9
|
align-items: center;
|
|
11
10
|
|
|
12
|
-
width: 340px;
|
|
13
|
-
height: 600px;
|
|
14
|
-
|
|
15
11
|
display: flex;
|
|
16
12
|
flex-direction: column;
|
|
17
|
-
border-radius: 24px 24px 0px 24px;
|
|
18
13
|
overflow: hidden;
|
|
19
14
|
|
|
20
15
|
box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px,
|
|
21
16
|
rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;
|
|
17
|
+
|
|
18
|
+
font-family: Poppins, "Open Sans", "Helvetica", sans-serif;
|
|
19
|
+
}
|
|
20
|
+
.pubnub-container__mobile {
|
|
21
|
+
right: 0px;
|
|
22
|
+
bottom: 0px;
|
|
23
|
+
width: 100%;
|
|
24
|
+
height: 100%;
|
|
25
|
+
border-radius: 0px;
|
|
26
|
+
}
|
|
27
|
+
.pubnub-container__desktop {
|
|
28
|
+
right: 0px;
|
|
29
|
+
bottom: 110px;
|
|
30
|
+
width: 340px;
|
|
31
|
+
height: 600px;
|
|
32
|
+
border-radius: 24px 24px 0px 24px;
|
|
22
33
|
}
|
|
23
34
|
#header {
|
|
24
35
|
height: 70px;
|
|
@@ -37,7 +48,6 @@ export const pubnubChatStyles = css`
|
|
|
37
48
|
#header-text {
|
|
38
49
|
font-size: 14px;
|
|
39
50
|
font-weight: bold;
|
|
40
|
-
font-family: Poppins, "Open Sans", "Helvetica", sans-serif;
|
|
41
51
|
}
|
|
42
52
|
#exit-chat-bttn {
|
|
43
53
|
display: flex;
|
|
@@ -66,6 +76,11 @@ export const pubnubChatStyles = css`
|
|
|
66
76
|
position: relative;
|
|
67
77
|
padding: 0px 16px;
|
|
68
78
|
gap: 16px;
|
|
79
|
+
list-style: none;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.disclaimer-message {
|
|
83
|
+
font-size: 12px;
|
|
69
84
|
}
|
|
70
85
|
|
|
71
86
|
.message-container {
|
|
@@ -93,8 +108,7 @@ export const pubnubChatStyles = css`
|
|
|
93
108
|
word-break: break-word;
|
|
94
109
|
margin: 0;
|
|
95
110
|
padding: 6px 12px;
|
|
96
|
-
line-height:
|
|
97
|
-
font-family: Poppins, "Open Sans", "Helvetica", sans-serif;
|
|
111
|
+
line-height: 130%;
|
|
98
112
|
}
|
|
99
113
|
|
|
100
114
|
.loading-dot {
|
|
@@ -157,7 +171,6 @@ export const pubnubChatStyles = css`
|
|
|
157
171
|
border: none;
|
|
158
172
|
color: white;
|
|
159
173
|
background: none;
|
|
160
|
-
font-family: Poppins, "Open Sans", "Helvetica", sans-serif;
|
|
161
174
|
}
|
|
162
175
|
#message-input:focus {
|
|
163
176
|
outline: none;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { html, LitElement, TemplateResult } from "lit";
|
|
2
2
|
import { customElement, property, query, state } from "lit/decorators.js";
|
|
3
|
+
import { classMap } from "lit/directives/class-map.js";
|
|
3
4
|
import { styleMap } from "lit/directives/style-map.js";
|
|
4
5
|
import { Building } from "../fetchBuildingInfo";
|
|
5
6
|
import MyPubnub, { ChatMessage } from "../MyPubnub";
|
|
@@ -42,6 +43,9 @@ export class PubnubChat extends LitElement {
|
|
|
42
43
|
@state()
|
|
43
44
|
messages: ChatMessage[] = [];
|
|
44
45
|
|
|
46
|
+
@state()
|
|
47
|
+
isMobile = false;
|
|
48
|
+
|
|
45
49
|
@state()
|
|
46
50
|
isLoadingMessages = false;
|
|
47
51
|
|
|
@@ -71,23 +75,24 @@ export class PubnubChat extends LitElement {
|
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
render(): TemplateResult {
|
|
74
|
-
if (
|
|
75
|
-
!this.channel ||
|
|
76
|
-
!this.myPubnub ||
|
|
77
|
-
!this.buildingSlug ||
|
|
78
|
-
!this.building
|
|
79
|
-
) {
|
|
78
|
+
if (!this.buildingSlug || !this.building) {
|
|
80
79
|
return html``; // error here
|
|
81
80
|
}
|
|
82
81
|
return html`
|
|
83
|
-
<div
|
|
82
|
+
<div
|
|
83
|
+
id="pubnub-chat-container"
|
|
84
|
+
class=${classMap({
|
|
85
|
+
["pubnub-container__mobile"]: this.isMobile,
|
|
86
|
+
["pubnub-container__desktop"]: !this.isMobile,
|
|
87
|
+
})}
|
|
88
|
+
>
|
|
84
89
|
<div
|
|
85
90
|
id="header"
|
|
86
91
|
style=${styleMap({
|
|
87
92
|
background:
|
|
88
93
|
this.brandColor !== defaultBrandColor
|
|
89
94
|
? hexToAlmostWhite(this.brandColor, 0.6)
|
|
90
|
-
:
|
|
95
|
+
: undefined,
|
|
91
96
|
})}
|
|
92
97
|
>
|
|
93
98
|
<div id="header-text">${this.building.name}</div>
|
|
@@ -97,6 +102,11 @@ export class PubnubChat extends LitElement {
|
|
|
97
102
|
</div>
|
|
98
103
|
<div id="conversation-body">
|
|
99
104
|
<ul id="message-thread-list">
|
|
105
|
+
<li class="message-container disclaimer-message">
|
|
106
|
+
By interacting with this system, you acknowledge that EliseAI may
|
|
107
|
+
log and retain any actions that you take and information that you
|
|
108
|
+
provide. Replies may be computer or human-generated.
|
|
109
|
+
</li>
|
|
100
110
|
${this.building.welcomeMessage
|
|
101
111
|
? html` <li class="message-container ai-message">
|
|
102
112
|
<p class="message-text">${this.building.welcomeMessage}</p>
|