@meetelise/chat 1.5.0 → 1.6.2

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.
@@ -4,12 +4,6 @@ object-assign
4
4
  @license MIT
5
5
  */
6
6
 
7
- /*!
8
- Copyright (c) 2018 Jed Watson.
9
- Licensed under the MIT License (MIT), see
10
- http://jedwatson.github.io/classnames
11
- */
12
-
13
7
  /** @license React v0.20.2
14
8
  * scheduler.production.min.js
15
9
  *
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ interface ChatBubbleProps {
3
+ messages: {
4
+ title: string;
5
+ text: string;
6
+ }[];
7
+ triggerBounce: () => void;
8
+ bounceIntervalInSeconds: number;
9
+ onClick: () => void;
10
+ }
11
+ declare const ChatBubble: React.FunctionComponent<ChatBubbleProps>;
12
+ export default ChatBubble;
@@ -46,9 +46,8 @@ export default class MEChat {
46
46
  show(): void;
47
47
  /** Hide the chat button from the screen (but don't remove from the DOM). */
48
48
  hide(): void;
49
- /** Show a custom launcher designed in-house instead of the default TalkJS launcher. */
50
- private mountInHouseLauncher;
51
- private getInHouseLauncher;
49
+ /** Show a speech bubble next to the chat button (launcher). Also adds some animations to the button. */
50
+ private addChatBubble;
52
51
  private buildingSlug;
53
52
  private orgSlug;
54
53
  private popup;
@@ -58,8 +57,6 @@ export default class MEChat {
58
57
  private chatId;
59
58
  private analytics;
60
59
  private launchDarklyClient;
61
- private useInHouseLauncher;
62
- private isMobile;
63
60
  private constructor();
64
61
  }
65
62
  export interface Options {
@@ -1,4 +1,4 @@
1
1
  import Talk from "talkjs";
2
2
  import { Building } from "./fetchBuildingInfo";
3
3
  import { Theme } from "./resolveTheme";
4
- export default function createConversation(session: Talk.Session, building: Building, theme: Theme, chatID: string, isMobile: boolean): Talk.ConversationBuilder;
4
+ export default function createConversation(session: Talk.Session, building: Building, theme: Theme, chatID: string): Talk.ConversationBuilder;
@@ -3,13 +3,20 @@
3
3
  */
4
4
  export interface Building {
5
5
  id: number;
6
- themeId: string;
7
6
  avatarInitials: string | null;
8
7
  avatarSrc: string | null;
9
8
  avatarType: "image" | "initials" | null;
9
+ backgroundColor: string | null;
10
+ bannerColor: string | null;
11
+ bannerTextColor: string | null;
10
12
  chatSubtitle: string | null;
11
13
  chatTitle: string | null;
14
+ launchButtonColor: string | null;
15
+ launchButtonIconColor: string | null;
16
+ launchButtonSize: string | null;
12
17
  logoSrc: string | null;
18
+ messageColor: string | null;
19
+ messageTextColor: string | null;
13
20
  name: string;
14
21
  primaryColor: string | null;
15
22
  userFirstName: string;
@@ -18,14 +25,6 @@ export interface Building {
18
25
  welcomeMessage: string | null;
19
26
  conversationMaintenanceMode: boolean;
20
27
  orgId: number;
21
- backgroundColor: string | null;
22
- bannerColor: string | null;
23
- bannerTextColor: string | null;
24
- launchButtonColor: string | null;
25
- launchButtonIconColor: string | null;
26
- launchButtonSize: string | null;
27
- messageColor: string | null;
28
- messageTextColor: string | null;
29
28
  }
30
29
  /**
31
30
  * Load the publicly-available info for a building.
@@ -1,2 +1 @@
1
1
  export declare function useInterval(callback: () => void, delay: number | null): void;
2
- export declare const isMobile: () => boolean;
package/src/DemoApp.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState } from "react";
1
+ import React, { useEffect } from "react";
2
2
  import ReactDOM from "react-dom";
3
3
  import { debounce } from "lodash";
4
4
  import MEChat from "./MEChat";
@@ -55,7 +55,6 @@ const DemoApp = () => {
55
55
  }
56
56
  }, []);
57
57
 
58
- const [showCookieBanner, setShowCookieBanner] = useState(true);
59
58
  return (
60
59
  <>
61
60
  <h1>Example Page</h1>
@@ -81,31 +80,23 @@ const DemoApp = () => {
81
80
  name="launchButtonIconColor"
82
81
  />
83
82
  </form>
84
- {showCookieBanner && (
85
- <div
86
- id="bottomBanner"
87
- style={{
88
- position: "absolute",
89
- bottom: 0,
90
- left: 0,
91
- width: "100%",
92
- height: "20vh",
93
- backgroundColor: "lightpink",
94
- textAlign: "center",
95
- }}
96
- >
97
- <p style={{ marginTop: "5em" }}>
98
- Hi, I'm a banner that appears at the bottom of the screen! I eat
99
- chat widgets for breakfast and I'll eat yours if you aren't careful!
100
- </p>
101
- <div
102
- style={{ position: "absolute", top: "1rem", right: "1rem" }}
103
- onClick={() => setShowCookieBanner(false)}
104
- >
105
-
106
- </div>
107
- </div>
108
- )}
83
+ <div
84
+ id="bottomBanner"
85
+ style={{
86
+ position: "absolute",
87
+ bottom: 0,
88
+ left: 0,
89
+ width: "100%",
90
+ height: "20vh",
91
+ backgroundColor: "lightpink",
92
+ textAlign: "center",
93
+ }}
94
+ >
95
+ <p style={{ marginTop: "5em" }}>
96
+ Hi, I'm a banner that appears at the bottom of the screen! I eat chat
97
+ widgets for breakfast and I'll eat yours if you aren't careful!
98
+ </p>
99
+ </div>
109
100
  </>
110
101
  );
111
102
  };
@@ -1,23 +1,58 @@
1
+ .wrapper {
2
+ position: fixed;
3
+ display: flex;
4
+ bottom: 10vh;
5
+ right: 10vh;
6
+ z-index: 100000;
7
+ }
8
+
1
9
  :global(#__talkjs_launcher):not(.shouldBeVisible) {
2
10
  display: none;
3
11
  }
4
12
 
5
- // This docks the chat panel at the right edge of the screen.
6
- // The 25px is because .ChatBox is styled by TalkJS with `width: calc(100% - 25px)`.
7
- :global(.meetelise-chat.pane).desktop {
8
- right: -25px;
13
+ :global(a#__talkjs_launcher) {
14
+ box-shadow: 0px 6px 8px rgba(0, 0, 0, 0.25);
15
+ background-position: 50% 50%;
16
+ background-size: 27px 27px;
17
+ }
18
+
19
+ :global(a#__talkjs_launcher).bouncingLauncherButton {
20
+ animation-name: bounce;
21
+ animation-duration: 1s;
22
+ animation-timing-function: cubic-bezier(0.28, 0.84, 0.42, 1);
23
+ }
24
+
25
+ /* https://css-tricks.com/making-css-animations-feel-natural/ */
26
+ @keyframes bounce {
27
+ 0% {
28
+ transform: scale(1, 1) translateY(0);
29
+ }
30
+ 10% {
31
+ transform: scale(1.1, 0.9) translateY(0);
32
+ }
33
+ 30% {
34
+ transform: scale(0.9, 1.1) translateY(-100px);
35
+ }
36
+ 50% {
37
+ transform: scale(1.05, 0.95) translateY(0);
38
+ }
39
+ 57% {
40
+ transform: scale(1, 1) translateY(-7px);
41
+ }
42
+ 64% {
43
+ transform: scale(1, 1) translateY(0);
44
+ }
45
+ 100% {
46
+ transform: scale(1, 1) translateY(0);
47
+ }
48
+ }
49
+
50
+ .fadeOut {
51
+ animation: fadeOut 0.5s;
9
52
  }
10
53
 
11
- .inHouseLauncherContainer {
12
- &.desktop {
13
- // TODO: fix
14
- width: 100px;
15
- height: 100px;
16
- position: relative;
17
- overflow: hidden;
18
- }
19
- &.mobile {
20
- width: 100%;
21
- height: 100px;
54
+ @keyframes fadeOut {
55
+ to {
56
+ opacity: 0;
22
57
  }
23
58
  }
@@ -2,25 +2,14 @@ import { expect } from "@esm-bundle/chai";
2
2
  import { stub, restore } from "sinon/pkg/sinon-esm";
3
3
  import MEChat from "../public/dist/bundle";
4
4
 
5
- const TIMEOUT = 15000;
6
-
7
5
  const stubResponse = {
8
6
  json: stub().resolves({
9
7
  id: 42,
10
- userId: 42.1,
11
- orgId: 42.2,
12
8
  name: "Unit Test Building",
13
- themeId: null,
14
- avatarSrc: null,
15
- avatarType: "",
9
+ launchButtonColor: "rgb(180, 190, 0)",
16
10
  userFirstName: "Ella",
17
- userLastName: "",
18
- logoSrc:
19
- "https://eliseusercontent.meetelise.com/building/3660/test-logo.png",
20
- chatTitle: "Elise",
21
- chatSubtitle: "Leasing Agent ExtraordinAIre",
22
- welcomeMessage:
23
- "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.",
11
+ userId: 42.1,
12
+ orgId: 42.2,
24
13
  conversationMaintenanceMode: false,
25
14
  }),
26
15
  };
@@ -47,8 +36,7 @@ afterEach(() => {
47
36
  restore();
48
37
  });
49
38
 
50
- it.skip("shows the launcher", async function (done) {
51
- this.timeout(TIMEOUT);
39
+ it("shows the chat icon", async function () {
52
40
  // Given an API that returns this building theme
53
41
  stub(window, "fetch").resolves(stubResponse);
54
42
 
@@ -57,12 +45,16 @@ it.skip("shows the launcher", async function (done) {
57
45
  organization: "unit-test-org",
58
46
  building: "unit-test-building",
59
47
  });
60
- await waitForElementWithSelectorToExist(".meetelise-chat.launcher > div");
48
+ await waitForElementWithSelectorToExist(".__talkjs_launcher");
61
49
 
62
- // Then I should see a launcher
63
- const launcher = document.querySelector<HTMLDivElement>(
64
- ".meetelise-chat.launcher"
65
- ).firstChild as HTMLDivElement;
50
+ // Then I should see a launcher with the right color
51
+ const launcher =
52
+ document.querySelector<HTMLAnchorElement>(".__talkjs_launcher");
53
+ expect(launcher).to.be.an.instanceof(HTMLAnchorElement);
54
+ // TODO: temporarily making the launcher always white because the new icon doesn't look good with all colors
55
+ // const launcherStyle = window.getComputedStyle(launcher);
56
+ // const launcherBG = launcherStyle.getPropertyValue("background-color");
57
+ // expect(launcherBG).to.equal("rgb(180, 190, 0)");
66
58
 
67
59
  // And the popup should be hidden
68
60
  const popup = document.querySelector<HTMLSpanElement>(".__talkjs_popup");
@@ -78,14 +70,12 @@ it.skip("shows the launcher", async function (done) {
78
70
  const popupStyle2 = window.getComputedStyle(popup);
79
71
  const popupDisplay2 = popupStyle2.getPropertyValue("display");
80
72
  expect(popupDisplay2).not.to.equal("none");
81
- done();
73
+
82
74
  // Ideally, expect welcome message, but we can't select inside the iframe
83
75
  // Ideally, expect theme colors, but we can't select inside the iframe
84
76
  });
85
77
 
86
- it.skip("works via the programmatic interface", async function () {
87
- this.timeout(TIMEOUT);
88
-
78
+ it("works via the programmatic interface", async () => {
89
79
  // Given an API that returns this building theme
90
80
  stub(window, "fetch").resolves(stubResponse);
91
81
 
@@ -95,7 +85,7 @@ it.skip("works via the programmatic interface", async function () {
95
85
  building: "unit-test-building",
96
86
  });
97
87
 
98
- await waitForElementWithSelectorToExist(".meetelise-chat.launcher");
88
+ await waitForElementWithSelectorToExist(".__talkjs_launcher");
99
89
 
100
90
  // Ideally, verify behavior, but this will at least verify nothing throws
101
91
  chat.show();
package/src/MEChat.tsx CHANGED
@@ -1,19 +1,16 @@
1
- import * as LDClient from "launchdarkly-js-client-sdk";
2
- import React from "react";
3
- import ReactDOM from "react-dom";
4
1
  import Talk from "talkjs";
5
-
6
2
  import fetchBuildingInfo, { Building } from "./fetchBuildingInfo";
7
3
  import { getChatID, createChatID } from "./chatID";
8
4
  import createConversation from "./createConversation";
9
5
  import installTalkJSStyles from "./installTalkJSStyles";
10
6
  import resolveTheme, { Theme } from "./resolveTheme";
11
7
  import Analytics from "./analytics";
12
- import { isMobile } from "./utils";
13
- import InHouseLauncher from "./InHouseLauncher";
8
+ import ChatBubble from "./ChatBubble";
9
+ import ReactDOM from "react-dom";
10
+ import React from "react";
11
+ import * as LDClient from "launchdarkly-js-client-sdk";
14
12
  import LaunchDarkly from "./launchDarklyManager";
15
13
  import styles from "./MEChat.module.scss";
16
- import { defaultThemeId, themesById } from "./themes";
17
14
 
18
15
  /**
19
16
  * The interface to MeetElise chat.
@@ -74,8 +71,7 @@ export default class MEChat {
74
71
  session,
75
72
  building,
76
73
  resolveTheme(building, this.theme),
77
- this.chatId,
78
- this.isMobile
74
+ this.chatId
79
75
  )
80
76
  );
81
77
  }
@@ -101,13 +97,7 @@ export default class MEChat {
101
97
  ...theme,
102
98
  }));
103
99
  popup.select(
104
- createConversation(
105
- session,
106
- building,
107
- resolvedTheme,
108
- this.chatId,
109
- this.isMobile
110
- )
100
+ createConversation(session, building, resolvedTheme, this.chatId)
111
101
  );
112
102
  installTalkJSStyles(resolvedTheme);
113
103
  return new Promise(requestAnimationFrame);
@@ -130,7 +120,6 @@ export default class MEChat {
130
120
  }
131
121
 
132
122
  /** Show the chat button on the screen if it was previously hidden. */
133
- // TODO: will this work with the new launcher? it needs to be display flex? will this just change the inline style and leave the stylesheet/style tag alone?
134
123
  show(): void {
135
124
  this.launcher.then((a) => (a.style.display = ""));
136
125
  }
@@ -140,57 +129,73 @@ export default class MEChat {
140
129
  this.launcher.then((a) => (a.style.display = "none"));
141
130
  }
142
131
 
143
- /** Show a custom launcher designed in-house instead of the default TalkJS launcher. */
144
- private async mountInHouseLauncher(
145
- targetElement: HTMLElement,
146
- firstMount: boolean
147
- ): Promise<void> {
148
- const chatTappedHandler = async () => {
149
- ReactDOM.unmountComponentAtNode(targetElement);
150
- (await this.popup).show();
132
+ /** Show a speech bubble next to the chat button (launcher). Also adds some animations to the button. */
133
+ private addChatBubble(popup: Talk.Popup, launcher: HTMLAnchorElement): void {
134
+ const chatBubbleTarget = document.createElement("div");
135
+ // set up scroll listener before mounting the chat bubble component so we don't miss any scroll events
136
+ const closeChatBubble = (shouldFade = false) => {
137
+ if (shouldFade) {
138
+ chatBubbleTarget.classList.add(styles.fadeOut);
139
+ setTimeout(() => {
140
+ ReactDOM.unmountComponentAtNode(chatBubbleTarget);
141
+ }, 500);
142
+ } else {
143
+ ReactDOM.unmountComponentAtNode(chatBubbleTarget);
144
+ }
145
+ };
146
+ // wrap the launcher and chat bubble so we can position them together but also manipulate them independently
147
+ const wrapper = document.createElement("div");
148
+ // for us, the wrapper contains the chat bubble and launcher. for consumers, we'll just call the wrapper the launcher.
149
+ wrapper.classList.add(styles.wrapper, "meetelise-chat", "launcher");
150
+ launcher.parentNode?.appendChild(wrapper);
151
+ wrapper.appendChild(launcher);
152
+ wrapper.appendChild(chatBubbleTarget);
153
+ // TalkJS positions the launcher, but we want to control its position ourselves
154
+ launcher.style.position = "unset";
155
+ launcher.style.top = "unset";
156
+ launcher.style.right = "unset";
157
+ // we initially hide the launcher in CSS so it doesn't visibly jump when we remove the native TalkJS positioning. Unhide it now.
158
+ launcher.classList.add(styles.shouldBeVisible);
159
+
160
+ popup.on("open", () => closeChatBubble());
161
+ const triggerBounce = () => {
162
+ launcher.classList.add(styles.bouncingLauncherButton);
163
+ launcher.addEventListener("animationend", () => {
164
+ launcher.classList.remove(styles.bouncingLauncherButton);
165
+ });
151
166
  };
152
- const building = await this.building;
153
- let theme = themesById[defaultThemeId];
154
- if (Object.keys(themesById).includes(building.themeId)) {
155
- theme = themesById[building.themeId as keyof typeof themesById];
156
- }
167
+ const bounceInterval = 3;
157
168
  ReactDOM.render(
158
- <InHouseLauncher
159
- onChatTapped={chatTappedHandler}
160
- mobile={this.isMobile}
161
- firstMount={firstMount}
162
- backgroundColor={theme.chatPaneBackgroundColor}
163
- textColor={theme.chatHeader.textColor}
169
+ <ChatBubble
170
+ messages={[
171
+ {
172
+ title: "Ask us a question",
173
+ text: "I can also help you schedule a tour.",
174
+ },
175
+ ]}
176
+ triggerBounce={triggerBounce}
177
+ bounceIntervalInSeconds={bounceInterval}
178
+ onClick={() => {
179
+ popup.show();
180
+ closeChatBubble();
181
+ }}
164
182
  />,
165
- targetElement
183
+ chatBubbleTarget
166
184
  );
167
- }
168
-
169
- private async getInHouseLauncher(): Promise<HTMLElement> {
170
- const inHouseLauncherTarget = document.createElement("div");
171
- inHouseLauncherTarget.classList.add(
172
- styles.inHouseLauncherContainer,
173
- this.isMobile ? styles.mobile : styles.desktop
174
- );
175
- document.body.appendChild(inHouseLauncherTarget);
176
- this.mountInHouseLauncher(inHouseLauncherTarget, true);
177
- (await this.popup).on("close", () => {
178
- this.mountInHouseLauncher(inHouseLauncherTarget, false);
179
- });
180
- return inHouseLauncherTarget;
185
+ setTimeout(() => closeChatBubble(true), bounceInterval * 1000 + 3000);
186
+ // TODO: remove? it seems to be triggered immediately on https://www.simpsonpropertygroup.com/apartments/houston-texas/skyhouse-river-oaks-galleria without scrolling
187
+ // document.addEventListener("scroll", () => closeChatBubble(true));
181
188
  }
182
189
 
183
190
  private buildingSlug: string;
184
191
  private orgSlug: string;
185
192
  private popup: Promise<Talk.Popup>;
186
- private launcher: Promise<HTMLElement>;
193
+ private launcher: Promise<HTMLAnchorElement>;
187
194
  private building: Promise<Building>;
188
195
  private theme: Partial<Theme>;
189
196
  private chatId: string;
190
197
  private analytics: Analytics;
191
198
  private launchDarklyClient: LDClient.LDClient;
192
- private useInHouseLauncher: boolean;
193
- private isMobile: boolean;
194
199
 
195
200
  private constructor({ organization, building, theme = {} }: Options) {
196
201
  this.orgSlug = organization;
@@ -201,8 +206,6 @@ export default class MEChat {
201
206
  this.analytics.ping("load");
202
207
  this.theme = theme;
203
208
  this.building = fetchBuildingInfo(organization, building);
204
- this.useInHouseLauncher = true;
205
- this.isMobile = isMobile();
206
209
 
207
210
  this.popup = Promise.all([
208
211
  this.building,
@@ -212,18 +215,7 @@ export default class MEChat {
212
215
  const resolvedTheme = (this.theme = resolveTheme(building, theme));
213
216
  installTalkJSStyles(resolvedTheme);
214
217
  const p = session.createPopup(
215
- createConversation(
216
- session,
217
- building,
218
- resolvedTheme,
219
- this.chatId,
220
- this.isMobile
221
- ),
222
- {
223
- launcher: this.useInHouseLauncher ? "never" : "always",
224
- showCloseInHeader: true,
225
- messageField: { placeholder: "Ask a question..." },
226
- }
218
+ createConversation(session, building, resolvedTheme, this.chatId)
227
219
  );
228
220
  p.on("open", () => {
229
221
  this.analytics.ping("open");
@@ -241,35 +233,38 @@ export default class MEChat {
241
233
  const talkjsPopupElement = document.querySelector(".__talkjs_popup");
242
234
  if (!talkjsPopupElement) throw new Error("Failed to find chat window");
243
235
  talkjsPopupElement.classList.add("meetelise-chat", "pane");
244
- if (!this.isMobile) {
245
- talkjsPopupElement.classList.add(styles.desktop);
246
- }
247
236
  return p;
248
237
  });
249
238
 
250
239
  this.launcher = Promise.all([this.popup, LaunchDarkly.isReady]).then(
251
- async () => {
252
- let launcherElement: HTMLElement;
240
+ async ([popup]) => {
241
+ const talkjsLauncherElement = document.querySelector<HTMLAnchorElement>(
242
+ "a#__talkjs_launcher"
243
+ );
244
+ if (!talkjsLauncherElement)
245
+ throw new Error("MeetElise Chat: Could not locate launcher.");
246
+
247
+ const webchatBubbleFlag = this.launchDarklyClient.variation(
248
+ "webchat-bubble",
249
+ false
250
+ );
251
+ this.analytics.setFeatureFlags({
252
+ webchatBubble: webchatBubbleFlag,
253
+ });
254
+ this.analytics.ping("receivedFeatureFlags");
253
255
 
254
- if (this.useInHouseLauncher) {
255
- // TODO: there's a big delay between page load and the launcher getting added, maybe 2s. Maybe put it earlier even if it has to wait for TalkJS to load to be functional?
256
- launcherElement = await this.getInHouseLauncher();
256
+ if (webchatBubbleFlag) {
257
+ // TODO: The new icon hasn't been designed for color customization yet, so temporarily disable the background theme color
258
+ talkjsLauncherElement.style.backgroundColor = "white";
259
+ this.addChatBubble(popup, talkjsLauncherElement);
257
260
  } else {
258
- const talkjsLauncherElement = document.querySelector<HTMLElement>(
259
- "a#__talkjs_launcher"
261
+ talkjsLauncherElement.classList.add(
262
+ "meetelise-chat",
263
+ "launcher",
264
+ styles.shouldBeVisible
260
265
  );
261
- if (!talkjsLauncherElement)
262
- throw new Error("MeetElise Chat: Could not locate launcher.");
263
- launcherElement = talkjsLauncherElement;
264
266
  }
265
-
266
- launcherElement.classList.add(
267
- "meetelise-chat",
268
- "launcher",
269
- styles.shouldBeVisible
270
- );
271
-
272
- return launcherElement;
267
+ return talkjsLauncherElement;
273
268
  }
274
269
  );
275
270
  }
@@ -2,17 +2,12 @@ import Talk from "talkjs";
2
2
  import { Building } from "./fetchBuildingInfo";
3
3
  import getAvatarUrl from "./getAvatarUrl";
4
4
  import { Theme } from "./resolveTheme";
5
- import { defaultThemeId, themesById } from "./themes";
6
-
7
- const defaultAvatarUrl =
8
- "https://s3.us-west-2.amazonaws.com/meetelise.com/looping-gradient.gif";
9
5
 
10
6
  export default function createConversation(
11
7
  session: Talk.Session,
12
8
  building: Building,
13
9
  theme: Theme,
14
- chatID: string,
15
- isMobile: boolean
10
+ chatID: string
16
11
  ): Talk.ConversationBuilder {
17
12
  const agent = new Talk.User({
18
13
  id: `building_${building.id}`,
@@ -26,34 +21,16 @@ export default function createConversation(
26
21
  conversation.subject = theme.chatTitle;
27
22
  conversation.setParticipant(session.me);
28
23
  conversation.setParticipant(agent);
29
- // TODO: duplicate identifier theme
30
- // TODO: typescript abuse
31
- let themeId = defaultThemeId;
32
- if (Object.keys(themesById).includes(building.themeId)) {
33
- themeId = building.themeId as keyof typeof themesById;
34
- }
35
- const _theme = themesById[themeId];
36
24
  conversation.custom = {
37
25
  buildingId: building.id.toString(),
38
26
  userId: building.userId.toString(),
39
27
  orgId: building.orgId.toString(),
40
28
  subtitle: theme.chatSubtitle ?? null,
29
+ bannerColor: theme.bannerColor,
30
+ bannerTextColor: theme.bannerTextColor,
31
+ messageColor: theme.messageColor,
32
+ messageTextColor: theme.messageTextColor,
41
33
  url: location.href,
42
- buildingName: building.name,
43
- isMobile: isMobile.toString(),
44
- chatHeaderBackgroundColor: _theme.chatHeader.backgroundColor,
45
- chatHeaderTextColor: _theme.chatHeader.textColor,
46
- chatPaneBackgroundColor: _theme.chatPaneBackgroundColor,
47
- userMessageTextColor: _theme.message.user.textColor,
48
- userMessageBackgroundColor: _theme.message.user.backgroundColor,
49
- agentMessageTextColor: _theme.message.agent.textColor,
50
- agentMessageBackgroundColor: _theme.message.agent.backgroundColor,
51
- avatarUrl:
52
- building.avatarType === "image" && building.avatarSrc
53
- ? building.avatarSrc
54
- : defaultAvatarUrl,
55
- // uncomment this to test changes to the default avatar if your test building has its own avatar
56
- // avatarUrl: defaultAvatarUrl,
57
34
  };
58
35
  return conversation;
59
36
  }
@@ -4,13 +4,20 @@
4
4
  export interface Building {
5
5
  id: number;
6
6
 
7
- themeId: string;
8
7
  avatarInitials: string | null;
9
8
  avatarSrc: string | null;
10
9
  avatarType: "image" | "initials" | null;
10
+ backgroundColor: string | null;
11
+ bannerColor: string | null;
12
+ bannerTextColor: string | null;
11
13
  chatSubtitle: string | null;
12
14
  chatTitle: string | null;
15
+ launchButtonColor: string | null;
16
+ launchButtonIconColor: string | null;
17
+ launchButtonSize: string | null;
13
18
  logoSrc: string | null;
19
+ messageColor: string | null;
20
+ messageTextColor: string | null;
14
21
  name: string;
15
22
  primaryColor: string | null;
16
23
  userFirstName: string;
@@ -19,15 +26,6 @@ export interface Building {
19
26
  welcomeMessage: string | null;
20
27
  conversationMaintenanceMode: boolean;
21
28
  orgId: number;
22
- // old: not sure if still present in API response, but we're not using (may have mised a few above)
23
- backgroundColor: string | null;
24
- bannerColor: string | null;
25
- bannerTextColor: string | null;
26
- launchButtonColor: string | null;
27
- launchButtonIconColor: string | null;
28
- launchButtonSize: string | null;
29
- messageColor: string | null;
30
- messageTextColor: string | null;
31
29
  }
32
30
 
33
31
  /**