@active-reach/web-sdk 1.35.2 → 1.36.0
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 +49 -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 +328 -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);
|
|
@@ -5899,6 +5968,11 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
5899
5968
|
}
|
|
5900
5969
|
if (typeof localStorage !== "undefined") {
|
|
5901
5970
|
localStorage.setItem("aegis_returning_user", "1");
|
|
5971
|
+
try {
|
|
5972
|
+
const pv = parseInt(localStorage.getItem("aegis_pv_count") || "0", 10);
|
|
5973
|
+
localStorage.setItem("aegis_pv_count", String(pv + 1));
|
|
5974
|
+
} catch {
|
|
5975
|
+
}
|
|
5902
5976
|
}
|
|
5903
5977
|
ensureInAppFont();
|
|
5904
5978
|
await this.refreshCampaigns();
|
|
@@ -5911,6 +5985,22 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
5911
5985
|
this.log("Re-fetching campaigns for surface/screen declared during init");
|
|
5912
5986
|
void this.refreshCampaigns();
|
|
5913
5987
|
}
|
|
5988
|
+
if (typeof Notification !== "undefined" && Notification.permission === "granted" && this.propertyId) {
|
|
5989
|
+
let alreadyHealed = false;
|
|
5990
|
+
try {
|
|
5991
|
+
alreadyHealed = sessionStorage.getItem("aegis_push_boot_heal") === "1";
|
|
5992
|
+
} catch {
|
|
5993
|
+
}
|
|
5994
|
+
if (!alreadyHealed) {
|
|
5995
|
+
try {
|
|
5996
|
+
sessionStorage.setItem("aegis_push_boot_heal", "1");
|
|
5997
|
+
} catch {
|
|
5998
|
+
}
|
|
5999
|
+
void this._subscribeFromPushPrimer().catch((err) => {
|
|
6000
|
+
this.log("boot push resubscribe failed", err);
|
|
6001
|
+
});
|
|
6002
|
+
}
|
|
6003
|
+
}
|
|
5914
6004
|
this.log("AegisInApp initialized successfully");
|
|
5915
6005
|
}
|
|
5916
6006
|
updateUserId(userId) {
|
|
@@ -6126,6 +6216,7 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6126
6216
|
return;
|
|
6127
6217
|
}
|
|
6128
6218
|
const url = new URL("/v1/stream/realtime", this.apiHost);
|
|
6219
|
+
url.searchParams.set("org", this.organizationId);
|
|
6129
6220
|
const headers = {
|
|
6130
6221
|
"X-Aegis-Write-Key": this.writeKey,
|
|
6131
6222
|
"X-Organization-ID": this.organizationId
|
|
@@ -6133,11 +6224,10 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6133
6224
|
if (this.contactId) {
|
|
6134
6225
|
headers["X-Contact-ID"] = this.contactId;
|
|
6135
6226
|
}
|
|
6136
|
-
const queryParams = new URLSearchParams();
|
|
6137
6227
|
Object.entries(headers).forEach(([key, value]) => {
|
|
6138
|
-
|
|
6228
|
+
url.searchParams.append(key, value);
|
|
6139
6229
|
});
|
|
6140
|
-
this.eventSource = new EventSource(
|
|
6230
|
+
this.eventSource = new EventSource(url.toString());
|
|
6141
6231
|
this.eventSource.addEventListener("open", () => {
|
|
6142
6232
|
this.log("SSE connection established");
|
|
6143
6233
|
this.reconnectAttempts = 0;
|
|
@@ -6300,7 +6390,7 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6300
6390
|
}
|
|
6301
6391
|
tryDisplayNextCampaign() {
|
|
6302
6392
|
const campaign = this.campaigns.find(
|
|
6303
|
-
(c) => !this.displayedCampaigns.has(c.id) && !c.client_trigger && !this.isSuppressed(c.id) && c.type !== "active_web_chat"
|
|
6393
|
+
(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
6394
|
);
|
|
6305
6395
|
if (campaign) {
|
|
6306
6396
|
this.displayCampaign(campaign);
|
|
@@ -6333,34 +6423,38 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6333
6423
|
* `<div data-aegis-slot="feedback">` will accept the first active
|
|
6334
6424
|
* campaign with `widget_category='feedback'` + embedded_card mode.
|
|
6335
6425
|
*/
|
|
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);
|
|
6426
|
+
/** Normalize a declared free-form screen name to a canonical surface key
|
|
6427
|
+
* ('storefront.catalog' → 'storefront_catalog'), or null when it maps
|
|
6428
|
+
* outside the enum (e.g. 'shopify.product'). Mirrors the server's
|
|
6429
|
+
* `_canonical_surface_from_screen`. */
|
|
6430
|
+
canonicalSurfaceFromScreen() {
|
|
6431
|
+
if (!this.currentScreen) return null;
|
|
6432
|
+
const norm = this.currentScreen.trim().toLowerCase().replace(/[.\- ]/g, "_");
|
|
6433
|
+
return _AegisInAppManager.KNOWN_SURFACES.has(norm) ? norm : null;
|
|
6348
6434
|
}
|
|
6349
6435
|
/**
|
|
6350
|
-
* Dynamic Screens P2 — combined surface + free-form
|
|
6351
|
-
*
|
|
6352
|
-
*
|
|
6353
|
-
*
|
|
6354
|
-
*
|
|
6436
|
+
* Dynamic Screens P2 + substrate isolation — combined surface + free-form
|
|
6437
|
+
* screen predicate, mirroring the server's `_screen_or_surface_matches`:
|
|
6438
|
+
* - screen-only campaign matches strictly on `currentScreen`
|
|
6439
|
+
* - a declared screen NORMALIZES into the canonical tier
|
|
6440
|
+
* ('storefront.catalog' → storefront_catalog) so surface pins match
|
|
6441
|
+
* - a declared screen that maps OUTSIDE the enum (e.g. 'shopify.product')
|
|
6442
|
+
* does NOT fall through to the all-surfaces default for surface-pinned
|
|
6443
|
+
* campaigns — actii.me-pinned campaigns never render on Shopify
|
|
6444
|
+
* - hosts that declare nothing keep the legacy render-everything default
|
|
6355
6445
|
*/
|
|
6356
6446
|
matchesCurrentScreenOrSurface(c) {
|
|
6357
6447
|
const screens = c.target_screens;
|
|
6358
|
-
|
|
6359
|
-
|
|
6448
|
+
const screenOk = !!screens && screens.length > 0 && this.currentScreen !== null && screens.includes(this.currentScreen);
|
|
6449
|
+
if (screens && screens.length > 0 && (!c.surface || c.surface.length === 0)) {
|
|
6450
|
+
return screenOk;
|
|
6360
6451
|
}
|
|
6361
|
-
|
|
6362
|
-
if (!c.surface || c.surface.length === 0) return
|
|
6363
|
-
|
|
6452
|
+
if (screenOk) return true;
|
|
6453
|
+
if (!c.surface || c.surface.length === 0) return true;
|
|
6454
|
+
const effective = this.currentSurface ?? this.canonicalSurfaceFromScreen();
|
|
6455
|
+
if (effective !== null) return c.surface.includes(effective);
|
|
6456
|
+
if (this.currentScreen !== null) return false;
|
|
6457
|
+
return true;
|
|
6364
6458
|
}
|
|
6365
6459
|
// Re-fill slots that mount after a poll (SPA route changes). Observes the DOM
|
|
6366
6460
|
// once; when a new [data-aegis-slot] appears it re-runs renderIntoSlots from
|
|
@@ -6396,6 +6490,7 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6396
6490
|
this.slotObserver.observe(document.body, { childList: true, subtree: true });
|
|
6397
6491
|
}
|
|
6398
6492
|
renderIntoSlots() {
|
|
6493
|
+
var _a, _b;
|
|
6399
6494
|
if (typeof document === "undefined") return;
|
|
6400
6495
|
this.ensureSlotObserver();
|
|
6401
6496
|
const slots = document.querySelectorAll("[data-aegis-slot]");
|
|
@@ -6408,6 +6503,16 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6408
6503
|
const category = c.widget_category;
|
|
6409
6504
|
if (!modes || !modes.includes("embedded_card")) continue;
|
|
6410
6505
|
if (!category) continue;
|
|
6506
|
+
const anchorSel = (_b = (_a = c.interactive_config) == null ? void 0 : _a.anchor) == null ? void 0 : _b.element_selector;
|
|
6507
|
+
if (typeof anchorSel === "string" && anchorSel.trim()) {
|
|
6508
|
+
let anchorResolves = false;
|
|
6509
|
+
try {
|
|
6510
|
+
anchorResolves = !!document.querySelector(anchorSel);
|
|
6511
|
+
} catch {
|
|
6512
|
+
anchorResolves = false;
|
|
6513
|
+
}
|
|
6514
|
+
if (anchorResolves) continue;
|
|
6515
|
+
}
|
|
6411
6516
|
if (seenIds.has(c.id)) continue;
|
|
6412
6517
|
seenIds.add(c.id);
|
|
6413
6518
|
const arr2 = eligibleByCategory.get(category);
|
|
@@ -6434,8 +6539,8 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6434
6539
|
slot.querySelectorAll(":scope > [data-aegis-slot-default]").forEach((d) => d.remove());
|
|
6435
6540
|
const attrMs = parseInt(slot.getAttribute("data-aegis-slot-rotate") || "0", 10);
|
|
6436
6541
|
const declaredMs = list.reduce((m2, c) => {
|
|
6437
|
-
var
|
|
6438
|
-
const v = Number((
|
|
6542
|
+
var _a2;
|
|
6543
|
+
const v = Number((_a2 = c.interactive_config) == null ? void 0 : _a2.slot_rotate_ms);
|
|
6439
6544
|
return Number.isFinite(v) && v > m2 ? v : m2;
|
|
6440
6545
|
}, 0);
|
|
6441
6546
|
const rotateMs = attrMs > 0 ? attrMs : declaredMs;
|
|
@@ -9128,6 +9233,72 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
9128
9233
|
* so journeys can react. Degrades gracefully where Notification is absent.
|
|
9129
9234
|
*/
|
|
9130
9235
|
renderPushPrimer(campaign, ic, bg, text) {
|
|
9236
|
+
if (typeof Notification !== "undefined" && Notification.permission !== "default") {
|
|
9237
|
+
void this.trackEvent(campaign.id, "dismissed", {
|
|
9238
|
+
meta: { reason: `permission_${Notification.permission}` }
|
|
9239
|
+
});
|
|
9240
|
+
return;
|
|
9241
|
+
}
|
|
9242
|
+
void this._fetchPushOptInConfig().then((optIn) => {
|
|
9243
|
+
if (optIn.mode !== "standard") {
|
|
9244
|
+
void this.trackEvent(campaign.id, "dismissed", {
|
|
9245
|
+
meta: { reason: `opt_in_mode_${optIn.mode}` }
|
|
9246
|
+
});
|
|
9247
|
+
return;
|
|
9248
|
+
}
|
|
9249
|
+
if (!this._promptTriggerSatisfied(optIn.trigger)) {
|
|
9250
|
+
return;
|
|
9251
|
+
}
|
|
9252
|
+
this._renderPushPrimerModal(campaign, ic, bg, text);
|
|
9253
|
+
});
|
|
9254
|
+
}
|
|
9255
|
+
/**
|
|
9256
|
+
* Fetch the property's push opt-in mode/trigger from /v1/sdk/config
|
|
9257
|
+
* (browser HTTP cache + ETag make repeat calls cheap). Fail-open to
|
|
9258
|
+
* 'standard'/'immediate' — a config blip must not silently kill the
|
|
9259
|
+
* only web-push opt-in surface.
|
|
9260
|
+
*/
|
|
9261
|
+
async _fetchPushOptInConfig() {
|
|
9262
|
+
var _a, _b;
|
|
9263
|
+
try {
|
|
9264
|
+
const res = await fetch(`${this.apiHost}/v1/sdk/config`, {
|
|
9265
|
+
headers: { "X-Aegis-Write-Key": this.writeKey }
|
|
9266
|
+
});
|
|
9267
|
+
const cfg = await res.json();
|
|
9268
|
+
return {
|
|
9269
|
+
mode: ((_a = cfg.push) == null ? void 0 : _a.opt_in_mode) || "standard",
|
|
9270
|
+
trigger: ((_b = cfg.push) == null ? void 0 : _b.prompt_trigger) || "immediate"
|
|
9271
|
+
};
|
|
9272
|
+
} catch {
|
|
9273
|
+
return { mode: "standard", trigger: "immediate" };
|
|
9274
|
+
}
|
|
9275
|
+
}
|
|
9276
|
+
/**
|
|
9277
|
+
* Prompt-timing gate for the standard primer.
|
|
9278
|
+
* immediate → always
|
|
9279
|
+
* second_page_view → from the 2nd page load on (localStorage counter,
|
|
9280
|
+
* incremented once per manager init)
|
|
9281
|
+
* after_first_order→ once `aegis_ordered` is set (host runtimes set it
|
|
9282
|
+
* on purchase; storefront checkout does)
|
|
9283
|
+
* manual → never auto-shows; host calls the primer explicitly
|
|
9284
|
+
*/
|
|
9285
|
+
_promptTriggerSatisfied(trigger) {
|
|
9286
|
+
try {
|
|
9287
|
+
switch (trigger) {
|
|
9288
|
+
case "second_page_view":
|
|
9289
|
+
return parseInt(localStorage.getItem("aegis_pv_count") || "0", 10) >= 2;
|
|
9290
|
+
case "after_first_order":
|
|
9291
|
+
return localStorage.getItem("aegis_ordered") === "1";
|
|
9292
|
+
case "manual":
|
|
9293
|
+
return false;
|
|
9294
|
+
default:
|
|
9295
|
+
return true;
|
|
9296
|
+
}
|
|
9297
|
+
} catch {
|
|
9298
|
+
return true;
|
|
9299
|
+
}
|
|
9300
|
+
}
|
|
9301
|
+
_renderPushPrimerModal(campaign, ic, bg, text) {
|
|
9131
9302
|
const overlay = this.createOverlay("aegis-in-app-push-primer-overlay");
|
|
9132
9303
|
const modal = document.createElement("div");
|
|
9133
9304
|
modal.style.cssText = `
|
|
@@ -9138,8 +9309,13 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
9138
9309
|
const body = document.createElement("div");
|
|
9139
9310
|
body.style.cssText = "padding: 24px; text-align: center;";
|
|
9140
9311
|
const icon = document.createElement("div");
|
|
9141
|
-
icon.style.cssText = "
|
|
9142
|
-
|
|
9312
|
+
icon.style.cssText = "margin-bottom: 8px; display: flex; justify-content: center;";
|
|
9313
|
+
if (typeof ic.icon === "string" && ic.icon.length > 0) {
|
|
9314
|
+
icon.textContent = ic.icon;
|
|
9315
|
+
icon.style.fontSize = "36px";
|
|
9316
|
+
} else {
|
|
9317
|
+
icon.innerHTML = lucideSvg("bell", { size: 36, color: text });
|
|
9318
|
+
}
|
|
9143
9319
|
body.appendChild(icon);
|
|
9144
9320
|
const title = document.createElement("div");
|
|
9145
9321
|
title.style.cssText = "font-size: 18px; font-weight: 700; margin-bottom: 8px;";
|
|
@@ -9159,7 +9335,8 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
9159
9335
|
if (overlay.parentNode) document.body.removeChild(overlay);
|
|
9160
9336
|
return;
|
|
9161
9337
|
}
|
|
9162
|
-
|
|
9338
|
+
enableBtn.disabled = true;
|
|
9339
|
+
void this._subscribeFromPushPrimer().then((perm) => {
|
|
9163
9340
|
void this.trackEvent(campaign.id, "clicked", { meta: { permission: perm } });
|
|
9164
9341
|
if (overlay.parentNode) document.body.removeChild(overlay);
|
|
9165
9342
|
});
|
|
@@ -9178,6 +9355,93 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
9178
9355
|
this.addAnimationStyles();
|
|
9179
9356
|
document.body.appendChild(overlay);
|
|
9180
9357
|
}
|
|
9358
|
+
/**
|
|
9359
|
+
* Push-primer Enable handler — runs the SAME end-to-end subscribe flow the
|
|
9360
|
+
* (now-retired) ACS PushOptInBanner did, so the primer is a complete web-push
|
|
9361
|
+
* opt-in on every surface (ACS / Shopify / 3rd-party site), not just an OS
|
|
9362
|
+
* permission prompt. Steps: fetch VAPID public key → register SW → hard-ask →
|
|
9363
|
+
* subscribe → POST /v1/web_push/subscriptions → stitch known contact.
|
|
9364
|
+
*
|
|
9365
|
+
* Push runtime resolution prefers the already-loaded `window.AegisWebPush`
|
|
9366
|
+
* global (the Shopify embed lazy-loads it via /apps/active_reach/push.js, so
|
|
9367
|
+
* we reuse that single instance instead of registering the SW twice), and
|
|
9368
|
+
* only dynamic-imports the module as a fallback for hosts that didn't preload
|
|
9369
|
+
* it (ACS / 3rd-party sites). Returns the resolved permission string for
|
|
9370
|
+
* engagement reporting; falls back to a bare permission ask if push can't be
|
|
9371
|
+
* wired (no propertyId, no VAPID).
|
|
9372
|
+
*/
|
|
9373
|
+
async _subscribeFromPushPrimer() {
|
|
9374
|
+
var _a, _b, _c, _d, _e;
|
|
9375
|
+
if (!this.propertyId) {
|
|
9376
|
+
return Notification.requestPermission();
|
|
9377
|
+
}
|
|
9378
|
+
try {
|
|
9379
|
+
const cfgRes = await fetch(`${this.apiHost}/v1/sdk/config`, {
|
|
9380
|
+
headers: { "X-Aegis-Write-Key": this.writeKey }
|
|
9381
|
+
});
|
|
9382
|
+
const cfg = await cfgRes.json();
|
|
9383
|
+
const vapidPublicKey = ((_a = cfg.push) == null ? void 0 : _a.vapid_public_key) || "";
|
|
9384
|
+
if (!((_b = cfg.push) == null ? void 0 : _b.enabled) || !vapidPublicKey) {
|
|
9385
|
+
return Notification.requestPermission();
|
|
9386
|
+
}
|
|
9387
|
+
if (((_c = cfg.push) == null ? void 0 : _c.opt_in_mode) === "off") {
|
|
9388
|
+
return Notification.permission;
|
|
9389
|
+
}
|
|
9390
|
+
const serviceWorkerPath = ((_d = cfg.push) == null ? void 0 : _d.service_worker_path) || "/aegis-sw.js";
|
|
9391
|
+
const serviceWorkerScope = ((_e = cfg.push) == null ? void 0 : _e.service_worker_scope) || "/";
|
|
9392
|
+
if (this.contactId && typeof window !== "undefined") {
|
|
9393
|
+
try {
|
|
9394
|
+
window.localStorage.setItem("aegis_contact_id", this.contactId);
|
|
9395
|
+
} catch {
|
|
9396
|
+
}
|
|
9397
|
+
}
|
|
9398
|
+
const writeKey = this.writeKey;
|
|
9399
|
+
const apiHost = this.apiHost;
|
|
9400
|
+
const organizationId = this.organizationId;
|
|
9401
|
+
const apiClient = {
|
|
9402
|
+
async post(endpoint, payload, headers) {
|
|
9403
|
+
const res = await fetch(`${apiHost}${endpoint}`, {
|
|
9404
|
+
method: "POST",
|
|
9405
|
+
headers: {
|
|
9406
|
+
"Content-Type": "application/json",
|
|
9407
|
+
"X-Aegis-Write-Key": writeKey,
|
|
9408
|
+
...organizationId ? { "X-Organization-ID": organizationId } : {},
|
|
9409
|
+
...headers ?? {}
|
|
9410
|
+
},
|
|
9411
|
+
body: JSON.stringify(payload)
|
|
9412
|
+
});
|
|
9413
|
+
if (!res.ok) throw new Error(`push api ${endpoint} → ${res.status}`);
|
|
9414
|
+
return res.json().catch(() => ({}));
|
|
9415
|
+
}
|
|
9416
|
+
};
|
|
9417
|
+
const AegisWebPush2 = typeof window !== "undefined" && window.AegisWebPush || (await import("./push/AegisWebPush.js")).AegisWebPush;
|
|
9418
|
+
const webPush = new AegisWebPush2(apiClient, {
|
|
9419
|
+
vapidPublicKey,
|
|
9420
|
+
propertyId: this.propertyId,
|
|
9421
|
+
// Anon id doubles as the device-fingerprint salt so a re-subscribe
|
|
9422
|
+
// after VAPID rotation upserts onto the same subscription row.
|
|
9423
|
+
firstPartyCookieId: readAnonIdFromStorage$1() || this.contactId || this.organizationId || "",
|
|
9424
|
+
serviceWorkerPath,
|
|
9425
|
+
serviceWorkerScope,
|
|
9426
|
+
autoPrompt: false,
|
|
9427
|
+
writeKey,
|
|
9428
|
+
organizationId
|
|
9429
|
+
});
|
|
9430
|
+
await webPush.initialize();
|
|
9431
|
+
const granted = await webPush.requestPermission();
|
|
9432
|
+
if (granted && this.contactId) {
|
|
9433
|
+
await webPush.identify({ contactId: this.contactId });
|
|
9434
|
+
}
|
|
9435
|
+
return Notification.permission;
|
|
9436
|
+
} catch (err) {
|
|
9437
|
+
console.warn("[Aegis in-app] push-primer subscribe failed:", err);
|
|
9438
|
+
try {
|
|
9439
|
+
return await Notification.requestPermission();
|
|
9440
|
+
} catch {
|
|
9441
|
+
return Notification.permission;
|
|
9442
|
+
}
|
|
9443
|
+
}
|
|
9444
|
+
}
|
|
9181
9445
|
renderQuiz(campaign, ic, bg, text) {
|
|
9182
9446
|
const overlay = this.createOverlay("aegis-in-app-quiz-overlay");
|
|
9183
9447
|
const modal = document.createElement("div");
|
|
@@ -11241,12 +11505,16 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
11241
11505
|
// journey/template/outlet on the engagement delivery_event.
|
|
11242
11506
|
journey_execution_id: trackedCampaign == null ? void 0 : trackedCampaign.journey_execution_id,
|
|
11243
11507
|
lifecycle_fork_id: trackedCampaign == null ? void 0 : trackedCampaign.lifecycle_fork_id,
|
|
11244
|
-
|
|
11508
|
+
// Outlet the customer is ACTIVELY viewing (setLocation) wins — that's
|
|
11509
|
+
// where the impression/click actually happened; fall back to the served
|
|
11510
|
+
// campaign's outlet, else brand-tier. The gateway promotes this to the
|
|
11511
|
+
// canonical event's top-level for per-outlet in-app analytics.
|
|
11512
|
+
location_id: this.currentLocationId ?? (trackedCampaign == null ? void 0 : trackedCampaign.location_id),
|
|
11245
11513
|
metadata: {
|
|
11246
11514
|
property_id: this.propertyId,
|
|
11247
11515
|
variant_id: this.getVariantId(campaignId) ?? void 0,
|
|
11248
11516
|
lifecycle_fork_id: trackedCampaign == null ? void 0 : trackedCampaign.lifecycle_fork_id,
|
|
11249
|
-
location_id: trackedCampaign == null ? void 0 : trackedCampaign.location_id,
|
|
11517
|
+
location_id: this.currentLocationId ?? (trackedCampaign == null ? void 0 : trackedCampaign.location_id),
|
|
11250
11518
|
// Renderer-supplied metadata (e.g. anchor_resolved/anchor_selector on
|
|
11251
11519
|
// an anchor_unresolved event). The gateway spreads payload.metadata
|
|
11252
11520
|
// into the canonical event so these land in in_app_events.metadata.
|
|
@@ -14537,7 +14805,7 @@ const _AegisMessageRuntime = class _AegisMessageRuntime {
|
|
|
14537
14805
|
var _a;
|
|
14538
14806
|
this.initialized = false;
|
|
14539
14807
|
{
|
|
14540
|
-
const v = "1.
|
|
14808
|
+
const v = "1.36.0";
|
|
14541
14809
|
try {
|
|
14542
14810
|
globalThis.__AEGIS_SDK_VERSION__ = v;
|
|
14543
14811
|
} catch {
|