@meetelise/chat 1.20.83 → 1.20.85
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 +3 -1
- package/public/demo/index.html +8 -2
- package/public/dist/index.js +514 -229
- package/public/dist/index.js.LICENSE.txt +17 -0
- package/src/MyPubnub.ts +236 -0
- package/src/WebComponent/Scheduler/tour-scheduler.ts +29 -2
- package/src/WebComponent/actions/email-us-window.ts +9 -0
- package/src/WebComponent/launcher/Launcher.ts +4 -0
- package/src/WebComponent/me-chat.ts +194 -67
- package/src/WebComponent/pubnub-chat-styles.ts +172 -0
- package/src/WebComponent/pubnub-chat.ts +155 -0
- package/src/disclaimers.ts +56 -0
- package/src/fetchFeatureFlag.ts +21 -0
- package/src/svgIcons.ts +9 -0
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
FeatureFlagsShowDropdown,
|
|
14
14
|
fetchFeatureFlagShowMarketingSourceDropdown,
|
|
15
15
|
fetchFeatureFlagUsePhoneNumberBySource,
|
|
16
|
+
fetchFeatureFlagUsePubnub,
|
|
16
17
|
} from "../fetchFeatureFlag";
|
|
17
18
|
import fetchWebchatPreferences, {
|
|
18
19
|
DesignConcepts,
|
|
@@ -37,10 +38,16 @@ import addMinutes from "date-fns/addMinutes";
|
|
|
37
38
|
import formatISO from "date-fns/formatISO";
|
|
38
39
|
import fetchLeadSources from "../fetchLeadSources";
|
|
39
40
|
import { formatPhoneNumber } from "./actions/formatPhoneNumber";
|
|
41
|
+
import MyPubnub from "../MyPubnub";
|
|
40
42
|
|
|
41
43
|
import "./actions/minimize-expand-button";
|
|
42
44
|
import "./launcher/mobile-launcher";
|
|
45
|
+
import "./pubnub-chat";
|
|
43
46
|
|
|
47
|
+
enum ChatProviders {
|
|
48
|
+
TALKJS = "talkjs",
|
|
49
|
+
PUBNUB = "pubnub",
|
|
50
|
+
}
|
|
44
51
|
@customElement("me-chat")
|
|
45
52
|
export class MEChat extends LitElement {
|
|
46
53
|
static styles = css`
|
|
@@ -66,7 +73,8 @@ export class MEChat extends LitElement {
|
|
|
66
73
|
justify-content: space-between;
|
|
67
74
|
gap: 32px;
|
|
68
75
|
}
|
|
69
|
-
#
|
|
76
|
+
#chatAdditionalActionsTalkjs,
|
|
77
|
+
#chatAdditionalActionsPubnub {
|
|
70
78
|
position: fixed;
|
|
71
79
|
box-sizing: border-box;
|
|
72
80
|
|
|
@@ -78,7 +86,8 @@ export class MEChat extends LitElement {
|
|
|
78
86
|
|
|
79
87
|
z-index: 1000000000;
|
|
80
88
|
}
|
|
81
|
-
#
|
|
89
|
+
#chatAdditionalActionsPubnub::after,
|
|
90
|
+
#chatAdditionalActionsTalkjs::after {
|
|
82
91
|
content: "";
|
|
83
92
|
position: absolute;
|
|
84
93
|
top: -6px;
|
|
@@ -88,6 +97,10 @@ export class MEChat extends LitElement {
|
|
|
88
97
|
border-bottom: 22px solid transparent;
|
|
89
98
|
border-right: 30px solid black;
|
|
90
99
|
}
|
|
100
|
+
#pubnub-bottom {
|
|
101
|
+
position: fixed;
|
|
102
|
+
bottom: 0;
|
|
103
|
+
}
|
|
91
104
|
`;
|
|
92
105
|
@property({ type: String })
|
|
93
106
|
private buildingSlug = "";
|
|
@@ -147,6 +160,15 @@ export class MEChat extends LitElement {
|
|
|
147
160
|
@state()
|
|
148
161
|
private talkjsPopupElement: Element | null = null;
|
|
149
162
|
|
|
163
|
+
@state()
|
|
164
|
+
private chatProvider: ChatProviders = ChatProviders.TALKJS;
|
|
165
|
+
|
|
166
|
+
@state()
|
|
167
|
+
private displayPubnubChat = false;
|
|
168
|
+
|
|
169
|
+
@state()
|
|
170
|
+
private myPubnub: MyPubnub | null = null;
|
|
171
|
+
|
|
150
172
|
private yardiDNIScriptInterval: NodeJS.Timer | null = null;
|
|
151
173
|
launcherRef: Ref<Launcher> = createRef();
|
|
152
174
|
|
|
@@ -378,11 +400,7 @@ export class MEChat extends LitElement {
|
|
|
378
400
|
(talkjsPopupElement as HTMLElement).style.zIndex = "99999999999";
|
|
379
401
|
this.popup = popup;
|
|
380
402
|
|
|
381
|
-
|
|
382
|
-
const shouldAutoOpen =
|
|
383
|
-
!autoOpenedTimestamp ||
|
|
384
|
-
(autoOpenedTimestamp && isPast(parseISO(autoOpenedTimestamp)));
|
|
385
|
-
if (building.autoOpenChatWidget && shouldAutoOpen && !isMobile()) {
|
|
403
|
+
if (this.shouldAutoOpenChatWidget(building.autoOpenChatWidget)) {
|
|
386
404
|
this.popup.show();
|
|
387
405
|
this.hideLauncher = true;
|
|
388
406
|
this.hasMounted = true;
|
|
@@ -394,6 +412,18 @@ export class MEChat extends LitElement {
|
|
|
394
412
|
}
|
|
395
413
|
};
|
|
396
414
|
|
|
415
|
+
private shouldAutoOpenChatWidget = (
|
|
416
|
+
buildingHasAutoOpen: boolean
|
|
417
|
+
): boolean => {
|
|
418
|
+
const autoOpenedTimestamp = sessionStorage.getItem("autoOpenedTimestamp");
|
|
419
|
+
const shouldAutoOpen =
|
|
420
|
+
!autoOpenedTimestamp ||
|
|
421
|
+
(autoOpenedTimestamp && isPast(parseISO(autoOpenedTimestamp)));
|
|
422
|
+
if (buildingHasAutoOpen && shouldAutoOpen && !isMobile()) {
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
return false;
|
|
426
|
+
};
|
|
397
427
|
/**
|
|
398
428
|
* Remove the instance from the screen.
|
|
399
429
|
*
|
|
@@ -432,26 +462,70 @@ export class MEChat extends LitElement {
|
|
|
432
462
|
this.launcher.style.display = "none";
|
|
433
463
|
}
|
|
434
464
|
|
|
435
|
-
firstUpdated = (): void => {
|
|
436
|
-
|
|
465
|
+
firstUpdated = async (): Promise<void> => {
|
|
466
|
+
const usePubnubProviderFlag = await fetchFeatureFlagUsePubnub(
|
|
467
|
+
this.buildingSlug
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
if (usePubnubProviderFlag) {
|
|
471
|
+
this.chatProvider = ChatProviders.PUBNUB;
|
|
472
|
+
this.initializePubnubVariables();
|
|
473
|
+
} else {
|
|
474
|
+
this.initializeInstanceVariables();
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
initializePubnubVariables = async (): Promise<void> => {
|
|
479
|
+
await this.setBuildingDerivedInfo();
|
|
480
|
+
if (!this.building) return;
|
|
481
|
+
|
|
482
|
+
const pubnubRaw = new MyPubnub(
|
|
483
|
+
this.orgSlug,
|
|
484
|
+
this.buildingSlug,
|
|
485
|
+
this.building
|
|
486
|
+
);
|
|
487
|
+
await pubnubRaw.initializePubnub();
|
|
488
|
+
this.myPubnub = pubnubRaw;
|
|
489
|
+
this.attachOnClickToLauncher();
|
|
490
|
+
if (this.shouldAutoOpenChatWidget(this.building.autoOpenChatWidget)) {
|
|
491
|
+
this.displayPubnubChat = true;
|
|
492
|
+
this.hideLauncher = true;
|
|
493
|
+
this.hasMounted = true;
|
|
494
|
+
sessionStorage.setItem(
|
|
495
|
+
"autoOpenedTimestamp",
|
|
496
|
+
formatISO(addMinutes(new Date(), 15))
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
this.isLoading = false;
|
|
437
500
|
};
|
|
438
501
|
|
|
439
502
|
handleContactClicked = (e: MouseEvent): void => {
|
|
440
|
-
this.
|
|
503
|
+
if (this.chatProvider === ChatProviders.TALKJS) {
|
|
504
|
+
this.popup?.hide();
|
|
505
|
+
} else if (this.chatProvider === ChatProviders.PUBNUB) {
|
|
506
|
+
this.displayPubnubChat = false;
|
|
507
|
+
}
|
|
441
508
|
this.hideLauncher = false;
|
|
442
509
|
|
|
443
510
|
this.launcherRef.value?.onClickEmailOption(e);
|
|
444
511
|
};
|
|
445
512
|
|
|
446
513
|
handleTourClicked = (e: MouseEvent): void => {
|
|
447
|
-
this.
|
|
514
|
+
if (this.chatProvider === ChatProviders.TALKJS) {
|
|
515
|
+
this.popup?.hide();
|
|
516
|
+
} else if (this.chatProvider === ChatProviders.PUBNUB) {
|
|
517
|
+
this.displayPubnubChat = false;
|
|
518
|
+
}
|
|
448
519
|
this.hideLauncher = false;
|
|
449
|
-
|
|
450
520
|
this.launcherRef.value?.onClickSSTOption(e);
|
|
451
521
|
};
|
|
452
522
|
|
|
453
523
|
handleContactTabClicked = (e: MouseEvent): void => {
|
|
454
|
-
this.
|
|
524
|
+
if (this.chatProvider === ChatProviders.TALKJS) {
|
|
525
|
+
this.popup?.hide();
|
|
526
|
+
} else if (this.chatProvider === ChatProviders.PUBNUB) {
|
|
527
|
+
this.displayPubnubChat = false;
|
|
528
|
+
}
|
|
455
529
|
this.hideLauncher = false;
|
|
456
530
|
|
|
457
531
|
this.launcherRef.value?.onClickPhoneOption(e);
|
|
@@ -461,14 +535,25 @@ export class MEChat extends LitElement {
|
|
|
461
535
|
// For the top header contact, we change its pos based on the current x and y
|
|
462
536
|
// of the talkjs popup - so we must adjust its coords on resize and mount
|
|
463
537
|
adjustTopHeaderContactCoords = (): void => {
|
|
464
|
-
|
|
538
|
+
const headerRef = this.shadowRoot?.getElementById(
|
|
539
|
+
this.chatProvider === ChatProviders.TALKJS
|
|
540
|
+
? "chatAdditionalActionsTalkjs"
|
|
541
|
+
: "chatAdditionalActionsPubnub"
|
|
542
|
+
);
|
|
543
|
+
if (!headerRef) return;
|
|
544
|
+
if (this.chatProvider === ChatProviders.TALKJS && this.talkjsPopupElement) {
|
|
465
545
|
const talkjsPopupCoords = this.talkjsPopupElement.getBoundingClientRect();
|
|
466
|
-
const headerRef = this.shadowRoot?.getElementById(
|
|
467
|
-
"chatAdditionalActions"
|
|
468
|
-
);
|
|
469
|
-
if (!headerRef) return;
|
|
470
546
|
headerRef.style.left = `${talkjsPopupCoords.left + 20}px`;
|
|
471
547
|
headerRef.style.top = `${talkjsPopupCoords.bottom - 24}px`;
|
|
548
|
+
} else if (this.chatProvider === ChatProviders.PUBNUB) {
|
|
549
|
+
const pubnubContainerElement = this.shadowRoot
|
|
550
|
+
?.getElementById("pubnub-chat")
|
|
551
|
+
?.shadowRoot?.getElementById("pubnub-chat-container");
|
|
552
|
+
|
|
553
|
+
const pubnubPopupCoords = pubnubContainerElement?.getBoundingClientRect();
|
|
554
|
+
if (!pubnubPopupCoords) return;
|
|
555
|
+
headerRef.style.left = `${pubnubPopupCoords.left}px`;
|
|
556
|
+
headerRef.style.top = `${pubnubPopupCoords.bottom}px`;
|
|
472
557
|
}
|
|
473
558
|
};
|
|
474
559
|
connectedCallback(): void {
|
|
@@ -476,6 +561,7 @@ export class MEChat extends LitElement {
|
|
|
476
561
|
window.addEventListener("resize", this.adjustTopHeaderContactCoords);
|
|
477
562
|
}
|
|
478
563
|
disconnectedCallback(): void {
|
|
564
|
+
this.myPubnub?.removeChatListeners();
|
|
479
565
|
window.removeEventListener("resize", this.adjustTopHeaderContactCoords);
|
|
480
566
|
super.disconnectedCallback();
|
|
481
567
|
}
|
|
@@ -501,6 +587,34 @@ export class MEChat extends LitElement {
|
|
|
501
587
|
EliseAI widget that allows you to chat with a virtual assistant, book
|
|
502
588
|
a tour, contact the leasing office, and more.
|
|
503
589
|
</div>
|
|
590
|
+
|
|
591
|
+
${
|
|
592
|
+
this.chatProvider === ChatProviders.PUBNUB &&
|
|
593
|
+
this.displayPubnubChat &&
|
|
594
|
+
this.myPubnub
|
|
595
|
+
? html`
|
|
596
|
+
<pubnub-chat
|
|
597
|
+
id="pubnub-chat"
|
|
598
|
+
.channel=${this.myPubnub.channel}
|
|
599
|
+
.myPubnub=${this.myPubnub}
|
|
600
|
+
.buildingSlug=${this.buildingSlug}
|
|
601
|
+
.building=${this.building}
|
|
602
|
+
.brandColor=${this.brandColor}
|
|
603
|
+
.onClickExit=${() => {
|
|
604
|
+
this.displayPubnubChat = false;
|
|
605
|
+
this.hideLauncher = false;
|
|
606
|
+
}}
|
|
607
|
+
.onMount=${() => {
|
|
608
|
+
this.adjustTopHeaderContactCoords();
|
|
609
|
+
}}
|
|
610
|
+
></pubnub-chat>
|
|
611
|
+
${this.renderChatAdditionalActions(
|
|
612
|
+
"chatAdditionalActionsPubnub",
|
|
613
|
+
showChatAdditionalActions
|
|
614
|
+
)}
|
|
615
|
+
`
|
|
616
|
+
: ""
|
|
617
|
+
}
|
|
504
618
|
<div
|
|
505
619
|
class=${classMap({
|
|
506
620
|
launcherContainer: true,
|
|
@@ -534,6 +648,7 @@ export class MEChat extends LitElement {
|
|
|
534
648
|
phoneNumber="${this.phoneNumberForSource?.number ??
|
|
535
649
|
this.building?.phoneNumber ??
|
|
536
650
|
""}"
|
|
651
|
+
buildingName=${this.building?.name ?? ""}
|
|
537
652
|
textColor="${this.theme.chatHeader.textColor}"
|
|
538
653
|
backgroundColor="${this.theme.chatPaneBackgroundColor}"
|
|
539
654
|
orgSlug="${this.orgSlug}"
|
|
@@ -567,54 +682,65 @@ export class MEChat extends LitElement {
|
|
|
567
682
|
}
|
|
568
683
|
|
|
569
684
|
</div>
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
.onClickEmailOption=${this.handleContactClicked}
|
|
580
|
-
.onClickPhoneOption=${this.handleContactTabClicked}
|
|
581
|
-
.onClickSSTOption=${this.handleTourClicked}
|
|
582
|
-
.isMobile=${this.isMobile}
|
|
583
|
-
.brandColor=${this.brandColor}
|
|
584
|
-
.hideChat=${true}
|
|
585
|
-
.hasChatEnabled=${
|
|
586
|
-
!this.building?.chatWidgets
|
|
587
|
-
? true
|
|
588
|
-
: this.building?.chatWidgets.includes("CHAT")
|
|
589
|
-
}
|
|
590
|
-
.hasEmailEnabled=${
|
|
591
|
-
!this.building?.chatWidgets
|
|
592
|
-
? true
|
|
593
|
-
: this.building?.chatWidgets.includes("EMAIL")
|
|
594
|
-
}
|
|
595
|
-
.hasCallUsEnabled=${
|
|
596
|
-
!this.building?.chatWidgets
|
|
597
|
-
? true
|
|
598
|
-
: this.building?.chatWidgets.includes("CALL")
|
|
599
|
-
}
|
|
600
|
-
.hasSSTEnabled=${
|
|
601
|
-
!this.building?.chatWidgets
|
|
602
|
-
? true
|
|
603
|
-
: this.building?.chatWidgets.includes("SST")
|
|
604
|
-
}
|
|
605
|
-
></mobile-launcher>
|
|
606
|
-
<minimize-expand-button
|
|
607
|
-
.brandColor=${this.brandColor}
|
|
608
|
-
.onClick=${() => {
|
|
609
|
-
this.popup?.hide();
|
|
610
|
-
this.hideLauncher = false;
|
|
611
|
-
}}
|
|
612
|
-
></minimize-expand-button>
|
|
613
|
-
</div>
|
|
685
|
+
${
|
|
686
|
+
this.chatProvider === ChatProviders.TALKJS
|
|
687
|
+
? this.renderChatAdditionalActions(
|
|
688
|
+
"chatAdditionalActionsTalkjs",
|
|
689
|
+
showChatAdditionalActions
|
|
690
|
+
)
|
|
691
|
+
: ""
|
|
692
|
+
}
|
|
693
|
+
|
|
614
694
|
</meta>
|
|
615
695
|
`;
|
|
616
696
|
}
|
|
617
697
|
|
|
698
|
+
private renderChatAdditionalActions = (
|
|
699
|
+
id: string,
|
|
700
|
+
showChatAdditionalActions: boolean
|
|
701
|
+
): TemplateResult => {
|
|
702
|
+
return html` <div
|
|
703
|
+
id=${id}
|
|
704
|
+
class=${classMap({
|
|
705
|
+
["showTab"]: showChatAdditionalActions,
|
|
706
|
+
["hideTab"]: !showChatAdditionalActions,
|
|
707
|
+
})}
|
|
708
|
+
>
|
|
709
|
+
<mobile-launcher
|
|
710
|
+
.onClickMinimize=${this.onClickMinimize}
|
|
711
|
+
.onClickEmailOption=${this.handleContactClicked}
|
|
712
|
+
.onClickPhoneOption=${this.handleContactTabClicked}
|
|
713
|
+
.onClickSSTOption=${this.handleTourClicked}
|
|
714
|
+
.isMobile=${this.isMobile}
|
|
715
|
+
.brandColor=${this.brandColor}
|
|
716
|
+
.hideChat=${true}
|
|
717
|
+
.hasChatEnabled=${!this.building?.chatWidgets
|
|
718
|
+
? true
|
|
719
|
+
: this.building?.chatWidgets.includes("CHAT")}
|
|
720
|
+
.hasEmailEnabled=${!this.building?.chatWidgets
|
|
721
|
+
? true
|
|
722
|
+
: this.building?.chatWidgets.includes("EMAIL")}
|
|
723
|
+
.hasCallUsEnabled=${!this.building?.chatWidgets
|
|
724
|
+
? true
|
|
725
|
+
: this.building?.chatWidgets.includes("CALL")}
|
|
726
|
+
.hasSSTEnabled=${!this.building?.chatWidgets
|
|
727
|
+
? true
|
|
728
|
+
: this.building?.chatWidgets.includes("SST")}
|
|
729
|
+
></mobile-launcher>
|
|
730
|
+
<minimize-expand-button
|
|
731
|
+
.brandColor=${this.brandColor}
|
|
732
|
+
.onClick=${() => {
|
|
733
|
+
if (this.chatProvider === ChatProviders.PUBNUB) {
|
|
734
|
+
this.displayPubnubChat = false;
|
|
735
|
+
} else if (this.chatProvider === ChatProviders.TALKJS) {
|
|
736
|
+
this.popup?.hide();
|
|
737
|
+
}
|
|
738
|
+
this.hideLauncher = false;
|
|
739
|
+
}}
|
|
740
|
+
></minimize-expand-button>
|
|
741
|
+
</div>`;
|
|
742
|
+
};
|
|
743
|
+
|
|
618
744
|
private attachOnClickToLauncher = () => {
|
|
619
745
|
const launcher = this.launcherRef.value;
|
|
620
746
|
if (!launcher) {
|
|
@@ -622,12 +748,13 @@ export class MEChat extends LitElement {
|
|
|
622
748
|
}
|
|
623
749
|
|
|
624
750
|
launcher.onChatTapped = async () => {
|
|
625
|
-
if (
|
|
626
|
-
|
|
751
|
+
if (this.chatProvider === ChatProviders.PUBNUB) {
|
|
752
|
+
this.displayPubnubChat = true;
|
|
753
|
+
} else if (this.chatProvider === ChatProviders.TALKJS) {
|
|
754
|
+
if (!this.popup) return;
|
|
755
|
+
this.popup.show();
|
|
756
|
+
this.analytics?.ping("open");
|
|
627
757
|
}
|
|
628
|
-
|
|
629
|
-
this.popup.show();
|
|
630
|
-
this.analytics?.ping("open");
|
|
631
758
|
this.hideLauncher = true;
|
|
632
759
|
this.hasMounted = true;
|
|
633
760
|
};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { css } from "lit";
|
|
2
|
+
|
|
3
|
+
export const pubnubChatStyles = css`
|
|
4
|
+
#pubnub-chat-container {
|
|
5
|
+
position: fixed;
|
|
6
|
+
right: 0px;
|
|
7
|
+
bottom: 110px;
|
|
8
|
+
z-index: 100000;
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
|
|
12
|
+
width: 340px;
|
|
13
|
+
height: 600px;
|
|
14
|
+
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
border-radius: 24px 24px 0px 24px;
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
|
|
20
|
+
box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px,
|
|
21
|
+
rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;
|
|
22
|
+
}
|
|
23
|
+
#header {
|
|
24
|
+
height: 70px;
|
|
25
|
+
width: 100%;
|
|
26
|
+
background: white;
|
|
27
|
+
overflow: hidden;
|
|
28
|
+
|
|
29
|
+
box-sizing: border-box;
|
|
30
|
+
|
|
31
|
+
display: flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
padding: 24px;
|
|
34
|
+
justify-content: space-between;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
#header-text {
|
|
38
|
+
font-size: 14px;
|
|
39
|
+
font-weight: bold;
|
|
40
|
+
font-family: Poppins, "Open Sans", "Helvetica", sans-serif;
|
|
41
|
+
}
|
|
42
|
+
#exit-chat-bttn {
|
|
43
|
+
display: flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
justify-content: center;
|
|
46
|
+
background: none;
|
|
47
|
+
border: none;
|
|
48
|
+
cursor: pointer;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#conversation-body {
|
|
52
|
+
flex: 1;
|
|
53
|
+
width: 100%;
|
|
54
|
+
height: 100%;
|
|
55
|
+
background: rgba(193, 193, 193, 0.5);
|
|
56
|
+
backdrop-filter: blur(4px);
|
|
57
|
+
overflow-y: scroll;
|
|
58
|
+
|
|
59
|
+
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
|
60
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
#message-thread-list {
|
|
64
|
+
display: flex;
|
|
65
|
+
flex-direction: column;
|
|
66
|
+
position: relative;
|
|
67
|
+
padding: 0px 16px;
|
|
68
|
+
gap: 16px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.message-container {
|
|
72
|
+
display: flex;
|
|
73
|
+
padding: 6px;
|
|
74
|
+
width: fit-content;
|
|
75
|
+
max-width: calc(100% - 60px);
|
|
76
|
+
box-sizing: border-box;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.lead-message {
|
|
80
|
+
background: black;
|
|
81
|
+
border-radius: 8px 8px 0px 8px;
|
|
82
|
+
|
|
83
|
+
align-self: flex-end;
|
|
84
|
+
color: white;
|
|
85
|
+
}
|
|
86
|
+
.ai-message {
|
|
87
|
+
background: white;
|
|
88
|
+
border-radius: 10px 10px 10px 0px;
|
|
89
|
+
color: black;
|
|
90
|
+
}
|
|
91
|
+
.message-text {
|
|
92
|
+
font-size: 14px;
|
|
93
|
+
word-break: break-word;
|
|
94
|
+
margin: 0;
|
|
95
|
+
padding: 6px 12px;
|
|
96
|
+
line-height: 150%;
|
|
97
|
+
font-family: Poppins, "Open Sans", "Helvetica", sans-serif;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.loading-dot {
|
|
101
|
+
width: 6px;
|
|
102
|
+
height: 6px;
|
|
103
|
+
border-radius: 50%;
|
|
104
|
+
margin: 5px;
|
|
105
|
+
animation: pulse 4s linear infinite;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.dot-1 {
|
|
109
|
+
background-color: #7d7d7d;
|
|
110
|
+
animation-delay: 0s;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.dot-2 {
|
|
114
|
+
background-color: #7d7d7d;
|
|
115
|
+
animation-delay: 1s;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.dot-3 {
|
|
119
|
+
background-color: #7d7d7d;
|
|
120
|
+
animation-delay: 2s;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@keyframes pulse {
|
|
124
|
+
0% {
|
|
125
|
+
background-color: #7d7d7d;
|
|
126
|
+
transform: scale(1);
|
|
127
|
+
}
|
|
128
|
+
50% {
|
|
129
|
+
background-color: #c7c7c7;
|
|
130
|
+
transform: scale(2);
|
|
131
|
+
}
|
|
132
|
+
100% {
|
|
133
|
+
background-color: #7d7d7d;
|
|
134
|
+
transform: scale(1);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
#footer {
|
|
139
|
+
height: 70px;
|
|
140
|
+
width: 100%;
|
|
141
|
+
background: black;
|
|
142
|
+
|
|
143
|
+
display: flex;
|
|
144
|
+
align-items: center;
|
|
145
|
+
justify-content: space-between;
|
|
146
|
+
box-sizing: border-box;
|
|
147
|
+
gap: 16px;
|
|
148
|
+
padding: 24px;
|
|
149
|
+
}
|
|
150
|
+
#message-input {
|
|
151
|
+
height: 40px;
|
|
152
|
+
width: 100%;
|
|
153
|
+
box-sizing: border-box;
|
|
154
|
+
font-weight: 300;
|
|
155
|
+
font-size: 14px;
|
|
156
|
+
line-height: 150%;
|
|
157
|
+
border: none;
|
|
158
|
+
color: white;
|
|
159
|
+
background: none;
|
|
160
|
+
font-family: Poppins, "Open Sans", "Helvetica", sans-serif;
|
|
161
|
+
}
|
|
162
|
+
#message-input:focus {
|
|
163
|
+
outline: none;
|
|
164
|
+
-webkit-box-shadow: none;
|
|
165
|
+
box-shadow: none;
|
|
166
|
+
}
|
|
167
|
+
#send-message-bttn {
|
|
168
|
+
background: none;
|
|
169
|
+
border: none;
|
|
170
|
+
cursor: pointer;
|
|
171
|
+
}
|
|
172
|
+
`;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { html, LitElement, TemplateResult } from "lit";
|
|
2
|
+
import { customElement, property, query, state } from "lit/decorators.js";
|
|
3
|
+
import { styleMap } from "lit/directives/style-map.js";
|
|
4
|
+
import { Building } from "../fetchBuildingInfo";
|
|
5
|
+
import MyPubnub, { ChatMessage } from "../MyPubnub";
|
|
6
|
+
import { SendMessageIconWhite, XBlackOutlineIcon } from "../svgIcons";
|
|
7
|
+
import { defaultBrandColor } from "../themes";
|
|
8
|
+
import { hexToAlmostWhite } from "../utils";
|
|
9
|
+
import { pubnubChatStyles } from "./pubnub-chat-styles";
|
|
10
|
+
|
|
11
|
+
@customElement("pubnub-chat")
|
|
12
|
+
export class PubnubChat extends LitElement {
|
|
13
|
+
static styles = [pubnubChatStyles];
|
|
14
|
+
|
|
15
|
+
@property({ attribute: true })
|
|
16
|
+
channel: string | undefined;
|
|
17
|
+
|
|
18
|
+
@property({ attribute: true })
|
|
19
|
+
myPubnub: MyPubnub | undefined;
|
|
20
|
+
|
|
21
|
+
@property({ attribute: true })
|
|
22
|
+
buildingSlug: string | undefined;
|
|
23
|
+
|
|
24
|
+
@property({ attribute: true })
|
|
25
|
+
building: Building | undefined;
|
|
26
|
+
|
|
27
|
+
@property({ attribute: true })
|
|
28
|
+
brandColor: string = defaultBrandColor;
|
|
29
|
+
|
|
30
|
+
@property({ attribute: true })
|
|
31
|
+
onClickExit: () => void = () => ({});
|
|
32
|
+
|
|
33
|
+
@property({ attribute: true })
|
|
34
|
+
onMount: () => void = () => ({});
|
|
35
|
+
|
|
36
|
+
@query("#message-input", true)
|
|
37
|
+
messageInput!: HTMLInputElement;
|
|
38
|
+
|
|
39
|
+
@query("#conversation-body", true)
|
|
40
|
+
messageBody!: HTMLDivElement;
|
|
41
|
+
|
|
42
|
+
@state()
|
|
43
|
+
messages: ChatMessage[] = [];
|
|
44
|
+
|
|
45
|
+
@state()
|
|
46
|
+
isLoadingMessages = false;
|
|
47
|
+
|
|
48
|
+
sendMessage = async (message: string): Promise<void> => {
|
|
49
|
+
this.messageInput.value = "";
|
|
50
|
+
await this.myPubnub?.sendMessage(message);
|
|
51
|
+
};
|
|
52
|
+
firstUpdated(): void {
|
|
53
|
+
this.messages = this.myPubnub?.messages || [];
|
|
54
|
+
this.myPubnub?.addChatListener(
|
|
55
|
+
(response: { messages: ChatMessage[]; isLoading: boolean }) => {
|
|
56
|
+
this.messages = response.messages;
|
|
57
|
+
this.isLoadingMessages = response.isLoading;
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
this.onMount();
|
|
61
|
+
}
|
|
62
|
+
scrollToChatBottom = (): void => {
|
|
63
|
+
this.messageBody.scrollTo({
|
|
64
|
+
top: this.messageBody.scrollHeight - this.messageBody.clientHeight,
|
|
65
|
+
behavior: "smooth",
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
updated(): void {
|
|
70
|
+
this.scrollToChatBottom();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
render(): TemplateResult {
|
|
74
|
+
if (
|
|
75
|
+
!this.channel ||
|
|
76
|
+
!this.myPubnub ||
|
|
77
|
+
!this.buildingSlug ||
|
|
78
|
+
!this.building
|
|
79
|
+
) {
|
|
80
|
+
return html``; // error here
|
|
81
|
+
}
|
|
82
|
+
return html`
|
|
83
|
+
<div id="pubnub-chat-container">
|
|
84
|
+
<div
|
|
85
|
+
id="header"
|
|
86
|
+
style=${styleMap({
|
|
87
|
+
background:
|
|
88
|
+
this.brandColor !== defaultBrandColor
|
|
89
|
+
? hexToAlmostWhite(this.brandColor, 0.6)
|
|
90
|
+
: this.brandColor,
|
|
91
|
+
})}
|
|
92
|
+
>
|
|
93
|
+
<div id="header-text">${this.building.name}</div>
|
|
94
|
+
<button id="exit-chat-bttn" @click=${this.onClickExit}>
|
|
95
|
+
${XBlackOutlineIcon}
|
|
96
|
+
</button>
|
|
97
|
+
</div>
|
|
98
|
+
<div id="conversation-body">
|
|
99
|
+
<ul id="message-thread-list">
|
|
100
|
+
${this.building.welcomeMessage
|
|
101
|
+
? html` <li class="message-container ai-message">
|
|
102
|
+
<p class="message-text">${this.building.welcomeMessage}</p>
|
|
103
|
+
</li>`
|
|
104
|
+
: ""}
|
|
105
|
+
${this.messages.map((message) => {
|
|
106
|
+
return html`
|
|
107
|
+
<li
|
|
108
|
+
class=${this.myPubnub?.isLeadMessage(message)
|
|
109
|
+
? "message-container lead-message"
|
|
110
|
+
: "message-container ai-message"}
|
|
111
|
+
key=${message.timetoken}
|
|
112
|
+
style=${styleMap({
|
|
113
|
+
background: this.myPubnub?.isLeadMessage(message)
|
|
114
|
+
? this.brandColor
|
|
115
|
+
: undefined,
|
|
116
|
+
})}
|
|
117
|
+
>
|
|
118
|
+
<p class="message-text">
|
|
119
|
+
${message.message.text
|
|
120
|
+
.split("\n")
|
|
121
|
+
.map((line) => html`${line}<br />`)}
|
|
122
|
+
</p>
|
|
123
|
+
</li>
|
|
124
|
+
`;
|
|
125
|
+
})}
|
|
126
|
+
${this.isLoadingMessages
|
|
127
|
+
? html`
|
|
128
|
+
<li id="loading-message" class="message-container ai-message">
|
|
129
|
+
<div class="loading-dot dot-1"></div>
|
|
130
|
+
<div class="loading-dot dot-2"></div>
|
|
131
|
+
<div class="loading-dot dot-3"></div>
|
|
132
|
+
</li>
|
|
133
|
+
`
|
|
134
|
+
: ""}
|
|
135
|
+
</ul>
|
|
136
|
+
</div>
|
|
137
|
+
<div id="footer">
|
|
138
|
+
<input
|
|
139
|
+
id="message-input"
|
|
140
|
+
placeholder="Ask a question..."
|
|
141
|
+
@keydown=${(e: KeyboardEvent) => {
|
|
142
|
+
if (e.key === "Enter") this.sendMessage(this.messageInput.value);
|
|
143
|
+
}}
|
|
144
|
+
/>
|
|
145
|
+
<button
|
|
146
|
+
id="send-message-bttn"
|
|
147
|
+
@click=${() => this.sendMessage(this.messageInput.value)}
|
|
148
|
+
>
|
|
149
|
+
${SendMessageIconWhite}
|
|
150
|
+
</button>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
`;
|
|
154
|
+
}
|
|
155
|
+
}
|