@active-reach/web-sdk 1.35.2 → 1.35.4
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/aegis.min.js +1 -1
- package/dist/aegis.min.js.map +1 -1
- package/dist/chat/AegisChat.d.ts +1 -0
- package/dist/chat/AegisChat.d.ts.map +1 -1
- package/dist/inapp/AegisInAppManager.d.ts +31 -15
- package/dist/inapp/AegisInAppManager.d.ts.map +1 -1
- package/dist/inapp/actions.d.ts +8 -0
- package/dist/inapp/actions.d.ts.map +1 -1
- package/dist/inapp/renderers/card-template.d.ts.map +1 -1
- package/dist/inapp/renderers/reviews.d.ts.map +1 -1
- package/dist/index.js +244 -60
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -524,7 +524,8 @@ function applyPerCardOverride(tpl, look) {
|
|
|
524
524
|
if (look.hide_price) drop("price");
|
|
525
525
|
if (look.hide_cta) drop("cta");
|
|
526
526
|
if (look.hide_rating) drop("rating");
|
|
527
|
-
if (look.cta_text)
|
|
527
|
+
if (look.cta_text)
|
|
528
|
+
elements = elements.map((e) => e.type === "cta" ? { ...e, text: look.cta_text } : e);
|
|
528
529
|
if (look.cta_action || look.cta_url || look.cta_custom_name) {
|
|
529
530
|
elements = elements.map(
|
|
530
531
|
(e) => e.type === "cta" ? {
|
|
@@ -537,7 +538,9 @@ function applyPerCardOverride(tpl, look) {
|
|
|
537
538
|
}
|
|
538
539
|
if (look.badge) {
|
|
539
540
|
const badgeEl = { type: "badge", text: look.badge, color: look.badge_color };
|
|
540
|
-
elements = elements.some((e) => e.type === "badge") ? elements.map((e) => e.type === "badge" ? badgeEl : e) : [elements[0], badgeEl, ...elements.slice(1)].filter(
|
|
541
|
+
elements = elements.some((e) => e.type === "badge") ? elements.map((e) => e.type === "badge" ? badgeEl : e) : [elements[0], badgeEl, ...elements.slice(1)].filter(
|
|
542
|
+
Boolean
|
|
543
|
+
);
|
|
541
544
|
}
|
|
542
545
|
return { ...tpl, frame, elements };
|
|
543
546
|
}
|
|
@@ -585,13 +588,12 @@ function paintRating(card) {
|
|
|
585
588
|
const count = Number(meta.review_count ?? 0);
|
|
586
589
|
const rr = document.createElement("div");
|
|
587
590
|
rr.style.cssText = "display:flex;align-items:center;gap:3px;font-size:11px;line-height:1;";
|
|
588
|
-
const star2 =
|
|
589
|
-
star2.
|
|
590
|
-
star2.style.cssText = "color:#059669;";
|
|
591
|
+
const star2 = lucideSvgEl("star", { size: 12, color: "#059669" });
|
|
592
|
+
if (star2) star2.setAttribute("fill", "#059669");
|
|
591
593
|
const val = document.createElement("span");
|
|
592
594
|
val.textContent = avg.toFixed(1);
|
|
593
595
|
val.style.cssText = "color:#059669;font-weight:700;";
|
|
594
|
-
rr.appendChild(star2);
|
|
596
|
+
if (star2) rr.appendChild(star2);
|
|
595
597
|
rr.appendChild(val);
|
|
596
598
|
if (count > 0) {
|
|
597
599
|
const c = document.createElement("span");
|
|
@@ -650,7 +652,9 @@ function appendRatingThanks(wrap, el2, ctx, fg, accent, isHigh) {
|
|
|
650
652
|
cta.textContent = el2.rating_high_cta || "Leave a review";
|
|
651
653
|
cta.style.cssText = `margin:4px auto 0;display:block;padding:9px 18px;border:none;border-radius:999px;background:${accent};color:#fff;font-size:13px;font-weight:800;cursor:pointer;`;
|
|
652
654
|
cta.addEventListener("click", () => {
|
|
653
|
-
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
655
|
+
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
656
|
+
meta: { element: "rating_input", cta: "high_review" }
|
|
657
|
+
});
|
|
654
658
|
if (ctx.navigate) ctx.navigate(url, "cta");
|
|
655
659
|
else window.location.href = url;
|
|
656
660
|
});
|
|
@@ -866,11 +870,25 @@ function buildProductRatingListElement(el2, ctx, fg) {
|
|
|
866
870
|
panel.style.display = "flex";
|
|
867
871
|
toggle.style.display = "none";
|
|
868
872
|
});
|
|
873
|
+
const hint = document.createElement("div");
|
|
874
|
+
hint.style.cssText = `font-size:11px;color:${fg};opacity:.65;`;
|
|
869
875
|
if (typeof el2.review_earn_hint === "string" && el2.review_earn_hint.trim()) {
|
|
870
|
-
const hint = document.createElement("div");
|
|
871
876
|
hint.textContent = el2.review_earn_hint.trim();
|
|
872
|
-
hint.style.cssText = `font-size:11px;color:${fg};opacity:.65;`;
|
|
873
877
|
panel.appendChild(hint);
|
|
878
|
+
} else if (ctx.apiHost && ctx.writeKey && ctx.organizationId) {
|
|
879
|
+
panel.appendChild(hint);
|
|
880
|
+
const url = `${ctx.apiHost}/v1/in-app/review-earn-hint`;
|
|
881
|
+
void fetch(url, {
|
|
882
|
+
headers: { "X-Aegis-Write-Key": ctx.writeKey, "X-Organization-ID": ctx.organizationId }
|
|
883
|
+
}).then((r) => r.ok ? r.json() : null).then((d) => {
|
|
884
|
+
if (!d || d.enabled !== true) return;
|
|
885
|
+
const name = typeof d.points_name === "string" ? d.points_name : "points";
|
|
886
|
+
const parts = [`Earn ${Number(d.base)} ${name}`];
|
|
887
|
+
if (Number(d.photo_bonus) > 0) parts.push(`+${Number(d.photo_bonus)} photo`);
|
|
888
|
+
if (Number(d.video_bonus) > 0) parts.push(`+${Number(d.video_bonus)} video`);
|
|
889
|
+
hint.textContent = parts.join(" · ");
|
|
890
|
+
}).catch(() => {
|
|
891
|
+
});
|
|
874
892
|
}
|
|
875
893
|
if (allowText) {
|
|
876
894
|
const ta = document.createElement("textarea");
|
|
@@ -970,7 +988,9 @@ function buildProductRatingListElement(el2, ctx, fg) {
|
|
|
970
988
|
submitBtn.disabled = true;
|
|
971
989
|
submitBtn.style.opacity = ".6";
|
|
972
990
|
submitBtn.textContent = "Saving…";
|
|
973
|
-
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
991
|
+
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
992
|
+
meta: { element: "product_rating_list", items: rated.length }
|
|
993
|
+
});
|
|
974
994
|
Promise.resolve(
|
|
975
995
|
(_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "rating", {
|
|
976
996
|
items: rated.map((pid) => ({ product_id: pid, score: scores[pid] })),
|
|
@@ -1005,10 +1025,12 @@ function buildProductRatingListElement(el2, ctx, fg) {
|
|
|
1005
1025
|
nameEl.textContent = it.name || it.product_id;
|
|
1006
1026
|
nameEl.style.cssText = `flex:1;min-width:0;font-size:13px;color:${fg};overflow:hidden;text-overflow:ellipsis;white-space:nowrap;`;
|
|
1007
1027
|
rowEl.appendChild(nameEl);
|
|
1008
|
-
rowEl.appendChild(
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1028
|
+
rowEl.appendChild(
|
|
1029
|
+
starRow(scale, (n) => {
|
|
1030
|
+
scores[it.product_id] = n;
|
|
1031
|
+
refreshSubmit();
|
|
1032
|
+
})
|
|
1033
|
+
);
|
|
1012
1034
|
list.appendChild(rowEl);
|
|
1013
1035
|
});
|
|
1014
1036
|
wrap.appendChild(list);
|
|
@@ -1029,12 +1051,13 @@ function buildProductRatingListElement(el2, ctx, fg) {
|
|
|
1029
1051
|
wrap.appendChild(loader);
|
|
1030
1052
|
const url = `${ctx.apiHost}/v1/in-app/rateable-items?token=${encodeURIComponent(token)}`;
|
|
1031
1053
|
const ac = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
1032
|
-
if (ac)
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1054
|
+
if (ac)
|
|
1055
|
+
setTimeout(() => {
|
|
1056
|
+
try {
|
|
1057
|
+
ac.abort();
|
|
1058
|
+
} catch {
|
|
1059
|
+
}
|
|
1060
|
+
}, 6e3);
|
|
1038
1061
|
fetch(url, { headers: { "X-Aegis-Write-Key": ctx.writeKey }, signal: ac == null ? void 0 : ac.signal }).then((r) => r.ok ? r.json() : null).then((data) => {
|
|
1039
1062
|
render(Array.isArray(data == null ? void 0 : data.items) ? data.items : []);
|
|
1040
1063
|
}).catch(() => {
|
|
@@ -1086,7 +1109,9 @@ function buildPollElement(el2, ctx, fg, accent) {
|
|
|
1086
1109
|
btn.style.background = accent;
|
|
1087
1110
|
btn.style.color = "#fff";
|
|
1088
1111
|
btn.style.opacity = "1";
|
|
1089
|
-
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
1112
|
+
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
1113
|
+
meta: { element: "poll", option_index: idx, option_label: label }
|
|
1114
|
+
});
|
|
1090
1115
|
void ((_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "poll", { option_index: idx, option_label: label }));
|
|
1091
1116
|
(_b = ctx.playReaction) == null ? void 0 : _b.call(ctx, wrap, "affirm");
|
|
1092
1117
|
appendThanks(wrap, el2, fg, "Thanks for your answer!");
|
|
@@ -1109,7 +1134,9 @@ function appendNpsThanks(wrap, el2, ctx, fg, accent, isPromoter) {
|
|
|
1109
1134
|
cta.textContent = el2.nps_promoter_cta || "Leave a review";
|
|
1110
1135
|
cta.style.cssText = `margin:4px auto 0;display:block;padding:9px 18px;border:none;border-radius:999px;background:${accent};color:#fff;font-size:13px;font-weight:800;cursor:pointer;`;
|
|
1111
1136
|
cta.addEventListener("click", () => {
|
|
1112
|
-
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
1137
|
+
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
1138
|
+
meta: { element: "nps", cta: "promoter_review" }
|
|
1139
|
+
});
|
|
1113
1140
|
if (ctx.navigate) ctx.navigate(url, "cta");
|
|
1114
1141
|
else window.location.href = url;
|
|
1115
1142
|
});
|
|
@@ -1154,7 +1181,11 @@ function renderNpsReasonStep(wrap, el2, ctx, score, fg, accent) {
|
|
|
1154
1181
|
done = true;
|
|
1155
1182
|
const comment = withComment ? ta.value.trim() : "";
|
|
1156
1183
|
void ((_a = ctx.submitResponse) == null ? void 0 : _a.call(ctx, "nps", comment ? { score, band, comment } : { score, band }));
|
|
1157
|
-
(_b = ctx.playReaction) == null ? void 0 : _b.call(
|
|
1184
|
+
(_b = ctx.playReaction) == null ? void 0 : _b.call(
|
|
1185
|
+
ctx,
|
|
1186
|
+
wrap,
|
|
1187
|
+
score >= promoterThreshold ? "affirm" : score <= 6 ? "empathize" : "acknowledge"
|
|
1188
|
+
);
|
|
1158
1189
|
appendNpsThanks(wrap, el2, ctx, fg, accent, score >= promoterThreshold);
|
|
1159
1190
|
};
|
|
1160
1191
|
const btnRow = document.createElement("div");
|
|
@@ -1210,7 +1241,9 @@ function buildNpsElement(el2, ctx, fg, accent, muted) {
|
|
|
1210
1241
|
btn.style.color = "#fff";
|
|
1211
1242
|
btn.style.opacity = "1";
|
|
1212
1243
|
const npsPayload = { score: n, band: npsBandFromScore(n) };
|
|
1213
|
-
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
1244
|
+
ctx.trackEvent(ctx.campaign.id, "clicked", {
|
|
1245
|
+
meta: { element: "nps", score: n, band: npsPayload.band }
|
|
1246
|
+
});
|
|
1214
1247
|
if (el2.nps_followup_enabled === true) {
|
|
1215
1248
|
renderNpsReasonStep(wrap, el2, ctx, n, fg, accent);
|
|
1216
1249
|
} else {
|
|
@@ -1327,13 +1360,21 @@ function buildCtaAction(el2, card, meta, inline, sanitizeUrl) {
|
|
|
1327
1360
|
const sku = meta.sku;
|
|
1328
1361
|
const platformProductId = meta.platform_product_id;
|
|
1329
1362
|
const platformUrl = safe(meta.platform_url);
|
|
1363
|
+
const platformVariantId = meta.platform_variant_id;
|
|
1330
1364
|
switch (kind) {
|
|
1331
1365
|
case "open_cart":
|
|
1332
1366
|
return { type: "open_cart" };
|
|
1333
1367
|
case "open_login":
|
|
1334
1368
|
return { type: "open_login" };
|
|
1335
1369
|
case "open_product":
|
|
1336
|
-
return {
|
|
1370
|
+
return {
|
|
1371
|
+
type: "open_product",
|
|
1372
|
+
productId,
|
|
1373
|
+
sku,
|
|
1374
|
+
url: platformUrl || url,
|
|
1375
|
+
platformProductId,
|
|
1376
|
+
platformUrl
|
|
1377
|
+
};
|
|
1337
1378
|
case "navigate":
|
|
1338
1379
|
return { type: "navigate", url: url || "" };
|
|
1339
1380
|
case "custom":
|
|
@@ -1345,6 +1386,7 @@ function buildCtaAction(el2, card, meta, inline, sanitizeUrl) {
|
|
|
1345
1386
|
productId,
|
|
1346
1387
|
sku,
|
|
1347
1388
|
platformProductId,
|
|
1389
|
+
platformVariantId,
|
|
1348
1390
|
title: card.title,
|
|
1349
1391
|
price: meta.price ?? card.body,
|
|
1350
1392
|
imageUrl: card.image_url,
|
|
@@ -1550,7 +1592,10 @@ function renderCardFromTemplate(card, opts) {
|
|
|
1550
1592
|
var _a;
|
|
1551
1593
|
e.stopPropagation();
|
|
1552
1594
|
trackEvent(campaign.id, "clicked", { stepId: el2.id || "add" });
|
|
1553
|
-
(_a = ctx.dispatchAction) == null ? void 0 : _a.call(
|
|
1595
|
+
(_a = ctx.dispatchAction) == null ? void 0 : _a.call(
|
|
1596
|
+
ctx,
|
|
1597
|
+
buildCtaAction(el2, card, meta, inline, sanitizeUrl)
|
|
1598
|
+
);
|
|
1554
1599
|
});
|
|
1555
1600
|
(overlayOnImage ? imageWrap : container).appendChild(cta);
|
|
1556
1601
|
break;
|
|
@@ -4018,6 +4063,7 @@ class AegisChat {
|
|
|
4018
4063
|
this.pendingEcho = [];
|
|
4019
4064
|
this.hasInteracted = false;
|
|
4020
4065
|
this.writeKey = config.writeKey;
|
|
4066
|
+
this.organizationId = config.organizationId;
|
|
4021
4067
|
this.apiHost = (config.apiHost ?? "").replace(/\/$/, "");
|
|
4022
4068
|
this.contactId = config.contactId;
|
|
4023
4069
|
this.channel = config.channel ?? "web";
|
|
@@ -4207,6 +4253,7 @@ class AegisChat {
|
|
|
4207
4253
|
const url = new URL("/v1/stream/realtime", this.apiHost || window.location.origin);
|
|
4208
4254
|
url.searchParams.set("ticket", ticket);
|
|
4209
4255
|
url.searchParams.set("mode", "ephemeral");
|
|
4256
|
+
if (this.organizationId) url.searchParams.set("org", this.organizationId);
|
|
4210
4257
|
try {
|
|
4211
4258
|
this.eventSource = new EventSource(url.toString());
|
|
4212
4259
|
this.eventSource.onopen = () => {
|
|
@@ -5368,6 +5415,27 @@ function verifiedBadge() {
|
|
|
5368
5415
|
b.appendChild(el("span", "", "Verified purchase"));
|
|
5369
5416
|
return b;
|
|
5370
5417
|
}
|
|
5418
|
+
function incentivizedBadge() {
|
|
5419
|
+
const b = el(
|
|
5420
|
+
"span",
|
|
5421
|
+
"display:inline-flex;align-items:center;gap:3px;font-size:12px;font-weight:600;color:#6b7280;"
|
|
5422
|
+
);
|
|
5423
|
+
const svg = document.createElementNS(SVG_NS, "svg");
|
|
5424
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
5425
|
+
svg.setAttribute("width", "13");
|
|
5426
|
+
svg.setAttribute("height", "13");
|
|
5427
|
+
const p = document.createElementNS(SVG_NS, "path");
|
|
5428
|
+
p.setAttribute("d", "M20 12v9H4v-9M2 7h20v5H2zM12 7v14M12 7a2.5 2.5 0 0 0-2.5-2.5C8 4.5 7 6 7 7M12 7a2.5 2.5 0 0 1 2.5-2.5C16 4.5 17 6 17 7");
|
|
5429
|
+
p.setAttribute("fill", "none");
|
|
5430
|
+
p.setAttribute("stroke", "#6b7280");
|
|
5431
|
+
p.setAttribute("stroke-width", "2");
|
|
5432
|
+
p.setAttribute("stroke-linecap", "round");
|
|
5433
|
+
p.setAttribute("stroke-linejoin", "round");
|
|
5434
|
+
svg.appendChild(p);
|
|
5435
|
+
b.appendChild(svg);
|
|
5436
|
+
b.appendChild(el("span", "", "Incentivized"));
|
|
5437
|
+
return b;
|
|
5438
|
+
}
|
|
5371
5439
|
function avatar(name, accent) {
|
|
5372
5440
|
const initial = (name.trim()[0] || "?").toUpperCase();
|
|
5373
5441
|
const a = el(
|
|
@@ -5495,6 +5563,7 @@ function renderReviewsInline(ctx, target, creds) {
|
|
|
5495
5563
|
const nameRow = el("div", "display:flex;flex-wrap:wrap;align-items:center;gap:8px;");
|
|
5496
5564
|
nameRow.appendChild(el("span", "font-size:15px;font-weight:700;color:#111827;", ((_b = r.reviewer_snapshot) == null ? void 0 : _b.name) ?? "Customer"));
|
|
5497
5565
|
if (r.verified_purchase) nameRow.appendChild(verifiedBadge());
|
|
5566
|
+
if (r.is_incentivized) nameRow.appendChild(incentivizedBadge());
|
|
5498
5567
|
idn.appendChild(nameRow);
|
|
5499
5568
|
idn.appendChild(stars(r.rating ?? 0, 14));
|
|
5500
5569
|
head.appendChild(idn);
|
|
@@ -6126,6 +6195,7 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6126
6195
|
return;
|
|
6127
6196
|
}
|
|
6128
6197
|
const url = new URL("/v1/stream/realtime", this.apiHost);
|
|
6198
|
+
url.searchParams.set("org", this.organizationId);
|
|
6129
6199
|
const headers = {
|
|
6130
6200
|
"X-Aegis-Write-Key": this.writeKey,
|
|
6131
6201
|
"X-Organization-ID": this.organizationId
|
|
@@ -6133,11 +6203,10 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6133
6203
|
if (this.contactId) {
|
|
6134
6204
|
headers["X-Contact-ID"] = this.contactId;
|
|
6135
6205
|
}
|
|
6136
|
-
const queryParams = new URLSearchParams();
|
|
6137
6206
|
Object.entries(headers).forEach(([key, value]) => {
|
|
6138
|
-
|
|
6207
|
+
url.searchParams.append(key, value);
|
|
6139
6208
|
});
|
|
6140
|
-
this.eventSource = new EventSource(
|
|
6209
|
+
this.eventSource = new EventSource(url.toString());
|
|
6141
6210
|
this.eventSource.addEventListener("open", () => {
|
|
6142
6211
|
this.log("SSE connection established");
|
|
6143
6212
|
this.reconnectAttempts = 0;
|
|
@@ -6300,7 +6369,7 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6300
6369
|
}
|
|
6301
6370
|
tryDisplayNextCampaign() {
|
|
6302
6371
|
const campaign = this.campaigns.find(
|
|
6303
|
-
(c) => !this.displayedCampaigns.has(c.id) && !c.client_trigger && !this.isSuppressed(c.id) && c.type !== "active_web_chat"
|
|
6372
|
+
(c) => !this.displayedCampaigns.has(c.id) && !c.client_trigger && !this.isSuppressed(c.id) && c.type !== "active_web_chat" && !(Array.isArray(c.delivery_modes) && c.delivery_modes.length > 0 && !c.delivery_modes.includes("in_app_overlay"))
|
|
6304
6373
|
);
|
|
6305
6374
|
if (campaign) {
|
|
6306
6375
|
this.displayCampaign(campaign);
|
|
@@ -6333,34 +6402,38 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6333
6402
|
* `<div data-aegis-slot="feedback">` will accept the first active
|
|
6334
6403
|
* campaign with `widget_category='feedback'` + embedded_card mode.
|
|
6335
6404
|
*/
|
|
6336
|
-
/**
|
|
6337
|
-
*
|
|
6338
|
-
*
|
|
6339
|
-
*
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
matchesCurrentSurface(c) {
|
|
6345
|
-
if (!c.surface || c.surface.length === 0) return true;
|
|
6346
|
-
if (this.currentSurface === null) return true;
|
|
6347
|
-
return c.surface.includes(this.currentSurface);
|
|
6405
|
+
/** Normalize a declared free-form screen name to a canonical surface key
|
|
6406
|
+
* ('storefront.catalog' → 'storefront_catalog'), or null when it maps
|
|
6407
|
+
* outside the enum (e.g. 'shopify.product'). Mirrors the server's
|
|
6408
|
+
* `_canonical_surface_from_screen`. */
|
|
6409
|
+
canonicalSurfaceFromScreen() {
|
|
6410
|
+
if (!this.currentScreen) return null;
|
|
6411
|
+
const norm = this.currentScreen.trim().toLowerCase().replace(/[.\- ]/g, "_");
|
|
6412
|
+
return _AegisInAppManager.KNOWN_SURFACES.has(norm) ? norm : null;
|
|
6348
6413
|
}
|
|
6349
6414
|
/**
|
|
6350
|
-
* Dynamic Screens P2 — combined surface + free-form
|
|
6351
|
-
*
|
|
6352
|
-
*
|
|
6353
|
-
*
|
|
6354
|
-
*
|
|
6415
|
+
* Dynamic Screens P2 + substrate isolation — combined surface + free-form
|
|
6416
|
+
* screen predicate, mirroring the server's `_screen_or_surface_matches`:
|
|
6417
|
+
* - screen-only campaign matches strictly on `currentScreen`
|
|
6418
|
+
* - a declared screen NORMALIZES into the canonical tier
|
|
6419
|
+
* ('storefront.catalog' → storefront_catalog) so surface pins match
|
|
6420
|
+
* - a declared screen that maps OUTSIDE the enum (e.g. 'shopify.product')
|
|
6421
|
+
* does NOT fall through to the all-surfaces default for surface-pinned
|
|
6422
|
+
* campaigns — actii.me-pinned campaigns never render on Shopify
|
|
6423
|
+
* - hosts that declare nothing keep the legacy render-everything default
|
|
6355
6424
|
*/
|
|
6356
6425
|
matchesCurrentScreenOrSurface(c) {
|
|
6357
6426
|
const screens = c.target_screens;
|
|
6358
|
-
|
|
6359
|
-
|
|
6427
|
+
const screenOk = !!screens && screens.length > 0 && this.currentScreen !== null && screens.includes(this.currentScreen);
|
|
6428
|
+
if (screens && screens.length > 0 && (!c.surface || c.surface.length === 0)) {
|
|
6429
|
+
return screenOk;
|
|
6360
6430
|
}
|
|
6361
|
-
|
|
6362
|
-
if (!c.surface || c.surface.length === 0) return
|
|
6363
|
-
|
|
6431
|
+
if (screenOk) return true;
|
|
6432
|
+
if (!c.surface || c.surface.length === 0) return true;
|
|
6433
|
+
const effective = this.currentSurface ?? this.canonicalSurfaceFromScreen();
|
|
6434
|
+
if (effective !== null) return c.surface.includes(effective);
|
|
6435
|
+
if (this.currentScreen !== null) return false;
|
|
6436
|
+
return true;
|
|
6364
6437
|
}
|
|
6365
6438
|
// Re-fill slots that mount after a poll (SPA route changes). Observes the DOM
|
|
6366
6439
|
// once; when a new [data-aegis-slot] appears it re-runs renderIntoSlots from
|
|
@@ -6396,6 +6469,7 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6396
6469
|
this.slotObserver.observe(document.body, { childList: true, subtree: true });
|
|
6397
6470
|
}
|
|
6398
6471
|
renderIntoSlots() {
|
|
6472
|
+
var _a, _b;
|
|
6399
6473
|
if (typeof document === "undefined") return;
|
|
6400
6474
|
this.ensureSlotObserver();
|
|
6401
6475
|
const slots = document.querySelectorAll("[data-aegis-slot]");
|
|
@@ -6408,6 +6482,16 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6408
6482
|
const category = c.widget_category;
|
|
6409
6483
|
if (!modes || !modes.includes("embedded_card")) continue;
|
|
6410
6484
|
if (!category) continue;
|
|
6485
|
+
const anchorSel = (_b = (_a = c.interactive_config) == null ? void 0 : _a.anchor) == null ? void 0 : _b.element_selector;
|
|
6486
|
+
if (typeof anchorSel === "string" && anchorSel.trim()) {
|
|
6487
|
+
let anchorResolves = false;
|
|
6488
|
+
try {
|
|
6489
|
+
anchorResolves = !!document.querySelector(anchorSel);
|
|
6490
|
+
} catch {
|
|
6491
|
+
anchorResolves = false;
|
|
6492
|
+
}
|
|
6493
|
+
if (anchorResolves) continue;
|
|
6494
|
+
}
|
|
6411
6495
|
if (seenIds.has(c.id)) continue;
|
|
6412
6496
|
seenIds.add(c.id);
|
|
6413
6497
|
const arr2 = eligibleByCategory.get(category);
|
|
@@ -6434,8 +6518,8 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6434
6518
|
slot.querySelectorAll(":scope > [data-aegis-slot-default]").forEach((d) => d.remove());
|
|
6435
6519
|
const attrMs = parseInt(slot.getAttribute("data-aegis-slot-rotate") || "0", 10);
|
|
6436
6520
|
const declaredMs = list.reduce((m2, c) => {
|
|
6437
|
-
var
|
|
6438
|
-
const v = Number((
|
|
6521
|
+
var _a2;
|
|
6522
|
+
const v = Number((_a2 = c.interactive_config) == null ? void 0 : _a2.slot_rotate_ms);
|
|
6439
6523
|
return Number.isFinite(v) && v > m2 ? v : m2;
|
|
6440
6524
|
}, 0);
|
|
6441
6525
|
const rotateMs = attrMs > 0 ? attrMs : declaredMs;
|
|
@@ -9128,6 +9212,12 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
9128
9212
|
* so journeys can react. Degrades gracefully where Notification is absent.
|
|
9129
9213
|
*/
|
|
9130
9214
|
renderPushPrimer(campaign, ic, bg, text) {
|
|
9215
|
+
if (typeof Notification !== "undefined" && Notification.permission !== "default") {
|
|
9216
|
+
void this.trackEvent(campaign.id, "dismissed", {
|
|
9217
|
+
meta: { reason: `permission_${Notification.permission}` }
|
|
9218
|
+
});
|
|
9219
|
+
return;
|
|
9220
|
+
}
|
|
9131
9221
|
const overlay = this.createOverlay("aegis-in-app-push-primer-overlay");
|
|
9132
9222
|
const modal = document.createElement("div");
|
|
9133
9223
|
modal.style.cssText = `
|
|
@@ -9138,8 +9228,13 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
9138
9228
|
const body = document.createElement("div");
|
|
9139
9229
|
body.style.cssText = "padding: 24px; text-align: center;";
|
|
9140
9230
|
const icon = document.createElement("div");
|
|
9141
|
-
icon.style.cssText = "
|
|
9142
|
-
|
|
9231
|
+
icon.style.cssText = "margin-bottom: 8px; display: flex; justify-content: center;";
|
|
9232
|
+
if (typeof ic.icon === "string" && ic.icon.length > 0) {
|
|
9233
|
+
icon.textContent = ic.icon;
|
|
9234
|
+
icon.style.fontSize = "36px";
|
|
9235
|
+
} else {
|
|
9236
|
+
icon.innerHTML = lucideSvg("bell", { size: 36, color: text });
|
|
9237
|
+
}
|
|
9143
9238
|
body.appendChild(icon);
|
|
9144
9239
|
const title = document.createElement("div");
|
|
9145
9240
|
title.style.cssText = "font-size: 18px; font-weight: 700; margin-bottom: 8px;";
|
|
@@ -9159,7 +9254,8 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
9159
9254
|
if (overlay.parentNode) document.body.removeChild(overlay);
|
|
9160
9255
|
return;
|
|
9161
9256
|
}
|
|
9162
|
-
|
|
9257
|
+
enableBtn.disabled = true;
|
|
9258
|
+
void this._subscribeFromPushPrimer().then((perm) => {
|
|
9163
9259
|
void this.trackEvent(campaign.id, "clicked", { meta: { permission: perm } });
|
|
9164
9260
|
if (overlay.parentNode) document.body.removeChild(overlay);
|
|
9165
9261
|
});
|
|
@@ -9178,6 +9274,90 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
9178
9274
|
this.addAnimationStyles();
|
|
9179
9275
|
document.body.appendChild(overlay);
|
|
9180
9276
|
}
|
|
9277
|
+
/**
|
|
9278
|
+
* Push-primer Enable handler — runs the SAME end-to-end subscribe flow the
|
|
9279
|
+
* (now-retired) ACS PushOptInBanner did, so the primer is a complete web-push
|
|
9280
|
+
* opt-in on every surface (ACS / Shopify / 3rd-party site), not just an OS
|
|
9281
|
+
* permission prompt. Steps: fetch VAPID public key → register SW → hard-ask →
|
|
9282
|
+
* subscribe → POST /v1/web_push/subscriptions → stitch known contact.
|
|
9283
|
+
*
|
|
9284
|
+
* Push runtime resolution prefers the already-loaded `window.AegisWebPush`
|
|
9285
|
+
* global (the Shopify embed lazy-loads it via /apps/active_reach/push.js, so
|
|
9286
|
+
* we reuse that single instance instead of registering the SW twice), and
|
|
9287
|
+
* only dynamic-imports the module as a fallback for hosts that didn't preload
|
|
9288
|
+
* it (ACS / 3rd-party sites). Returns the resolved permission string for
|
|
9289
|
+
* engagement reporting; falls back to a bare permission ask if push can't be
|
|
9290
|
+
* wired (no propertyId, no VAPID).
|
|
9291
|
+
*/
|
|
9292
|
+
async _subscribeFromPushPrimer() {
|
|
9293
|
+
var _a, _b, _c, _d;
|
|
9294
|
+
if (!this.propertyId) {
|
|
9295
|
+
return Notification.requestPermission();
|
|
9296
|
+
}
|
|
9297
|
+
try {
|
|
9298
|
+
const cfgRes = await fetch(`${this.apiHost}/v1/sdk/config`, {
|
|
9299
|
+
headers: { "X-Aegis-Write-Key": this.writeKey }
|
|
9300
|
+
});
|
|
9301
|
+
const cfg = await cfgRes.json();
|
|
9302
|
+
const vapidPublicKey = ((_a = cfg.push) == null ? void 0 : _a.vapid_public_key) || "";
|
|
9303
|
+
if (!((_b = cfg.push) == null ? void 0 : _b.enabled) || !vapidPublicKey) {
|
|
9304
|
+
return Notification.requestPermission();
|
|
9305
|
+
}
|
|
9306
|
+
const serviceWorkerPath = ((_c = cfg.push) == null ? void 0 : _c.service_worker_path) || "/aegis-sw.js";
|
|
9307
|
+
const serviceWorkerScope = ((_d = cfg.push) == null ? void 0 : _d.service_worker_scope) || "/";
|
|
9308
|
+
if (this.contactId && typeof window !== "undefined") {
|
|
9309
|
+
try {
|
|
9310
|
+
window.localStorage.setItem("aegis_contact_id", this.contactId);
|
|
9311
|
+
} catch {
|
|
9312
|
+
}
|
|
9313
|
+
}
|
|
9314
|
+
const writeKey = this.writeKey;
|
|
9315
|
+
const apiHost = this.apiHost;
|
|
9316
|
+
const organizationId = this.organizationId;
|
|
9317
|
+
const apiClient = {
|
|
9318
|
+
async post(endpoint, payload, headers) {
|
|
9319
|
+
const res = await fetch(`${apiHost}${endpoint}`, {
|
|
9320
|
+
method: "POST",
|
|
9321
|
+
headers: {
|
|
9322
|
+
"Content-Type": "application/json",
|
|
9323
|
+
"X-Aegis-Write-Key": writeKey,
|
|
9324
|
+
...organizationId ? { "X-Organization-ID": organizationId } : {},
|
|
9325
|
+
...headers ?? {}
|
|
9326
|
+
},
|
|
9327
|
+
body: JSON.stringify(payload)
|
|
9328
|
+
});
|
|
9329
|
+
if (!res.ok) throw new Error(`push api ${endpoint} → ${res.status}`);
|
|
9330
|
+
return res.json().catch(() => ({}));
|
|
9331
|
+
}
|
|
9332
|
+
};
|
|
9333
|
+
const AegisWebPush2 = typeof window !== "undefined" && window.AegisWebPush || (await import("./push/AegisWebPush.js")).AegisWebPush;
|
|
9334
|
+
const webPush = new AegisWebPush2(apiClient, {
|
|
9335
|
+
vapidPublicKey,
|
|
9336
|
+
propertyId: this.propertyId,
|
|
9337
|
+
// Anon id doubles as the device-fingerprint salt so a re-subscribe
|
|
9338
|
+
// after VAPID rotation upserts onto the same subscription row.
|
|
9339
|
+
firstPartyCookieId: readAnonIdFromStorage$1() || this.contactId || this.organizationId || "",
|
|
9340
|
+
serviceWorkerPath,
|
|
9341
|
+
serviceWorkerScope,
|
|
9342
|
+
autoPrompt: false,
|
|
9343
|
+
writeKey,
|
|
9344
|
+
organizationId
|
|
9345
|
+
});
|
|
9346
|
+
await webPush.initialize();
|
|
9347
|
+
const granted = await webPush.requestPermission();
|
|
9348
|
+
if (granted && this.contactId) {
|
|
9349
|
+
await webPush.identify({ contactId: this.contactId });
|
|
9350
|
+
}
|
|
9351
|
+
return Notification.permission;
|
|
9352
|
+
} catch (err) {
|
|
9353
|
+
console.warn("[Aegis in-app] push-primer subscribe failed:", err);
|
|
9354
|
+
try {
|
|
9355
|
+
return await Notification.requestPermission();
|
|
9356
|
+
} catch {
|
|
9357
|
+
return Notification.permission;
|
|
9358
|
+
}
|
|
9359
|
+
}
|
|
9360
|
+
}
|
|
9181
9361
|
renderQuiz(campaign, ic, bg, text) {
|
|
9182
9362
|
const overlay = this.createOverlay("aegis-in-app-quiz-overlay");
|
|
9183
9363
|
const modal = document.createElement("div");
|
|
@@ -11241,12 +11421,16 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
11241
11421
|
// journey/template/outlet on the engagement delivery_event.
|
|
11242
11422
|
journey_execution_id: trackedCampaign == null ? void 0 : trackedCampaign.journey_execution_id,
|
|
11243
11423
|
lifecycle_fork_id: trackedCampaign == null ? void 0 : trackedCampaign.lifecycle_fork_id,
|
|
11244
|
-
|
|
11424
|
+
// Outlet the customer is ACTIVELY viewing (setLocation) wins — that's
|
|
11425
|
+
// where the impression/click actually happened; fall back to the served
|
|
11426
|
+
// campaign's outlet, else brand-tier. The gateway promotes this to the
|
|
11427
|
+
// canonical event's top-level for per-outlet in-app analytics.
|
|
11428
|
+
location_id: this.currentLocationId ?? (trackedCampaign == null ? void 0 : trackedCampaign.location_id),
|
|
11245
11429
|
metadata: {
|
|
11246
11430
|
property_id: this.propertyId,
|
|
11247
11431
|
variant_id: this.getVariantId(campaignId) ?? void 0,
|
|
11248
11432
|
lifecycle_fork_id: trackedCampaign == null ? void 0 : trackedCampaign.lifecycle_fork_id,
|
|
11249
|
-
location_id: trackedCampaign == null ? void 0 : trackedCampaign.location_id,
|
|
11433
|
+
location_id: this.currentLocationId ?? (trackedCampaign == null ? void 0 : trackedCampaign.location_id),
|
|
11250
11434
|
// Renderer-supplied metadata (e.g. anchor_resolved/anchor_selector on
|
|
11251
11435
|
// an anchor_unresolved event). The gateway spreads payload.metadata
|
|
11252
11436
|
// into the canonical event so these land in in_app_events.metadata.
|
|
@@ -14537,7 +14721,7 @@ const _AegisMessageRuntime = class _AegisMessageRuntime {
|
|
|
14537
14721
|
var _a;
|
|
14538
14722
|
this.initialized = false;
|
|
14539
14723
|
{
|
|
14540
|
-
const v = "1.35.
|
|
14724
|
+
const v = "1.35.4";
|
|
14541
14725
|
try {
|
|
14542
14726
|
globalThis.__AEGIS_SDK_VERSION__ = v;
|
|
14543
14727
|
} catch {
|