@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/react-native.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
BUSY_JS,
|
|
2
3
|
LABELS,
|
|
3
4
|
PRIMARY_COLOR_JS,
|
|
4
5
|
RESOLVE_LOCALE_JS,
|
|
@@ -17,7 +18,7 @@ import {
|
|
|
17
18
|
sdkWarn,
|
|
18
19
|
setLogger,
|
|
19
20
|
style_default
|
|
20
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-EBJZDVC6.mjs";
|
|
21
22
|
|
|
22
23
|
// src/react-native/index.tsx
|
|
23
24
|
import { createContext, useContext, useEffect, useRef, useState } from "react";
|
|
@@ -431,6 +432,7 @@ ${customCss}
|
|
|
431
432
|
var footer=document.getElementById('dd-footer');
|
|
432
433
|
var spinner=document.getElementById('dd-spinner');
|
|
433
434
|
var formHost=document.getElementById('mf');
|
|
435
|
+
var formWrapper=document.getElementById('dd-form-wrapper');
|
|
434
436
|
var errorHint=document.getElementById('dd-error');
|
|
435
437
|
var backBtn=document.getElementById('dd-back');
|
|
436
438
|
var startBtn=document.getElementById('dd-start');
|
|
@@ -590,6 +592,7 @@ ${customCss}
|
|
|
590
592
|
}
|
|
591
593
|
|
|
592
594
|
${PRIMARY_COLOR_JS}
|
|
595
|
+
${BUSY_JS}
|
|
593
596
|
${REVEAL_JS}
|
|
594
597
|
// El popup se ense\xF1a cuando el survey est\xE1 pintado; \`ready\` deja al host abrir su propio
|
|
595
598
|
// Modal en ese momento en vez de montar un WebView en blanco.
|
|
@@ -600,6 +603,11 @@ ${REVEAL_JS}
|
|
|
600
603
|
|
|
601
604
|
function setLoading(isLoading){
|
|
602
605
|
spinner.style.display=isLoading?'flex':'none';
|
|
606
|
+
// El spinner no tapa el survey (28px en posicion absoluta), asi que sin esto el usuario
|
|
607
|
+
// puede seguir cambiando de opcion mientras se envia la pagina, y ese cambio ya no viaja
|
|
608
|
+
// con ella. Se bloquea el wrapper, que es el elemento estable: el div de dentro lo
|
|
609
|
+
// sustituye @magicfeedback/native por su propio contenedor al generar el survey.
|
|
610
|
+
ddSetSurveyBusy(formWrapper, isLoading);
|
|
603
611
|
if(!isLoading){
|
|
604
612
|
formHost.style.visibility='visible';
|
|
605
613
|
// Fin de la carga, sea por survey cargado o por error. Entre p\xE1ginas es no-op: la
|
|
@@ -862,7 +870,7 @@ function createReactNativeRenderer() {
|
|
|
862
870
|
}
|
|
863
871
|
|
|
864
872
|
// src/ui/preload.ts
|
|
865
|
-
var defaultLoader = () => import("./renderPopup-
|
|
873
|
+
var defaultLoader = () => import("./renderPopup-E2OLIXIQ.mjs");
|
|
866
874
|
var pending = null;
|
|
867
875
|
function preloadRenderPopup(loader = defaultLoader) {
|
|
868
876
|
if (pending) return pending;
|
|
@@ -935,7 +943,7 @@ var BrowserPopupRenderer = class {
|
|
|
935
943
|
if (!this.container) return;
|
|
936
944
|
this.visible = true;
|
|
937
945
|
const container = this.container;
|
|
938
|
-
void import("./renderPopup-
|
|
946
|
+
void import("./renderPopup-E2OLIXIQ.mjs").then(({ renderPopup }) => {
|
|
939
947
|
renderPopup(container, surveyId, productId, actions, emit, onClose, env, userId, style, sessionId, miniService, analyticsFeedbackSessionId, options);
|
|
940
948
|
});
|
|
941
949
|
}
|
|
@@ -1200,14 +1208,7 @@ function createFeedbackSink(options) {
|
|
|
1200
1208
|
const post = async (body, final, sessionEnd = false) => {
|
|
1201
1209
|
const json = JSON.stringify(body);
|
|
1202
1210
|
const small = json.length <= KEEPALIVE_MAX_BYTES;
|
|
1203
|
-
if (final
|
|
1204
|
-
const payload = typeof Blob !== "undefined" ? new Blob([json], { type: "application/json" }) : json;
|
|
1205
|
-
if (options.sendBeaconImpl(url, payload)) {
|
|
1206
|
-
options.log?.("[DeepdotsAnalytics] final flush via sendBeacon");
|
|
1207
|
-
return;
|
|
1208
|
-
}
|
|
1209
|
-
options.log?.("[DeepdotsAnalytics] sendBeacon rejected the batch; falling back to fetch");
|
|
1210
|
-
}
|
|
1211
|
+
if (final) options.log?.("[DeepdotsAnalytics] final flush via fetch keepalive");
|
|
1211
1212
|
const f = options.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
1212
1213
|
if (!f) {
|
|
1213
1214
|
options.log?.("[DeepdotsAnalytics] no fetch available; payload not sent", body);
|
|
@@ -1217,6 +1218,9 @@ function createFeedbackSink(options) {
|
|
|
1217
1218
|
method: "POST",
|
|
1218
1219
|
headers: { "Content-Type": "application/json" },
|
|
1219
1220
|
body: json,
|
|
1221
|
+
// La API autentica por `publicKey` en el body y responde `Allow-Origin: *`: una
|
|
1222
|
+
// petición con credenciales fallaría el CORS. Explícito para no depender del default.
|
|
1223
|
+
credentials: "omit",
|
|
1220
1224
|
// keepalive: el POST sobrevive a la navegación mientras el body sea pequeño.
|
|
1221
1225
|
...small ? { keepalive: true } : {}
|
|
1222
1226
|
});
|
|
@@ -1309,6 +1313,15 @@ var NavigationObserver = class {
|
|
|
1309
1313
|
this.closeCurrent();
|
|
1310
1314
|
this.currentScreen = null;
|
|
1311
1315
|
}
|
|
1316
|
+
/**
|
|
1317
|
+
* Retoma el seguimiento en la pantalla actual tras un `stop()`, cuando se abre una sesión
|
|
1318
|
+
* nueva sin navegar (cambio de usuario, vuelta de la bfcache). No-op si nunca se instaló
|
|
1319
|
+
* (navegación manual por `setScreen`, RN) o si ya hay pantalla en curso.
|
|
1320
|
+
*/
|
|
1321
|
+
restart() {
|
|
1322
|
+
if (!this.installed || this.currentScreen != null || typeof window === "undefined") return;
|
|
1323
|
+
this.begin(window.location.href);
|
|
1324
|
+
}
|
|
1312
1325
|
closeCurrent() {
|
|
1313
1326
|
if (this.currentScreen == null) return;
|
|
1314
1327
|
const exit = this.now();
|
|
@@ -1948,8 +1961,6 @@ var DeepdotsPopups = class {
|
|
|
1948
1961
|
baseUrl: this.baseUrl,
|
|
1949
1962
|
keys: config.analytics,
|
|
1950
1963
|
log: (...a) => this.log(...a),
|
|
1951
|
-
// sendBeacon: único transporte que sobrevive al cierre de la página.
|
|
1952
|
-
sendBeaconImpl: typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function" ? (url, body) => navigator.sendBeacon(url, body) : void 0,
|
|
1953
1964
|
onSessionId: (id) => {
|
|
1954
1965
|
this.analyticsFeedbackSessionId = id;
|
|
1955
1966
|
},
|
|
@@ -1997,7 +2008,6 @@ var DeepdotsPopups = class {
|
|
|
1997
2008
|
this.navObserver.onVisit((v) => this.track("deepdots_page_view", { screen: v.screen, duration_seconds: v.durationSeconds }));
|
|
1998
2009
|
this.navObserver.install();
|
|
1999
2010
|
this.engagement = new EngagementTracker();
|
|
2000
|
-
this.engagement.resume();
|
|
2001
2011
|
this.setupAnalyticsFlush();
|
|
2002
2012
|
this.openSession();
|
|
2003
2013
|
const pendingCrashes = this.crashReporter.drainPendingCrashes();
|
|
@@ -2216,6 +2226,7 @@ var DeepdotsPopups = class {
|
|
|
2216
2226
|
this.navStarted = false;
|
|
2217
2227
|
this.analytics?.exitAllMiniServices();
|
|
2218
2228
|
this.flushEngagement();
|
|
2229
|
+
this.engagement?.pause();
|
|
2219
2230
|
this.track("deepdots_session_end", { reason });
|
|
2220
2231
|
this.flushAnalytics({ final: reason === "page_hide", sessionEnd: true });
|
|
2221
2232
|
this.analyticsFeedbackSessionId = void 0;
|
|
@@ -2231,6 +2242,12 @@ var DeepdotsPopups = class {
|
|
|
2231
2242
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2232
2243
|
this.sessionOpen = true;
|
|
2233
2244
|
this.track("deepdots_session_start", {});
|
|
2245
|
+
this.navObserver?.restart();
|
|
2246
|
+
if (this.isDocumentVisible()) this.engagement?.resume();
|
|
2247
|
+
}
|
|
2248
|
+
/** Web: si la pestaña se ve. Sin `document` (RN) manda AppState vía onForeground/onBackground. */
|
|
2249
|
+
isDocumentVisible() {
|
|
2250
|
+
return typeof document === "undefined" || document.visibilityState !== "hidden";
|
|
2234
2251
|
}
|
|
2235
2252
|
/** Payload que se ENVIARÍA al endpoint de analytics (no envía ni vacía el buffer). */
|
|
2236
2253
|
previewAnalytics() {
|
|
@@ -2243,8 +2260,8 @@ var DeepdotsPopups = class {
|
|
|
2243
2260
|
}
|
|
2244
2261
|
/**
|
|
2245
2262
|
* Envía el lote acumulado de analytics y vacía el buffer.
|
|
2246
|
-
* `final: true` (cierre de página/app)
|
|
2247
|
-
*
|
|
2263
|
+
* `final: true` (cierre de página/app): el lote no espera al primer POST en vuelo, porque
|
|
2264
|
+
* el documento se muere; un lote que falle por red o 5xx se re-encola para el siguiente flush.
|
|
2248
2265
|
*/
|
|
2249
2266
|
flushAnalytics(options) {
|
|
2250
2267
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
@@ -2265,21 +2282,30 @@ var DeepdotsPopups = class {
|
|
|
2265
2282
|
/** Flush automático al ocultar/cerrar la página (no perder el lote pendiente). */
|
|
2266
2283
|
setupAnalyticsFlush() {
|
|
2267
2284
|
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
2268
|
-
this.
|
|
2285
|
+
this.startAnalyticsFlushTimer();
|
|
2269
2286
|
window.addEventListener("pagehide", () => {
|
|
2270
2287
|
clearInterval(this.analyticsFlushTimer);
|
|
2271
2288
|
this.closeSession("page_hide");
|
|
2272
2289
|
});
|
|
2290
|
+
window.addEventListener("pageshow", (event) => {
|
|
2291
|
+
if (!event.persisted) return;
|
|
2292
|
+
this.startAnalyticsFlushTimer();
|
|
2293
|
+
this.openSession();
|
|
2294
|
+
});
|
|
2273
2295
|
document.addEventListener("visibilitychange", () => {
|
|
2274
2296
|
if (document.visibilityState === "hidden") {
|
|
2275
2297
|
this.flushEngagement();
|
|
2276
2298
|
this.engagement?.pause();
|
|
2277
2299
|
this.flushAnalytics();
|
|
2278
2300
|
} else if (document.visibilityState === "visible") {
|
|
2279
|
-
this.engagement?.resume();
|
|
2301
|
+
if (this.sessionOpen) this.engagement?.resume();
|
|
2280
2302
|
}
|
|
2281
2303
|
});
|
|
2282
2304
|
}
|
|
2305
|
+
startAnalyticsFlushTimer() {
|
|
2306
|
+
clearInterval(this.analyticsFlushTimer);
|
|
2307
|
+
this.analyticsFlushTimer = setInterval(() => this.flushAnalytics(), ANALYTICS_FLUSH_INTERVAL_MS);
|
|
2308
|
+
}
|
|
2283
2309
|
/** Enable auto-launch functionality with configured triggers */
|
|
2284
2310
|
autoLaunch() {
|
|
2285
2311
|
if (!this.initialized) {
|
|
@@ -2373,6 +2399,7 @@ var DeepdotsPopups = class {
|
|
|
2373
2399
|
this.emitEvent("popup_shown", def.surveyId, { popupId: def.id });
|
|
2374
2400
|
}
|
|
2375
2401
|
shouldShow(def, pathUrl, skipPathCheck = false) {
|
|
2402
|
+
if (this.storage) this.loadPopupState(this.storage);
|
|
2376
2403
|
if (this.isPathExcluded(def, pathUrl)) {
|
|
2377
2404
|
return false;
|
|
2378
2405
|
}
|
|
@@ -2781,7 +2808,7 @@ var DeepdotsPopups = class {
|
|
|
2781
2808
|
}
|
|
2782
2809
|
if (!this.popupContainer) return;
|
|
2783
2810
|
const container = this.popupContainer;
|
|
2784
|
-
void import("./renderPopup-
|
|
2811
|
+
void import("./renderPopup-E2OLIXIQ.mjs").then(({ renderPopup }) => {
|
|
2785
2812
|
renderPopup(
|
|
2786
2813
|
container,
|
|
2787
2814
|
surveyId,
|
|
@@ -2899,22 +2926,32 @@ var DeepdotsPopups = class {
|
|
|
2899
2926
|
this.persistPopupState();
|
|
2900
2927
|
}
|
|
2901
2928
|
/**
|
|
2902
|
-
*
|
|
2903
|
-
*
|
|
2929
|
+
* Fusiona el estado de redisplay del storage con el de memoria: por cada popup/survey gana
|
|
2930
|
+
* la entrada MÁS RECIENTE. Fusionar y no sustituir es lo que hace convivir a varias
|
|
2931
|
+
* pestañas sobre el mismo `localStorage`: se llama en el `init()`, antes de cada decisión
|
|
2932
|
+
* de mostrar y antes de cada escritura. `answeredSurveys` se reconstruye desde las
|
|
2933
|
+
* entradas COMPLETED: es exactamente lo que persiste `markSurveyAnswered`.
|
|
2904
2934
|
*/
|
|
2905
2935
|
loadPopupState(storage) {
|
|
2906
2936
|
const state = readPopupState(storage);
|
|
2907
2937
|
Object.entries(state.lastShown).forEach(([popupId, timestamp]) => {
|
|
2908
|
-
this.lastShown.set(popupId, timestamp);
|
|
2938
|
+
if (timestamp > (this.lastShown.get(popupId) ?? -Infinity)) this.lastShown.set(popupId, timestamp);
|
|
2909
2939
|
});
|
|
2910
2940
|
Object.entries(state.progress).forEach(([surveyId, entry]) => {
|
|
2941
|
+
const current = this.surveyProgress.get(surveyId);
|
|
2942
|
+
if (current && current.timestamp >= entry.timestamp) return;
|
|
2911
2943
|
this.surveyProgress.set(surveyId, { status: entry.status, timestamp: entry.timestamp });
|
|
2912
2944
|
if (entry.status === "COMPLETED") this.answeredSurveys.add(surveyId);
|
|
2913
2945
|
});
|
|
2914
2946
|
}
|
|
2915
|
-
/**
|
|
2947
|
+
/**
|
|
2948
|
+
* Vuelca el estado de redisplay al storage. Sin storage (o bloqueado) degrada a memoria.
|
|
2949
|
+
* Fusiona antes de escribir: escribir solo la foto en memoria borraba lo que otra pestaña
|
|
2950
|
+
* hubiese guardado desde nuestro `init()`.
|
|
2951
|
+
*/
|
|
2916
2952
|
persistPopupState() {
|
|
2917
2953
|
if (!this.storage) return;
|
|
2954
|
+
this.loadPopupState(this.storage);
|
|
2918
2955
|
writePopupState(this.storage, {
|
|
2919
2956
|
lastShown: Object.fromEntries(this.lastShown),
|
|
2920
2957
|
progress: Object.fromEntries(this.surveyProgress)
|