@meetelise/chat 1.20.113 → 1.20.115
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/public/demo/index.html +4 -0
- package/public/dist/index.js +376 -389
- package/public/dist/index.js.LICENSE.txt +6 -0
- package/src/MEChat.ts +2 -23
- package/src/MyPubnub.ts +4 -1
- package/src/WebComponent/actions/call-us-window.ts +282 -44
- package/src/WebComponent/launcher/Launcher.ts +69 -113
- package/src/WebComponent/me-chat.ts +76 -321
- package/src/WebComponent/pubnub-chat.ts +4 -5
- package/src/fetchWebchatPreferences.ts +1 -0
- package/src/themes.ts +45 -155
- package/src/utils.ts +0 -53
- package/src/WebComponent/actions/text-us-window.ts +0 -286
- package/src/chatID.ts +0 -64
- package/src/createConversation.ts +0 -57
package/src/themes.ts
CHANGED
|
@@ -1,164 +1,54 @@
|
|
|
1
|
-
|
|
1
|
+
export const defaultBrandColor = "#636889"; // we have a brand color defined in the BE too, but this is a fallback
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
3
|
+
export const tintColor = (
|
|
4
|
+
originalColor: string,
|
|
5
|
+
tintOpacity: number
|
|
6
|
+
): string => {
|
|
7
|
+
const originalRgb = hexToRgb(originalColor);
|
|
8
|
+
const tintRgb = { r: 0, g: 0, b: 0 };
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Dark = "Dark",
|
|
14
|
-
Purple = "Purple",
|
|
15
|
-
Blue = "Blue",
|
|
16
|
-
Teal = "Teal",
|
|
17
|
-
Green = "Green",
|
|
18
|
-
Yellow = "Yellow",
|
|
19
|
-
Orange = "Orange",
|
|
20
|
-
Pink = "Pink",
|
|
21
|
-
Glass = "Glass",
|
|
22
|
-
}
|
|
23
|
-
export type ThemeIdString = `${ThemeId}`;
|
|
24
|
-
|
|
25
|
-
export interface Theme {
|
|
26
|
-
chatHeader: {
|
|
27
|
-
backgroundColor: string;
|
|
28
|
-
textColor: string;
|
|
10
|
+
const newRgb = {
|
|
11
|
+
r: originalRgb.r + Math.round((tintRgb.r - originalRgb.r) * tintOpacity),
|
|
12
|
+
g: originalRgb.g + Math.round((tintRgb.g - originalRgb.g) * tintOpacity),
|
|
13
|
+
b: originalRgb.b + Math.round((tintRgb.b - originalRgb.b) * tintOpacity),
|
|
29
14
|
};
|
|
30
|
-
chatPaneBackgroundColor: string;
|
|
31
|
-
message: {
|
|
32
|
-
user: {
|
|
33
|
-
textColor: string;
|
|
34
|
-
backgroundColor: string;
|
|
35
|
-
};
|
|
36
|
-
agent: {
|
|
37
|
-
textColor: string;
|
|
38
|
-
backgroundColor: string;
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export const defaultThemeId = ThemeId.Light;
|
|
44
15
|
|
|
45
|
-
|
|
46
|
-
user: { textColor: white, backgroundColor: lightBlue },
|
|
47
|
-
agent: { textColor: white, backgroundColor: offBlack },
|
|
16
|
+
return rgbToHex(newRgb.r, newRgb.g, newRgb.b);
|
|
48
17
|
};
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
18
|
+
|
|
19
|
+
const hexToRgb = (hex: string): { r: number; g: number; b: number } => {
|
|
20
|
+
const r = parseInt(hex.substring(1, 3), 16);
|
|
21
|
+
const g = parseInt(hex.substring(3, 5), 16);
|
|
22
|
+
const b = parseInt(hex.substring(5, 7), 16);
|
|
23
|
+
return { r, g, b };
|
|
52
24
|
};
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
backgroundColor: darkGray,
|
|
65
|
-
textColor: white,
|
|
66
|
-
},
|
|
67
|
-
chatPaneBackgroundColor: "rgba(32, 32, 32, 0.9)",
|
|
68
|
-
message: lightMessage,
|
|
69
|
-
},
|
|
70
|
-
[ThemeId.Purple]: {
|
|
71
|
-
chatHeader: {
|
|
72
|
-
backgroundColor: "#550098",
|
|
73
|
-
textColor: white,
|
|
74
|
-
},
|
|
75
|
-
chatPaneBackgroundColor: "rgba(85, 0, 152, 0.6)",
|
|
76
|
-
message: lightMessage,
|
|
77
|
-
},
|
|
78
|
-
[ThemeId.Blue]: {
|
|
79
|
-
chatHeader: {
|
|
80
|
-
backgroundColor: "#0814E5",
|
|
81
|
-
textColor: white,
|
|
82
|
-
},
|
|
83
|
-
chatPaneBackgroundColor: "rgba(4, 17, 245, 0.6)",
|
|
84
|
-
message: lightMessage,
|
|
85
|
-
},
|
|
86
|
-
[ThemeId.Teal]: {
|
|
87
|
-
chatHeader: {
|
|
88
|
-
backgroundColor: "#6EE7ED",
|
|
89
|
-
textColor: darkGray,
|
|
90
|
-
},
|
|
91
|
-
chatPaneBackgroundColor: "rgba(115, 247, 253, 0.8)",
|
|
92
|
-
message: darkMessage,
|
|
93
|
-
},
|
|
94
|
-
[ThemeId.Green]: {
|
|
95
|
-
chatHeader: {
|
|
96
|
-
backgroundColor: "#147B0E",
|
|
97
|
-
textColor: white,
|
|
98
|
-
},
|
|
99
|
-
chatPaneBackgroundColor: "rgba(13, 141, 5, 0.6)",
|
|
100
|
-
message: lightMessage,
|
|
101
|
-
},
|
|
102
|
-
[ThemeId.Yellow]: {
|
|
103
|
-
chatHeader: {
|
|
104
|
-
backgroundColor: "#F1E54F",
|
|
105
|
-
textColor: darkGray,
|
|
106
|
-
},
|
|
107
|
-
chatPaneBackgroundColor: "rgba(251, 239, 80, 0.9)",
|
|
108
|
-
message: darkMessage,
|
|
109
|
-
},
|
|
110
|
-
[ThemeId.Orange]: {
|
|
111
|
-
chatHeader: {
|
|
112
|
-
backgroundColor: "#C06C31",
|
|
113
|
-
textColor: white,
|
|
114
|
-
},
|
|
115
|
-
chatPaneBackgroundColor: "rgba(238, 126, 49, 0.7)",
|
|
116
|
-
message: lightMessage,
|
|
117
|
-
},
|
|
118
|
-
[ThemeId.Pink]: {
|
|
119
|
-
chatHeader: {
|
|
120
|
-
backgroundColor: "#A24599",
|
|
121
|
-
textColor: white,
|
|
122
|
-
},
|
|
123
|
-
chatPaneBackgroundColor: "rgba(167, 70, 157, 0.8)",
|
|
124
|
-
message: lightMessage,
|
|
125
|
-
},
|
|
126
|
-
[ThemeId.Glass]: {
|
|
127
|
-
chatHeader: {
|
|
128
|
-
backgroundColor: white,
|
|
129
|
-
textColor: darkGray,
|
|
130
|
-
},
|
|
131
|
-
chatPaneBackgroundColor: "rgba(193, 193, 193, 0.5)",
|
|
132
|
-
message: {
|
|
133
|
-
user: {
|
|
134
|
-
textColor: "#FFFFFF",
|
|
135
|
-
backgroundColor: "#000000",
|
|
136
|
-
},
|
|
137
|
-
agent: {
|
|
138
|
-
textColor: "#000000",
|
|
139
|
-
backgroundColor: "#FFFFFF",
|
|
140
|
-
},
|
|
141
|
-
},
|
|
142
|
-
},
|
|
25
|
+
|
|
26
|
+
const rgbToHex = (r: number, g: number, b: number): string => {
|
|
27
|
+
return (
|
|
28
|
+
"#" +
|
|
29
|
+
[r, g, b]
|
|
30
|
+
.map((c) => {
|
|
31
|
+
const hex = c.toString(16);
|
|
32
|
+
return hex.length === 1 ? "0" + hex : hex;
|
|
33
|
+
})
|
|
34
|
+
.join("")
|
|
35
|
+
);
|
|
143
36
|
};
|
|
144
37
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
theme.chatHeader.backgroundColor = hexToAlmostWhite(brandColor, 0.6);
|
|
162
|
-
}
|
|
163
|
-
return theme;
|
|
38
|
+
export const hexToAlmostWhite = (hexColor: string, ratio: number): string => {
|
|
39
|
+
let r = parseInt(hexColor.substr(1, 2), 16);
|
|
40
|
+
let g = parseInt(hexColor.substr(3, 2), 16);
|
|
41
|
+
let b = parseInt(hexColor.substr(5, 2), 16);
|
|
42
|
+
|
|
43
|
+
// the ratio of white to add (1 = white, 0 = no change)
|
|
44
|
+
r = Math.round(r * (1 - ratio) + 255 * ratio);
|
|
45
|
+
g = Math.round(g * (1 - ratio) + 255 * ratio);
|
|
46
|
+
b = Math.round(b * (1 - ratio) + 255 * ratio);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
"#" +
|
|
50
|
+
r.toString(16).padStart(2, "0") +
|
|
51
|
+
g.toString(16).padStart(2, "0") +
|
|
52
|
+
b.toString(16).padStart(2, "0")
|
|
53
|
+
);
|
|
164
54
|
};
|
package/src/utils.ts
CHANGED
|
@@ -49,56 +49,3 @@ export function useInterval(callback: () => void, delay: number | null): void {
|
|
|
49
49
|
|
|
50
50
|
export const isMobile = (): boolean =>
|
|
51
51
|
window.matchMedia("(max-width: 767px)").matches;
|
|
52
|
-
|
|
53
|
-
export const tintColor = (
|
|
54
|
-
originalColor: string,
|
|
55
|
-
tintOpacity: number
|
|
56
|
-
): string => {
|
|
57
|
-
const originalRgb = hexToRgb(originalColor);
|
|
58
|
-
const tintRgb = { r: 0, g: 0, b: 0 };
|
|
59
|
-
|
|
60
|
-
const newRgb = {
|
|
61
|
-
r: originalRgb.r + Math.round((tintRgb.r - originalRgb.r) * tintOpacity),
|
|
62
|
-
g: originalRgb.g + Math.round((tintRgb.g - originalRgb.g) * tintOpacity),
|
|
63
|
-
b: originalRgb.b + Math.round((tintRgb.b - originalRgb.b) * tintOpacity),
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
return rgbToHex(newRgb.r, newRgb.g, newRgb.b);
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const hexToRgb = (hex: string): { r: number; g: number; b: number } => {
|
|
70
|
-
const r = parseInt(hex.substring(1, 3), 16);
|
|
71
|
-
const g = parseInt(hex.substring(3, 5), 16);
|
|
72
|
-
const b = parseInt(hex.substring(5, 7), 16);
|
|
73
|
-
return { r, g, b };
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const rgbToHex = (r: number, g: number, b: number): string => {
|
|
77
|
-
return (
|
|
78
|
-
"#" +
|
|
79
|
-
[r, g, b]
|
|
80
|
-
.map((c) => {
|
|
81
|
-
const hex = c.toString(16);
|
|
82
|
-
return hex.length === 1 ? "0" + hex : hex;
|
|
83
|
-
})
|
|
84
|
-
.join("")
|
|
85
|
-
);
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
export const hexToAlmostWhite = (hexColor: string, ratio: number): string => {
|
|
89
|
-
let r = parseInt(hexColor.substr(1, 2), 16);
|
|
90
|
-
let g = parseInt(hexColor.substr(3, 2), 16);
|
|
91
|
-
let b = parseInt(hexColor.substr(5, 2), 16);
|
|
92
|
-
|
|
93
|
-
// the ratio of white to add (1 = white, 0 = no change)
|
|
94
|
-
r = Math.round(r * (1 - ratio) + 255 * ratio);
|
|
95
|
-
g = Math.round(g * (1 - ratio) + 255 * ratio);
|
|
96
|
-
b = Math.round(b * (1 - ratio) + 255 * ratio);
|
|
97
|
-
|
|
98
|
-
return (
|
|
99
|
-
"#" +
|
|
100
|
-
r.toString(16).padStart(2, "0") +
|
|
101
|
-
g.toString(16).padStart(2, "0") +
|
|
102
|
-
b.toString(16).padStart(2, "0")
|
|
103
|
-
);
|
|
104
|
-
};
|
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
import { css, html, LitElement, TemplateResult } from "lit";
|
|
2
|
-
import { customElement, property, state } from "lit/decorators.js";
|
|
3
|
-
import { createRef, ref, Ref } from "lit/directives/ref.js";
|
|
4
|
-
import { installActionConfirmButton } from "./action-confirm-button";
|
|
5
|
-
import { installDetailsWindow } from "./details-window";
|
|
6
|
-
import {
|
|
7
|
-
formatToPhoneInput,
|
|
8
|
-
isModifierKey,
|
|
9
|
-
isNumericInput,
|
|
10
|
-
} from "./formatPhoneNumber";
|
|
11
|
-
import { InputStyles } from "./InputStyles";
|
|
12
|
-
import axios from "axios";
|
|
13
|
-
import { pushGtmEvent } from "../../gtm";
|
|
14
|
-
|
|
15
|
-
@customElement("text-us-window")
|
|
16
|
-
export class TextUsWindow extends LitElement {
|
|
17
|
-
static styles = [
|
|
18
|
-
css`
|
|
19
|
-
.text-us-wrapper {
|
|
20
|
-
margin-top: 33px;
|
|
21
|
-
color: #202020;
|
|
22
|
-
}
|
|
23
|
-
.text-us-window__header {
|
|
24
|
-
font-family: "Helvetica Neue", Arial;
|
|
25
|
-
font-style: normal;
|
|
26
|
-
font-weight: 700;
|
|
27
|
-
line-height: 140%;
|
|
28
|
-
font-size: 14px;
|
|
29
|
-
margin: 0;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
.text-us-window__input-container {
|
|
33
|
-
margin-top: 8px;
|
|
34
|
-
margin-bottom: 8px;
|
|
35
|
-
width: -webkit-fill-available;
|
|
36
|
-
position: relative;
|
|
37
|
-
}
|
|
38
|
-
.text-us-window__submit-text {
|
|
39
|
-
position: absolute;
|
|
40
|
-
top: 14px;
|
|
41
|
-
right: 6px;
|
|
42
|
-
}
|
|
43
|
-
.text-us-window__vertical-spacer {
|
|
44
|
-
height: 20px;
|
|
45
|
-
}
|
|
46
|
-
.text-us-window__description,
|
|
47
|
-
.text-us-window__error {
|
|
48
|
-
font-family: "Helvetica Neue", Arial;
|
|
49
|
-
font-style: normal;
|
|
50
|
-
font-weight: 400;
|
|
51
|
-
font-size: 10px;
|
|
52
|
-
line-height: 14px;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.text-us-window__error {
|
|
56
|
-
color: rgb(255, 49, 3);
|
|
57
|
-
}
|
|
58
|
-
`,
|
|
59
|
-
InputStyles,
|
|
60
|
-
];
|
|
61
|
-
|
|
62
|
-
@property({ attribute: false })
|
|
63
|
-
onCloseClicked?: (e: MouseEvent) => void;
|
|
64
|
-
|
|
65
|
-
@property({ attribute: false })
|
|
66
|
-
buildingId = 0;
|
|
67
|
-
@property({ attribute: true })
|
|
68
|
-
buildingSlug = "";
|
|
69
|
-
@property({ attribute: true })
|
|
70
|
-
orgSlug = "";
|
|
71
|
-
@property({ attribute: true })
|
|
72
|
-
inPopup = false;
|
|
73
|
-
|
|
74
|
-
phoneNumberInputRef: Ref<HTMLInputElement> = createRef();
|
|
75
|
-
|
|
76
|
-
@state()
|
|
77
|
-
phoneNumber = "";
|
|
78
|
-
@state()
|
|
79
|
-
hasSubmittedForm = false;
|
|
80
|
-
@state()
|
|
81
|
-
hasPhoneNumberError = false;
|
|
82
|
-
@state()
|
|
83
|
-
hasSubmissionError = false;
|
|
84
|
-
@state()
|
|
85
|
-
isSubmitting = false;
|
|
86
|
-
|
|
87
|
-
@property({ attribute: true })
|
|
88
|
-
currentLeadSource = "";
|
|
89
|
-
|
|
90
|
-
onChangePhoneNumber = (e: Event): void => {
|
|
91
|
-
if (!e.target || !this.phoneNumberInputRef.value) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
if (isModifierKey(e as KeyboardEvent)) {
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
const inputElement = e.target as HTMLInputElement;
|
|
98
|
-
|
|
99
|
-
this.phoneNumber = formatToPhoneInput(inputElement.value);
|
|
100
|
-
|
|
101
|
-
this.phoneNumberInputRef.value.value = this.phoneNumber;
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
enforceFormat = (e: KeyboardEvent): void => {
|
|
105
|
-
if (!isNumericInput(e) && !isModifierKey(e)) {
|
|
106
|
-
e.preventDefault();
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
validateFormFields = (): void => {
|
|
111
|
-
this.hasPhoneNumberError = false;
|
|
112
|
-
this.hasSubmissionError = false;
|
|
113
|
-
if (!this.phoneNumber || this.phoneNumber.length !== 14) {
|
|
114
|
-
this.hasPhoneNumberError = true;
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
onClick = async (): Promise<void> => {
|
|
119
|
-
this.validateFormFields();
|
|
120
|
-
if (this.hasPhoneNumberError) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
try {
|
|
124
|
-
this.isSubmitting = true;
|
|
125
|
-
await createTextWithUs(
|
|
126
|
-
this.phoneNumber,
|
|
127
|
-
this.buildingId,
|
|
128
|
-
this.orgSlug,
|
|
129
|
-
this.buildingSlug,
|
|
130
|
-
[
|
|
131
|
-
...new Set(
|
|
132
|
-
this.currentLeadSource
|
|
133
|
-
? [this.currentLeadSource, "property-website"]
|
|
134
|
-
: ["property-website"]
|
|
135
|
-
),
|
|
136
|
-
]
|
|
137
|
-
);
|
|
138
|
-
this.hasSubmittedForm = true;
|
|
139
|
-
this.isSubmitting = false;
|
|
140
|
-
} catch (e) {
|
|
141
|
-
this.isSubmitting = false;
|
|
142
|
-
this.hasSubmissionError = true;
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
renderTextUs = (): TemplateResult => {
|
|
147
|
-
return html` <div class="text-us-wrapper">
|
|
148
|
-
<h1 class="text-us-window__header">Send Us a Text</h1>
|
|
149
|
-
<div class="text-us-window__input-container">
|
|
150
|
-
<input
|
|
151
|
-
${ref(this.phoneNumberInputRef)}
|
|
152
|
-
maxlength="14"
|
|
153
|
-
type="text"
|
|
154
|
-
placeholder="Enter phone"
|
|
155
|
-
inputmode="tel"
|
|
156
|
-
class="webchat-input text-us-window__input-container"
|
|
157
|
-
.value=${this.phoneNumber}
|
|
158
|
-
@keydown=${this.enforceFormat}
|
|
159
|
-
@keyup=${this.onChangePhoneNumber}
|
|
160
|
-
/>
|
|
161
|
-
<div class="text-us-window__submit-text">
|
|
162
|
-
<action-confirm-button
|
|
163
|
-
.onClick=${this.onClick}
|
|
164
|
-
.isLoading=${this.isSubmitting}
|
|
165
|
-
text="SEND"
|
|
166
|
-
></action-confirm-button>
|
|
167
|
-
</div>
|
|
168
|
-
${this.hasPhoneNumberError
|
|
169
|
-
? html`
|
|
170
|
-
<div class="text-us-window__error">
|
|
171
|
-
Enter a valid phone number
|
|
172
|
-
</div>
|
|
173
|
-
`
|
|
174
|
-
: ""}
|
|
175
|
-
</div>
|
|
176
|
-
|
|
177
|
-
<div class="text-us-window__description">
|
|
178
|
-
By entering your number and selecting Send, you consent to be contacted
|
|
179
|
-
by our AI Leasing Assistant. Your consent to this process is not a
|
|
180
|
-
requirement for leasing at our property.
|
|
181
|
-
<div class="text-us-window__description">
|
|
182
|
-
Message and data rates may apply.
|
|
183
|
-
</div>
|
|
184
|
-
</div>
|
|
185
|
-
</div>`;
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
renderSubmitted = (): TemplateResult => {
|
|
189
|
-
return html`
|
|
190
|
-
<div class="text-us-wrapper">
|
|
191
|
-
<div class="text-us-window__vertical-spacer"></div>
|
|
192
|
-
<svg
|
|
193
|
-
width="20"
|
|
194
|
-
height="20"
|
|
195
|
-
viewBox="0 0 20 20"
|
|
196
|
-
fill="none"
|
|
197
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
198
|
-
>
|
|
199
|
-
<path
|
|
200
|
-
d="M4.455 16L0 19.5V1C0 0.734784 0.105357 0.48043 0.292893 0.292893C0.48043 0.105357 0.734784 0 1 0H19C19.2652 0 19.5196 0.105357 19.7071 0.292893C19.8946 0.48043 20 0.734784 20 1V15C20 15.2652 19.8946 15.5196 19.7071 15.7071C19.5196 15.8946 19.2652 16 19 16H4.455ZM2 15.385L3.763 14H18V2H2V15.385ZM10 7V4L14 8L10 12V9H6V7H10Z"
|
|
201
|
-
fill="#202020"
|
|
202
|
-
/>
|
|
203
|
-
</svg>
|
|
204
|
-
<div class="text-us-window__description">
|
|
205
|
-
Thank you!<br />Look for a text message from our team. We can answer
|
|
206
|
-
questions and help you book a tour through text.
|
|
207
|
-
</div>
|
|
208
|
-
<div class="text-us-window__description">
|
|
209
|
-
Opt out at anytime by texting “Stop”
|
|
210
|
-
</div>
|
|
211
|
-
</div>
|
|
212
|
-
`;
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
render = (): TemplateResult => {
|
|
216
|
-
installDetailsWindow();
|
|
217
|
-
installActionConfirmButton();
|
|
218
|
-
if (this.inPopup) {
|
|
219
|
-
if (this.hasSubmittedForm) return this.renderSubmitted();
|
|
220
|
-
else return this.renderTextUs();
|
|
221
|
-
}
|
|
222
|
-
if (this.hasSubmittedForm) {
|
|
223
|
-
return html`
|
|
224
|
-
<details-window
|
|
225
|
-
headerText="Text us"
|
|
226
|
-
.onCloseClick=${this.onCloseClicked}
|
|
227
|
-
>
|
|
228
|
-
${this.renderSubmitted}
|
|
229
|
-
</details-window>
|
|
230
|
-
`;
|
|
231
|
-
}
|
|
232
|
-
return html`
|
|
233
|
-
<details-window headerText="Text us" .onCloseClick=${this.onCloseClicked}>
|
|
234
|
-
${this.renderTextUs}
|
|
235
|
-
</details-window>
|
|
236
|
-
`;
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
export const installTextUsWindow = (): void => {
|
|
241
|
-
if (!window.customElements.get("text-us-window")) {
|
|
242
|
-
window.customElements.define("text-us-window", TextUsWindow);
|
|
243
|
-
}
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
const createTextWithUs = async (
|
|
247
|
-
rawPhoneNumber: string,
|
|
248
|
-
buildingId: number,
|
|
249
|
-
orgSlug: string,
|
|
250
|
-
buildingSlug: string,
|
|
251
|
-
leadSources: string[]
|
|
252
|
-
) => {
|
|
253
|
-
const formattedPhoneNumber =
|
|
254
|
-
"+1" +
|
|
255
|
-
rawPhoneNumber
|
|
256
|
-
.replace("(", "")
|
|
257
|
-
.replace(")", "")
|
|
258
|
-
.replace(" ", "")
|
|
259
|
-
.replace("-", "");
|
|
260
|
-
const queryParams = new URLSearchParams(window.location.search);
|
|
261
|
-
const requestBody = {
|
|
262
|
-
building_id: buildingId,
|
|
263
|
-
lead_sources: leadSources,
|
|
264
|
-
phone_number: formattedPhoneNumber,
|
|
265
|
-
referrer: document.referrer,
|
|
266
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
267
|
-
// @ts-ignore
|
|
268
|
-
query_params: Object.fromEntries(queryParams.entries()),
|
|
269
|
-
};
|
|
270
|
-
pushGtmEvent("textUsSubmitted", {
|
|
271
|
-
phone: formattedPhoneNumber,
|
|
272
|
-
originatingSource:
|
|
273
|
-
leadSources.find((i) => i !== "property-website") || null,
|
|
274
|
-
});
|
|
275
|
-
await axios.post(
|
|
276
|
-
"https://app.meetelise.com/platformApi/state/create/textMe",
|
|
277
|
-
requestBody,
|
|
278
|
-
{
|
|
279
|
-
headers: {
|
|
280
|
-
["building-slug"]: buildingSlug,
|
|
281
|
-
["org-slug"]: orgSlug,
|
|
282
|
-
["X-SecurityKey"]: "JRL8jV4VcSCwOSir5gWkpgNLfKghmhBG",
|
|
283
|
-
},
|
|
284
|
-
}
|
|
285
|
-
);
|
|
286
|
-
};
|
package/src/chatID.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { v4 as uuid } from "uuid";
|
|
2
|
-
|
|
3
|
-
const localStorageNamespacingPrefix = "com.meetelise";
|
|
4
|
-
const chatIdKeyPrefix = `${localStorageNamespacingPrefix}.chatID`;
|
|
5
|
-
const chatIdIssueTimeKeyPrefix = `${localStorageNamespacingPrefix}.chatIDIssueTime`;
|
|
6
|
-
const chatIdTimeToLiveInMilliseconds = 6 * 60 * 60 * 1000; // 6 hours
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Get or create a UUID that is the same between browser sessions.
|
|
10
|
-
* If the UUID is past its expiration time, create a new one.
|
|
11
|
-
*
|
|
12
|
-
* @returns the chat ID.
|
|
13
|
-
*/
|
|
14
|
-
export function getChatID(org: string, building: string): string {
|
|
15
|
-
const key = getChatIdKey(org, building);
|
|
16
|
-
const existingID = localStorage.getItem(key);
|
|
17
|
-
if (existingID && !existingChatIdIsExpired(org, building)) return existingID;
|
|
18
|
-
else return createChatID(org, building);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Create a new chat ID, discard any old one, and record the time it was issued.
|
|
23
|
-
*/
|
|
24
|
-
export function createChatID(org: string, building: string): string {
|
|
25
|
-
const chatIdKey = getChatIdKey(org, building);
|
|
26
|
-
const chatId = uuid();
|
|
27
|
-
localStorage.setItem(chatIdKey, chatId);
|
|
28
|
-
|
|
29
|
-
setIssueTime(org, building);
|
|
30
|
-
|
|
31
|
-
return chatId;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function setIssueTime(org: string, building: string): string {
|
|
35
|
-
const issueTimeKey = getChatIdIssueTimeKey(org, building);
|
|
36
|
-
const issueTimeString = new Date().toISOString();
|
|
37
|
-
localStorage.setItem(issueTimeKey, issueTimeString);
|
|
38
|
-
return issueTimeString;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function getChatIdKey(org: string, building: string): string {
|
|
42
|
-
return `${chatIdKeyPrefix}-${org}-${building}`;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function getChatIdIssueTimeKey(org: string, building: string): string {
|
|
46
|
-
return `${chatIdIssueTimeKeyPrefix}-${org}-${building}`;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Check if the existing chat ID is expired.
|
|
51
|
-
*
|
|
52
|
-
* If the issue time is missing, set the issue time to the current time.
|
|
53
|
-
* (This will be the case for chat IDs created before the expiration functionality was added.)
|
|
54
|
-
*/
|
|
55
|
-
function existingChatIdIsExpired(org: string, building: string): boolean {
|
|
56
|
-
const issueTimeKey = getChatIdIssueTimeKey(org, building);
|
|
57
|
-
const existingIdIssueTimeString =
|
|
58
|
-
localStorage.getItem(issueTimeKey) || setIssueTime(org, building);
|
|
59
|
-
const existingIdExpiration = new Date(
|
|
60
|
-
new Date(existingIdIssueTimeString).getTime() +
|
|
61
|
-
chatIdTimeToLiveInMilliseconds
|
|
62
|
-
);
|
|
63
|
-
return new Date() > existingIdExpiration;
|
|
64
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import Talk from "talkjs";
|
|
2
|
-
import { Building } from "./fetchBuildingInfo";
|
|
3
|
-
import { Theme } from "./themes";
|
|
4
|
-
import { darkMessage } from "./themes";
|
|
5
|
-
|
|
6
|
-
const defaultAvatarUrl =
|
|
7
|
-
"https://eliseusercontent.meetelise.com/webchat/looping-gradient.gif";
|
|
8
|
-
|
|
9
|
-
export default function createConversation(
|
|
10
|
-
session: Talk.Session,
|
|
11
|
-
building: Building,
|
|
12
|
-
theme: Theme,
|
|
13
|
-
avatarSrc: Building["avatarSrc"],
|
|
14
|
-
chatID: string,
|
|
15
|
-
isMobile: boolean
|
|
16
|
-
): Talk.ConversationBuilder {
|
|
17
|
-
const agent = new Talk.User({
|
|
18
|
-
id: `building_${building.id}`,
|
|
19
|
-
name: building.userFirstName,
|
|
20
|
-
email: null,
|
|
21
|
-
role: "Default",
|
|
22
|
-
welcomeMessage: building.welcomeMessage,
|
|
23
|
-
availabilityText: building.systemWelcomeMessage,
|
|
24
|
-
});
|
|
25
|
-
const conversation = session.getOrCreateConversation(chatID);
|
|
26
|
-
conversation.setParticipant(session.me);
|
|
27
|
-
conversation.setParticipant(agent);
|
|
28
|
-
conversation.subject =
|
|
29
|
-
building.orgId === 16
|
|
30
|
-
? "Leasing team" // by AVB request
|
|
31
|
-
: building.chatTitle ?? building.userFirstName;
|
|
32
|
-
conversation.custom = {
|
|
33
|
-
buildingId: building.id.toString(),
|
|
34
|
-
userId: building.userId.toString(),
|
|
35
|
-
orgId: building.orgId.toString(),
|
|
36
|
-
subtitle: building.chatSubtitle,
|
|
37
|
-
url: location.href,
|
|
38
|
-
buildingName: building.name,
|
|
39
|
-
isMobile: isMobile.toString(),
|
|
40
|
-
chatHeaderBackgroundColor: theme.chatHeader.backgroundColor,
|
|
41
|
-
chatHeaderTextColor: theme.chatHeader.textColor,
|
|
42
|
-
chatPaneBackgroundColor: theme.chatPaneBackgroundColor,
|
|
43
|
-
userMessageTextColor: theme.message.user.textColor,
|
|
44
|
-
userMessageBackgroundColor: theme.message.user.backgroundColor,
|
|
45
|
-
agentMessageTextColor: theme.message.agent.textColor,
|
|
46
|
-
agentMessageBackgroundColor: theme.message.agent.backgroundColor,
|
|
47
|
-
// it is sometimes helpful to know inside TalkJS if the theme is a light theme (examples: Light, Teal, Yellow) or a dark theme (Dark, Purple, Green)
|
|
48
|
-
isLightTheme: (theme.message === darkMessage).toString(),
|
|
49
|
-
avatarUrl:
|
|
50
|
-
building.avatarType === "image" && building.avatarSrc
|
|
51
|
-
? avatarSrc
|
|
52
|
-
: defaultAvatarUrl,
|
|
53
|
-
// uncomment the following line to test changes to the default avatar if your test building has its own avatar
|
|
54
|
-
// avatarUrl: defaultAvatarUrl,
|
|
55
|
-
};
|
|
56
|
-
return conversation;
|
|
57
|
-
}
|