@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.d.mts
CHANGED
|
@@ -95,7 +95,8 @@ interface AnalyticsEnvelope {
|
|
|
95
95
|
interface AnalyticsFlushMeta {
|
|
96
96
|
/**
|
|
97
97
|
* `true` cuando el flush ocurre porque la página/app se está cerrando. El sink debe usar
|
|
98
|
-
* un transporte que sobreviva al unload (
|
|
98
|
+
* un transporte que sobreviva al unload (`fetch` con `keepalive`) y no puede esperar
|
|
99
|
+
* respuestas.
|
|
99
100
|
*/
|
|
100
101
|
final?: boolean;
|
|
101
102
|
/**
|
|
@@ -174,9 +175,11 @@ interface FeedbackSinkOptions {
|
|
|
174
175
|
/** fetch inyectable (tests / RN). */
|
|
175
176
|
fetchImpl?: typeof fetch;
|
|
176
177
|
/**
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
178
|
+
* @deprecated Se ignora. El flush final salía por `sendBeacon`, que va SIEMPRE con
|
|
179
|
+
* credenciales; contra la API (`Access-Control-Allow-Origin: *` + `Allow-Credentials:
|
|
180
|
+
* true`) el preflight fallaba y el lote de cierre, el único con `completed:true`, no
|
|
181
|
+
* llegaba nunca. Ahora sale por `fetch` con `keepalive`. Se mantiene en el tipo para no
|
|
182
|
+
* romper a quien lo pase.
|
|
180
183
|
*/
|
|
181
184
|
sendBeaconImpl?: (url: string, body: Blob | string) => boolean;
|
|
182
185
|
/** Callback invocado cuando el backend devuelve un nuevo sessionId (primer POST). */
|
|
@@ -194,8 +197,9 @@ interface FeedbackSinkOptions {
|
|
|
194
197
|
* Garantías de entrega:
|
|
195
198
|
* - mientras no se conozca el `sessionId`, los lotes se **serializan** (esperan la primera
|
|
196
199
|
* respuesta): dos POSTs a la vez sin `sessionId` crearían dos registros y partirían los datos;
|
|
197
|
-
* - `keepalive` para que el navegador no cancele el POST al navegar fuera
|
|
198
|
-
* en el flush final (
|
|
200
|
+
* - `keepalive` para que el navegador no cancele el POST al navegar fuera ni al cerrar la
|
|
201
|
+
* página, también en el flush final (sin `sendBeacon`: ver `sendBeaconImpl`);
|
|
202
|
+
* - sin credenciales, que es lo único que pasa el CORS de la API (`Allow-Origin: *`);
|
|
199
203
|
* - la promesa **rechaza** en fallo transitorio (red, 5xx, 408, 429) para que el
|
|
200
204
|
* `AnalyticsManager` re-encole el lote; un 4xx se loguea y se descarta.
|
|
201
205
|
*/
|
|
@@ -801,12 +805,14 @@ declare class DeepdotsPopups {
|
|
|
801
805
|
* se llama en init(), al volver a foreground y al conceder el consentimiento.
|
|
802
806
|
*/
|
|
803
807
|
private openSession;
|
|
808
|
+
/** Web: si la pestaña se ve. Sin `document` (RN) manda AppState vía onForeground/onBackground. */
|
|
809
|
+
private isDocumentVisible;
|
|
804
810
|
/** Payload que se ENVIARÍA al endpoint de analytics (no envía ni vacía el buffer). */
|
|
805
811
|
previewAnalytics(): AnalyticsEnvelope;
|
|
806
812
|
/**
|
|
807
813
|
* Envía el lote acumulado de analytics y vacía el buffer.
|
|
808
|
-
* `final: true` (cierre de página/app)
|
|
809
|
-
*
|
|
814
|
+
* `final: true` (cierre de página/app): el lote no espera al primer POST en vuelo, porque
|
|
815
|
+
* el documento se muere; un lote que falle por red o 5xx se re-encola para el siguiente flush.
|
|
810
816
|
*/
|
|
811
817
|
flushAnalytics(options?: {
|
|
812
818
|
final?: boolean;
|
|
@@ -817,6 +823,7 @@ declare class DeepdotsPopups {
|
|
|
817
823
|
private analyticsIdentity;
|
|
818
824
|
/** Flush automático al ocultar/cerrar la página (no perder el lote pendiente). */
|
|
819
825
|
private setupAnalyticsFlush;
|
|
826
|
+
private startAnalyticsFlushTimer;
|
|
820
827
|
/** Enable auto-launch functionality with configured triggers */
|
|
821
828
|
autoLaunch(): void;
|
|
822
829
|
/** Inicia los triggers configurados */
|
|
@@ -896,11 +903,18 @@ declare class DeepdotsPopups {
|
|
|
896
903
|
private mapPopupTriggerType;
|
|
897
904
|
private markSurveyProgress;
|
|
898
905
|
/**
|
|
899
|
-
*
|
|
900
|
-
*
|
|
906
|
+
* Fusiona el estado de redisplay del storage con el de memoria: por cada popup/survey gana
|
|
907
|
+
* la entrada MÁS RECIENTE. Fusionar y no sustituir es lo que hace convivir a varias
|
|
908
|
+
* pestañas sobre el mismo `localStorage`: se llama en el `init()`, antes de cada decisión
|
|
909
|
+
* de mostrar y antes de cada escritura. `answeredSurveys` se reconstruye desde las
|
|
910
|
+
* entradas COMPLETED: es exactamente lo que persiste `markSurveyAnswered`.
|
|
901
911
|
*/
|
|
902
912
|
private loadPopupState;
|
|
903
|
-
/**
|
|
913
|
+
/**
|
|
914
|
+
* Vuelca el estado de redisplay al storage. Sin storage (o bloqueado) degrada a memoria.
|
|
915
|
+
* Fusiona antes de escribir: escribir solo la foto en memoria borraba lo que otra pestaña
|
|
916
|
+
* hubiese guardado desde nuestro `init()`.
|
|
917
|
+
*/
|
|
904
918
|
private persistPopupState;
|
|
905
919
|
private isPopupDefinition;
|
|
906
920
|
private isPopupTrigger;
|
package/dist/index.d.ts
CHANGED
|
@@ -95,7 +95,8 @@ interface AnalyticsEnvelope {
|
|
|
95
95
|
interface AnalyticsFlushMeta {
|
|
96
96
|
/**
|
|
97
97
|
* `true` cuando el flush ocurre porque la página/app se está cerrando. El sink debe usar
|
|
98
|
-
* un transporte que sobreviva al unload (
|
|
98
|
+
* un transporte que sobreviva al unload (`fetch` con `keepalive`) y no puede esperar
|
|
99
|
+
* respuestas.
|
|
99
100
|
*/
|
|
100
101
|
final?: boolean;
|
|
101
102
|
/**
|
|
@@ -174,9 +175,11 @@ interface FeedbackSinkOptions {
|
|
|
174
175
|
/** fetch inyectable (tests / RN). */
|
|
175
176
|
fetchImpl?: typeof fetch;
|
|
176
177
|
/**
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
178
|
+
* @deprecated Se ignora. El flush final salía por `sendBeacon`, que va SIEMPRE con
|
|
179
|
+
* credenciales; contra la API (`Access-Control-Allow-Origin: *` + `Allow-Credentials:
|
|
180
|
+
* true`) el preflight fallaba y el lote de cierre, el único con `completed:true`, no
|
|
181
|
+
* llegaba nunca. Ahora sale por `fetch` con `keepalive`. Se mantiene en el tipo para no
|
|
182
|
+
* romper a quien lo pase.
|
|
180
183
|
*/
|
|
181
184
|
sendBeaconImpl?: (url: string, body: Blob | string) => boolean;
|
|
182
185
|
/** Callback invocado cuando el backend devuelve un nuevo sessionId (primer POST). */
|
|
@@ -194,8 +197,9 @@ interface FeedbackSinkOptions {
|
|
|
194
197
|
* Garantías de entrega:
|
|
195
198
|
* - mientras no se conozca el `sessionId`, los lotes se **serializan** (esperan la primera
|
|
196
199
|
* respuesta): dos POSTs a la vez sin `sessionId` crearían dos registros y partirían los datos;
|
|
197
|
-
* - `keepalive` para que el navegador no cancele el POST al navegar fuera
|
|
198
|
-
* en el flush final (
|
|
200
|
+
* - `keepalive` para que el navegador no cancele el POST al navegar fuera ni al cerrar la
|
|
201
|
+
* página, también en el flush final (sin `sendBeacon`: ver `sendBeaconImpl`);
|
|
202
|
+
* - sin credenciales, que es lo único que pasa el CORS de la API (`Allow-Origin: *`);
|
|
199
203
|
* - la promesa **rechaza** en fallo transitorio (red, 5xx, 408, 429) para que el
|
|
200
204
|
* `AnalyticsManager` re-encole el lote; un 4xx se loguea y se descarta.
|
|
201
205
|
*/
|
|
@@ -801,12 +805,14 @@ declare class DeepdotsPopups {
|
|
|
801
805
|
* se llama en init(), al volver a foreground y al conceder el consentimiento.
|
|
802
806
|
*/
|
|
803
807
|
private openSession;
|
|
808
|
+
/** Web: si la pestaña se ve. Sin `document` (RN) manda AppState vía onForeground/onBackground. */
|
|
809
|
+
private isDocumentVisible;
|
|
804
810
|
/** Payload que se ENVIARÍA al endpoint de analytics (no envía ni vacía el buffer). */
|
|
805
811
|
previewAnalytics(): AnalyticsEnvelope;
|
|
806
812
|
/**
|
|
807
813
|
* Envía el lote acumulado de analytics y vacía el buffer.
|
|
808
|
-
* `final: true` (cierre de página/app)
|
|
809
|
-
*
|
|
814
|
+
* `final: true` (cierre de página/app): el lote no espera al primer POST en vuelo, porque
|
|
815
|
+
* el documento se muere; un lote que falle por red o 5xx se re-encola para el siguiente flush.
|
|
810
816
|
*/
|
|
811
817
|
flushAnalytics(options?: {
|
|
812
818
|
final?: boolean;
|
|
@@ -817,6 +823,7 @@ declare class DeepdotsPopups {
|
|
|
817
823
|
private analyticsIdentity;
|
|
818
824
|
/** Flush automático al ocultar/cerrar la página (no perder el lote pendiente). */
|
|
819
825
|
private setupAnalyticsFlush;
|
|
826
|
+
private startAnalyticsFlushTimer;
|
|
820
827
|
/** Enable auto-launch functionality with configured triggers */
|
|
821
828
|
autoLaunch(): void;
|
|
822
829
|
/** Inicia los triggers configurados */
|
|
@@ -896,11 +903,18 @@ declare class DeepdotsPopups {
|
|
|
896
903
|
private mapPopupTriggerType;
|
|
897
904
|
private markSurveyProgress;
|
|
898
905
|
/**
|
|
899
|
-
*
|
|
900
|
-
*
|
|
906
|
+
* Fusiona el estado de redisplay del storage con el de memoria: por cada popup/survey gana
|
|
907
|
+
* la entrada MÁS RECIENTE. Fusionar y no sustituir es lo que hace convivir a varias
|
|
908
|
+
* pestañas sobre el mismo `localStorage`: se llama en el `init()`, antes de cada decisión
|
|
909
|
+
* de mostrar y antes de cada escritura. `answeredSurveys` se reconstruye desde las
|
|
910
|
+
* entradas COMPLETED: es exactamente lo que persiste `markSurveyAnswered`.
|
|
901
911
|
*/
|
|
902
912
|
private loadPopupState;
|
|
903
|
-
/**
|
|
913
|
+
/**
|
|
914
|
+
* Vuelca el estado de redisplay al storage. Sin storage (o bloqueado) degrada a memoria.
|
|
915
|
+
* Fusiona antes de escribir: escribir solo la foto en memoria borraba lo que otra pestaña
|
|
916
|
+
* hubiese guardado desde nuestro `init()`.
|
|
917
|
+
*/
|
|
904
918
|
private persistPopupState;
|
|
905
919
|
private isPopupDefinition;
|
|
906
920
|
private isPopupTrigger;
|
package/dist/index.js
CHANGED
|
@@ -542,6 +542,29 @@ function ddApplySurveyPrimaryColor(el, style){
|
|
|
542
542
|
}
|
|
543
543
|
});
|
|
544
544
|
|
|
545
|
+
// src/ui/busy.ts
|
|
546
|
+
function setSurveyBusy(el, busy) {
|
|
547
|
+
if (!el) return;
|
|
548
|
+
el.style.pointerEvents = busy ? "none" : "";
|
|
549
|
+
el.setAttribute("aria-busy", busy ? "true" : "false");
|
|
550
|
+
if ("inert" in el) {
|
|
551
|
+
el.inert = busy;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
var BUSY_JS;
|
|
555
|
+
var init_busy = __esm({
|
|
556
|
+
"src/ui/busy.ts"() {
|
|
557
|
+
"use strict";
|
|
558
|
+
BUSY_JS = `
|
|
559
|
+
function ddSetSurveyBusy(el, busy){
|
|
560
|
+
if(!el){ return; }
|
|
561
|
+
el.style.pointerEvents = busy ? 'none' : '';
|
|
562
|
+
el.setAttribute('aria-busy', busy ? 'true' : 'false');
|
|
563
|
+
if('inert' in el){ el.inert = busy; }
|
|
564
|
+
}`;
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
|
|
545
568
|
// src/assets/style.css
|
|
546
569
|
var style_default;
|
|
547
570
|
var init_style = __esm({
|
|
@@ -2431,9 +2454,9 @@ var init_logo = __esm({
|
|
|
2431
2454
|
}
|
|
2432
2455
|
});
|
|
2433
2456
|
|
|
2434
|
-
// node_modules/@magicfeedback/native/dist/magicfeedback-sdk.browser.js
|
|
2457
|
+
// ../../../../../../../Users/sarias/developer/Deepdots/deepdots-popup-sdk/node_modules/@magicfeedback/native/dist/magicfeedback-sdk.browser.js
|
|
2435
2458
|
var require_magicfeedback_sdk_browser = __commonJS({
|
|
2436
|
-
"node_modules/@magicfeedback/native/dist/magicfeedback-sdk.browser.js"(exports, module2) {
|
|
2459
|
+
"../../../../../../../Users/sarias/developer/Deepdots/deepdots-popup-sdk/node_modules/@magicfeedback/native/dist/magicfeedback-sdk.browser.js"(exports, module2) {
|
|
2437
2460
|
"use strict";
|
|
2438
2461
|
!(function(e, t) {
|
|
2439
2462
|
"object" == typeof exports && "object" == typeof module2 ? module2.exports = t() : "function" == typeof define && define.amd ? define([], t) : "object" == typeof exports ? exports.magicfeedback = t() : e.magicfeedback = t();
|
|
@@ -5381,6 +5404,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
5381
5404
|
revealTimer = setTimeout(reveal, REVEAL_TIMEOUT_MS);
|
|
5382
5405
|
function setLoading(isLoading) {
|
|
5383
5406
|
spinnerEl.style.display = isLoading ? "flex" : "none";
|
|
5407
|
+
setSurveyBusy(formWrapper, isLoading);
|
|
5384
5408
|
if (!isLoading) {
|
|
5385
5409
|
formHost.style.visibility = "visible";
|
|
5386
5410
|
revealWhenPainted2();
|
|
@@ -5536,6 +5560,7 @@ var init_renderPopup = __esm({
|
|
|
5536
5560
|
init_font();
|
|
5537
5561
|
init_logo();
|
|
5538
5562
|
init_surveyPalette();
|
|
5563
|
+
init_busy();
|
|
5539
5564
|
init_logger();
|
|
5540
5565
|
init_labels();
|
|
5541
5566
|
init_reveal();
|
|
@@ -5778,6 +5803,7 @@ init_font();
|
|
|
5778
5803
|
init_labels();
|
|
5779
5804
|
init_reveal();
|
|
5780
5805
|
init_surveyPalette();
|
|
5806
|
+
init_busy();
|
|
5781
5807
|
init_style();
|
|
5782
5808
|
var REVEAL_JS = `
|
|
5783
5809
|
function ddCreateReveal(popup, onReveal, timeoutMs){
|
|
@@ -5997,6 +6023,7 @@ ${customCss}
|
|
|
5997
6023
|
var footer=document.getElementById('dd-footer');
|
|
5998
6024
|
var spinner=document.getElementById('dd-spinner');
|
|
5999
6025
|
var formHost=document.getElementById('mf');
|
|
6026
|
+
var formWrapper=document.getElementById('dd-form-wrapper');
|
|
6000
6027
|
var errorHint=document.getElementById('dd-error');
|
|
6001
6028
|
var backBtn=document.getElementById('dd-back');
|
|
6002
6029
|
var startBtn=document.getElementById('dd-start');
|
|
@@ -6156,6 +6183,7 @@ ${customCss}
|
|
|
6156
6183
|
}
|
|
6157
6184
|
|
|
6158
6185
|
${PRIMARY_COLOR_JS}
|
|
6186
|
+
${BUSY_JS}
|
|
6159
6187
|
${REVEAL_JS}
|
|
6160
6188
|
// El popup se ense\xF1a cuando el survey est\xE1 pintado; \`ready\` deja al host abrir su propio
|
|
6161
6189
|
// Modal en ese momento en vez de montar un WebView en blanco.
|
|
@@ -6166,6 +6194,11 @@ ${REVEAL_JS}
|
|
|
6166
6194
|
|
|
6167
6195
|
function setLoading(isLoading){
|
|
6168
6196
|
spinner.style.display=isLoading?'flex':'none';
|
|
6197
|
+
// El spinner no tapa el survey (28px en posicion absoluta), asi que sin esto el usuario
|
|
6198
|
+
// puede seguir cambiando de opcion mientras se envia la pagina, y ese cambio ya no viaja
|
|
6199
|
+
// con ella. Se bloquea el wrapper, que es el elemento estable: el div de dentro lo
|
|
6200
|
+
// sustituye @magicfeedback/native por su propio contenedor al generar el survey.
|
|
6201
|
+
ddSetSurveyBusy(formWrapper, isLoading);
|
|
6169
6202
|
if(!isLoading){
|
|
6170
6203
|
formHost.style.visibility='visible';
|
|
6171
6204
|
// Fin de la carga, sea por survey cargado o por error. Entre p\xE1ginas es no-op: la
|
|
@@ -6771,14 +6804,7 @@ function createFeedbackSink(options) {
|
|
|
6771
6804
|
const post = async (body, final, sessionEnd = false) => {
|
|
6772
6805
|
const json = JSON.stringify(body);
|
|
6773
6806
|
const small = json.length <= KEEPALIVE_MAX_BYTES;
|
|
6774
|
-
if (final
|
|
6775
|
-
const payload = typeof Blob !== "undefined" ? new Blob([json], { type: "application/json" }) : json;
|
|
6776
|
-
if (options.sendBeaconImpl(url, payload)) {
|
|
6777
|
-
options.log?.("[DeepdotsAnalytics] final flush via sendBeacon");
|
|
6778
|
-
return;
|
|
6779
|
-
}
|
|
6780
|
-
options.log?.("[DeepdotsAnalytics] sendBeacon rejected the batch; falling back to fetch");
|
|
6781
|
-
}
|
|
6807
|
+
if (final) options.log?.("[DeepdotsAnalytics] final flush via fetch keepalive");
|
|
6782
6808
|
const f = options.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
6783
6809
|
if (!f) {
|
|
6784
6810
|
options.log?.("[DeepdotsAnalytics] no fetch available; payload not sent", body);
|
|
@@ -6788,6 +6814,9 @@ function createFeedbackSink(options) {
|
|
|
6788
6814
|
method: "POST",
|
|
6789
6815
|
headers: { "Content-Type": "application/json" },
|
|
6790
6816
|
body: json,
|
|
6817
|
+
// La API autentica por `publicKey` en el body y responde `Allow-Origin: *`: una
|
|
6818
|
+
// petición con credenciales fallaría el CORS. Explícito para no depender del default.
|
|
6819
|
+
credentials: "omit",
|
|
6791
6820
|
// keepalive: el POST sobrevive a la navegación mientras el body sea pequeño.
|
|
6792
6821
|
...small ? { keepalive: true } : {}
|
|
6793
6822
|
});
|
|
@@ -6880,6 +6909,15 @@ var NavigationObserver = class {
|
|
|
6880
6909
|
this.closeCurrent();
|
|
6881
6910
|
this.currentScreen = null;
|
|
6882
6911
|
}
|
|
6912
|
+
/**
|
|
6913
|
+
* Retoma el seguimiento en la pantalla actual tras un `stop()`, cuando se abre una sesión
|
|
6914
|
+
* nueva sin navegar (cambio de usuario, vuelta de la bfcache). No-op si nunca se instaló
|
|
6915
|
+
* (navegación manual por `setScreen`, RN) o si ya hay pantalla en curso.
|
|
6916
|
+
*/
|
|
6917
|
+
restart() {
|
|
6918
|
+
if (!this.installed || this.currentScreen != null || typeof window === "undefined") return;
|
|
6919
|
+
this.begin(window.location.href);
|
|
6920
|
+
}
|
|
6883
6921
|
closeCurrent() {
|
|
6884
6922
|
if (this.currentScreen == null) return;
|
|
6885
6923
|
const exit = this.now();
|
|
@@ -7523,8 +7561,6 @@ var DeepdotsPopups = class {
|
|
|
7523
7561
|
baseUrl: this.baseUrl,
|
|
7524
7562
|
keys: config.analytics,
|
|
7525
7563
|
log: (...a) => this.log(...a),
|
|
7526
|
-
// sendBeacon: único transporte que sobrevive al cierre de la página.
|
|
7527
|
-
sendBeaconImpl: typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function" ? (url, body) => navigator.sendBeacon(url, body) : void 0,
|
|
7528
7564
|
onSessionId: (id) => {
|
|
7529
7565
|
this.analyticsFeedbackSessionId = id;
|
|
7530
7566
|
},
|
|
@@ -7572,7 +7608,6 @@ var DeepdotsPopups = class {
|
|
|
7572
7608
|
this.navObserver.onVisit((v) => this.track("deepdots_page_view", { screen: v.screen, duration_seconds: v.durationSeconds }));
|
|
7573
7609
|
this.navObserver.install();
|
|
7574
7610
|
this.engagement = new EngagementTracker();
|
|
7575
|
-
this.engagement.resume();
|
|
7576
7611
|
this.setupAnalyticsFlush();
|
|
7577
7612
|
this.openSession();
|
|
7578
7613
|
const pendingCrashes = this.crashReporter.drainPendingCrashes();
|
|
@@ -7791,6 +7826,7 @@ var DeepdotsPopups = class {
|
|
|
7791
7826
|
this.navStarted = false;
|
|
7792
7827
|
this.analytics?.exitAllMiniServices();
|
|
7793
7828
|
this.flushEngagement();
|
|
7829
|
+
this.engagement?.pause();
|
|
7794
7830
|
this.track("deepdots_session_end", { reason });
|
|
7795
7831
|
this.flushAnalytics({ final: reason === "page_hide", sessionEnd: true });
|
|
7796
7832
|
this.analyticsFeedbackSessionId = void 0;
|
|
@@ -7806,6 +7842,12 @@ var DeepdotsPopups = class {
|
|
|
7806
7842
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
7807
7843
|
this.sessionOpen = true;
|
|
7808
7844
|
this.track("deepdots_session_start", {});
|
|
7845
|
+
this.navObserver?.restart();
|
|
7846
|
+
if (this.isDocumentVisible()) this.engagement?.resume();
|
|
7847
|
+
}
|
|
7848
|
+
/** Web: si la pestaña se ve. Sin `document` (RN) manda AppState vía onForeground/onBackground. */
|
|
7849
|
+
isDocumentVisible() {
|
|
7850
|
+
return typeof document === "undefined" || document.visibilityState !== "hidden";
|
|
7809
7851
|
}
|
|
7810
7852
|
/** Payload que se ENVIARÍA al endpoint de analytics (no envía ni vacía el buffer). */
|
|
7811
7853
|
previewAnalytics() {
|
|
@@ -7818,8 +7860,8 @@ var DeepdotsPopups = class {
|
|
|
7818
7860
|
}
|
|
7819
7861
|
/**
|
|
7820
7862
|
* Envía el lote acumulado de analytics y vacía el buffer.
|
|
7821
|
-
* `final: true` (cierre de página/app)
|
|
7822
|
-
*
|
|
7863
|
+
* `final: true` (cierre de página/app): el lote no espera al primer POST en vuelo, porque
|
|
7864
|
+
* el documento se muere; un lote que falle por red o 5xx se re-encola para el siguiente flush.
|
|
7823
7865
|
*/
|
|
7824
7866
|
flushAnalytics(options) {
|
|
7825
7867
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
@@ -7840,21 +7882,30 @@ var DeepdotsPopups = class {
|
|
|
7840
7882
|
/** Flush automático al ocultar/cerrar la página (no perder el lote pendiente). */
|
|
7841
7883
|
setupAnalyticsFlush() {
|
|
7842
7884
|
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
7843
|
-
this.
|
|
7885
|
+
this.startAnalyticsFlushTimer();
|
|
7844
7886
|
window.addEventListener("pagehide", () => {
|
|
7845
7887
|
clearInterval(this.analyticsFlushTimer);
|
|
7846
7888
|
this.closeSession("page_hide");
|
|
7847
7889
|
});
|
|
7890
|
+
window.addEventListener("pageshow", (event) => {
|
|
7891
|
+
if (!event.persisted) return;
|
|
7892
|
+
this.startAnalyticsFlushTimer();
|
|
7893
|
+
this.openSession();
|
|
7894
|
+
});
|
|
7848
7895
|
document.addEventListener("visibilitychange", () => {
|
|
7849
7896
|
if (document.visibilityState === "hidden") {
|
|
7850
7897
|
this.flushEngagement();
|
|
7851
7898
|
this.engagement?.pause();
|
|
7852
7899
|
this.flushAnalytics();
|
|
7853
7900
|
} else if (document.visibilityState === "visible") {
|
|
7854
|
-
this.engagement?.resume();
|
|
7901
|
+
if (this.sessionOpen) this.engagement?.resume();
|
|
7855
7902
|
}
|
|
7856
7903
|
});
|
|
7857
7904
|
}
|
|
7905
|
+
startAnalyticsFlushTimer() {
|
|
7906
|
+
clearInterval(this.analyticsFlushTimer);
|
|
7907
|
+
this.analyticsFlushTimer = setInterval(() => this.flushAnalytics(), ANALYTICS_FLUSH_INTERVAL_MS);
|
|
7908
|
+
}
|
|
7858
7909
|
/** Enable auto-launch functionality with configured triggers */
|
|
7859
7910
|
autoLaunch() {
|
|
7860
7911
|
if (!this.initialized) {
|
|
@@ -7948,6 +7999,7 @@ var DeepdotsPopups = class {
|
|
|
7948
7999
|
this.emitEvent("popup_shown", def.surveyId, { popupId: def.id });
|
|
7949
8000
|
}
|
|
7950
8001
|
shouldShow(def, pathUrl, skipPathCheck = false) {
|
|
8002
|
+
if (this.storage) this.loadPopupState(this.storage);
|
|
7951
8003
|
if (this.isPathExcluded(def, pathUrl)) {
|
|
7952
8004
|
return false;
|
|
7953
8005
|
}
|
|
@@ -8474,22 +8526,32 @@ var DeepdotsPopups = class {
|
|
|
8474
8526
|
this.persistPopupState();
|
|
8475
8527
|
}
|
|
8476
8528
|
/**
|
|
8477
|
-
*
|
|
8478
|
-
*
|
|
8529
|
+
* Fusiona el estado de redisplay del storage con el de memoria: por cada popup/survey gana
|
|
8530
|
+
* la entrada MÁS RECIENTE. Fusionar y no sustituir es lo que hace convivir a varias
|
|
8531
|
+
* pestañas sobre el mismo `localStorage`: se llama en el `init()`, antes de cada decisión
|
|
8532
|
+
* de mostrar y antes de cada escritura. `answeredSurveys` se reconstruye desde las
|
|
8533
|
+
* entradas COMPLETED: es exactamente lo que persiste `markSurveyAnswered`.
|
|
8479
8534
|
*/
|
|
8480
8535
|
loadPopupState(storage) {
|
|
8481
8536
|
const state = readPopupState(storage);
|
|
8482
8537
|
Object.entries(state.lastShown).forEach(([popupId, timestamp]) => {
|
|
8483
|
-
this.lastShown.set(popupId, timestamp);
|
|
8538
|
+
if (timestamp > (this.lastShown.get(popupId) ?? -Infinity)) this.lastShown.set(popupId, timestamp);
|
|
8484
8539
|
});
|
|
8485
8540
|
Object.entries(state.progress).forEach(([surveyId, entry]) => {
|
|
8541
|
+
const current = this.surveyProgress.get(surveyId);
|
|
8542
|
+
if (current && current.timestamp >= entry.timestamp) return;
|
|
8486
8543
|
this.surveyProgress.set(surveyId, { status: entry.status, timestamp: entry.timestamp });
|
|
8487
8544
|
if (entry.status === "COMPLETED") this.answeredSurveys.add(surveyId);
|
|
8488
8545
|
});
|
|
8489
8546
|
}
|
|
8490
|
-
/**
|
|
8547
|
+
/**
|
|
8548
|
+
* Vuelca el estado de redisplay al storage. Sin storage (o bloqueado) degrada a memoria.
|
|
8549
|
+
* Fusiona antes de escribir: escribir solo la foto en memoria borraba lo que otra pestaña
|
|
8550
|
+
* hubiese guardado desde nuestro `init()`.
|
|
8551
|
+
*/
|
|
8491
8552
|
persistPopupState() {
|
|
8492
8553
|
if (!this.storage) return;
|
|
8554
|
+
this.loadPopupState(this.storage);
|
|
8493
8555
|
writePopupState(this.storage, {
|
|
8494
8556
|
lastShown: Object.fromEntries(this.lastShown),
|
|
8495
8557
|
progress: Object.fromEntries(this.surveyProgress)
|