@meetelise/chat 1.29.0 → 1.30.1
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/dist/src/MyPubnub.d.ts +116 -0
- package/dist/src/WebComponent/FeeCalculator/components/addon-item/addon-item-styles.d.ts +2 -0
- package/dist/src/WebComponent/FeeCalculator/components/addon-item/addon-item.d.ts +22 -0
- package/dist/src/WebComponent/FeeCalculator/components/addon-item/index.d.ts +1 -0
- package/dist/src/WebComponent/FeeCalculator/components/fee-calculator-layout/fee-calculator-layout-styles.d.ts +2 -0
- package/dist/src/WebComponent/FeeCalculator/components/fee-calculator-layout/fee-calculator-layout.d.ts +34 -0
- package/dist/src/WebComponent/FeeCalculator/components/fee-card/fee-card-styles.d.ts +2 -0
- package/dist/src/WebComponent/FeeCalculator/components/fee-card/fee-card.d.ts +21 -0
- package/dist/src/WebComponent/FeeCalculator/components/fee-item/fee-item-styles.d.ts +2 -0
- package/dist/src/WebComponent/FeeCalculator/components/fee-item/fee-item.d.ts +13 -0
- package/dist/src/WebComponent/FeeCalculator/components/floor-plan-selector/floor-plan-selector-styles.d.ts +2 -0
- package/dist/src/WebComponent/FeeCalculator/components/floor-plan-selector/floor-plan-selector.d.ts +33 -0
- package/dist/src/WebComponent/FeeCalculator/components/floorplan-image-card/floorplan-image-card-styles.d.ts +2 -0
- package/dist/src/WebComponent/FeeCalculator/components/floorplan-image-card/floorplan-image-card.d.ts +18 -0
- package/dist/src/WebComponent/FeeCalculator/components/index.d.ts +5 -0
- package/dist/src/WebComponent/FeeCalculator/components/promo-card/promo-card-styles.d.ts +2 -0
- package/dist/src/WebComponent/FeeCalculator/components/promo-card/promo-card.d.ts +13 -0
- package/dist/src/WebComponent/FeeCalculator/constants.d.ts +2 -0
- package/dist/src/WebComponent/FeeCalculator/fee-calculator-styles.d.ts +1 -0
- package/dist/src/WebComponent/FeeCalculator/fee-calculator.d.ts +59 -0
- package/dist/src/WebComponent/FeeCalculator/index.d.ts +2 -0
- package/dist/src/WebComponent/FeeCalculator/model/building-fee.d.ts +80 -0
- package/dist/src/WebComponent/FeeCalculator/model/transaction-category.d.ts +23 -0
- package/dist/src/WebComponent/LeadSourceClient.d.ts +46 -0
- package/dist/src/WebComponent/OfficeHours.d.ts +21 -0
- package/dist/src/WebComponent/Scheduler/date-picker.d.ts +28 -0
- package/dist/src/WebComponent/Scheduler/time-picker.d.ts +14 -0
- package/dist/src/WebComponent/Scheduler/tour-scheduler.d.ts +100 -0
- package/dist/src/WebComponent/Scheduler/tour-type-option.d.ts +13 -0
- package/dist/src/WebComponent/Scheduler/tourSchedulerStyles.d.ts +1 -0
- package/dist/src/WebComponent/actions/InputStyles.d.ts +1 -0
- package/dist/src/WebComponent/actions/action-confirm-button.d.ts +13 -0
- package/dist/src/WebComponent/actions/call-us-window.d.ts +37 -0
- package/dist/src/WebComponent/actions/collapse-expand-button.d.ts +8 -0
- package/dist/src/WebComponent/actions/details-window.d.ts +14 -0
- package/dist/src/WebComponent/actions/email-us-window.d.ts +46 -0
- package/dist/src/WebComponent/actions/formatPhoneNumber.d.ts +17 -0
- package/dist/src/WebComponent/actions/minimize-expand-button.d.ts +8 -0
- package/dist/src/WebComponent/chat-additional-actions.d.ts +28 -0
- package/dist/src/WebComponent/health-chat.d.ts +47 -0
- package/dist/src/WebComponent/healthchat-styles.d.ts +1 -0
- package/dist/src/WebComponent/icons/ApplyOutlineIcon.d.ts +2 -0
- package/dist/src/WebComponent/icons/BookTourOutlineIcon.d.ts +2 -0
- package/dist/src/WebComponent/icons/CalculatorOutlineIcon.d.ts +2 -0
- package/dist/src/WebComponent/icons/ChatOutlineIcon.d.ts +2 -0
- package/dist/src/WebComponent/icons/ChevronLeftIcon.d.ts +2 -0
- package/dist/src/WebComponent/icons/ChevronRightIcon.d.ts +2 -0
- package/dist/src/WebComponent/icons/ContactResidentIcon.d.ts +2 -0
- package/dist/src/WebComponent/icons/DollarOutlineIcon.d.ts +3 -0
- package/dist/src/WebComponent/icons/EmailOutlineIcon.d.ts +2 -0
- package/dist/src/WebComponent/icons/HeyThereEmojiIcon.d.ts +2 -0
- package/dist/src/WebComponent/icons/PhoneOutlineIcon.d.ts +2 -0
- package/dist/src/WebComponent/icons/SendMessageIcon.d.ts +3 -0
- package/dist/src/WebComponent/icons/TourSelfGuidedIcon.d.ts +2 -0
- package/dist/src/WebComponent/icons/TourVirtuallyIcon.d.ts +2 -0
- package/dist/src/WebComponent/icons/TourWithAgentIcon.d.ts +2 -0
- package/dist/src/WebComponent/icons/XOutlineIcon.d.ts +2 -0
- package/dist/src/WebComponent/index.d.ts +2 -0
- package/dist/src/WebComponent/launcher/Launcher.d.ts +98 -0
- package/dist/src/WebComponent/launcher/launcherStyles.d.ts +1 -0
- package/dist/src/WebComponent/launcher/mobile-launcher.d.ts +27 -0
- package/dist/src/WebComponent/launcher/typeEmojiStyles.d.ts +1 -0
- package/dist/src/WebComponent/launcher/typeMiniStyles.d.ts +1 -0
- package/dist/src/WebComponent/launcher/typeMobileStyles.d.ts +1 -0
- package/dist/src/WebComponent/leasing-chat-styles.d.ts +1 -0
- package/dist/src/WebComponent/me-chat.d.ts +91 -0
- package/dist/src/WebComponent/me-select.d.ts +24 -0
- package/dist/src/WebComponent/mega-loader.d.ts +7 -0
- package/dist/src/WebComponent/mini-loader.d.ts +5 -0
- package/dist/src/WebComponent/pubnub-chat-styles.d.ts +1 -0
- package/dist/src/WebComponent/pubnub-chat.d.ts +49 -0
- package/dist/src/WebComponent/pubnub-media.d.ts +14 -0
- package/dist/src/WebComponent/pubnub-message-styles.d.ts +1 -0
- package/dist/src/WebComponent/pubnub-message.d.ts +39 -0
- package/dist/src/WebComponent/simple-launcher/simple-launcher-styles.d.ts +1 -0
- package/dist/src/WebComponent/simple-launcher/simple-launcher.d.ts +12 -0
- package/dist/src/WebComponent/utilities-chat.d.ts +47 -0
- package/dist/src/WebComponent/utilities-styles.d.ts +1 -0
- package/dist/src/WebComponent/utils.d.ts +31 -0
- package/dist/src/analytics.d.ts +64 -0
- package/dist/src/disclaimers.d.ts +8 -0
- package/dist/src/fetchBuildingABTestType.d.ts +8 -0
- package/dist/src/fetchBuildingInfo.d.ts +57 -0
- package/dist/src/fetchBuildingWebchatView.d.ts +123 -0
- package/dist/src/fetchFeatureFlag.d.ts +14 -0
- package/dist/src/fetchLeadSources.d.ts +4 -0
- package/dist/src/fetchPhoneNumberFromSource.d.ts +6 -0
- package/dist/src/getAvailabilities.d.ts +45 -0
- package/dist/src/getBuildingPhoneNumber.d.ts +1 -0
- package/dist/src/getShouldAllowScheduling.d.ts +1 -0
- package/dist/src/getShouldShowWebchat.d.ts +3 -0
- package/dist/src/getTimezoneString.d.ts +1 -0
- package/dist/src/globals.d.ts +1 -0
- package/dist/src/gtm.d.ts +6 -0
- package/dist/src/handleChatId.d.ts +11 -0
- package/dist/src/insertDNIIntoWebsite.d.ts +5 -0
- package/dist/src/insertLeadSourceIntoSchedulerLinks.d.ts +4 -0
- package/dist/src/main/MEChat.d.ts +74 -0
- package/dist/src/main/utils.d.ts +2 -0
- package/dist/src/postLeadSources.d.ts +3 -0
- package/dist/src/rentgrata.d.ts +4 -0
- package/dist/src/replaceSelectButtonsWithNewLink.d.ts +5 -0
- package/dist/src/services/fees/calculateQuote.d.ts +52 -0
- package/dist/src/services/fees/fetchBuildingFees.d.ts +24 -0
- package/dist/src/services/fees/fetchBuildingFloorplans.d.ts +21 -0
- package/dist/src/services/fees/utils.d.ts +1 -0
- package/dist/src/svgIcons.d.ts +5 -0
- package/dist/src/themes.d.ts +5 -0
- package/dist/src/types/rest-sdk.types.d.ts +11 -0
- package/dist/src/types/webchat-no-show-reason.d.ts +1 -0
- package/dist/src/utils.d.ts +13 -0
- package/package.json +1 -1
- package/public/dist/index.js +384 -179
- package/src/MyPubnub.ts +792 -0
- package/src/WebComponent/FeeCalculator/components/addon-item/addon-item-styles.ts +79 -0
- package/src/WebComponent/FeeCalculator/components/addon-item/addon-item.ts +121 -0
- package/src/WebComponent/FeeCalculator/components/addon-item/index.ts +1 -0
- package/src/WebComponent/FeeCalculator/components/fee-calculator-layout/fee-calculator-layout-styles.ts +127 -0
- package/src/WebComponent/FeeCalculator/components/fee-calculator-layout/fee-calculator-layout.ts +191 -0
- package/src/WebComponent/FeeCalculator/components/fee-card/fee-card-styles.ts +65 -0
- package/src/WebComponent/FeeCalculator/components/fee-card/fee-card.ts +91 -0
- package/src/WebComponent/FeeCalculator/components/fee-item/fee-item-styles.ts +44 -0
- package/src/WebComponent/FeeCalculator/components/fee-item/fee-item.ts +38 -0
- package/src/WebComponent/FeeCalculator/components/floor-plan-selector/floor-plan-selector-styles.ts +144 -0
- package/src/WebComponent/FeeCalculator/components/floor-plan-selector/floor-plan-selector.ts +241 -0
- package/src/WebComponent/FeeCalculator/components/floorplan-image-card/floorplan-image-card-styles.ts +74 -0
- package/src/WebComponent/FeeCalculator/components/floorplan-image-card/floorplan-image-card.ts +72 -0
- package/src/WebComponent/FeeCalculator/components/index.ts +5 -0
- package/src/WebComponent/FeeCalculator/components/promo-card/promo-card-styles.ts +39 -0
- package/src/WebComponent/FeeCalculator/components/promo-card/promo-card.ts +39 -0
- package/src/WebComponent/FeeCalculator/constants.ts +3 -0
- package/src/WebComponent/FeeCalculator/fee-calculator-styles.ts +334 -0
- package/src/WebComponent/FeeCalculator/fee-calculator.ts +369 -0
- package/src/WebComponent/FeeCalculator/index.ts +4 -0
- package/src/WebComponent/FeeCalculator/model/building-fee.ts +120 -0
- package/src/WebComponent/FeeCalculator/model/transaction-category.ts +23 -0
- package/src/WebComponent/LeadSourceClient.ts +332 -0
- package/src/WebComponent/MEChat.css +5 -0
- package/src/WebComponent/OfficeHours.ts +73 -0
- package/src/WebComponent/Scheduler/date-picker.ts +405 -0
- package/src/WebComponent/Scheduler/time-picker.ts +190 -0
- package/src/WebComponent/Scheduler/tour-scheduler.ts +1373 -0
- package/src/WebComponent/Scheduler/tour-type-option.ts +112 -0
- package/src/WebComponent/Scheduler/tourSchedulerStyles.ts +418 -0
- package/src/WebComponent/actions/InputStyles.ts +57 -0
- package/src/WebComponent/actions/action-confirm-button.ts +125 -0
- package/src/WebComponent/actions/call-us-window.ts +445 -0
- package/src/WebComponent/actions/collapse-expand-button.ts +65 -0
- package/src/WebComponent/actions/details-window.ts +150 -0
- package/src/WebComponent/actions/email-us-window.ts +555 -0
- package/src/WebComponent/actions/formatPhoneNumber.ts +72 -0
- package/src/WebComponent/actions/minimize-expand-button.ts +93 -0
- package/src/WebComponent/chat-additional-actions.ts +135 -0
- package/src/WebComponent/health-chat.ts +270 -0
- package/src/WebComponent/healthchat-styles.ts +119 -0
- package/src/WebComponent/icons/ApplyOutlineIcon.ts +22 -0
- package/src/WebComponent/icons/BookTourOutlineIcon.ts +13 -0
- package/src/WebComponent/icons/CalculatorOutlineIcon.ts +22 -0
- package/src/WebComponent/icons/ChatOutlineIcon.ts +10 -0
- package/src/WebComponent/icons/ChevronLeftIcon.ts +7 -0
- package/src/WebComponent/icons/ChevronRightIcon.ts +7 -0
- package/src/WebComponent/icons/ContactResidentIcon.ts +9 -0
- package/src/WebComponent/icons/DollarOutlineIcon.ts +18 -0
- package/src/WebComponent/icons/EmailOutlineIcon.ts +7 -0
- package/src/WebComponent/icons/HeyThereEmojiIcon.ts +12 -0
- package/src/WebComponent/icons/PhoneOutlineIcon.ts +7 -0
- package/src/WebComponent/icons/SendMessageIcon.ts +17 -0
- package/src/WebComponent/icons/TourSelfGuidedIcon.ts +17 -0
- package/src/WebComponent/icons/TourVirtuallyIcon.ts +17 -0
- package/src/WebComponent/icons/TourWithAgentIcon.ts +17 -0
- package/src/WebComponent/icons/XOutlineIcon.ts +8 -0
- package/src/WebComponent/index.ts +2 -0
- package/src/WebComponent/launcher/Launcher.ts +1282 -0
- package/src/WebComponent/launcher/launcherStyles.ts +500 -0
- package/src/WebComponent/launcher/mobile-launcher.ts +162 -0
- package/src/WebComponent/launcher/typeEmojiStyles.ts +161 -0
- package/src/WebComponent/launcher/typeMiniStyles.ts +60 -0
- package/src/WebComponent/launcher/typeMobileStyles.ts +50 -0
- package/src/WebComponent/leasing-chat-styles.ts +114 -0
- package/src/WebComponent/me-chat.ts +1262 -0
- package/src/WebComponent/me-select.ts +322 -0
- package/src/WebComponent/mega-loader.ts +36 -0
- package/src/WebComponent/mini-loader.ts +28 -0
- package/src/WebComponent/pubnub-chat-styles.ts +204 -0
- package/src/WebComponent/pubnub-chat.ts +928 -0
- package/src/WebComponent/pubnub-media.ts +208 -0
- package/src/WebComponent/pubnub-message-styles.ts +54 -0
- package/src/WebComponent/pubnub-message.ts +431 -0
- package/src/WebComponent/simple-launcher/simple-launcher-styles.ts +34 -0
- package/src/WebComponent/simple-launcher/simple-launcher.ts +100 -0
- package/src/WebComponent/utilities-chat.ts +270 -0
- package/src/WebComponent/utilities-styles.ts +110 -0
- package/src/WebComponent/utils.ts +82 -0
- package/src/analytics.ts +217 -0
- package/src/assetUrls.ts +6 -0
- package/src/disclaimers.ts +58 -0
- package/src/fetchBuildingABTestType.ts +21 -0
- package/src/fetchBuildingInfo.ts +87 -0
- package/src/fetchBuildingWebchatView.ts +156 -0
- package/src/fetchFeatureFlag.ts +250 -0
- package/src/fetchLeadSources.ts +98 -0
- package/src/fetchPhoneNumberFromSource.ts +31 -0
- package/src/fetchWebchatPreferences.ts +54 -0
- package/src/getAvailabilities.ts +179 -0
- package/src/getBuildingPhoneNumber.ts +26 -0
- package/src/getShouldAllowScheduling.ts +16 -0
- package/src/getShouldShowWebchat.ts +114 -0
- package/src/getTimezoneString.ts +39 -0
- package/src/globals.ts +1 -0
- package/src/gtm.ts +17 -0
- package/src/handleChatId.ts +101 -0
- package/src/insertDNIIntoWebsite.ts +146 -0
- package/src/insertLeadSourceIntoSchedulerLinks.ts +71 -0
- package/src/main/MEChat.test.ts +110 -0
- package/src/main/MEChat.ts +404 -0
- package/src/main/utils.ts +70 -0
- package/src/postLeadSources.ts +44 -0
- package/src/rentgrata.ts +74 -0
- package/src/replaceSelectButtonsWithNewLink.ts +69 -0
- package/src/services/fees/calculateQuote.ts +135 -0
- package/src/services/fees/fetchBuildingFees.ts +63 -0
- package/src/services/fees/fetchBuildingFloorplans.ts +74 -0
- package/src/services/fees/utils.ts +4 -0
- package/src/svgIcons.ts +14 -0
- package/src/themes.ts +65 -0
- package/src/types/rest-sdk.types.ts +13 -0
- package/src/types/webchat-no-show-reason.ts +6 -0
- package/src/utils.ts +121 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { logSignal } from "./analytics";
|
|
2
|
+
import { BuildingWebchatView } from "./fetchBuildingWebchatView";
|
|
3
|
+
import { fetchFeatureFlagMaintenanceMode } from "./fetchFeatureFlag";
|
|
4
|
+
import { WebchatNoShowReason } from "./types/webchat-no-show-reason";
|
|
5
|
+
|
|
6
|
+
const getShouldShowWebchat = async (
|
|
7
|
+
buildingWebchatView: BuildingWebchatView | null,
|
|
8
|
+
buildingSlug: string,
|
|
9
|
+
orgSlug: string
|
|
10
|
+
): Promise<boolean> => {
|
|
11
|
+
const sampleRateForWebchat = 0.01;
|
|
12
|
+
|
|
13
|
+
if (!buildingWebchatView) {
|
|
14
|
+
// eslint-disable-next-line no-console
|
|
15
|
+
console.warn("MeetElise Chat will NOT appear. Building does NOT exist.");
|
|
16
|
+
log(
|
|
17
|
+
orgSlug,
|
|
18
|
+
buildingSlug,
|
|
19
|
+
true,
|
|
20
|
+
false,
|
|
21
|
+
"building_does_not_exist",
|
|
22
|
+
sampleRateForWebchat
|
|
23
|
+
);
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (!buildingWebchatView.launchedDateTime) {
|
|
27
|
+
// eslint-disable-next-line no-console
|
|
28
|
+
console.warn(
|
|
29
|
+
"MeetElise Chat will NOT appear. Building is NOT launched (based on launched date time)."
|
|
30
|
+
);
|
|
31
|
+
log(
|
|
32
|
+
orgSlug,
|
|
33
|
+
buildingSlug,
|
|
34
|
+
true,
|
|
35
|
+
false,
|
|
36
|
+
"building_not_launched",
|
|
37
|
+
sampleRateForWebchat
|
|
38
|
+
);
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!buildingWebchatView.active) {
|
|
43
|
+
// eslint-disable-next-line no-console
|
|
44
|
+
console.warn("MeetElise Chat will NOT appear. Building is NOT active.");
|
|
45
|
+
log(
|
|
46
|
+
orgSlug,
|
|
47
|
+
buildingSlug,
|
|
48
|
+
true,
|
|
49
|
+
false,
|
|
50
|
+
"building_not_active",
|
|
51
|
+
sampleRateForWebchat
|
|
52
|
+
);
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
if (!buildingWebchatView.webchatScopeConfigured) {
|
|
56
|
+
// eslint-disable-next-line no-console
|
|
57
|
+
console.warn(
|
|
58
|
+
"MeetElise Chat will NOT appear. Building is NOT webchat scope configured."
|
|
59
|
+
);
|
|
60
|
+
log(
|
|
61
|
+
orgSlug,
|
|
62
|
+
buildingSlug,
|
|
63
|
+
true,
|
|
64
|
+
false,
|
|
65
|
+
"building_not_webchat_scope_configured",
|
|
66
|
+
sampleRateForWebchat
|
|
67
|
+
);
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const leasingMaintenanceMode = await fetchFeatureFlagMaintenanceMode(
|
|
72
|
+
buildingSlug
|
|
73
|
+
);
|
|
74
|
+
// https://app.launchdarkly.com/projects/default/flags/webchat-leasing-in-maintenance-mode/targeting?env=production&selected-env=production
|
|
75
|
+
if (leasingMaintenanceMode) {
|
|
76
|
+
// eslint-disable-next-line no-console
|
|
77
|
+
console.warn(
|
|
78
|
+
"MeetElise Chat will NOT appear. Chat is in maintenance mode."
|
|
79
|
+
);
|
|
80
|
+
log(
|
|
81
|
+
orgSlug,
|
|
82
|
+
buildingSlug,
|
|
83
|
+
true,
|
|
84
|
+
false,
|
|
85
|
+
"building_in_maintenance_mode",
|
|
86
|
+
sampleRateForWebchat
|
|
87
|
+
);
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
return true;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const log = (
|
|
94
|
+
orgSlug: string,
|
|
95
|
+
buildingSlug: string,
|
|
96
|
+
isPixelOnSite = true,
|
|
97
|
+
isAnyWebchatShowing = false,
|
|
98
|
+
webchatNoShowReason: WebchatNoShowReason,
|
|
99
|
+
sampleRate = 0.01
|
|
100
|
+
) =>
|
|
101
|
+
logSignal({
|
|
102
|
+
params: {
|
|
103
|
+
org_slug: orgSlug,
|
|
104
|
+
building_slug: buildingSlug,
|
|
105
|
+
is_pixel_on_site: isPixelOnSite,
|
|
106
|
+
is_any_webchat_showing: isAnyWebchatShowing,
|
|
107
|
+
extra_data: {
|
|
108
|
+
webchat_no_show_reason: webchatNoShowReason,
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
sampleRate,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
export default getShouldShowWebchat;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const isDaylightSavingTime = (date: Date): boolean => {
|
|
2
|
+
const jan = new Date(date.getFullYear(), 0, 1);
|
|
3
|
+
const jul = new Date(date.getFullYear(), 6, 1);
|
|
4
|
+
const stdTimezoneOffset = Math.max(
|
|
5
|
+
jan.getTimezoneOffset(),
|
|
6
|
+
jul.getTimezoneOffset()
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
return date.getTimezoneOffset() < stdTimezoneOffset;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const getTimezoneAbbreviation = (): string => {
|
|
13
|
+
const date = new Date();
|
|
14
|
+
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
15
|
+
const isDST = isDaylightSavingTime(date);
|
|
16
|
+
|
|
17
|
+
switch (timeZone) {
|
|
18
|
+
case "America/New_York":
|
|
19
|
+
return isDST ? "EDT" : "EST";
|
|
20
|
+
case "America/Chicago":
|
|
21
|
+
return isDST ? "CDT" : "CST";
|
|
22
|
+
case "America/Denver":
|
|
23
|
+
return isDST ? "MDT" : "MST";
|
|
24
|
+
case "America/Los_Angeles":
|
|
25
|
+
return isDST ? "PDT" : "PST";
|
|
26
|
+
case "America/Anchorage":
|
|
27
|
+
return isDST ? "AKDT" : "AKST";
|
|
28
|
+
case "America/Honolulu":
|
|
29
|
+
return "HST";
|
|
30
|
+
case "America/Phoenix":
|
|
31
|
+
return "MST";
|
|
32
|
+
case "America/Indiana/Indianapolis":
|
|
33
|
+
return isDST ? "EDT" : "EST";
|
|
34
|
+
case "America/Indiana/Knox":
|
|
35
|
+
return isDST ? "CDT" : "CST";
|
|
36
|
+
default:
|
|
37
|
+
return "Unknown";
|
|
38
|
+
}
|
|
39
|
+
};
|
package/src/globals.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const BASE_DOMAIN = "https://app.meetelise.com";
|
package/src/gtm.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
interface Window {
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4
|
+
dataLayer: any[];
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const pushGtmEvent = (
|
|
9
|
+
eventName: string,
|
|
10
|
+
event: Record<string, unknown> = {}
|
|
11
|
+
): void => {
|
|
12
|
+
window.dataLayer = window.dataLayer || [];
|
|
13
|
+
window.dataLayer.push({
|
|
14
|
+
event: eventName,
|
|
15
|
+
...event,
|
|
16
|
+
});
|
|
17
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import parseISO from "date-fns/parseISO";
|
|
2
|
+
import { v4 as uuid } from "uuid";
|
|
3
|
+
import formatISO from "date-fns/formatISO";
|
|
4
|
+
import { isTimestampExpired } from "./utils";
|
|
5
|
+
import isBefore from "date-fns/isBefore";
|
|
6
|
+
|
|
7
|
+
export interface ChatStorageKey {
|
|
8
|
+
leadId: string | null;
|
|
9
|
+
timestamp: Date | null;
|
|
10
|
+
buildingSlug: string | null;
|
|
11
|
+
initiatedChat: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const ttlHours = 24;
|
|
15
|
+
|
|
16
|
+
export const clearChatStorageKey = (buildingSlug: string): void =>
|
|
17
|
+
localStorage.removeItem("com.elise.webchat.slug=" + buildingSlug);
|
|
18
|
+
|
|
19
|
+
export const createChatStorageKey = (
|
|
20
|
+
buildingSlug: string,
|
|
21
|
+
withChatinitialized: boolean,
|
|
22
|
+
leadId?: string
|
|
23
|
+
): ChatStorageKey => {
|
|
24
|
+
const storageTimestamp = formatISO(new Date());
|
|
25
|
+
|
|
26
|
+
// must preface with lead for BE to extract lead id
|
|
27
|
+
const leadUserId = leadId ? leadId : `lead_${uuid()}`;
|
|
28
|
+
localStorage.setItem(
|
|
29
|
+
"com.elise.webchat.slug=" + buildingSlug,
|
|
30
|
+
JSON.stringify({
|
|
31
|
+
buildingSlug: buildingSlug,
|
|
32
|
+
leadId: leadUserId,
|
|
33
|
+
timestamp: storageTimestamp,
|
|
34
|
+
initiatedChat: withChatinitialized,
|
|
35
|
+
})
|
|
36
|
+
);
|
|
37
|
+
return {
|
|
38
|
+
leadId: leadUserId,
|
|
39
|
+
timestamp: parseISO(storageTimestamp),
|
|
40
|
+
buildingSlug: buildingSlug,
|
|
41
|
+
initiatedChat: withChatinitialized,
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const getChatStorageKey = (buildingSlug: string): ChatStorageKey => {
|
|
46
|
+
const eliseaiLocalStorageValue = localStorage.getItem(
|
|
47
|
+
"com.elise.webchat.slug=" + buildingSlug
|
|
48
|
+
);
|
|
49
|
+
if (eliseaiLocalStorageValue) {
|
|
50
|
+
try {
|
|
51
|
+
const eliseaiLocalStorageValueParsed = JSON.parse(
|
|
52
|
+
eliseaiLocalStorageValue
|
|
53
|
+
);
|
|
54
|
+
const newStorageKey = {
|
|
55
|
+
leadId: eliseaiLocalStorageValueParsed.leadId,
|
|
56
|
+
timestamp: new Date(eliseaiLocalStorageValueParsed.timestamp),
|
|
57
|
+
buildingSlug: eliseaiLocalStorageValueParsed.buildingSlug,
|
|
58
|
+
initiatedChat: eliseaiLocalStorageValueParsed.initiatedChat,
|
|
59
|
+
};
|
|
60
|
+
if (isChatKeyValid(buildingSlug, newStorageKey)) {
|
|
61
|
+
return newStorageKey;
|
|
62
|
+
}
|
|
63
|
+
} catch (_) {
|
|
64
|
+
// eslint-disable-next-line no-console
|
|
65
|
+
console.warn("Error getting chat storage key");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
leadId: null,
|
|
71
|
+
timestamp: null,
|
|
72
|
+
buildingSlug: null,
|
|
73
|
+
initiatedChat: false,
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
export const isChatKeyValid = (
|
|
77
|
+
buildingSlug: string,
|
|
78
|
+
storageValue: ChatStorageKey
|
|
79
|
+
): boolean => {
|
|
80
|
+
// If we want to do force invalidation
|
|
81
|
+
try {
|
|
82
|
+
if (storageValue.timestamp && storageValue.initiatedChat) {
|
|
83
|
+
const topBoundaryTimestamp = new Date("2024-06-06T21:30:00Z");
|
|
84
|
+
if (isBefore(storageValue.timestamp, topBoundaryTimestamp)) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
} catch (_) {
|
|
89
|
+
// eslint-disable-next-line no-console
|
|
90
|
+
console.warn("Error checking chat storage key validity");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (
|
|
94
|
+
storageValue.buildingSlug !== buildingSlug ||
|
|
95
|
+
!storageValue.leadId ||
|
|
96
|
+
!storageValue.timestamp
|
|
97
|
+
) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
return !isTimestampExpired(storageValue.timestamp, ttlHours);
|
|
101
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* General script to find all phone numbers on a page and replace them with a
|
|
3
|
+
* DNI phone number.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { LogType, sendLoggingEvent } from "./analytics";
|
|
7
|
+
|
|
8
|
+
export const insertDNIIntoWebsite = (
|
|
9
|
+
cleanPhoneNumber: string, // format of 1234567890
|
|
10
|
+
orgSlug: string,
|
|
11
|
+
buildingSlug?: string
|
|
12
|
+
): number => {
|
|
13
|
+
const newHrefNumber = `tel:${cleanPhoneNumber}`;
|
|
14
|
+
const newHrefSmsNumber = `sms:${cleanPhoneNumber}`;
|
|
15
|
+
const newWhatsAppNumber = `whatsapp://send?phone=${cleanPhoneNumber}}`;
|
|
16
|
+
const newFacetimeNumber = `facetime:${cleanPhoneNumber}`;
|
|
17
|
+
const newFacetimeAudioNumber = `facetime-audio:${cleanPhoneNumber}`;
|
|
18
|
+
|
|
19
|
+
const phoneNumberPattern = /(?:\(\d{3}\)\s|\d{3}-)\d{3}-\d{4}/g;
|
|
20
|
+
const hrefNumberPattern = /(tel|sms):(?:\(\d{3}\)\s|\d{3}-)\d{3}-\d{4}/g;
|
|
21
|
+
|
|
22
|
+
const whatsappNumberPattern = /whatsapp:\/\/send\?phone=\d+/g;
|
|
23
|
+
const facetimeNumberPattern = /(facetime|facetime-audio):\d+/g;
|
|
24
|
+
|
|
25
|
+
const allHTMLItems = document.body.getElementsByTagName("*");
|
|
26
|
+
|
|
27
|
+
let foundPhoneNumber = false;
|
|
28
|
+
|
|
29
|
+
// from a phone format of 1234567890 to (123) 456-7890
|
|
30
|
+
const newPhoneNumber = cleanPhoneNumber.replace(
|
|
31
|
+
/(\d{3})(\d{3})(\d{4})/,
|
|
32
|
+
"($1) $2-$3"
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
let shouldLogTextInsertionError = false;
|
|
36
|
+
let shouldLogHrefInsertionError = false;
|
|
37
|
+
|
|
38
|
+
let totalReplacements = 0;
|
|
39
|
+
|
|
40
|
+
for (let i = 0; i < allHTMLItems.length; i++) {
|
|
41
|
+
if (allHTMLItems[i].nodeType !== Node.ELEMENT_NODE) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Handles the actual display phone number
|
|
46
|
+
if (
|
|
47
|
+
allHTMLItems[i].childNodes.length === 1 &&
|
|
48
|
+
allHTMLItems[i].childNodes[0].nodeType == Node.TEXT_NODE
|
|
49
|
+
) {
|
|
50
|
+
const oldInnerHTML = allHTMLItems[i].innerHTML;
|
|
51
|
+
const newInnerHTML = oldInnerHTML.replace(
|
|
52
|
+
phoneNumberPattern,
|
|
53
|
+
newPhoneNumber
|
|
54
|
+
);
|
|
55
|
+
if (oldInnerHTML !== newInnerHTML) {
|
|
56
|
+
totalReplacements++;
|
|
57
|
+
}
|
|
58
|
+
allHTMLItems[i].innerHTML = newInnerHTML;
|
|
59
|
+
|
|
60
|
+
foundPhoneNumber = true;
|
|
61
|
+
|
|
62
|
+
if (!shouldLogTextInsertionError) {
|
|
63
|
+
shouldLogTextInsertionError = true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Handles the href phone number (so when you click the link, it will call the phone number)
|
|
68
|
+
if (allHTMLItems[i].hasAttribute("href")) {
|
|
69
|
+
const href = allHTMLItems[i].getAttribute("href");
|
|
70
|
+
if (!href) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let newHrefValue = "";
|
|
75
|
+
let currentPattern = hrefNumberPattern;
|
|
76
|
+
|
|
77
|
+
if (href.startsWith("sms:")) {
|
|
78
|
+
newHrefValue = newHrefSmsNumber;
|
|
79
|
+
} else if (href.startsWith("whatsapp:")) {
|
|
80
|
+
newHrefValue = newWhatsAppNumber;
|
|
81
|
+
currentPattern = whatsappNumberPattern;
|
|
82
|
+
} else if (href.startsWith("facetime:")) {
|
|
83
|
+
newHrefValue = newFacetimeNumber;
|
|
84
|
+
currentPattern = facetimeNumberPattern;
|
|
85
|
+
} else if (href.startsWith("facetime-audio:")) {
|
|
86
|
+
newHrefValue = newFacetimeAudioNumber;
|
|
87
|
+
currentPattern = facetimeNumberPattern;
|
|
88
|
+
} else {
|
|
89
|
+
newHrefValue = newHrefNumber;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const newHref = href.replace(currentPattern, newHrefValue);
|
|
93
|
+
allHTMLItems[i].setAttribute("href", newHref);
|
|
94
|
+
|
|
95
|
+
if (!shouldLogHrefInsertionError) {
|
|
96
|
+
shouldLogHrefInsertionError = true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// We could make these logs more specific but for now we'll just log the
|
|
102
|
+
// if there is even a single error to limit the error logs we throw
|
|
103
|
+
|
|
104
|
+
if (shouldLogTextInsertionError) {
|
|
105
|
+
sendLoggingEvent({
|
|
106
|
+
logTitle: "DNI_WEBSITE_INSERTION",
|
|
107
|
+
logData: {
|
|
108
|
+
type: "text-insertion",
|
|
109
|
+
newPhoneNumber,
|
|
110
|
+
website: window.location.href,
|
|
111
|
+
},
|
|
112
|
+
logType: LogType.info,
|
|
113
|
+
buildingSlug,
|
|
114
|
+
orgSlug,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (shouldLogHrefInsertionError) {
|
|
119
|
+
sendLoggingEvent({
|
|
120
|
+
logTitle: "DNI_WEBSITE_INSERTION",
|
|
121
|
+
logData: {
|
|
122
|
+
type: "href-insertion",
|
|
123
|
+
newPhoneNumber,
|
|
124
|
+
website: window.location.href,
|
|
125
|
+
},
|
|
126
|
+
logType: LogType.info,
|
|
127
|
+
buildingSlug,
|
|
128
|
+
orgSlug,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!foundPhoneNumber) {
|
|
133
|
+
sendLoggingEvent({
|
|
134
|
+
logTitle: "DNI_WEBSITE_INSERTION",
|
|
135
|
+
logData: {
|
|
136
|
+
type: "no-phone-number-found",
|
|
137
|
+
website: window.location.href,
|
|
138
|
+
},
|
|
139
|
+
logType: LogType.warn,
|
|
140
|
+
buildingSlug,
|
|
141
|
+
orgSlug,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return totalReplacements;
|
|
146
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// DEPRECATED: This function is no longer used. It was used to insert the lead source into the scheduler links.
|
|
2
|
+
// We now prevent the redirect and force tour SST widget to open instead in replaceSSTLinkWithTourWidgetTrigger.ts.
|
|
3
|
+
export const insertLeadSourceIntoSchedulerLinks = (
|
|
4
|
+
leadSource: string
|
|
5
|
+
): void => {
|
|
6
|
+
const links = document.querySelectorAll("a");
|
|
7
|
+
links.forEach(function (link) {
|
|
8
|
+
const href = link.getAttribute("href");
|
|
9
|
+
if (href && href.startsWith("https://app.meetelise.com/")) {
|
|
10
|
+
const separator = href.includes("?") ? "&" : "?";
|
|
11
|
+
link.setAttribute(
|
|
12
|
+
"href",
|
|
13
|
+
href +
|
|
14
|
+
separator +
|
|
15
|
+
`eliseParsedLeadSource=${encodeURIComponent(leadSource)}`
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const replaceSSTLinkWithTourWidgetTrigger = (
|
|
22
|
+
handleTourClicked: (e: MouseEvent) => void
|
|
23
|
+
): void => {
|
|
24
|
+
const sstRedirectElements = document.querySelectorAll("a");
|
|
25
|
+
sstRedirectElements.forEach(function (element) {
|
|
26
|
+
const href = element.getAttribute("href");
|
|
27
|
+
if (href && href.startsWith("https://app.meetelise.com/")) {
|
|
28
|
+
element.addEventListener("click", (event) => {
|
|
29
|
+
event.preventDefault(); // Prevents the default href redirect
|
|
30
|
+
handleTourClicked(event as MouseEvent);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const replacePageRedirectElementWithTourWidgetTrigger = (
|
|
37
|
+
handleTourClicked: (e: MouseEvent) => void
|
|
38
|
+
): void => {
|
|
39
|
+
// Gets any elements in format like <a href="/apartments/oh/westerville/schedule-a-tour" class=" ">Schedule a Tour</a>
|
|
40
|
+
const scheduleLinks = document.querySelectorAll('a[href$="schedule-a-tour"]');
|
|
41
|
+
scheduleLinks.forEach((linkElement) => {
|
|
42
|
+
if (!linkElement) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
linkElement.addEventListener("click", (event) => {
|
|
46
|
+
event.preventDefault(); // Prevents the default href redirect
|
|
47
|
+
handleTourClicked(event as MouseEvent);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const replaceTourTriggerElementWithTourWidgetTrigger = (
|
|
53
|
+
handleTourClicked: (e: MouseEvent) => void
|
|
54
|
+
): void => {
|
|
55
|
+
const scheduleLinks = document.querySelectorAll(
|
|
56
|
+
'a[href^="#leaseleads-tour-open"]'
|
|
57
|
+
);
|
|
58
|
+
scheduleLinks.forEach((linkElement) => {
|
|
59
|
+
if (!linkElement) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const newLink = document.createElement("a");
|
|
63
|
+
newLink.innerHTML = linkElement.innerHTML;
|
|
64
|
+
newLink.style.cursor = "pointer";
|
|
65
|
+
newLink.addEventListener("click", (event) => {
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
handleTourClicked(event as MouseEvent);
|
|
68
|
+
});
|
|
69
|
+
linkElement.parentNode?.replaceChild(newLink, linkElement);
|
|
70
|
+
});
|
|
71
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { expect } from "@esm-bundle/chai";
|
|
2
|
+
import { stub, restore } from "sinon/pkg/sinon-esm";
|
|
3
|
+
import MEChat from "../../public/dist/index";
|
|
4
|
+
|
|
5
|
+
const stubResponse = {
|
|
6
|
+
json: stub().resolves({
|
|
7
|
+
id: 42,
|
|
8
|
+
userId: 42.1,
|
|
9
|
+
orgId: 42.2,
|
|
10
|
+
name: "Unit Test Building",
|
|
11
|
+
themeId: null,
|
|
12
|
+
avatarSrc: null,
|
|
13
|
+
avatarType: "",
|
|
14
|
+
userFirstName: "Ella",
|
|
15
|
+
userLastName: "",
|
|
16
|
+
logoSrc:
|
|
17
|
+
"https://eliseusercontent.meetelise.com/building/3660/test-logo.png",
|
|
18
|
+
chatTitle: "Elise",
|
|
19
|
+
chatSubtitle: "Leasing Agent ExtraordinAIre",
|
|
20
|
+
welcomeMessage:
|
|
21
|
+
"Welcome, I'm Elise! If you have any questions about Unit Test Building or would like to schedule a tour, I'm happy to help.",
|
|
22
|
+
systemWelcomeMessage: "I am an AI and may record this conversation!",
|
|
23
|
+
conversationMaintenanceMode: false,
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const waitForElementWithSelectorToExist = async (
|
|
28
|
+
selector: string,
|
|
29
|
+
target: Node = document.body
|
|
30
|
+
): Promise<void> => {
|
|
31
|
+
return new Promise<void>((resolve) => {
|
|
32
|
+
// in case the element already exists
|
|
33
|
+
if (document.querySelector(selector)) {
|
|
34
|
+
resolve();
|
|
35
|
+
}
|
|
36
|
+
const observer = new MutationObserver(() => {
|
|
37
|
+
if (document.querySelector(selector)) {
|
|
38
|
+
resolve();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
observer.observe(target, { subtree: true, childList: true });
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// const wait = (delay, ...args) =>
|
|
46
|
+
// new Promise((resolve) => setTimeout(resolve, delay, ...args));
|
|
47
|
+
|
|
48
|
+
afterEach(() => {
|
|
49
|
+
restore();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it.skip("shows the launcher", async function () {
|
|
53
|
+
// Given an API that returns this building theme
|
|
54
|
+
stub(window, "fetch").resolves(stubResponse);
|
|
55
|
+
|
|
56
|
+
// When I start MEChat
|
|
57
|
+
MEChat.start({
|
|
58
|
+
organization: "unit-test-org",
|
|
59
|
+
building: "unit-test-building",
|
|
60
|
+
});
|
|
61
|
+
await waitForElementWithSelectorToExist(".meetelise-chat.launcher > div");
|
|
62
|
+
|
|
63
|
+
// Then I should see a launcher
|
|
64
|
+
const launcher = document.querySelector<HTMLDivElement>(
|
|
65
|
+
".meetelise-chat.launcher"
|
|
66
|
+
).firstChild as HTMLDivElement;
|
|
67
|
+
|
|
68
|
+
// And the popup should be hidden
|
|
69
|
+
const popup = document.querySelector<HTMLSpanElement>(".__talkjs_popup");
|
|
70
|
+
expect(popup).to.be.an.instanceof(HTMLSpanElement);
|
|
71
|
+
const popupStyle = window.getComputedStyle(popup);
|
|
72
|
+
const popupDisplay = popupStyle.getPropertyValue("display");
|
|
73
|
+
expect(popupDisplay).to.equal("none");
|
|
74
|
+
|
|
75
|
+
launcher.click();
|
|
76
|
+
// Without waiting, the popup doesn't open in time for the next assertion
|
|
77
|
+
//await wait.skip(0);
|
|
78
|
+
|
|
79
|
+
// Then the popup should be visible
|
|
80
|
+
const popupStyle2 = window.getComputedStyle(popup);
|
|
81
|
+
const popupDisplay2 = popupStyle2.getPropertyValue("display");
|
|
82
|
+
expect(popupDisplay2).not.to.equal("none");
|
|
83
|
+
|
|
84
|
+
// Ideally, expect welcome message, but we can't select inside the iframe
|
|
85
|
+
// Ideally, expect theme colors, but we can't select inside the iframe
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it.skip("works via the programmatic interface", async function () {
|
|
89
|
+
// Given an API that returns this building theme
|
|
90
|
+
stub(window, "fetch").resolves(stubResponse);
|
|
91
|
+
|
|
92
|
+
// When I start MEChat
|
|
93
|
+
const chat = MEChat.start({
|
|
94
|
+
organization: "unit-test-org",
|
|
95
|
+
building: "unit-test-building",
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
await waitForElementWithSelectorToExist(".meetelise-chat.launcher > div");
|
|
99
|
+
|
|
100
|
+
// Ideally, verify behavior, but this will at least verify nothing throws
|
|
101
|
+
chat.show();
|
|
102
|
+
chat.hide();
|
|
103
|
+
chat.setTheme({
|
|
104
|
+
avatarInitials: "G",
|
|
105
|
+
});
|
|
106
|
+
chat.open();
|
|
107
|
+
chat.close();
|
|
108
|
+
chat.restartConversation();
|
|
109
|
+
chat.remove();
|
|
110
|
+
});
|