@meetelise/chat 1.20.84 → 1.20.86

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.
@@ -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
- #chatAdditionalActions {
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
- #chatAdditionalActions::after {
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,22 +400,35 @@ export class MEChat extends LitElement {
378
400
  (talkjsPopupElement as HTMLElement).style.zIndex = "99999999999";
379
401
  this.popup = popup;
380
402
 
381
- const autoOpenedTimestamp = sessionStorage.getItem("autoOpenedTimestamp");
382
- const shouldAutoOpen =
383
- !autoOpenedTimestamp ||
384
- (autoOpenedTimestamp && isPast(parseISO(autoOpenedTimestamp)));
385
- if (building.autoOpenChatWidget && shouldAutoOpen && !isMobile()) {
403
+ if (this.shouldAutoOpenChat(building.autoOpenChatWidget)) {
386
404
  this.popup.show();
387
405
  this.hideLauncher = true;
388
406
  this.hasMounted = true;
389
- sessionStorage.setItem(
390
- "autoOpenedTimestamp",
391
- formatISO(addMinutes(new Date(), 15))
392
- );
407
+ this.updateAlreadyAutoOpenedTimestamp();
393
408
  this.analytics?.ping("autoOpen");
394
409
  }
395
410
  };
396
411
 
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
416
+ const shouldAutoOpen =
417
+ !alreadyAutoOpenedTimestamp ||
418
+ !(
419
+ alreadyAutoOpenedTimestamp &&
420
+ !isPast(parseISO(alreadyAutoOpenedTimestamp))
421
+ );
422
+ return !!buildingHasAutoOpen && !!shouldAutoOpen && !isMobile();
423
+ };
424
+
425
+ private updateAlreadyAutoOpenedTimestamp = (): void => {
426
+ sessionStorage.setItem(
427
+ "alreadyAutoOpenedTimestamp",
428
+ formatISO(addMinutes(new Date(), 15))
429
+ );
430
+ };
431
+
397
432
  /**
398
433
  * Remove the instance from the screen.
399
434
  *
@@ -432,26 +467,64 @@ export class MEChat extends LitElement {
432
467
  this.launcher.style.display = "none";
433
468
  }
434
469
 
435
- firstUpdated = (): void => {
436
- this.initializeInstanceVariables();
470
+ firstUpdated = async (): Promise<void> => {
471
+ const usePubnubProviderFlag = await fetchFeatureFlagUsePubnub(
472
+ this.buildingSlug
473
+ );
474
+
475
+ if (usePubnubProviderFlag) {
476
+ this.chatProvider = ChatProviders.PUBNUB;
477
+ this.initializePubnubVariables();
478
+ } else {
479
+ this.initializeInstanceVariables();
480
+ }
481
+ };
482
+
483
+ initializePubnubVariables = async (): Promise<void> => {
484
+ await this.setBuildingDerivedInfo();
485
+ if (!this.building) return;
486
+
487
+ this.myPubnub = new MyPubnub(this.buildingSlug, this.building);
488
+ if (this.myPubnub.isChatKeyValid(this.myPubnub.getChatStorageKey())) {
489
+ await this.myPubnub.initializePubnub();
490
+ }
491
+ this.attachOnClickToLauncher();
492
+ if (this.shouldAutoOpenChat(this.building.autoOpenChatWidget)) {
493
+ this.displayPubnubChat = true;
494
+ this.hideLauncher = true;
495
+ this.hasMounted = true;
496
+ this.updateAlreadyAutoOpenedTimestamp();
497
+ }
498
+ this.isLoading = false;
437
499
  };
438
500
 
439
501
  handleContactClicked = (e: MouseEvent): void => {
440
- this.popup?.hide();
502
+ if (this.chatProvider === ChatProviders.TALKJS) {
503
+ this.popup?.hide();
504
+ } else if (this.chatProvider === ChatProviders.PUBNUB) {
505
+ this.displayPubnubChat = false;
506
+ }
441
507
  this.hideLauncher = false;
442
508
 
443
509
  this.launcherRef.value?.onClickEmailOption(e);
444
510
  };
445
511
 
446
512
  handleTourClicked = (e: MouseEvent): void => {
447
- this.popup?.hide();
513
+ if (this.chatProvider === ChatProviders.TALKJS) {
514
+ this.popup?.hide();
515
+ } else if (this.chatProvider === ChatProviders.PUBNUB) {
516
+ this.displayPubnubChat = false;
517
+ }
448
518
  this.hideLauncher = false;
449
-
450
519
  this.launcherRef.value?.onClickSSTOption(e);
451
520
  };
452
521
 
453
522
  handleContactTabClicked = (e: MouseEvent): void => {
454
- this.popup?.hide();
523
+ if (this.chatProvider === ChatProviders.TALKJS) {
524
+ this.popup?.hide();
525
+ } else if (this.chatProvider === ChatProviders.PUBNUB) {
526
+ this.displayPubnubChat = false;
527
+ }
455
528
  this.hideLauncher = false;
456
529
 
457
530
  this.launcherRef.value?.onClickPhoneOption(e);
@@ -461,14 +534,25 @@ export class MEChat extends LitElement {
461
534
  // For the top header contact, we change its pos based on the current x and y
462
535
  // of the talkjs popup - so we must adjust its coords on resize and mount
463
536
  adjustTopHeaderContactCoords = (): void => {
464
- if (this.talkjsPopupElement) {
537
+ const headerRef = this.shadowRoot?.getElementById(
538
+ this.chatProvider === ChatProviders.TALKJS
539
+ ? "chatAdditionalActionsTalkjs"
540
+ : "chatAdditionalActionsPubnub"
541
+ );
542
+ if (!headerRef) return;
543
+ if (this.chatProvider === ChatProviders.TALKJS && this.talkjsPopupElement) {
465
544
  const talkjsPopupCoords = this.talkjsPopupElement.getBoundingClientRect();
466
- const headerRef = this.shadowRoot?.getElementById(
467
- "chatAdditionalActions"
468
- );
469
- if (!headerRef) return;
470
545
  headerRef.style.left = `${talkjsPopupCoords.left + 20}px`;
471
546
  headerRef.style.top = `${talkjsPopupCoords.bottom - 24}px`;
547
+ } else if (this.chatProvider === ChatProviders.PUBNUB) {
548
+ const pubnubContainerElement = this.shadowRoot
549
+ ?.getElementById("pubnub-chat")
550
+ ?.shadowRoot?.getElementById("pubnub-chat-container");
551
+
552
+ const pubnubPopupCoords = pubnubContainerElement?.getBoundingClientRect();
553
+ if (!pubnubPopupCoords) return;
554
+ headerRef.style.left = `${pubnubPopupCoords.left}px`;
555
+ headerRef.style.top = `${pubnubPopupCoords.bottom}px`;
472
556
  }
473
557
  };
474
558
  connectedCallback(): void {
@@ -476,6 +560,7 @@ export class MEChat extends LitElement {
476
560
  window.addEventListener("resize", this.adjustTopHeaderContactCoords);
477
561
  }
478
562
  disconnectedCallback(): void {
563
+ this.myPubnub?.removeChatListeners();
479
564
  window.removeEventListener("resize", this.adjustTopHeaderContactCoords);
480
565
  super.disconnectedCallback();
481
566
  }
@@ -501,6 +586,33 @@ export class MEChat extends LitElement {
501
586
  EliseAI widget that allows you to chat with a virtual assistant, book
502
587
  a tour, contact the leasing office, and more.
503
588
  </div>
589
+
590
+ ${
591
+ this.chatProvider === ChatProviders.PUBNUB && this.displayPubnubChat
592
+ ? html`
593
+ <pubnub-chat
594
+ id="pubnub-chat"
595
+ .channel=${this.myPubnub?.channel}
596
+ .myPubnub=${this.myPubnub}
597
+ .buildingSlug=${this.buildingSlug}
598
+ .building=${this.building}
599
+ .brandColor=${this.brandColor}
600
+ .onClickExit=${() => {
601
+ this.displayPubnubChat = false;
602
+ this.hideLauncher = false;
603
+ }}
604
+ .onMount=${() => {
605
+ this.adjustTopHeaderContactCoords();
606
+ }}
607
+ .isMobile=${this.isMobile}
608
+ ></pubnub-chat>
609
+ ${this.renderChatAdditionalActions(
610
+ "chatAdditionalActionsPubnub",
611
+ showChatAdditionalActions
612
+ )}
613
+ `
614
+ : ""
615
+ }
504
616
  <div
505
617
  class=${classMap({
506
618
  launcherContainer: true,
@@ -568,54 +680,65 @@ export class MEChat extends LitElement {
568
680
  }
569
681
 
570
682
  </div>
571
- <div
572
- id="chatAdditionalActions"
573
- class=${classMap({
574
- ["showTab"]: showChatAdditionalActions,
575
- ["hideTab"]: !showChatAdditionalActions,
576
- })}
577
- >
578
- <mobile-launcher
579
- .onClickMinimize=${this.onClickMinimize}
580
- .onClickEmailOption=${this.handleContactClicked}
581
- .onClickPhoneOption=${this.handleContactTabClicked}
582
- .onClickSSTOption=${this.handleTourClicked}
583
- .isMobile=${this.isMobile}
584
- .brandColor=${this.brandColor}
585
- .hideChat=${true}
586
- .hasChatEnabled=${
587
- !this.building?.chatWidgets
588
- ? true
589
- : this.building?.chatWidgets.includes("CHAT")
590
- }
591
- .hasEmailEnabled=${
592
- !this.building?.chatWidgets
593
- ? true
594
- : this.building?.chatWidgets.includes("EMAIL")
595
- }
596
- .hasCallUsEnabled=${
597
- !this.building?.chatWidgets
598
- ? true
599
- : this.building?.chatWidgets.includes("CALL")
600
- }
601
- .hasSSTEnabled=${
602
- !this.building?.chatWidgets
603
- ? true
604
- : this.building?.chatWidgets.includes("SST")
605
- }
606
- ></mobile-launcher>
607
- <minimize-expand-button
608
- .brandColor=${this.brandColor}
609
- .onClick=${() => {
610
- this.popup?.hide();
611
- this.hideLauncher = false;
612
- }}
613
- ></minimize-expand-button>
614
- </div>
683
+ ${
684
+ this.chatProvider === ChatProviders.TALKJS
685
+ ? this.renderChatAdditionalActions(
686
+ "chatAdditionalActionsTalkjs",
687
+ showChatAdditionalActions
688
+ )
689
+ : ""
690
+ }
691
+
615
692
  </meta>
616
693
  `;
617
694
  }
618
695
 
696
+ private renderChatAdditionalActions = (
697
+ id: string,
698
+ showChatAdditionalActions: boolean
699
+ ): TemplateResult => {
700
+ return html` <div
701
+ id=${id}
702
+ class=${classMap({
703
+ ["showTab"]: showChatAdditionalActions,
704
+ ["hideTab"]: !showChatAdditionalActions,
705
+ })}
706
+ >
707
+ <mobile-launcher
708
+ .onClickMinimize=${this.onClickMinimize}
709
+ .onClickEmailOption=${this.handleContactClicked}
710
+ .onClickPhoneOption=${this.handleContactTabClicked}
711
+ .onClickSSTOption=${this.handleTourClicked}
712
+ .isMobile=${this.isMobile}
713
+ .brandColor=${this.brandColor}
714
+ .hideChat=${true}
715
+ .hasChatEnabled=${!this.building?.chatWidgets
716
+ ? true
717
+ : this.building?.chatWidgets.includes("CHAT")}
718
+ .hasEmailEnabled=${!this.building?.chatWidgets
719
+ ? true
720
+ : this.building?.chatWidgets.includes("EMAIL")}
721
+ .hasCallUsEnabled=${!this.building?.chatWidgets
722
+ ? true
723
+ : this.building?.chatWidgets.includes("CALL")}
724
+ .hasSSTEnabled=${!this.building?.chatWidgets
725
+ ? true
726
+ : this.building?.chatWidgets.includes("SST")}
727
+ ></mobile-launcher>
728
+ <minimize-expand-button
729
+ .brandColor=${this.brandColor}
730
+ .onClick=${() => {
731
+ if (this.chatProvider === ChatProviders.PUBNUB) {
732
+ this.displayPubnubChat = false;
733
+ } else if (this.chatProvider === ChatProviders.TALKJS) {
734
+ this.popup?.hide();
735
+ }
736
+ this.hideLauncher = false;
737
+ }}
738
+ ></minimize-expand-button>
739
+ </div>`;
740
+ };
741
+
619
742
  private attachOnClickToLauncher = () => {
620
743
  const launcher = this.launcherRef.value;
621
744
  if (!launcher) {
@@ -623,12 +746,13 @@ export class MEChat extends LitElement {
623
746
  }
624
747
 
625
748
  launcher.onChatTapped = async () => {
626
- if (!this.popup) {
627
- return;
749
+ if (this.chatProvider === ChatProviders.PUBNUB) {
750
+ this.displayPubnubChat = true;
751
+ } else if (this.chatProvider === ChatProviders.TALKJS) {
752
+ if (!this.popup) return;
753
+ this.popup.show();
754
+ this.analytics?.ping("open");
628
755
  }
629
-
630
- this.popup.show();
631
- this.analytics?.ping("open");
632
756
  this.hideLauncher = true;
633
757
  this.hasMounted = true;
634
758
  };
@@ -0,0 +1,185 @@
1
+ import { css } from "lit";
2
+
3
+ export const pubnubChatStyles = css`
4
+ #pubnub-chat-container {
5
+ position: fixed;
6
+
7
+ z-index: 100000;
8
+ display: flex;
9
+ align-items: center;
10
+
11
+ display: flex;
12
+ flex-direction: column;
13
+ overflow: hidden;
14
+
15
+ box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px,
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;
33
+ }
34
+ #header {
35
+ height: 70px;
36
+ width: 100%;
37
+ background: white;
38
+ overflow: hidden;
39
+
40
+ box-sizing: border-box;
41
+
42
+ display: flex;
43
+ align-items: center;
44
+ padding: 24px;
45
+ justify-content: space-between;
46
+ }
47
+
48
+ #header-text {
49
+ font-size: 14px;
50
+ font-weight: bold;
51
+ }
52
+ #exit-chat-bttn {
53
+ display: flex;
54
+ align-items: center;
55
+ justify-content: center;
56
+ background: none;
57
+ border: none;
58
+ cursor: pointer;
59
+ }
60
+
61
+ #conversation-body {
62
+ flex: 1;
63
+ width: 100%;
64
+ height: 100%;
65
+ background: rgba(193, 193, 193, 0.5);
66
+ backdrop-filter: blur(4px);
67
+ overflow-y: scroll;
68
+
69
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
70
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
71
+ }
72
+
73
+ #message-thread-list {
74
+ display: flex;
75
+ flex-direction: column;
76
+ position: relative;
77
+ padding: 0px 16px;
78
+ gap: 16px;
79
+ list-style: none;
80
+ }
81
+
82
+ .disclaimer-message {
83
+ font-size: 12px;
84
+ }
85
+
86
+ .message-container {
87
+ display: flex;
88
+ padding: 6px;
89
+ width: fit-content;
90
+ max-width: calc(100% - 60px);
91
+ box-sizing: border-box;
92
+ }
93
+
94
+ .lead-message {
95
+ background: black;
96
+ border-radius: 8px 8px 0px 8px;
97
+
98
+ align-self: flex-end;
99
+ color: white;
100
+ }
101
+ .ai-message {
102
+ background: white;
103
+ border-radius: 10px 10px 10px 0px;
104
+ color: black;
105
+ }
106
+ .message-text {
107
+ font-size: 14px;
108
+ word-break: break-word;
109
+ margin: 0;
110
+ padding: 6px 12px;
111
+ line-height: 130%;
112
+ }
113
+
114
+ .loading-dot {
115
+ width: 6px;
116
+ height: 6px;
117
+ border-radius: 50%;
118
+ margin: 5px;
119
+ animation: pulse 4s linear infinite;
120
+ }
121
+
122
+ .dot-1 {
123
+ background-color: #7d7d7d;
124
+ animation-delay: 0s;
125
+ }
126
+
127
+ .dot-2 {
128
+ background-color: #7d7d7d;
129
+ animation-delay: 1s;
130
+ }
131
+
132
+ .dot-3 {
133
+ background-color: #7d7d7d;
134
+ animation-delay: 2s;
135
+ }
136
+
137
+ @keyframes pulse {
138
+ 0% {
139
+ background-color: #7d7d7d;
140
+ transform: scale(1);
141
+ }
142
+ 50% {
143
+ background-color: #c7c7c7;
144
+ transform: scale(2);
145
+ }
146
+ 100% {
147
+ background-color: #7d7d7d;
148
+ transform: scale(1);
149
+ }
150
+ }
151
+
152
+ #footer {
153
+ height: 70px;
154
+ width: 100%;
155
+ background: black;
156
+
157
+ display: flex;
158
+ align-items: center;
159
+ justify-content: space-between;
160
+ box-sizing: border-box;
161
+ gap: 16px;
162
+ padding: 24px;
163
+ }
164
+ #message-input {
165
+ height: 40px;
166
+ width: 100%;
167
+ box-sizing: border-box;
168
+ font-weight: 300;
169
+ font-size: 14px;
170
+ line-height: 150%;
171
+ border: none;
172
+ color: white;
173
+ background: none;
174
+ }
175
+ #message-input:focus {
176
+ outline: none;
177
+ -webkit-box-shadow: none;
178
+ box-shadow: none;
179
+ }
180
+ #send-message-bttn {
181
+ background: none;
182
+ border: none;
183
+ cursor: pointer;
184
+ }
185
+ `;