@magicfeedback/popup-sdk 1.8.0 → 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/{chunk-XHR3MHJO.mjs → chunk-AV2TTQXB.mjs} +20 -1
- package/dist/chunk-AV2TTQXB.mjs.map +1 -0
- package/dist/{chunk-322DDMX6.mjs → chunk-EBJZDVC6.mjs} +20 -1
- package/dist/chunk-EBJZDVC6.mjs.map +1 -0
- package/dist/index.d.mts +25 -11
- package/dist/index.d.ts +25 -11
- package/dist/index.js +83 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +60 -23
- 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 +81 -19
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +60 -23
- package/dist/react-native.mjs.map +1 -1
- package/dist/{renderPopup-SNDPX5RX.mjs → renderPopup-E2OLIXIQ.mjs} +4 -2
- package/dist/renderPopup-E2OLIXIQ.mjs.map +1 -0
- package/dist/{renderPopup-HTNVACFV.mjs → renderPopup-ERUKQE4U.mjs} +6 -4
- package/dist/renderPopup-ERUKQE4U.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-322DDMX6.mjs.map +0 -1
- package/dist/chunk-XHR3MHJO.mjs.map +0 -1
- package/dist/renderPopup-HTNVACFV.mjs.map +0 -1
- package/dist/renderPopup-SNDPX5RX.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
BUSY_JS,
|
|
2
3
|
InMemoryStorage,
|
|
3
4
|
LABELS,
|
|
4
5
|
PRIMARY_COLOR_JS,
|
|
@@ -20,7 +21,7 @@ import {
|
|
|
20
21
|
sdkWarn,
|
|
21
22
|
setLogger,
|
|
22
23
|
style_default
|
|
23
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-AV2TTQXB.mjs";
|
|
24
25
|
|
|
25
26
|
// src/types/index.ts
|
|
26
27
|
var POPUP_TRIGGER_CONDITION_STATUSES = [
|
|
@@ -429,6 +430,7 @@ ${customCss}
|
|
|
429
430
|
var footer=document.getElementById('dd-footer');
|
|
430
431
|
var spinner=document.getElementById('dd-spinner');
|
|
431
432
|
var formHost=document.getElementById('mf');
|
|
433
|
+
var formWrapper=document.getElementById('dd-form-wrapper');
|
|
432
434
|
var errorHint=document.getElementById('dd-error');
|
|
433
435
|
var backBtn=document.getElementById('dd-back');
|
|
434
436
|
var startBtn=document.getElementById('dd-start');
|
|
@@ -588,6 +590,7 @@ ${customCss}
|
|
|
588
590
|
}
|
|
589
591
|
|
|
590
592
|
${PRIMARY_COLOR_JS}
|
|
593
|
+
${BUSY_JS}
|
|
591
594
|
${REVEAL_JS}
|
|
592
595
|
// El popup se ense\xF1a cuando el survey est\xE1 pintado; \`ready\` deja al host abrir su propio
|
|
593
596
|
// Modal en ese momento en vez de montar un WebView en blanco.
|
|
@@ -598,6 +601,11 @@ ${REVEAL_JS}
|
|
|
598
601
|
|
|
599
602
|
function setLoading(isLoading){
|
|
600
603
|
spinner.style.display=isLoading?'flex':'none';
|
|
604
|
+
// El spinner no tapa el survey (28px en posicion absoluta), asi que sin esto el usuario
|
|
605
|
+
// puede seguir cambiando de opcion mientras se envia la pagina, y ese cambio ya no viaja
|
|
606
|
+
// con ella. Se bloquea el wrapper, que es el elemento estable: el div de dentro lo
|
|
607
|
+
// sustituye @magicfeedback/native por su propio contenedor al generar el survey.
|
|
608
|
+
ddSetSurveyBusy(formWrapper, isLoading);
|
|
601
609
|
if(!isLoading){
|
|
602
610
|
formHost.style.visibility='visible';
|
|
603
611
|
// Fin de la carga, sea por survey cargado o por error. Entre p\xE1ginas es no-op: la
|
|
@@ -860,7 +868,7 @@ function createReactNativeRenderer() {
|
|
|
860
868
|
}
|
|
861
869
|
|
|
862
870
|
// src/ui/preload.ts
|
|
863
|
-
var defaultLoader = () => import("./renderPopup-
|
|
871
|
+
var defaultLoader = () => import("./renderPopup-ERUKQE4U.mjs");
|
|
864
872
|
var pending = null;
|
|
865
873
|
function preloadRenderPopup(loader = defaultLoader) {
|
|
866
874
|
if (pending) return pending;
|
|
@@ -933,7 +941,7 @@ var BrowserPopupRenderer = class {
|
|
|
933
941
|
if (!this.container) return;
|
|
934
942
|
this.visible = true;
|
|
935
943
|
const container = this.container;
|
|
936
|
-
void import("./renderPopup-
|
|
944
|
+
void import("./renderPopup-ERUKQE4U.mjs").then(({ renderPopup }) => {
|
|
937
945
|
renderPopup(container, surveyId, productId, actions, emit, onClose, env, userId, style, sessionId, miniService, analyticsFeedbackSessionId, options);
|
|
938
946
|
});
|
|
939
947
|
}
|
|
@@ -1198,14 +1206,7 @@ function createFeedbackSink(options) {
|
|
|
1198
1206
|
const post = async (body, final, sessionEnd = false) => {
|
|
1199
1207
|
const json = JSON.stringify(body);
|
|
1200
1208
|
const small = json.length <= KEEPALIVE_MAX_BYTES;
|
|
1201
|
-
if (final
|
|
1202
|
-
const payload = typeof Blob !== "undefined" ? new Blob([json], { type: "application/json" }) : json;
|
|
1203
|
-
if (options.sendBeaconImpl(url, payload)) {
|
|
1204
|
-
options.log?.("[DeepdotsAnalytics] final flush via sendBeacon");
|
|
1205
|
-
return;
|
|
1206
|
-
}
|
|
1207
|
-
options.log?.("[DeepdotsAnalytics] sendBeacon rejected the batch; falling back to fetch");
|
|
1208
|
-
}
|
|
1209
|
+
if (final) options.log?.("[DeepdotsAnalytics] final flush via fetch keepalive");
|
|
1209
1210
|
const f = options.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
1210
1211
|
if (!f) {
|
|
1211
1212
|
options.log?.("[DeepdotsAnalytics] no fetch available; payload not sent", body);
|
|
@@ -1215,6 +1216,9 @@ function createFeedbackSink(options) {
|
|
|
1215
1216
|
method: "POST",
|
|
1216
1217
|
headers: { "Content-Type": "application/json" },
|
|
1217
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",
|
|
1218
1222
|
// keepalive: el POST sobrevive a la navegación mientras el body sea pequeño.
|
|
1219
1223
|
...small ? { keepalive: true } : {}
|
|
1220
1224
|
});
|
|
@@ -1307,6 +1311,15 @@ var NavigationObserver = class {
|
|
|
1307
1311
|
this.closeCurrent();
|
|
1308
1312
|
this.currentScreen = null;
|
|
1309
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
|
+
}
|
|
1310
1323
|
closeCurrent() {
|
|
1311
1324
|
if (this.currentScreen == null) return;
|
|
1312
1325
|
const exit = this.now();
|
|
@@ -1946,8 +1959,6 @@ var DeepdotsPopups = class {
|
|
|
1946
1959
|
baseUrl: this.baseUrl,
|
|
1947
1960
|
keys: config.analytics,
|
|
1948
1961
|
log: (...a) => this.log(...a),
|
|
1949
|
-
// sendBeacon: único transporte que sobrevive al cierre de la página.
|
|
1950
|
-
sendBeaconImpl: typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function" ? (url, body) => navigator.sendBeacon(url, body) : void 0,
|
|
1951
1962
|
onSessionId: (id) => {
|
|
1952
1963
|
this.analyticsFeedbackSessionId = id;
|
|
1953
1964
|
},
|
|
@@ -1995,7 +2006,6 @@ var DeepdotsPopups = class {
|
|
|
1995
2006
|
this.navObserver.onVisit((v) => this.track("deepdots_page_view", { screen: v.screen, duration_seconds: v.durationSeconds }));
|
|
1996
2007
|
this.navObserver.install();
|
|
1997
2008
|
this.engagement = new EngagementTracker();
|
|
1998
|
-
this.engagement.resume();
|
|
1999
2009
|
this.setupAnalyticsFlush();
|
|
2000
2010
|
this.openSession();
|
|
2001
2011
|
const pendingCrashes = this.crashReporter.drainPendingCrashes();
|
|
@@ -2214,6 +2224,7 @@ var DeepdotsPopups = class {
|
|
|
2214
2224
|
this.navStarted = false;
|
|
2215
2225
|
this.analytics?.exitAllMiniServices();
|
|
2216
2226
|
this.flushEngagement();
|
|
2227
|
+
this.engagement?.pause();
|
|
2217
2228
|
this.track("deepdots_session_end", { reason });
|
|
2218
2229
|
this.flushAnalytics({ final: reason === "page_hide", sessionEnd: true });
|
|
2219
2230
|
this.analyticsFeedbackSessionId = void 0;
|
|
@@ -2229,6 +2240,12 @@ var DeepdotsPopups = class {
|
|
|
2229
2240
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2230
2241
|
this.sessionOpen = true;
|
|
2231
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";
|
|
2232
2249
|
}
|
|
2233
2250
|
/** Payload que se ENVIARÍA al endpoint de analytics (no envía ni vacía el buffer). */
|
|
2234
2251
|
previewAnalytics() {
|
|
@@ -2241,8 +2258,8 @@ var DeepdotsPopups = class {
|
|
|
2241
2258
|
}
|
|
2242
2259
|
/**
|
|
2243
2260
|
* Envía el lote acumulado de analytics y vacía el buffer.
|
|
2244
|
-
* `final: true` (cierre de página/app)
|
|
2245
|
-
*
|
|
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.
|
|
2246
2263
|
*/
|
|
2247
2264
|
flushAnalytics(options) {
|
|
2248
2265
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
@@ -2263,21 +2280,30 @@ var DeepdotsPopups = class {
|
|
|
2263
2280
|
/** Flush automático al ocultar/cerrar la página (no perder el lote pendiente). */
|
|
2264
2281
|
setupAnalyticsFlush() {
|
|
2265
2282
|
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
2266
|
-
this.
|
|
2283
|
+
this.startAnalyticsFlushTimer();
|
|
2267
2284
|
window.addEventListener("pagehide", () => {
|
|
2268
2285
|
clearInterval(this.analyticsFlushTimer);
|
|
2269
2286
|
this.closeSession("page_hide");
|
|
2270
2287
|
});
|
|
2288
|
+
window.addEventListener("pageshow", (event) => {
|
|
2289
|
+
if (!event.persisted) return;
|
|
2290
|
+
this.startAnalyticsFlushTimer();
|
|
2291
|
+
this.openSession();
|
|
2292
|
+
});
|
|
2271
2293
|
document.addEventListener("visibilitychange", () => {
|
|
2272
2294
|
if (document.visibilityState === "hidden") {
|
|
2273
2295
|
this.flushEngagement();
|
|
2274
2296
|
this.engagement?.pause();
|
|
2275
2297
|
this.flushAnalytics();
|
|
2276
2298
|
} else if (document.visibilityState === "visible") {
|
|
2277
|
-
this.engagement?.resume();
|
|
2299
|
+
if (this.sessionOpen) this.engagement?.resume();
|
|
2278
2300
|
}
|
|
2279
2301
|
});
|
|
2280
2302
|
}
|
|
2303
|
+
startAnalyticsFlushTimer() {
|
|
2304
|
+
clearInterval(this.analyticsFlushTimer);
|
|
2305
|
+
this.analyticsFlushTimer = setInterval(() => this.flushAnalytics(), ANALYTICS_FLUSH_INTERVAL_MS);
|
|
2306
|
+
}
|
|
2281
2307
|
/** Enable auto-launch functionality with configured triggers */
|
|
2282
2308
|
autoLaunch() {
|
|
2283
2309
|
if (!this.initialized) {
|
|
@@ -2371,6 +2397,7 @@ var DeepdotsPopups = class {
|
|
|
2371
2397
|
this.emitEvent("popup_shown", def.surveyId, { popupId: def.id });
|
|
2372
2398
|
}
|
|
2373
2399
|
shouldShow(def, pathUrl, skipPathCheck = false) {
|
|
2400
|
+
if (this.storage) this.loadPopupState(this.storage);
|
|
2374
2401
|
if (this.isPathExcluded(def, pathUrl)) {
|
|
2375
2402
|
return false;
|
|
2376
2403
|
}
|
|
@@ -2779,7 +2806,7 @@ var DeepdotsPopups = class {
|
|
|
2779
2806
|
}
|
|
2780
2807
|
if (!this.popupContainer) return;
|
|
2781
2808
|
const container = this.popupContainer;
|
|
2782
|
-
void import("./renderPopup-
|
|
2809
|
+
void import("./renderPopup-ERUKQE4U.mjs").then(({ renderPopup }) => {
|
|
2783
2810
|
renderPopup(
|
|
2784
2811
|
container,
|
|
2785
2812
|
surveyId,
|
|
@@ -2897,22 +2924,32 @@ var DeepdotsPopups = class {
|
|
|
2897
2924
|
this.persistPopupState();
|
|
2898
2925
|
}
|
|
2899
2926
|
/**
|
|
2900
|
-
*
|
|
2901
|
-
*
|
|
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`.
|
|
2902
2932
|
*/
|
|
2903
2933
|
loadPopupState(storage) {
|
|
2904
2934
|
const state = readPopupState(storage);
|
|
2905
2935
|
Object.entries(state.lastShown).forEach(([popupId, timestamp]) => {
|
|
2906
|
-
this.lastShown.set(popupId, timestamp);
|
|
2936
|
+
if (timestamp > (this.lastShown.get(popupId) ?? -Infinity)) this.lastShown.set(popupId, timestamp);
|
|
2907
2937
|
});
|
|
2908
2938
|
Object.entries(state.progress).forEach(([surveyId, entry]) => {
|
|
2939
|
+
const current = this.surveyProgress.get(surveyId);
|
|
2940
|
+
if (current && current.timestamp >= entry.timestamp) return;
|
|
2909
2941
|
this.surveyProgress.set(surveyId, { status: entry.status, timestamp: entry.timestamp });
|
|
2910
2942
|
if (entry.status === "COMPLETED") this.answeredSurveys.add(surveyId);
|
|
2911
2943
|
});
|
|
2912
2944
|
}
|
|
2913
|
-
/**
|
|
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
|
+
*/
|
|
2914
2950
|
persistPopupState() {
|
|
2915
2951
|
if (!this.storage) return;
|
|
2952
|
+
this.loadPopupState(this.storage);
|
|
2916
2953
|
writePopupState(this.storage, {
|
|
2917
2954
|
lastShown: Object.fromEntries(this.lastShown),
|
|
2918
2955
|
progress: Object.fromEntries(this.surveyProgress)
|