@meetelise/chat 1.20.136 → 1.20.138
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/dist/index.js +149 -215
- package/src/MyPubnub.ts +82 -5
- package/src/WebComponent/Scheduler/tour-scheduler.ts +65 -213
- package/src/WebComponent/Scheduler/tourSchedulerStyles.ts +0 -32
- package/src/WebComponent/me-chat.ts +3 -18
- package/src/analytics.ts +1 -1
- package/src/handleChatIds.ts +0 -98
package/src/MyPubnub.ts
CHANGED
|
@@ -3,12 +3,14 @@ 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";
|
|
6
8
|
import addHours from "date-fns/addHours";
|
|
7
9
|
import isAfter from "date-fns/isAfter";
|
|
10
|
+
import formatISO from "date-fns/formatISO";
|
|
11
|
+
import isBefore from "date-fns/isBefore";
|
|
8
12
|
import { LogType, sendLoggingEvent } from "./analytics";
|
|
9
13
|
|
|
10
|
-
import { createChatStorageKey, getChatStorageKey } from "./handleChatIds";
|
|
11
|
-
|
|
12
14
|
interface TokenResponse {
|
|
13
15
|
auth: {
|
|
14
16
|
result: {
|
|
@@ -38,7 +40,7 @@ export interface ChatMessage {
|
|
|
38
40
|
timetoken: number;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
interface ChatInfo {
|
|
42
44
|
leadId: string | null;
|
|
43
45
|
timestamp: Date | null;
|
|
44
46
|
buildingSlug: string | null;
|
|
@@ -114,7 +116,7 @@ class MyPubnub {
|
|
|
114
116
|
}
|
|
115
117
|
|
|
116
118
|
async initializePubnub(): Promise<Pubnub | undefined> {
|
|
117
|
-
const storedChatKeyValues = getChatStorageKey(
|
|
119
|
+
const storedChatKeyValues = this.getChatStorageKey();
|
|
118
120
|
if (!storedChatKeyValues.leadId) {
|
|
119
121
|
// eslint-disable-next-line no-console
|
|
120
122
|
sendLoggingEvent({
|
|
@@ -143,8 +145,9 @@ class MyPubnub {
|
|
|
143
145
|
addHours(new Date(), this.ttlHours)
|
|
144
146
|
)
|
|
145
147
|
) {
|
|
146
|
-
createChatStorageKey(this.
|
|
148
|
+
this.createChatStorageKey(this.leadUserId);
|
|
147
149
|
}
|
|
150
|
+
|
|
148
151
|
const pubnubToken = await this.fetchToken(this.leadUserId, this.channel);
|
|
149
152
|
if (!pubnubToken) return;
|
|
150
153
|
|
|
@@ -379,6 +382,80 @@ class MyPubnub {
|
|
|
379
382
|
isLeadMessage = (message: ChatMessage): boolean =>
|
|
380
383
|
message.publisher.includes("lead_") &&
|
|
381
384
|
message.message.customType === "lead_message";
|
|
385
|
+
|
|
386
|
+
clearChatStorageKey = (): void =>
|
|
387
|
+
localStorage.removeItem("com.eliseai.webchat.slug=" + this.buildingSlug);
|
|
388
|
+
|
|
389
|
+
createChatStorageKey = (existingUserId?: string): ChatInfo => {
|
|
390
|
+
const storageTimestamp = formatISO(new Date());
|
|
391
|
+
const leadUserId = existingUserId ?? `lead_${uuid()}_${this.buildingSlug}`;
|
|
392
|
+
localStorage.setItem(
|
|
393
|
+
"com.eliseai.webchat.slug=" + this.buildingSlug,
|
|
394
|
+
JSON.stringify({
|
|
395
|
+
buildingSlug: this.buildingSlug,
|
|
396
|
+
leadId: leadUserId,
|
|
397
|
+
timestamp: storageTimestamp,
|
|
398
|
+
})
|
|
399
|
+
);
|
|
400
|
+
return {
|
|
401
|
+
leadId: leadUserId,
|
|
402
|
+
timestamp: parseISO(storageTimestamp),
|
|
403
|
+
buildingSlug: this.buildingSlug,
|
|
404
|
+
};
|
|
405
|
+
};
|
|
406
|
+
getChatStorageKey = (createNewIfNotExist = true): ChatInfo => {
|
|
407
|
+
const eliseaiLocalStorageValue = localStorage.getItem(
|
|
408
|
+
"com.eliseai.webchat.slug=" + this.buildingSlug
|
|
409
|
+
);
|
|
410
|
+
if (eliseaiLocalStorageValue) {
|
|
411
|
+
try {
|
|
412
|
+
const eliseaiLocalStorageValueParsed = JSON.parse(
|
|
413
|
+
eliseaiLocalStorageValue
|
|
414
|
+
);
|
|
415
|
+
const lsBuildingSlug = eliseaiLocalStorageValueParsed.buildingSlug;
|
|
416
|
+
const lsLeadId = eliseaiLocalStorageValueParsed.leadId;
|
|
417
|
+
const lsExpiration = new Date(eliseaiLocalStorageValueParsed.timestamp);
|
|
418
|
+
|
|
419
|
+
if (
|
|
420
|
+
this.isChatKeyValid({
|
|
421
|
+
leadId: lsLeadId,
|
|
422
|
+
timestamp: lsExpiration,
|
|
423
|
+
buildingSlug: lsBuildingSlug,
|
|
424
|
+
})
|
|
425
|
+
)
|
|
426
|
+
return {
|
|
427
|
+
leadId: lsLeadId,
|
|
428
|
+
timestamp: lsExpiration,
|
|
429
|
+
buildingSlug: lsBuildingSlug,
|
|
430
|
+
};
|
|
431
|
+
} catch (_) {
|
|
432
|
+
// eslint-disable-next-line no-console
|
|
433
|
+
console.warn("Error getting chat storage key");
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (createNewIfNotExist) return this.createChatStorageKey();
|
|
438
|
+
return {
|
|
439
|
+
leadId: null,
|
|
440
|
+
timestamp: null,
|
|
441
|
+
buildingSlug: null,
|
|
442
|
+
};
|
|
443
|
+
};
|
|
444
|
+
isChatKeyValid = (storageValueDeconstructed: ChatInfo): boolean => {
|
|
445
|
+
if (
|
|
446
|
+
storageValueDeconstructed.buildingSlug !== this.buildingSlug ||
|
|
447
|
+
!storageValueDeconstructed.leadId ||
|
|
448
|
+
!storageValueDeconstructed.timestamp
|
|
449
|
+
) {
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const expirationDate = addHours(
|
|
454
|
+
storageValueDeconstructed.timestamp,
|
|
455
|
+
this.ttlHours
|
|
456
|
+
);
|
|
457
|
+
return !isBefore(expirationDate, Date.now());
|
|
458
|
+
};
|
|
382
459
|
}
|
|
383
460
|
|
|
384
461
|
export default MyPubnub;
|
|
@@ -22,9 +22,7 @@ import { TimePicker } from "./time-picker";
|
|
|
22
22
|
import { LabeledOption, UnitV2 } from "../../fetchBuildingInfo";
|
|
23
23
|
import { isMobile } from "../../utils";
|
|
24
24
|
import axios from "axios";
|
|
25
|
-
import isNumber from "lodash/isNumber";
|
|
26
25
|
import mapValues from "lodash/mapValues";
|
|
27
|
-
import isString from "lodash/isString";
|
|
28
26
|
import classnames from "classnames";
|
|
29
27
|
import parseISO from "date-fns/parseISO";
|
|
30
28
|
import compareAsc from "date-fns/compareAsc";
|
|
@@ -38,22 +36,6 @@ import { tourSchedulerStyles } from "./tourSchedulerStyles";
|
|
|
38
36
|
import { defaultBrandColor } from "../../themes";
|
|
39
37
|
import { LogType, sendLoggingEvent } from "../../analytics";
|
|
40
38
|
|
|
41
|
-
const getHumanReadableLayout = (layout: string) => {
|
|
42
|
-
if (layout == "studio") return "Studio";
|
|
43
|
-
return {
|
|
44
|
-
"1br": "1 bedroom",
|
|
45
|
-
"2br": "2 bedroom",
|
|
46
|
-
"3br": "3 bedroom",
|
|
47
|
-
"4br": "4 bedroom",
|
|
48
|
-
"5br": "5 bedroom",
|
|
49
|
-
"6br": "6 bedroom",
|
|
50
|
-
"7br": "7 bedroom",
|
|
51
|
-
"8br": "8 bedroom",
|
|
52
|
-
"9br": "9 bedroom",
|
|
53
|
-
"10br": "10 bedroom",
|
|
54
|
-
}[layout];
|
|
55
|
-
};
|
|
56
|
-
|
|
57
39
|
@customElement("tour-scheduler")
|
|
58
40
|
export class TourScheduler extends LitElement {
|
|
59
41
|
@property({ attribute: false })
|
|
@@ -134,10 +116,6 @@ export class TourScheduler extends LitElement {
|
|
|
134
116
|
emailInput!: HTMLInputElement;
|
|
135
117
|
@query(".inputContainer#phone input")
|
|
136
118
|
phoneInput!: HTMLInputElement;
|
|
137
|
-
@query("me-select#unitType")
|
|
138
|
-
unitTypeSelect!: MESelect;
|
|
139
|
-
@query("me-select#layoutType")
|
|
140
|
-
layoutTypeSelect!: MESelect;
|
|
141
119
|
@query("me-select#leadSource")
|
|
142
120
|
selectedLeadSource!: MESelect;
|
|
143
121
|
|
|
@@ -455,13 +433,6 @@ export class TourScheduler extends LitElement {
|
|
|
455
433
|
lastName: this.lastNameInput.value,
|
|
456
434
|
tourType: tourTypeForSubmission[this.tourType],
|
|
457
435
|
tourTime: `${this.selectedTime.datetime}${this.selectedTime.offset}`,
|
|
458
|
-
layouts: this.layoutTypeSelect.value
|
|
459
|
-
? [this.layoutTypeSelect.value]
|
|
460
|
-
: null,
|
|
461
|
-
unitNumbers:
|
|
462
|
-
this.unitTypeSelect && this.unitTypeSelect.value
|
|
463
|
-
? [this.unitTypeSelect.value]
|
|
464
|
-
: null,
|
|
465
436
|
originatingSource:
|
|
466
437
|
leadSources.find((i) => i !== "property-website") || null,
|
|
467
438
|
});
|
|
@@ -474,13 +445,6 @@ export class TourScheduler extends LitElement {
|
|
|
474
445
|
last_name: this.lastNameInput.value,
|
|
475
446
|
tour_type: tourTypeForSubmission[this.tourType],
|
|
476
447
|
tour_time: `${this.selectedTime.datetime}${this.selectedTime.offset}`, // e.g., "2022-06-27T09:00:00-07:00"
|
|
477
|
-
layouts: this.layoutTypeSelect.value
|
|
478
|
-
? [this.layoutTypeSelect.value]
|
|
479
|
-
: null,
|
|
480
|
-
unit_numbers:
|
|
481
|
-
this.unitTypeSelect && this.unitTypeSelect.value
|
|
482
|
-
? [this.unitTypeSelect.value]
|
|
483
|
-
: null,
|
|
484
448
|
lead_sources: [
|
|
485
449
|
...new Set(
|
|
486
450
|
parsedLeadSource
|
|
@@ -793,12 +757,12 @@ export class TourScheduler extends LitElement {
|
|
|
793
757
|
<div id="namesWrapper">
|
|
794
758
|
<div class="nameContainer" id="firstName">
|
|
795
759
|
<input
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
760
|
+
class=${classMap({
|
|
761
|
+
["webchat-input"]: true,
|
|
762
|
+
["nameInput"]: true,
|
|
763
|
+
["webchat-font__desktop"]: !isMobile(),
|
|
764
|
+
["webchat-font__mobile"]: isMobile(),
|
|
765
|
+
})}
|
|
802
766
|
type="text"
|
|
803
767
|
placeholder="First name"
|
|
804
768
|
name="firstName"
|
|
@@ -823,139 +787,67 @@ export class TourScheduler extends LitElement {
|
|
|
823
787
|
</div>
|
|
824
788
|
</div>
|
|
825
789
|
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
</div>
|
|
842
|
-
<div class="inputContainer" id="phone">
|
|
843
|
-
<input
|
|
844
|
-
class=${classMap({
|
|
845
|
-
["webchat-input"]: true,
|
|
846
|
-
["webchat-font__desktop"]: !isMobile(),
|
|
847
|
-
["webchat-font__mobile"]: isMobile(),
|
|
848
|
-
})}
|
|
849
|
-
type="tel"
|
|
850
|
-
inputmode="tel"
|
|
851
|
-
placeholder="Phone"
|
|
852
|
-
name="phone"
|
|
853
|
-
autocomplete="tel-national"
|
|
854
|
-
maxlength="14"
|
|
855
|
-
.value=${this.phoneNumber}
|
|
856
|
-
@keydown=${this.handlePhoneKeydown}
|
|
857
|
-
@keyup=${this.handlePhoneKeyup}
|
|
858
|
-
@input=${(e: Event) => {
|
|
859
|
-
if (!e.target) {
|
|
860
|
-
return;
|
|
861
|
-
}
|
|
862
|
-
this.phoneNumber = formatToPhoneInput(
|
|
863
|
-
(e.target as HTMLInputElement).value
|
|
864
|
-
);
|
|
865
|
-
}}
|
|
866
|
-
/>
|
|
867
|
-
</div>
|
|
868
|
-
${
|
|
869
|
-
this.leadSources.length > 0 &&
|
|
870
|
-
(this.featureFlagShowDropdown === FeatureFlagsShowDropdown.always ||
|
|
871
|
-
(this.featureFlagShowDropdown ===
|
|
872
|
-
FeatureFlagsShowDropdown.onAttributionFailure &&
|
|
873
|
-
this.currentLeadSource.length === 0))
|
|
874
|
-
? html` <me-select
|
|
875
|
-
id="leadSource"
|
|
876
|
-
value="${this.currentLeadSource}"
|
|
877
|
-
placeholder="How did you hear about us?"
|
|
878
|
-
.options="${this.leadSources.map((i) => ({
|
|
879
|
-
label: i,
|
|
880
|
-
value: i,
|
|
881
|
-
}))}"
|
|
882
|
-
@change=${() => {
|
|
883
|
-
this.requestUpdate();
|
|
884
|
-
}}
|
|
885
|
-
>
|
|
886
|
-
</me-select>`
|
|
887
|
-
: ""
|
|
888
|
-
}
|
|
790
|
+
<div class="inputContainer" id="email">
|
|
791
|
+
<input
|
|
792
|
+
class=${classMap({
|
|
793
|
+
["webchat-input"]: true,
|
|
794
|
+
["webchat-font__desktop"]: !isMobile(),
|
|
795
|
+
["webchat-font__mobile"]: isMobile(),
|
|
796
|
+
})}
|
|
797
|
+
type="email"
|
|
798
|
+
inputmode="email"
|
|
799
|
+
placeholder="Email"
|
|
800
|
+
name="email"
|
|
801
|
+
autocomplete="email"
|
|
802
|
+
.value=${this.email}
|
|
803
|
+
@input=${this.onChangeEmail}
|
|
804
|
+
/>
|
|
889
805
|
</div>
|
|
890
|
-
<div class="
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
this.requestUpdate();
|
|
916
|
-
}}
|
|
917
|
-
>Studio
|
|
918
|
-
</me-select>
|
|
919
|
-
</div>
|
|
920
|
-
</div>`
|
|
921
|
-
: ""
|
|
922
|
-
}
|
|
923
|
-
${
|
|
924
|
-
this.unitOptions.filter(
|
|
925
|
-
(i) =>
|
|
926
|
-
!this.layoutTypeSelect ||
|
|
927
|
-
i.layout === this.layoutTypeSelect.value
|
|
928
|
-
).length > 0
|
|
929
|
-
? html`
|
|
930
|
-
<div class="unitLayoutChoice">
|
|
931
|
-
<me-select
|
|
932
|
-
id="unitType"
|
|
933
|
-
placeholder="Select unit"
|
|
934
|
-
.options="${this.unitOptions
|
|
935
|
-
.filter(
|
|
936
|
-
(i) =>
|
|
937
|
-
!this.layoutTypeSelect ||
|
|
938
|
-
i.layout === this.layoutTypeSelect.value
|
|
939
|
-
)
|
|
940
|
-
.map((i) => ({
|
|
941
|
-
label: i.name,
|
|
942
|
-
value: i.name,
|
|
943
|
-
}))}"
|
|
944
|
-
defaultOption="Studio"
|
|
945
|
-
@change=${() => {
|
|
946
|
-
// to revalidate the form
|
|
947
|
-
this.requestUpdate();
|
|
948
|
-
}}
|
|
949
|
-
>Studio
|
|
950
|
-
</me-select>
|
|
951
|
-
</div>
|
|
952
|
-
`
|
|
953
|
-
: ""
|
|
954
|
-
}
|
|
955
|
-
</div>
|
|
956
|
-
|
|
957
|
-
|
|
806
|
+
<div class="inputContainer" id="phone">
|
|
807
|
+
<input
|
|
808
|
+
class=${classMap({
|
|
809
|
+
["webchat-input"]: true,
|
|
810
|
+
["webchat-font__desktop"]: !isMobile(),
|
|
811
|
+
["webchat-font__mobile"]: isMobile(),
|
|
812
|
+
})}
|
|
813
|
+
type="tel"
|
|
814
|
+
inputmode="tel"
|
|
815
|
+
placeholder="Phone"
|
|
816
|
+
name="phone"
|
|
817
|
+
autocomplete="tel-national"
|
|
818
|
+
maxlength="14"
|
|
819
|
+
.value=${this.phoneNumber}
|
|
820
|
+
@keydown=${this.handlePhoneKeydown}
|
|
821
|
+
@keyup=${this.handlePhoneKeyup}
|
|
822
|
+
@input=${(e: Event) => {
|
|
823
|
+
if (!e.target) {
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
this.phoneNumber = formatToPhoneInput(
|
|
827
|
+
(e.target as HTMLInputElement).value
|
|
828
|
+
);
|
|
829
|
+
}}
|
|
830
|
+
/>
|
|
958
831
|
</div>
|
|
832
|
+
${this.leadSources.length > 0 &&
|
|
833
|
+
(this.featureFlagShowDropdown === FeatureFlagsShowDropdown.always ||
|
|
834
|
+
(this.featureFlagShowDropdown ===
|
|
835
|
+
FeatureFlagsShowDropdown.onAttributionFailure &&
|
|
836
|
+
this.currentLeadSource.length === 0))
|
|
837
|
+
? html` <me-select
|
|
838
|
+
id="leadSource"
|
|
839
|
+
value="${this.currentLeadSource}"
|
|
840
|
+
placeholder="How did you hear about us?"
|
|
841
|
+
.options="${this.leadSources.map((i) => ({
|
|
842
|
+
label: i,
|
|
843
|
+
value: i,
|
|
844
|
+
}))}"
|
|
845
|
+
@change=${() => {
|
|
846
|
+
this.requestUpdate();
|
|
847
|
+
}}
|
|
848
|
+
>
|
|
849
|
+
</me-select>`
|
|
850
|
+
: ""}
|
|
959
851
|
</div> `;
|
|
960
852
|
}
|
|
961
853
|
|
|
@@ -1195,43 +1087,3 @@ const tourTypeForSubmission = {
|
|
|
1195
1087
|
[TourType.Self]: "self-guided-tour",
|
|
1196
1088
|
[TourType.Virtual]: "live-virtual-tour",
|
|
1197
1089
|
};
|
|
1198
|
-
|
|
1199
|
-
const getLayoutOrder = (layout: string) => {
|
|
1200
|
-
return {
|
|
1201
|
-
studio: 0,
|
|
1202
|
-
"1br": 1,
|
|
1203
|
-
"2br": 2,
|
|
1204
|
-
"3br": 3,
|
|
1205
|
-
"4br": 4,
|
|
1206
|
-
"5br": 5,
|
|
1207
|
-
"6br": 6,
|
|
1208
|
-
"7br": 7,
|
|
1209
|
-
"8br": 8,
|
|
1210
|
-
"9br": 9,
|
|
1211
|
-
"10br": 10,
|
|
1212
|
-
}[layout];
|
|
1213
|
-
};
|
|
1214
|
-
|
|
1215
|
-
const getLayoutFromOrder = (order: number) => {
|
|
1216
|
-
return {
|
|
1217
|
-
0: "studio",
|
|
1218
|
-
1: "1br",
|
|
1219
|
-
2: "2br",
|
|
1220
|
-
3: "3br",
|
|
1221
|
-
4: "4br",
|
|
1222
|
-
5: "5br",
|
|
1223
|
-
6: "6br",
|
|
1224
|
-
7: "7br",
|
|
1225
|
-
8: "8br",
|
|
1226
|
-
9: "9br",
|
|
1227
|
-
10: "10br",
|
|
1228
|
-
}[order];
|
|
1229
|
-
};
|
|
1230
|
-
|
|
1231
|
-
const sortedLayouts = (layouts: string[]) => {
|
|
1232
|
-
const layoutOrder = layouts
|
|
1233
|
-
.map((layout) => getLayoutOrder(layout))
|
|
1234
|
-
.filter(isNumber)
|
|
1235
|
-
.sort();
|
|
1236
|
-
return layoutOrder.map((order) => getLayoutFromOrder(order)).filter(isString);
|
|
1237
|
-
};
|
|
@@ -236,38 +236,6 @@ otherwise there's some empty space at the bottom of the button, which interferes
|
|
|
236
236
|
height: 49px;
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
-
.unitLayoutChoices {
|
|
240
|
-
grid-row: 5 / 6;
|
|
241
|
-
grid-column: 3;
|
|
242
|
-
align-self: start;
|
|
243
|
-
display: flex;
|
|
244
|
-
flex-direction: column;
|
|
245
|
-
}
|
|
246
|
-
.unitLayoutChoicesDropdowns {
|
|
247
|
-
display: flex;
|
|
248
|
-
justify-content: space-between;
|
|
249
|
-
gap: 6px;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
.what-to-view-subheader {
|
|
253
|
-
font-size: 12px;
|
|
254
|
-
padding-top: 4px;
|
|
255
|
-
padding-bottom: 4px;
|
|
256
|
-
}
|
|
257
|
-
.unitLayoutChoice {
|
|
258
|
-
width: -webkit-fill-available;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
h2.unitLayoutChoice {
|
|
262
|
-
margin-bottom: 7px;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
.unitLayoutOptions {
|
|
266
|
-
display: flex;
|
|
267
|
-
flex-direction: column;
|
|
268
|
-
gap: 8px;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
239
|
hr {
|
|
272
240
|
grid-row: 6;
|
|
273
241
|
grid-column: 1 / 5;
|
|
@@ -40,11 +40,6 @@ import "./actions/minimize-expand-button";
|
|
|
40
40
|
import "./launcher/mobile-launcher";
|
|
41
41
|
import "./pubnub-chat";
|
|
42
42
|
import { getBuildingPhoneNumber } from "../getBuildingPhoneNumber";
|
|
43
|
-
import {
|
|
44
|
-
clearChatStorageKey,
|
|
45
|
-
getChatStorageKey,
|
|
46
|
-
isChatKeyValid,
|
|
47
|
-
} from "../handleChatIds";
|
|
48
43
|
|
|
49
44
|
@customElement("me-chat")
|
|
50
45
|
export class MEChat extends LitElement {
|
|
@@ -321,13 +316,7 @@ export class MEChat extends LitElement {
|
|
|
321
316
|
this.orgSlug,
|
|
322
317
|
this.currentLeadSource
|
|
323
318
|
);
|
|
324
|
-
if (
|
|
325
|
-
// handle if we already have a valid session to open up to
|
|
326
|
-
isChatKeyValid(
|
|
327
|
-
getChatStorageKey(false, this.buildingSlug),
|
|
328
|
-
this.buildingSlug
|
|
329
|
-
)
|
|
330
|
-
) {
|
|
319
|
+
if (this.myPubnub.isChatKeyValid(this.myPubnub.getChatStorageKey(false))) {
|
|
331
320
|
await this.myPubnub.initializePubnub();
|
|
332
321
|
}
|
|
333
322
|
this.attachOnClickToLauncher();
|
|
@@ -354,22 +343,18 @@ export class MEChat extends LitElement {
|
|
|
354
343
|
};
|
|
355
344
|
|
|
356
345
|
private async handleChatInitializeAnalytics(): Promise<void> {
|
|
357
|
-
// Although we may create the chat id here, we DO NOT create a channel here. We only create a channel when the user
|
|
358
|
-
// actually sends a message. This is to prevent unnecessary channels from being created.
|
|
359
346
|
this.analytics = new Analytics(
|
|
360
347
|
this.orgSlug,
|
|
361
348
|
this.buildingSlug,
|
|
362
|
-
|
|
349
|
+
this.myPubnub?.channel ?? "", // this will be empty if the user does not have a current chat session.
|
|
363
350
|
this.currentLeadSource
|
|
364
351
|
);
|
|
365
|
-
// ping both the load and the heartbeat events for legacy support
|
|
366
|
-
this.analytics.ping("load");
|
|
367
352
|
this.analytics.ping("webchat_heartbeat");
|
|
368
353
|
}
|
|
369
354
|
|
|
370
355
|
public async restartConversation(): Promise<void> {
|
|
371
356
|
this.myPubnub?.handleDisconnect();
|
|
372
|
-
clearChatStorageKey(
|
|
357
|
+
this.myPubnub?.clearChatStorageKey();
|
|
373
358
|
this.myPubnub = null;
|
|
374
359
|
this.displayPubnubChat = false;
|
|
375
360
|
await this.initializeChatVariables();
|
package/src/analytics.ts
CHANGED
package/src/handleChatIds.ts
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import isBefore from "date-fns/isBefore";
|
|
2
|
-
import addHours from "date-fns/addHours";
|
|
3
|
-
import { ChatInfo } from "./MyPubnub";
|
|
4
|
-
|
|
5
|
-
import parseISO from "date-fns/parseISO";
|
|
6
|
-
import { v4 as uuid } from "uuid";
|
|
7
|
-
|
|
8
|
-
import formatISO from "date-fns/formatISO";
|
|
9
|
-
|
|
10
|
-
export const ttlHoursChat = 24;
|
|
11
|
-
|
|
12
|
-
export const isChatKeyValid = (
|
|
13
|
-
storageValueDeconstructed: ChatInfo,
|
|
14
|
-
buildingSlug: string
|
|
15
|
-
): boolean => {
|
|
16
|
-
if (
|
|
17
|
-
storageValueDeconstructed.buildingSlug !== buildingSlug ||
|
|
18
|
-
!storageValueDeconstructed.leadId ||
|
|
19
|
-
!storageValueDeconstructed.timestamp
|
|
20
|
-
) {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const expirationDate = addHours(
|
|
25
|
-
storageValueDeconstructed.timestamp,
|
|
26
|
-
ttlHoursChat
|
|
27
|
-
);
|
|
28
|
-
return !isBefore(expirationDate, Date.now());
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export const getChatStorageKey = (
|
|
32
|
-
createNewIfNotExist = true,
|
|
33
|
-
buildingSlug: string
|
|
34
|
-
): ChatInfo => {
|
|
35
|
-
const eliseaiLocalStorageValue = localStorage.getItem(
|
|
36
|
-
"com.eliseai.webchat.slug=" + buildingSlug
|
|
37
|
-
);
|
|
38
|
-
if (eliseaiLocalStorageValue) {
|
|
39
|
-
try {
|
|
40
|
-
const eliseaiLocalStorageValueParsed = JSON.parse(
|
|
41
|
-
eliseaiLocalStorageValue
|
|
42
|
-
);
|
|
43
|
-
const lsBuildingSlug = eliseaiLocalStorageValueParsed.buildingSlug;
|
|
44
|
-
const lsLeadId = eliseaiLocalStorageValueParsed.leadId;
|
|
45
|
-
const lsExpiration = new Date(eliseaiLocalStorageValueParsed.timestamp);
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
isChatKeyValid(
|
|
49
|
-
{
|
|
50
|
-
leadId: lsLeadId,
|
|
51
|
-
timestamp: lsExpiration,
|
|
52
|
-
buildingSlug: lsBuildingSlug,
|
|
53
|
-
},
|
|
54
|
-
buildingSlug
|
|
55
|
-
)
|
|
56
|
-
)
|
|
57
|
-
return {
|
|
58
|
-
leadId: lsLeadId,
|
|
59
|
-
timestamp: lsExpiration,
|
|
60
|
-
buildingSlug: lsBuildingSlug,
|
|
61
|
-
};
|
|
62
|
-
} catch (_) {
|
|
63
|
-
// eslint-disable-next-line no-console
|
|
64
|
-
console.warn("Error getting chat storage key");
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (createNewIfNotExist) return createChatStorageKey(buildingSlug);
|
|
69
|
-
return {
|
|
70
|
-
leadId: null,
|
|
71
|
-
timestamp: null,
|
|
72
|
-
buildingSlug: null,
|
|
73
|
-
};
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
export const clearChatStorageKey = (buildingSlug: string): void =>
|
|
77
|
-
localStorage.removeItem("com.eliseai.webchat.slug=" + buildingSlug);
|
|
78
|
-
|
|
79
|
-
export const createChatStorageKey = (
|
|
80
|
-
buildingSlug: string,
|
|
81
|
-
existingUserId?: string
|
|
82
|
-
): ChatInfo => {
|
|
83
|
-
const storageTimestamp = formatISO(new Date());
|
|
84
|
-
const leadUserId = existingUserId ?? `lead_${uuid()}_${buildingSlug}`;
|
|
85
|
-
localStorage.setItem(
|
|
86
|
-
"com.eliseai.webchat.slug=" + buildingSlug,
|
|
87
|
-
JSON.stringify({
|
|
88
|
-
buildingSlug: buildingSlug,
|
|
89
|
-
leadId: leadUserId,
|
|
90
|
-
timestamp: storageTimestamp,
|
|
91
|
-
})
|
|
92
|
-
);
|
|
93
|
-
return {
|
|
94
|
-
leadId: leadUserId,
|
|
95
|
-
timestamp: parseISO(storageTimestamp),
|
|
96
|
-
buildingSlug: buildingSlug,
|
|
97
|
-
};
|
|
98
|
-
};
|