@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.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
|
@@ -2454,9 +2454,9 @@ var init_logo = __esm({
|
|
|
2454
2454
|
}
|
|
2455
2455
|
});
|
|
2456
2456
|
|
|
2457
|
-
// 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
|
|
2458
2458
|
var require_magicfeedback_sdk_browser = __commonJS({
|
|
2459
|
-
"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) {
|
|
2460
2460
|
"use strict";
|
|
2461
2461
|
!(function(e, t) {
|
|
2462
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();
|
|
@@ -6804,14 +6804,7 @@ function createFeedbackSink(options) {
|
|
|
6804
6804
|
const post = async (body, final, sessionEnd = false) => {
|
|
6805
6805
|
const json = JSON.stringify(body);
|
|
6806
6806
|
const small = json.length <= KEEPALIVE_MAX_BYTES;
|
|
6807
|
-
if (final
|
|
6808
|
-
const payload = typeof Blob !== "undefined" ? new Blob([json], { type: "application/json" }) : json;
|
|
6809
|
-
if (options.sendBeaconImpl(url, payload)) {
|
|
6810
|
-
options.log?.("[DeepdotsAnalytics] final flush via sendBeacon");
|
|
6811
|
-
return;
|
|
6812
|
-
}
|
|
6813
|
-
options.log?.("[DeepdotsAnalytics] sendBeacon rejected the batch; falling back to fetch");
|
|
6814
|
-
}
|
|
6807
|
+
if (final) options.log?.("[DeepdotsAnalytics] final flush via fetch keepalive");
|
|
6815
6808
|
const f = options.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
6816
6809
|
if (!f) {
|
|
6817
6810
|
options.log?.("[DeepdotsAnalytics] no fetch available; payload not sent", body);
|
|
@@ -6821,6 +6814,9 @@ function createFeedbackSink(options) {
|
|
|
6821
6814
|
method: "POST",
|
|
6822
6815
|
headers: { "Content-Type": "application/json" },
|
|
6823
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",
|
|
6824
6820
|
// keepalive: el POST sobrevive a la navegación mientras el body sea pequeño.
|
|
6825
6821
|
...small ? { keepalive: true } : {}
|
|
6826
6822
|
});
|
|
@@ -6913,6 +6909,15 @@ var NavigationObserver = class {
|
|
|
6913
6909
|
this.closeCurrent();
|
|
6914
6910
|
this.currentScreen = null;
|
|
6915
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
|
+
}
|
|
6916
6921
|
closeCurrent() {
|
|
6917
6922
|
if (this.currentScreen == null) return;
|
|
6918
6923
|
const exit = this.now();
|
|
@@ -7556,8 +7561,6 @@ var DeepdotsPopups = class {
|
|
|
7556
7561
|
baseUrl: this.baseUrl,
|
|
7557
7562
|
keys: config.analytics,
|
|
7558
7563
|
log: (...a) => this.log(...a),
|
|
7559
|
-
// sendBeacon: único transporte que sobrevive al cierre de la página.
|
|
7560
|
-
sendBeaconImpl: typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function" ? (url, body) => navigator.sendBeacon(url, body) : void 0,
|
|
7561
7564
|
onSessionId: (id) => {
|
|
7562
7565
|
this.analyticsFeedbackSessionId = id;
|
|
7563
7566
|
},
|
|
@@ -7605,7 +7608,6 @@ var DeepdotsPopups = class {
|
|
|
7605
7608
|
this.navObserver.onVisit((v) => this.track("deepdots_page_view", { screen: v.screen, duration_seconds: v.durationSeconds }));
|
|
7606
7609
|
this.navObserver.install();
|
|
7607
7610
|
this.engagement = new EngagementTracker();
|
|
7608
|
-
this.engagement.resume();
|
|
7609
7611
|
this.setupAnalyticsFlush();
|
|
7610
7612
|
this.openSession();
|
|
7611
7613
|
const pendingCrashes = this.crashReporter.drainPendingCrashes();
|
|
@@ -7824,6 +7826,7 @@ var DeepdotsPopups = class {
|
|
|
7824
7826
|
this.navStarted = false;
|
|
7825
7827
|
this.analytics?.exitAllMiniServices();
|
|
7826
7828
|
this.flushEngagement();
|
|
7829
|
+
this.engagement?.pause();
|
|
7827
7830
|
this.track("deepdots_session_end", { reason });
|
|
7828
7831
|
this.flushAnalytics({ final: reason === "page_hide", sessionEnd: true });
|
|
7829
7832
|
this.analyticsFeedbackSessionId = void 0;
|
|
@@ -7839,6 +7842,12 @@ var DeepdotsPopups = class {
|
|
|
7839
7842
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
7840
7843
|
this.sessionOpen = true;
|
|
7841
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";
|
|
7842
7851
|
}
|
|
7843
7852
|
/** Payload que se ENVIARÍA al endpoint de analytics (no envía ni vacía el buffer). */
|
|
7844
7853
|
previewAnalytics() {
|
|
@@ -7851,8 +7860,8 @@ var DeepdotsPopups = class {
|
|
|
7851
7860
|
}
|
|
7852
7861
|
/**
|
|
7853
7862
|
* Envía el lote acumulado de analytics y vacía el buffer.
|
|
7854
|
-
* `final: true` (cierre de página/app)
|
|
7855
|
-
*
|
|
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.
|
|
7856
7865
|
*/
|
|
7857
7866
|
flushAnalytics(options) {
|
|
7858
7867
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
@@ -7873,21 +7882,30 @@ var DeepdotsPopups = class {
|
|
|
7873
7882
|
/** Flush automático al ocultar/cerrar la página (no perder el lote pendiente). */
|
|
7874
7883
|
setupAnalyticsFlush() {
|
|
7875
7884
|
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
7876
|
-
this.
|
|
7885
|
+
this.startAnalyticsFlushTimer();
|
|
7877
7886
|
window.addEventListener("pagehide", () => {
|
|
7878
7887
|
clearInterval(this.analyticsFlushTimer);
|
|
7879
7888
|
this.closeSession("page_hide");
|
|
7880
7889
|
});
|
|
7890
|
+
window.addEventListener("pageshow", (event) => {
|
|
7891
|
+
if (!event.persisted) return;
|
|
7892
|
+
this.startAnalyticsFlushTimer();
|
|
7893
|
+
this.openSession();
|
|
7894
|
+
});
|
|
7881
7895
|
document.addEventListener("visibilitychange", () => {
|
|
7882
7896
|
if (document.visibilityState === "hidden") {
|
|
7883
7897
|
this.flushEngagement();
|
|
7884
7898
|
this.engagement?.pause();
|
|
7885
7899
|
this.flushAnalytics();
|
|
7886
7900
|
} else if (document.visibilityState === "visible") {
|
|
7887
|
-
this.engagement?.resume();
|
|
7901
|
+
if (this.sessionOpen) this.engagement?.resume();
|
|
7888
7902
|
}
|
|
7889
7903
|
});
|
|
7890
7904
|
}
|
|
7905
|
+
startAnalyticsFlushTimer() {
|
|
7906
|
+
clearInterval(this.analyticsFlushTimer);
|
|
7907
|
+
this.analyticsFlushTimer = setInterval(() => this.flushAnalytics(), ANALYTICS_FLUSH_INTERVAL_MS);
|
|
7908
|
+
}
|
|
7891
7909
|
/** Enable auto-launch functionality with configured triggers */
|
|
7892
7910
|
autoLaunch() {
|
|
7893
7911
|
if (!this.initialized) {
|
|
@@ -7981,6 +7999,7 @@ var DeepdotsPopups = class {
|
|
|
7981
7999
|
this.emitEvent("popup_shown", def.surveyId, { popupId: def.id });
|
|
7982
8000
|
}
|
|
7983
8001
|
shouldShow(def, pathUrl, skipPathCheck = false) {
|
|
8002
|
+
if (this.storage) this.loadPopupState(this.storage);
|
|
7984
8003
|
if (this.isPathExcluded(def, pathUrl)) {
|
|
7985
8004
|
return false;
|
|
7986
8005
|
}
|
|
@@ -8507,22 +8526,32 @@ var DeepdotsPopups = class {
|
|
|
8507
8526
|
this.persistPopupState();
|
|
8508
8527
|
}
|
|
8509
8528
|
/**
|
|
8510
|
-
*
|
|
8511
|
-
*
|
|
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`.
|
|
8512
8534
|
*/
|
|
8513
8535
|
loadPopupState(storage) {
|
|
8514
8536
|
const state = readPopupState(storage);
|
|
8515
8537
|
Object.entries(state.lastShown).forEach(([popupId, timestamp]) => {
|
|
8516
|
-
this.lastShown.set(popupId, timestamp);
|
|
8538
|
+
if (timestamp > (this.lastShown.get(popupId) ?? -Infinity)) this.lastShown.set(popupId, timestamp);
|
|
8517
8539
|
});
|
|
8518
8540
|
Object.entries(state.progress).forEach(([surveyId, entry]) => {
|
|
8541
|
+
const current = this.surveyProgress.get(surveyId);
|
|
8542
|
+
if (current && current.timestamp >= entry.timestamp) return;
|
|
8519
8543
|
this.surveyProgress.set(surveyId, { status: entry.status, timestamp: entry.timestamp });
|
|
8520
8544
|
if (entry.status === "COMPLETED") this.answeredSurveys.add(surveyId);
|
|
8521
8545
|
});
|
|
8522
8546
|
}
|
|
8523
|
-
/**
|
|
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
|
+
*/
|
|
8524
8552
|
persistPopupState() {
|
|
8525
8553
|
if (!this.storage) return;
|
|
8554
|
+
this.loadPopupState(this.storage);
|
|
8526
8555
|
writePopupState(this.storage, {
|
|
8527
8556
|
lastShown: Object.fromEntries(this.lastShown),
|
|
8528
8557
|
progress: Object.fromEntries(this.surveyProgress)
|