@meetelise/chat 1.47.2 → 1.48.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.
@@ -49,6 +49,25 @@ afterEach(() => {
49
49
  restore();
50
50
  });
51
51
 
52
+ it("does not throw when removing a widget that already detached itself", async function () {
53
+ const element = document.createElement("me-chat");
54
+ document.body.appendChild(element);
55
+
56
+ (MEChat as unknown as { meChat: HTMLElement | null }).meChat = element;
57
+
58
+ await (
59
+ MEChat as unknown as { removeWidget: () => Promise<void> }
60
+ ).removeWidget();
61
+
62
+ expect((MEChat as unknown as { meChat: HTMLElement | null }).meChat).to.equal(
63
+ null
64
+ );
65
+
66
+ await (
67
+ MEChat as unknown as { removeWidget: () => Promise<void> }
68
+ ).removeWidget();
69
+ });
70
+
52
71
  it.skip("shows the launcher", async function () {
53
72
  // Given an API that returns this building theme
54
73
  stub(window, "fetch").resolves(stubResponse);
@@ -34,6 +34,47 @@ interface PositioningOptions {
34
34
  export interface HealthcareOptions extends PositioningOptions {
35
35
  id: string;
36
36
  }
37
+
38
+ export type StudioWebchatMode = "host_managed_context";
39
+
40
+ export type StudioWebchatSessionScopeType =
41
+ | "building"
42
+ | "building_group"
43
+ | "site";
44
+
45
+ export interface StudioWebchatSessionScope {
46
+ type: StudioWebchatSessionScopeType;
47
+ key: string;
48
+ buildingSlug?: string;
49
+ siteId?: string;
50
+ buildingGroupId?: string | number;
51
+ defaultBuildingSlug?: string;
52
+ allowedBuildingSlugs?: string[];
53
+ }
54
+
55
+ export interface StudioWebchatContext {
56
+ source?: "elise_studio";
57
+ siteId?: string;
58
+ propertyId?: string | number | null;
59
+ organization?: string | null;
60
+ building?: string | null;
61
+ buildingGroupId?: string | number | null;
62
+ sessionScope?: StudioWebchatSessionScope;
63
+ path?: string;
64
+ pageId?: string;
65
+ }
66
+
67
+ export interface StudioWebchatClearContext {
68
+ source?: "elise_studio";
69
+ reason?: "no_property_for_route" | "webchat_disabled" | "ambiguous_route" | string;
70
+ preserveCache?: boolean;
71
+ }
72
+
73
+ export interface StudioWebchatOptions {
74
+ siteId: string;
75
+ mode?: StudioWebchatMode;
76
+ }
77
+
37
78
  export default class MEChat {
38
79
  static meChat: MEChatLitElement | null = null;
39
80
  static healthChat: HealthChatLitElement | null = null;
@@ -45,6 +86,11 @@ export default class MEChat {
45
86
  static brandColor = "";
46
87
  static backgroundColor = "";
47
88
  static healthcareId = "";
89
+ static activeStudioOrgSlug = "";
90
+ static activeStudioBuildingSlug = "";
91
+ static activeStudioSessionScopeKey = "";
92
+ static activeStudioOptions: Options | null = null;
93
+ static studioUpdateToken = 0;
48
94
  static overridePlacement: {
49
95
  right?: number;
50
96
  bottom?: number;
@@ -91,7 +137,7 @@ export default class MEChat {
91
137
  this.orgSlug =
92
138
  opts.organization === "Pacific Urban Residential"
93
139
  ? "cbc11aba-21c4-4571-bc43-ff9d86a029e3"
94
- : opts.organization;
140
+ : opts.organization || "";
95
141
  this.hasBuildingSlug = !!opts.building;
96
142
  this.overridePlacement = {
97
143
  right: opts?.position?.right,
@@ -102,6 +148,11 @@ export default class MEChat {
102
148
  }
103
149
  installFont();
104
150
 
151
+ if (opts.studio && !opts.building) {
152
+ this.handleStudioContext(opts);
153
+ return;
154
+ }
155
+
105
156
  let meChatElement:
106
157
  | MEChatLitElement
107
158
  | UtilitiesChatElement
@@ -131,7 +182,7 @@ export default class MEChat {
131
182
  "orgSlug",
132
183
  opts.organization === "Pacific Urban Residential"
133
184
  ? "cbc11aba-21c4-4571-bc43-ff9d86a029e3"
134
- : opts.organization
185
+ : opts.organization || ""
135
186
  );
136
187
  meChatElement.setAttribute(
137
188
  "widgetType",
@@ -216,14 +267,102 @@ export default class MEChat {
216
267
  }
217
268
 
218
269
  static async remove(): Promise<void> {
270
+ this.studioUpdateToken += 1;
271
+ await this.removeWidget();
272
+ this.activeStudioOrgSlug = "";
273
+ this.activeStudioBuildingSlug = "";
274
+ this.activeStudioSessionScopeKey = "";
275
+ this.activeStudioOptions = null;
276
+ }
277
+
278
+ static async removeWidget(): Promise<void> {
219
279
  if (!this.meChat) {
220
280
  return;
221
281
  }
222
- await this.meChat.remove();
223
- document.body.removeChild(this.meChat);
282
+ const meChat = this.meChat;
283
+ await meChat.remove();
284
+ meChat.parentNode?.removeChild(meChat);
224
285
  MEChat.meChat = null;
225
286
  }
226
287
 
288
+ static handleStudioContext(opts: Options): void {
289
+ if (!opts.studio) return;
290
+
291
+ this.activeStudioOptions = opts;
292
+ }
293
+
294
+ static updateStudioContext(context: StudioWebchatContext): void {
295
+ const opts = this.activeStudioOptions;
296
+ if (!opts?.studio) return;
297
+ const updateToken = ++this.studioUpdateToken;
298
+
299
+ if (context.siteId && context.siteId !== opts.studio.siteId) {
300
+ sendLoggingEvent({
301
+ logType: LogType.warn,
302
+ logTitle: "[STUDIO_WEBCHAT_CONTEXT_SITE_MISMATCH]",
303
+ logData: {
304
+ activeSiteId: opts.studio.siteId,
305
+ contextSiteId: context.siteId,
306
+ url: window.location.href,
307
+ },
308
+ });
309
+ return;
310
+ }
311
+
312
+ const orgSlug = coerceStudioContextString(context.organization);
313
+ const buildingSlug = coerceStudioContextString(context.building);
314
+
315
+ if (!orgSlug || !buildingSlug) {
316
+ this.clearStudioContext({
317
+ source: "elise_studio",
318
+ reason: "missing_context",
319
+ preserveCache: true,
320
+ });
321
+ return;
322
+ }
323
+
324
+ const sessionScopeKey = coerceStudioSessionScopeKey(
325
+ context.sessionScope,
326
+ buildingSlug
327
+ );
328
+ const normalizedOrgSlug = normalizeOrgSlug(orgSlug);
329
+
330
+ if (
331
+ this.activeStudioOrgSlug === normalizedOrgSlug &&
332
+ this.activeStudioBuildingSlug === buildingSlug &&
333
+ this.activeStudioSessionScopeKey === sessionScopeKey
334
+ ) {
335
+ return;
336
+ }
337
+
338
+ void this.removeWidget().then(() => {
339
+ if (updateToken !== this.studioUpdateToken) return;
340
+
341
+ this.activeStudioOrgSlug = normalizedOrgSlug;
342
+ this.activeStudioBuildingSlug = buildingSlug;
343
+ this.activeStudioSessionScopeKey = sessionScopeKey;
344
+ this.orgSlug = normalizedOrgSlug;
345
+
346
+ this.start(
347
+ {
348
+ ...opts,
349
+ organization: orgSlug,
350
+ building: buildingSlug,
351
+ },
352
+ false
353
+ );
354
+ });
355
+ }
356
+
357
+ static clearStudioContext(_context?: StudioWebchatClearContext): void {
358
+ void _context;
359
+ this.studioUpdateToken += 1;
360
+ this.activeStudioOrgSlug = "";
361
+ this.activeStudioBuildingSlug = "";
362
+ this.activeStudioSessionScopeKey = "";
363
+ void this.removeWidget();
364
+ }
365
+
227
366
  static async handleBuildingslug(
228
367
  meChat: MEChatLitElement,
229
368
  orgSlug?: string,
@@ -369,9 +508,35 @@ export default class MEChat {
369
508
  }
370
509
  }
371
510
 
511
+ function coerceStudioContextString(value: string | null | undefined): string {
512
+ if (typeof value !== "string") {
513
+ return "";
514
+ }
515
+
516
+ return value.trim();
517
+ }
518
+
519
+ function normalizeOrgSlug(orgSlug: string): string {
520
+ return orgSlug === "Pacific Urban Residential"
521
+ ? "cbc11aba-21c4-4571-bc43-ff9d86a029e3"
522
+ : orgSlug;
523
+ }
524
+
525
+ function coerceStudioSessionScopeKey(
526
+ sessionScope: StudioWebchatSessionScope | undefined,
527
+ buildingSlug: string
528
+ ): string {
529
+ if (!sessionScope?.key) {
530
+ return `building:${buildingSlug}`;
531
+ }
532
+
533
+ return sessionScope.key;
534
+ }
535
+
372
536
  export interface Options {
373
- building: string;
374
- organization: string;
537
+ building?: string;
538
+ organization?: string;
539
+ studio?: StudioWebchatOptions;
375
540
  avatarSrc?: string;
376
541
  mini?: boolean;
377
542
  launcherStyles?: StyleInfo;