@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.d.mts
CHANGED
|
@@ -507,12 +507,14 @@ declare class DeepdotsPopups {
|
|
|
507
507
|
* se llama en init(), al volver a foreground y al conceder el consentimiento.
|
|
508
508
|
*/
|
|
509
509
|
private openSession;
|
|
510
|
+
/** Web: si la pestaña se ve. Sin `document` (RN) manda AppState vía onForeground/onBackground. */
|
|
511
|
+
private isDocumentVisible;
|
|
510
512
|
/** Payload que se ENVIARÍA al endpoint de analytics (no envía ni vacía el buffer). */
|
|
511
513
|
previewAnalytics(): AnalyticsEnvelope;
|
|
512
514
|
/**
|
|
513
515
|
* Envía el lote acumulado de analytics y vacía el buffer.
|
|
514
|
-
* `final: true` (cierre de página/app)
|
|
515
|
-
*
|
|
516
|
+
* `final: true` (cierre de página/app): el lote no espera al primer POST en vuelo, porque
|
|
517
|
+
* el documento se muere; un lote que falle por red o 5xx se re-encola para el siguiente flush.
|
|
516
518
|
*/
|
|
517
519
|
flushAnalytics(options?: {
|
|
518
520
|
final?: boolean;
|
|
@@ -523,6 +525,7 @@ declare class DeepdotsPopups {
|
|
|
523
525
|
private analyticsIdentity;
|
|
524
526
|
/** Flush automático al ocultar/cerrar la página (no perder el lote pendiente). */
|
|
525
527
|
private setupAnalyticsFlush;
|
|
528
|
+
private startAnalyticsFlushTimer;
|
|
526
529
|
/** Enable auto-launch functionality with configured triggers */
|
|
527
530
|
autoLaunch(): void;
|
|
528
531
|
/** Inicia los triggers configurados */
|
|
@@ -602,11 +605,18 @@ declare class DeepdotsPopups {
|
|
|
602
605
|
private mapPopupTriggerType;
|
|
603
606
|
private markSurveyProgress;
|
|
604
607
|
/**
|
|
605
|
-
*
|
|
606
|
-
*
|
|
608
|
+
* Fusiona el estado de redisplay del storage con el de memoria: por cada popup/survey gana
|
|
609
|
+
* la entrada MÁS RECIENTE. Fusionar y no sustituir es lo que hace convivir a varias
|
|
610
|
+
* pestañas sobre el mismo `localStorage`: se llama en el `init()`, antes de cada decisión
|
|
611
|
+
* de mostrar y antes de cada escritura. `answeredSurveys` se reconstruye desde las
|
|
612
|
+
* entradas COMPLETED: es exactamente lo que persiste `markSurveyAnswered`.
|
|
607
613
|
*/
|
|
608
614
|
private loadPopupState;
|
|
609
|
-
/**
|
|
615
|
+
/**
|
|
616
|
+
* Vuelca el estado de redisplay al storage. Sin storage (o bloqueado) degrada a memoria.
|
|
617
|
+
* Fusiona antes de escribir: escribir solo la foto en memoria borraba lo que otra pestaña
|
|
618
|
+
* hubiese guardado desde nuestro `init()`.
|
|
619
|
+
*/
|
|
610
620
|
private persistPopupState;
|
|
611
621
|
private isPopupDefinition;
|
|
612
622
|
private isPopupTrigger;
|
package/dist/react-native.d.ts
CHANGED
|
@@ -507,12 +507,14 @@ declare class DeepdotsPopups {
|
|
|
507
507
|
* se llama en init(), al volver a foreground y al conceder el consentimiento.
|
|
508
508
|
*/
|
|
509
509
|
private openSession;
|
|
510
|
+
/** Web: si la pestaña se ve. Sin `document` (RN) manda AppState vía onForeground/onBackground. */
|
|
511
|
+
private isDocumentVisible;
|
|
510
512
|
/** Payload que se ENVIARÍA al endpoint de analytics (no envía ni vacía el buffer). */
|
|
511
513
|
previewAnalytics(): AnalyticsEnvelope;
|
|
512
514
|
/**
|
|
513
515
|
* Envía el lote acumulado de analytics y vacía el buffer.
|
|
514
|
-
* `final: true` (cierre de página/app)
|
|
515
|
-
*
|
|
516
|
+
* `final: true` (cierre de página/app): el lote no espera al primer POST en vuelo, porque
|
|
517
|
+
* el documento se muere; un lote que falle por red o 5xx se re-encola para el siguiente flush.
|
|
516
518
|
*/
|
|
517
519
|
flushAnalytics(options?: {
|
|
518
520
|
final?: boolean;
|
|
@@ -523,6 +525,7 @@ declare class DeepdotsPopups {
|
|
|
523
525
|
private analyticsIdentity;
|
|
524
526
|
/** Flush automático al ocultar/cerrar la página (no perder el lote pendiente). */
|
|
525
527
|
private setupAnalyticsFlush;
|
|
528
|
+
private startAnalyticsFlushTimer;
|
|
526
529
|
/** Enable auto-launch functionality with configured triggers */
|
|
527
530
|
autoLaunch(): void;
|
|
528
531
|
/** Inicia los triggers configurados */
|
|
@@ -602,11 +605,18 @@ declare class DeepdotsPopups {
|
|
|
602
605
|
private mapPopupTriggerType;
|
|
603
606
|
private markSurveyProgress;
|
|
604
607
|
/**
|
|
605
|
-
*
|
|
606
|
-
*
|
|
608
|
+
* Fusiona el estado de redisplay del storage con el de memoria: por cada popup/survey gana
|
|
609
|
+
* la entrada MÁS RECIENTE. Fusionar y no sustituir es lo que hace convivir a varias
|
|
610
|
+
* pestañas sobre el mismo `localStorage`: se llama en el `init()`, antes de cada decisión
|
|
611
|
+
* de mostrar y antes de cada escritura. `answeredSurveys` se reconstruye desde las
|
|
612
|
+
* entradas COMPLETED: es exactamente lo que persiste `markSurveyAnswered`.
|
|
607
613
|
*/
|
|
608
614
|
private loadPopupState;
|
|
609
|
-
/**
|
|
615
|
+
/**
|
|
616
|
+
* Vuelca el estado de redisplay al storage. Sin storage (o bloqueado) degrada a memoria.
|
|
617
|
+
* Fusiona antes de escribir: escribir solo la foto en memoria borraba lo que otra pestaña
|
|
618
|
+
* hubiese guardado desde nuestro `init()`.
|
|
619
|
+
*/
|
|
610
620
|
private persistPopupState;
|
|
611
621
|
private isPopupDefinition;
|
|
612
622
|
private isPopupTrigger;
|
package/dist/react-native.js
CHANGED
|
@@ -522,6 +522,29 @@ function ddApplySurveyPrimaryColor(el, style){
|
|
|
522
522
|
}
|
|
523
523
|
});
|
|
524
524
|
|
|
525
|
+
// src/ui/busy.ts
|
|
526
|
+
function setSurveyBusy(el, busy) {
|
|
527
|
+
if (!el) return;
|
|
528
|
+
el.style.pointerEvents = busy ? "none" : "";
|
|
529
|
+
el.setAttribute("aria-busy", busy ? "true" : "false");
|
|
530
|
+
if ("inert" in el) {
|
|
531
|
+
el.inert = busy;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
var BUSY_JS;
|
|
535
|
+
var init_busy = __esm({
|
|
536
|
+
"src/ui/busy.ts"() {
|
|
537
|
+
"use strict";
|
|
538
|
+
BUSY_JS = `
|
|
539
|
+
function ddSetSurveyBusy(el, busy){
|
|
540
|
+
if(!el){ return; }
|
|
541
|
+
el.style.pointerEvents = busy ? 'none' : '';
|
|
542
|
+
el.setAttribute('aria-busy', busy ? 'true' : 'false');
|
|
543
|
+
if('inert' in el){ el.inert = busy; }
|
|
544
|
+
}`;
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
|
|
525
548
|
// src/assets/style.css
|
|
526
549
|
var style_default;
|
|
527
550
|
var init_style = __esm({
|
|
@@ -2964,6 +2987,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
2964
2987
|
revealTimer = setTimeout(reveal, REVEAL_TIMEOUT_MS);
|
|
2965
2988
|
function setLoading(isLoading) {
|
|
2966
2989
|
spinnerEl.style.display = isLoading ? "flex" : "none";
|
|
2990
|
+
setSurveyBusy(formWrapper, isLoading);
|
|
2967
2991
|
if (!isLoading) {
|
|
2968
2992
|
formHost.style.visibility = "visible";
|
|
2969
2993
|
revealWhenPainted2();
|
|
@@ -3119,6 +3143,7 @@ var init_renderPopup = __esm({
|
|
|
3119
3143
|
init_font();
|
|
3120
3144
|
init_logo();
|
|
3121
3145
|
init_surveyPalette();
|
|
3146
|
+
init_busy();
|
|
3122
3147
|
init_logger();
|
|
3123
3148
|
init_labels();
|
|
3124
3149
|
init_reveal();
|
|
@@ -3338,6 +3363,7 @@ init_font();
|
|
|
3338
3363
|
init_labels();
|
|
3339
3364
|
init_reveal();
|
|
3340
3365
|
init_surveyPalette();
|
|
3366
|
+
init_busy();
|
|
3341
3367
|
init_style();
|
|
3342
3368
|
var REVEAL_JS = `
|
|
3343
3369
|
function ddCreateReveal(popup, onReveal, timeoutMs){
|
|
@@ -3557,6 +3583,7 @@ ${customCss}
|
|
|
3557
3583
|
var footer=document.getElementById('dd-footer');
|
|
3558
3584
|
var spinner=document.getElementById('dd-spinner');
|
|
3559
3585
|
var formHost=document.getElementById('mf');
|
|
3586
|
+
var formWrapper=document.getElementById('dd-form-wrapper');
|
|
3560
3587
|
var errorHint=document.getElementById('dd-error');
|
|
3561
3588
|
var backBtn=document.getElementById('dd-back');
|
|
3562
3589
|
var startBtn=document.getElementById('dd-start');
|
|
@@ -3716,6 +3743,7 @@ ${customCss}
|
|
|
3716
3743
|
}
|
|
3717
3744
|
|
|
3718
3745
|
${PRIMARY_COLOR_JS}
|
|
3746
|
+
${BUSY_JS}
|
|
3719
3747
|
${REVEAL_JS}
|
|
3720
3748
|
// El popup se ense\xF1a cuando el survey est\xE1 pintado; \`ready\` deja al host abrir su propio
|
|
3721
3749
|
// Modal en ese momento en vez de montar un WebView en blanco.
|
|
@@ -3726,6 +3754,11 @@ ${REVEAL_JS}
|
|
|
3726
3754
|
|
|
3727
3755
|
function setLoading(isLoading){
|
|
3728
3756
|
spinner.style.display=isLoading?'flex':'none';
|
|
3757
|
+
// El spinner no tapa el survey (28px en posicion absoluta), asi que sin esto el usuario
|
|
3758
|
+
// puede seguir cambiando de opcion mientras se envia la pagina, y ese cambio ya no viaja
|
|
3759
|
+
// con ella. Se bloquea el wrapper, que es el elemento estable: el div de dentro lo
|
|
3760
|
+
// sustituye @magicfeedback/native por su propio contenedor al generar el survey.
|
|
3761
|
+
ddSetSurveyBusy(formWrapper, isLoading);
|
|
3729
3762
|
if(!isLoading){
|
|
3730
3763
|
formHost.style.visibility='visible';
|
|
3731
3764
|
// Fin de la carga, sea por survey cargado o por error. Entre p\xE1ginas es no-op: la
|
|
@@ -4331,14 +4364,7 @@ function createFeedbackSink(options) {
|
|
|
4331
4364
|
const post = async (body, final, sessionEnd = false) => {
|
|
4332
4365
|
const json = JSON.stringify(body);
|
|
4333
4366
|
const small = json.length <= KEEPALIVE_MAX_BYTES;
|
|
4334
|
-
if (final
|
|
4335
|
-
const payload = typeof Blob !== "undefined" ? new Blob([json], { type: "application/json" }) : json;
|
|
4336
|
-
if (options.sendBeaconImpl(url, payload)) {
|
|
4337
|
-
options.log?.("[DeepdotsAnalytics] final flush via sendBeacon");
|
|
4338
|
-
return;
|
|
4339
|
-
}
|
|
4340
|
-
options.log?.("[DeepdotsAnalytics] sendBeacon rejected the batch; falling back to fetch");
|
|
4341
|
-
}
|
|
4367
|
+
if (final) options.log?.("[DeepdotsAnalytics] final flush via fetch keepalive");
|
|
4342
4368
|
const f = options.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
4343
4369
|
if (!f) {
|
|
4344
4370
|
options.log?.("[DeepdotsAnalytics] no fetch available; payload not sent", body);
|
|
@@ -4348,6 +4374,9 @@ function createFeedbackSink(options) {
|
|
|
4348
4374
|
method: "POST",
|
|
4349
4375
|
headers: { "Content-Type": "application/json" },
|
|
4350
4376
|
body: json,
|
|
4377
|
+
// La API autentica por `publicKey` en el body y responde `Allow-Origin: *`: una
|
|
4378
|
+
// petición con credenciales fallaría el CORS. Explícito para no depender del default.
|
|
4379
|
+
credentials: "omit",
|
|
4351
4380
|
// keepalive: el POST sobrevive a la navegación mientras el body sea pequeño.
|
|
4352
4381
|
...small ? { keepalive: true } : {}
|
|
4353
4382
|
});
|
|
@@ -4440,6 +4469,15 @@ var NavigationObserver = class {
|
|
|
4440
4469
|
this.closeCurrent();
|
|
4441
4470
|
this.currentScreen = null;
|
|
4442
4471
|
}
|
|
4472
|
+
/**
|
|
4473
|
+
* Retoma el seguimiento en la pantalla actual tras un `stop()`, cuando se abre una sesión
|
|
4474
|
+
* nueva sin navegar (cambio de usuario, vuelta de la bfcache). No-op si nunca se instaló
|
|
4475
|
+
* (navegación manual por `setScreen`, RN) o si ya hay pantalla en curso.
|
|
4476
|
+
*/
|
|
4477
|
+
restart() {
|
|
4478
|
+
if (!this.installed || this.currentScreen != null || typeof window === "undefined") return;
|
|
4479
|
+
this.begin(window.location.href);
|
|
4480
|
+
}
|
|
4443
4481
|
closeCurrent() {
|
|
4444
4482
|
if (this.currentScreen == null) return;
|
|
4445
4483
|
const exit = this.now();
|
|
@@ -5083,8 +5121,6 @@ var DeepdotsPopups = class {
|
|
|
5083
5121
|
baseUrl: this.baseUrl,
|
|
5084
5122
|
keys: config.analytics,
|
|
5085
5123
|
log: (...a) => this.log(...a),
|
|
5086
|
-
// sendBeacon: único transporte que sobrevive al cierre de la página.
|
|
5087
|
-
sendBeaconImpl: typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function" ? (url, body) => navigator.sendBeacon(url, body) : void 0,
|
|
5088
5124
|
onSessionId: (id) => {
|
|
5089
5125
|
this.analyticsFeedbackSessionId = id;
|
|
5090
5126
|
},
|
|
@@ -5132,7 +5168,6 @@ var DeepdotsPopups = class {
|
|
|
5132
5168
|
this.navObserver.onVisit((v) => this.track("deepdots_page_view", { screen: v.screen, duration_seconds: v.durationSeconds }));
|
|
5133
5169
|
this.navObserver.install();
|
|
5134
5170
|
this.engagement = new EngagementTracker();
|
|
5135
|
-
this.engagement.resume();
|
|
5136
5171
|
this.setupAnalyticsFlush();
|
|
5137
5172
|
this.openSession();
|
|
5138
5173
|
const pendingCrashes = this.crashReporter.drainPendingCrashes();
|
|
@@ -5351,6 +5386,7 @@ var DeepdotsPopups = class {
|
|
|
5351
5386
|
this.navStarted = false;
|
|
5352
5387
|
this.analytics?.exitAllMiniServices();
|
|
5353
5388
|
this.flushEngagement();
|
|
5389
|
+
this.engagement?.pause();
|
|
5354
5390
|
this.track("deepdots_session_end", { reason });
|
|
5355
5391
|
this.flushAnalytics({ final: reason === "page_hide", sessionEnd: true });
|
|
5356
5392
|
this.analyticsFeedbackSessionId = void 0;
|
|
@@ -5366,6 +5402,12 @@ var DeepdotsPopups = class {
|
|
|
5366
5402
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
5367
5403
|
this.sessionOpen = true;
|
|
5368
5404
|
this.track("deepdots_session_start", {});
|
|
5405
|
+
this.navObserver?.restart();
|
|
5406
|
+
if (this.isDocumentVisible()) this.engagement?.resume();
|
|
5407
|
+
}
|
|
5408
|
+
/** Web: si la pestaña se ve. Sin `document` (RN) manda AppState vía onForeground/onBackground. */
|
|
5409
|
+
isDocumentVisible() {
|
|
5410
|
+
return typeof document === "undefined" || document.visibilityState !== "hidden";
|
|
5369
5411
|
}
|
|
5370
5412
|
/** Payload que se ENVIARÍA al endpoint de analytics (no envía ni vacía el buffer). */
|
|
5371
5413
|
previewAnalytics() {
|
|
@@ -5378,8 +5420,8 @@ var DeepdotsPopups = class {
|
|
|
5378
5420
|
}
|
|
5379
5421
|
/**
|
|
5380
5422
|
* Envía el lote acumulado de analytics y vacía el buffer.
|
|
5381
|
-
* `final: true` (cierre de página/app)
|
|
5382
|
-
*
|
|
5423
|
+
* `final: true` (cierre de página/app): el lote no espera al primer POST en vuelo, porque
|
|
5424
|
+
* el documento se muere; un lote que falle por red o 5xx se re-encola para el siguiente flush.
|
|
5383
5425
|
*/
|
|
5384
5426
|
flushAnalytics(options) {
|
|
5385
5427
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
@@ -5400,21 +5442,30 @@ var DeepdotsPopups = class {
|
|
|
5400
5442
|
/** Flush automático al ocultar/cerrar la página (no perder el lote pendiente). */
|
|
5401
5443
|
setupAnalyticsFlush() {
|
|
5402
5444
|
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
5403
|
-
this.
|
|
5445
|
+
this.startAnalyticsFlushTimer();
|
|
5404
5446
|
window.addEventListener("pagehide", () => {
|
|
5405
5447
|
clearInterval(this.analyticsFlushTimer);
|
|
5406
5448
|
this.closeSession("page_hide");
|
|
5407
5449
|
});
|
|
5450
|
+
window.addEventListener("pageshow", (event) => {
|
|
5451
|
+
if (!event.persisted) return;
|
|
5452
|
+
this.startAnalyticsFlushTimer();
|
|
5453
|
+
this.openSession();
|
|
5454
|
+
});
|
|
5408
5455
|
document.addEventListener("visibilitychange", () => {
|
|
5409
5456
|
if (document.visibilityState === "hidden") {
|
|
5410
5457
|
this.flushEngagement();
|
|
5411
5458
|
this.engagement?.pause();
|
|
5412
5459
|
this.flushAnalytics();
|
|
5413
5460
|
} else if (document.visibilityState === "visible") {
|
|
5414
|
-
this.engagement?.resume();
|
|
5461
|
+
if (this.sessionOpen) this.engagement?.resume();
|
|
5415
5462
|
}
|
|
5416
5463
|
});
|
|
5417
5464
|
}
|
|
5465
|
+
startAnalyticsFlushTimer() {
|
|
5466
|
+
clearInterval(this.analyticsFlushTimer);
|
|
5467
|
+
this.analyticsFlushTimer = setInterval(() => this.flushAnalytics(), ANALYTICS_FLUSH_INTERVAL_MS);
|
|
5468
|
+
}
|
|
5418
5469
|
/** Enable auto-launch functionality with configured triggers */
|
|
5419
5470
|
autoLaunch() {
|
|
5420
5471
|
if (!this.initialized) {
|
|
@@ -5508,6 +5559,7 @@ var DeepdotsPopups = class {
|
|
|
5508
5559
|
this.emitEvent("popup_shown", def.surveyId, { popupId: def.id });
|
|
5509
5560
|
}
|
|
5510
5561
|
shouldShow(def, pathUrl, skipPathCheck = false) {
|
|
5562
|
+
if (this.storage) this.loadPopupState(this.storage);
|
|
5511
5563
|
if (this.isPathExcluded(def, pathUrl)) {
|
|
5512
5564
|
return false;
|
|
5513
5565
|
}
|
|
@@ -6034,22 +6086,32 @@ var DeepdotsPopups = class {
|
|
|
6034
6086
|
this.persistPopupState();
|
|
6035
6087
|
}
|
|
6036
6088
|
/**
|
|
6037
|
-
*
|
|
6038
|
-
*
|
|
6089
|
+
* Fusiona el estado de redisplay del storage con el de memoria: por cada popup/survey gana
|
|
6090
|
+
* la entrada MÁS RECIENTE. Fusionar y no sustituir es lo que hace convivir a varias
|
|
6091
|
+
* pestañas sobre el mismo `localStorage`: se llama en el `init()`, antes de cada decisión
|
|
6092
|
+
* de mostrar y antes de cada escritura. `answeredSurveys` se reconstruye desde las
|
|
6093
|
+
* entradas COMPLETED: es exactamente lo que persiste `markSurveyAnswered`.
|
|
6039
6094
|
*/
|
|
6040
6095
|
loadPopupState(storage) {
|
|
6041
6096
|
const state = readPopupState(storage);
|
|
6042
6097
|
Object.entries(state.lastShown).forEach(([popupId, timestamp]) => {
|
|
6043
|
-
this.lastShown.set(popupId, timestamp);
|
|
6098
|
+
if (timestamp > (this.lastShown.get(popupId) ?? -Infinity)) this.lastShown.set(popupId, timestamp);
|
|
6044
6099
|
});
|
|
6045
6100
|
Object.entries(state.progress).forEach(([surveyId, entry]) => {
|
|
6101
|
+
const current = this.surveyProgress.get(surveyId);
|
|
6102
|
+
if (current && current.timestamp >= entry.timestamp) return;
|
|
6046
6103
|
this.surveyProgress.set(surveyId, { status: entry.status, timestamp: entry.timestamp });
|
|
6047
6104
|
if (entry.status === "COMPLETED") this.answeredSurveys.add(surveyId);
|
|
6048
6105
|
});
|
|
6049
6106
|
}
|
|
6050
|
-
/**
|
|
6107
|
+
/**
|
|
6108
|
+
* Vuelca el estado de redisplay al storage. Sin storage (o bloqueado) degrada a memoria.
|
|
6109
|
+
* Fusiona antes de escribir: escribir solo la foto en memoria borraba lo que otra pestaña
|
|
6110
|
+
* hubiese guardado desde nuestro `init()`.
|
|
6111
|
+
*/
|
|
6051
6112
|
persistPopupState() {
|
|
6052
6113
|
if (!this.storage) return;
|
|
6114
|
+
this.loadPopupState(this.storage);
|
|
6053
6115
|
writePopupState(this.storage, {
|
|
6054
6116
|
lastShown: Object.fromEntries(this.lastShown),
|
|
6055
6117
|
progress: Object.fromEntries(this.surveyProgress)
|