@magicfeedback/popup-sdk 1.8.1 → 1.8.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.
- package/dist/index.d.mts +25 -11
- package/dist/index.d.ts +25 -11
- package/dist/index.js +50 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +51 -22
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +15 -5
- package/dist/react-native.d.ts +15 -5
- package/dist/react-native.js +48 -19
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +48 -19
- package/dist/react-native.mjs.map +1 -1
- package/dist/{renderPopup-3CZ6WTXF.mjs → renderPopup-ERUKQE4U.mjs} +3 -3
- package/dist/renderPopup-ERUKQE4U.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/renderPopup-3CZ6WTXF.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -868,7 +868,7 @@ function createReactNativeRenderer() {
|
|
|
868
868
|
}
|
|
869
869
|
|
|
870
870
|
// src/ui/preload.ts
|
|
871
|
-
var defaultLoader = () => import("./renderPopup-
|
|
871
|
+
var defaultLoader = () => import("./renderPopup-ERUKQE4U.mjs");
|
|
872
872
|
var pending = null;
|
|
873
873
|
function preloadRenderPopup(loader = defaultLoader) {
|
|
874
874
|
if (pending) return pending;
|
|
@@ -941,7 +941,7 @@ var BrowserPopupRenderer = class {
|
|
|
941
941
|
if (!this.container) return;
|
|
942
942
|
this.visible = true;
|
|
943
943
|
const container = this.container;
|
|
944
|
-
void import("./renderPopup-
|
|
944
|
+
void import("./renderPopup-ERUKQE4U.mjs").then(({ renderPopup }) => {
|
|
945
945
|
renderPopup(container, surveyId, productId, actions, emit, onClose, env, userId, style, sessionId, miniService, analyticsFeedbackSessionId, options);
|
|
946
946
|
});
|
|
947
947
|
}
|
|
@@ -1206,14 +1206,7 @@ function createFeedbackSink(options) {
|
|
|
1206
1206
|
const post = async (body, final, sessionEnd = false) => {
|
|
1207
1207
|
const json = JSON.stringify(body);
|
|
1208
1208
|
const small = json.length <= KEEPALIVE_MAX_BYTES;
|
|
1209
|
-
if (final
|
|
1210
|
-
const payload = typeof Blob !== "undefined" ? new Blob([json], { type: "application/json" }) : json;
|
|
1211
|
-
if (options.sendBeaconImpl(url, payload)) {
|
|
1212
|
-
options.log?.("[DeepdotsAnalytics] final flush via sendBeacon");
|
|
1213
|
-
return;
|
|
1214
|
-
}
|
|
1215
|
-
options.log?.("[DeepdotsAnalytics] sendBeacon rejected the batch; falling back to fetch");
|
|
1216
|
-
}
|
|
1209
|
+
if (final) options.log?.("[DeepdotsAnalytics] final flush via fetch keepalive");
|
|
1217
1210
|
const f = options.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
1218
1211
|
if (!f) {
|
|
1219
1212
|
options.log?.("[DeepdotsAnalytics] no fetch available; payload not sent", body);
|
|
@@ -1223,6 +1216,9 @@ function createFeedbackSink(options) {
|
|
|
1223
1216
|
method: "POST",
|
|
1224
1217
|
headers: { "Content-Type": "application/json" },
|
|
1225
1218
|
body: json,
|
|
1219
|
+
// La API autentica por `publicKey` en el body y responde `Allow-Origin: *`: una
|
|
1220
|
+
// petición con credenciales fallaría el CORS. Explícito para no depender del default.
|
|
1221
|
+
credentials: "omit",
|
|
1226
1222
|
// keepalive: el POST sobrevive a la navegación mientras el body sea pequeño.
|
|
1227
1223
|
...small ? { keepalive: true } : {}
|
|
1228
1224
|
});
|
|
@@ -1315,6 +1311,15 @@ var NavigationObserver = class {
|
|
|
1315
1311
|
this.closeCurrent();
|
|
1316
1312
|
this.currentScreen = null;
|
|
1317
1313
|
}
|
|
1314
|
+
/**
|
|
1315
|
+
* Retoma el seguimiento en la pantalla actual tras un `stop()`, cuando se abre una sesión
|
|
1316
|
+
* nueva sin navegar (cambio de usuario, vuelta de la bfcache). No-op si nunca se instaló
|
|
1317
|
+
* (navegación manual por `setScreen`, RN) o si ya hay pantalla en curso.
|
|
1318
|
+
*/
|
|
1319
|
+
restart() {
|
|
1320
|
+
if (!this.installed || this.currentScreen != null || typeof window === "undefined") return;
|
|
1321
|
+
this.begin(window.location.href);
|
|
1322
|
+
}
|
|
1318
1323
|
closeCurrent() {
|
|
1319
1324
|
if (this.currentScreen == null) return;
|
|
1320
1325
|
const exit = this.now();
|
|
@@ -1954,8 +1959,6 @@ var DeepdotsPopups = class {
|
|
|
1954
1959
|
baseUrl: this.baseUrl,
|
|
1955
1960
|
keys: config.analytics,
|
|
1956
1961
|
log: (...a) => this.log(...a),
|
|
1957
|
-
// sendBeacon: único transporte que sobrevive al cierre de la página.
|
|
1958
|
-
sendBeaconImpl: typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function" ? (url, body) => navigator.sendBeacon(url, body) : void 0,
|
|
1959
1962
|
onSessionId: (id) => {
|
|
1960
1963
|
this.analyticsFeedbackSessionId = id;
|
|
1961
1964
|
},
|
|
@@ -2003,7 +2006,6 @@ var DeepdotsPopups = class {
|
|
|
2003
2006
|
this.navObserver.onVisit((v) => this.track("deepdots_page_view", { screen: v.screen, duration_seconds: v.durationSeconds }));
|
|
2004
2007
|
this.navObserver.install();
|
|
2005
2008
|
this.engagement = new EngagementTracker();
|
|
2006
|
-
this.engagement.resume();
|
|
2007
2009
|
this.setupAnalyticsFlush();
|
|
2008
2010
|
this.openSession();
|
|
2009
2011
|
const pendingCrashes = this.crashReporter.drainPendingCrashes();
|
|
@@ -2222,6 +2224,7 @@ var DeepdotsPopups = class {
|
|
|
2222
2224
|
this.navStarted = false;
|
|
2223
2225
|
this.analytics?.exitAllMiniServices();
|
|
2224
2226
|
this.flushEngagement();
|
|
2227
|
+
this.engagement?.pause();
|
|
2225
2228
|
this.track("deepdots_session_end", { reason });
|
|
2226
2229
|
this.flushAnalytics({ final: reason === "page_hide", sessionEnd: true });
|
|
2227
2230
|
this.analyticsFeedbackSessionId = void 0;
|
|
@@ -2237,6 +2240,12 @@ var DeepdotsPopups = class {
|
|
|
2237
2240
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2238
2241
|
this.sessionOpen = true;
|
|
2239
2242
|
this.track("deepdots_session_start", {});
|
|
2243
|
+
this.navObserver?.restart();
|
|
2244
|
+
if (this.isDocumentVisible()) this.engagement?.resume();
|
|
2245
|
+
}
|
|
2246
|
+
/** Web: si la pestaña se ve. Sin `document` (RN) manda AppState vía onForeground/onBackground. */
|
|
2247
|
+
isDocumentVisible() {
|
|
2248
|
+
return typeof document === "undefined" || document.visibilityState !== "hidden";
|
|
2240
2249
|
}
|
|
2241
2250
|
/** Payload que se ENVIARÍA al endpoint de analytics (no envía ni vacía el buffer). */
|
|
2242
2251
|
previewAnalytics() {
|
|
@@ -2249,8 +2258,8 @@ var DeepdotsPopups = class {
|
|
|
2249
2258
|
}
|
|
2250
2259
|
/**
|
|
2251
2260
|
* Envía el lote acumulado de analytics y vacía el buffer.
|
|
2252
|
-
* `final: true` (cierre de página/app)
|
|
2253
|
-
*
|
|
2261
|
+
* `final: true` (cierre de página/app): el lote no espera al primer POST en vuelo, porque
|
|
2262
|
+
* el documento se muere; un lote que falle por red o 5xx se re-encola para el siguiente flush.
|
|
2254
2263
|
*/
|
|
2255
2264
|
flushAnalytics(options) {
|
|
2256
2265
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
@@ -2271,21 +2280,30 @@ var DeepdotsPopups = class {
|
|
|
2271
2280
|
/** Flush automático al ocultar/cerrar la página (no perder el lote pendiente). */
|
|
2272
2281
|
setupAnalyticsFlush() {
|
|
2273
2282
|
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
2274
|
-
this.
|
|
2283
|
+
this.startAnalyticsFlushTimer();
|
|
2275
2284
|
window.addEventListener("pagehide", () => {
|
|
2276
2285
|
clearInterval(this.analyticsFlushTimer);
|
|
2277
2286
|
this.closeSession("page_hide");
|
|
2278
2287
|
});
|
|
2288
|
+
window.addEventListener("pageshow", (event) => {
|
|
2289
|
+
if (!event.persisted) return;
|
|
2290
|
+
this.startAnalyticsFlushTimer();
|
|
2291
|
+
this.openSession();
|
|
2292
|
+
});
|
|
2279
2293
|
document.addEventListener("visibilitychange", () => {
|
|
2280
2294
|
if (document.visibilityState === "hidden") {
|
|
2281
2295
|
this.flushEngagement();
|
|
2282
2296
|
this.engagement?.pause();
|
|
2283
2297
|
this.flushAnalytics();
|
|
2284
2298
|
} else if (document.visibilityState === "visible") {
|
|
2285
|
-
this.engagement?.resume();
|
|
2299
|
+
if (this.sessionOpen) this.engagement?.resume();
|
|
2286
2300
|
}
|
|
2287
2301
|
});
|
|
2288
2302
|
}
|
|
2303
|
+
startAnalyticsFlushTimer() {
|
|
2304
|
+
clearInterval(this.analyticsFlushTimer);
|
|
2305
|
+
this.analyticsFlushTimer = setInterval(() => this.flushAnalytics(), ANALYTICS_FLUSH_INTERVAL_MS);
|
|
2306
|
+
}
|
|
2289
2307
|
/** Enable auto-launch functionality with configured triggers */
|
|
2290
2308
|
autoLaunch() {
|
|
2291
2309
|
if (!this.initialized) {
|
|
@@ -2379,6 +2397,7 @@ var DeepdotsPopups = class {
|
|
|
2379
2397
|
this.emitEvent("popup_shown", def.surveyId, { popupId: def.id });
|
|
2380
2398
|
}
|
|
2381
2399
|
shouldShow(def, pathUrl, skipPathCheck = false) {
|
|
2400
|
+
if (this.storage) this.loadPopupState(this.storage);
|
|
2382
2401
|
if (this.isPathExcluded(def, pathUrl)) {
|
|
2383
2402
|
return false;
|
|
2384
2403
|
}
|
|
@@ -2787,7 +2806,7 @@ var DeepdotsPopups = class {
|
|
|
2787
2806
|
}
|
|
2788
2807
|
if (!this.popupContainer) return;
|
|
2789
2808
|
const container = this.popupContainer;
|
|
2790
|
-
void import("./renderPopup-
|
|
2809
|
+
void import("./renderPopup-ERUKQE4U.mjs").then(({ renderPopup }) => {
|
|
2791
2810
|
renderPopup(
|
|
2792
2811
|
container,
|
|
2793
2812
|
surveyId,
|
|
@@ -2905,22 +2924,32 @@ var DeepdotsPopups = class {
|
|
|
2905
2924
|
this.persistPopupState();
|
|
2906
2925
|
}
|
|
2907
2926
|
/**
|
|
2908
|
-
*
|
|
2909
|
-
*
|
|
2927
|
+
* Fusiona el estado de redisplay del storage con el de memoria: por cada popup/survey gana
|
|
2928
|
+
* la entrada MÁS RECIENTE. Fusionar y no sustituir es lo que hace convivir a varias
|
|
2929
|
+
* pestañas sobre el mismo `localStorage`: se llama en el `init()`, antes de cada decisión
|
|
2930
|
+
* de mostrar y antes de cada escritura. `answeredSurveys` se reconstruye desde las
|
|
2931
|
+
* entradas COMPLETED: es exactamente lo que persiste `markSurveyAnswered`.
|
|
2910
2932
|
*/
|
|
2911
2933
|
loadPopupState(storage) {
|
|
2912
2934
|
const state = readPopupState(storage);
|
|
2913
2935
|
Object.entries(state.lastShown).forEach(([popupId, timestamp]) => {
|
|
2914
|
-
this.lastShown.set(popupId, timestamp);
|
|
2936
|
+
if (timestamp > (this.lastShown.get(popupId) ?? -Infinity)) this.lastShown.set(popupId, timestamp);
|
|
2915
2937
|
});
|
|
2916
2938
|
Object.entries(state.progress).forEach(([surveyId, entry]) => {
|
|
2939
|
+
const current = this.surveyProgress.get(surveyId);
|
|
2940
|
+
if (current && current.timestamp >= entry.timestamp) return;
|
|
2917
2941
|
this.surveyProgress.set(surveyId, { status: entry.status, timestamp: entry.timestamp });
|
|
2918
2942
|
if (entry.status === "COMPLETED") this.answeredSurveys.add(surveyId);
|
|
2919
2943
|
});
|
|
2920
2944
|
}
|
|
2921
|
-
/**
|
|
2945
|
+
/**
|
|
2946
|
+
* Vuelca el estado de redisplay al storage. Sin storage (o bloqueado) degrada a memoria.
|
|
2947
|
+
* Fusiona antes de escribir: escribir solo la foto en memoria borraba lo que otra pestaña
|
|
2948
|
+
* hubiese guardado desde nuestro `init()`.
|
|
2949
|
+
*/
|
|
2922
2950
|
persistPopupState() {
|
|
2923
2951
|
if (!this.storage) return;
|
|
2952
|
+
this.loadPopupState(this.storage);
|
|
2924
2953
|
writePopupState(this.storage, {
|
|
2925
2954
|
lastShown: Object.fromEntries(this.lastShown),
|
|
2926
2955
|
progress: Object.fromEntries(this.surveyProgress)
|