@magicfeedback/popup-sdk 1.7.0 → 1.7.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/README.md +33 -0
- package/dist/{chunk-WNIUIOKF.mjs → chunk-322DDMX6.mjs} +69 -1
- package/dist/chunk-322DDMX6.mjs.map +1 -0
- package/dist/{chunk-W6MPSWMS.mjs → chunk-XHR3MHJO.mjs} +69 -1
- package/dist/chunk-XHR3MHJO.mjs.map +1 -0
- package/dist/index.d.mts +81 -6
- package/dist/index.d.ts +81 -6
- package/dist/index.js +361 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +239 -10
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +48 -5
- package/dist/react-native.d.ts +48 -5
- package/dist/react-native.js +374 -22
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +255 -20
- package/dist/react-native.mjs.map +1 -1
- package/dist/{renderPopup-IHGYFMXO.mjs → renderPopup-5QXL6EJW.mjs} +48 -8
- package/dist/renderPopup-5QXL6EJW.mjs.map +1 -0
- package/dist/{renderPopup-EWGKG53W.mjs → renderPopup-SNDPX5RX.mjs} +46 -6
- package/dist/renderPopup-SNDPX5RX.mjs.map +1 -0
- package/package.json +4 -3
- package/dist/chunk-W6MPSWMS.mjs.map +0 -1
- package/dist/chunk-WNIUIOKF.mjs.map +0 -1
- package/dist/renderPopup-EWGKG53W.mjs.map +0 -1
- package/dist/renderPopup-IHGYFMXO.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
InMemoryStorage,
|
|
3
3
|
LABELS,
|
|
4
|
+
PRIMARY_COLOR_JS,
|
|
4
5
|
RESOLVE_LOCALE_JS,
|
|
6
|
+
REVEAL_TIMEOUT_MS,
|
|
5
7
|
RTL_LOCALES,
|
|
6
8
|
STORAGE_KEYS,
|
|
7
9
|
SUPPORTED_LANGUAGES,
|
|
@@ -18,7 +20,7 @@ import {
|
|
|
18
20
|
sdkWarn,
|
|
19
21
|
setLogger,
|
|
20
22
|
style_default
|
|
21
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-XHR3MHJO.mjs";
|
|
22
24
|
|
|
23
25
|
// src/types/index.ts
|
|
24
26
|
var POPUP_TRIGGER_CONDITION_STATUSES = [
|
|
@@ -209,6 +211,30 @@ function resolveEnvironment(nodeEnv) {
|
|
|
209
211
|
}
|
|
210
212
|
|
|
211
213
|
// src/ui/surveyHtml.ts
|
|
214
|
+
var REVEAL_JS = `
|
|
215
|
+
function ddCreateReveal(popup, onReveal, timeoutMs){
|
|
216
|
+
var revealed=false, timer=null;
|
|
217
|
+
function reveal(){
|
|
218
|
+
if(revealed){ return; }
|
|
219
|
+
revealed=true;
|
|
220
|
+
if(timer){ clearTimeout(timer); timer=null; }
|
|
221
|
+
onReveal();
|
|
222
|
+
}
|
|
223
|
+
function revealWhenPainted(){
|
|
224
|
+
if(revealed){ return; }
|
|
225
|
+
var imgs=popup.querySelectorAll('img'), pending=[];
|
|
226
|
+
for(var i=0;i<imgs.length;i++){ if(!imgs[i].complete){ pending.push(imgs[i]); } }
|
|
227
|
+
if(!pending.length){ reveal(); return; }
|
|
228
|
+
var left=pending.length;
|
|
229
|
+
function onSettled(){ left--; if(left<=0){ reveal(); } }
|
|
230
|
+
for(var j=0;j<pending.length;j++){
|
|
231
|
+
pending[j].addEventListener('load', onSettled);
|
|
232
|
+
pending[j].addEventListener('error', onSettled);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
timer=setTimeout(reveal, timeoutMs);
|
|
236
|
+
return { reveal: reveal, whenPainted: revealWhenPainted };
|
|
237
|
+
}`;
|
|
212
238
|
var POSITION_MAP = {
|
|
213
239
|
center: { justifyContent: "center", alignItems: "center", padding: "0", background: "rgba(0,0,0,0.5)" },
|
|
214
240
|
bottom: { justifyContent: "center", alignItems: "flex-end", padding: "16px", background: "transparent" },
|
|
@@ -282,7 +308,13 @@ function buildSurveyHtml(opts) {
|
|
|
282
308
|
${style_default}
|
|
283
309
|
${fontFaceCss}
|
|
284
310
|
html,body{margin:0;padding:0;height:100%;font-family:${fontFamilyValue}}
|
|
285
|
-
|
|
311
|
+
/* Apertura diferida: ni la tarjeta ni el velo se pintan hasta que el survey est\xE1 listo
|
|
312
|
+
(\`dd-ready\`). Si el fondo entrara antes, en RN se ver\xEDa el velo oscuro varios cientos de ms
|
|
313
|
+
antes que el contenido, que es la misma mala sensaci\xF3n que el spinner. */
|
|
314
|
+
body{display:${bodyDisplay};justify-content:${pos.justifyContent};align-items:${pos.alignItems};background:transparent;padding:${bodyPadding};box-sizing:border-box}
|
|
315
|
+
body.dd-ready{background:${bodyBg}}
|
|
316
|
+
#dd-popup{visibility:hidden}
|
|
317
|
+
body.dd-ready #dd-popup{visibility:visible}
|
|
286
318
|
.deepdots-popup{position:relative;display:flex;flex-direction:column;justify-content:flex-start;background:${cardBg};color-scheme:${colorScheme};border-radius:${cardRadius};padding:24px;box-shadow:${cardShadow};max-width:${cardMaxWidth};width:${cardWidth};min-height:${cardMinHeight};height:${cardHeight};max-height:${cardMaxHeight};box-sizing:border-box}
|
|
287
319
|
.deepdots-popup-header{display:flex;justify-content:space-between;align-items:center;gap:12px;width:100%;flex:0 0 auto}
|
|
288
320
|
/* Neutraliza la regla \`.deepdots-popup h2\` del CSS del survey (uppercase, centrado y
|
|
@@ -316,6 +348,13 @@ body{display:${bodyDisplay};justify-content:${pos.justifyContent};align-items:${
|
|
|
316
348
|
#dd-form-wrapper{width:100%;flex:1 1 auto}
|
|
317
349
|
#mf{width:100%;box-sizing:border-box;visibility:hidden}
|
|
318
350
|
.deepdots-popup-main *{max-width:100%;box-sizing:border-box}
|
|
351
|
+
/* El margen lateral lo pone la tarjeta: el CSS del survey lo a\xF1ade otra vez en el contenedor, en
|
|
352
|
+
el formulario y en el bloque de cada pregunta, y eso deja el enunciado y las opciones m\xE1s
|
|
353
|
+
adentro que el logo, el t\xEDtulo y la barra de progreso. Solo el lateral; el vertical separa las
|
|
354
|
+
preguntas. Mismas reglas que en el popup DOM (renderPopup.ts). */
|
|
355
|
+
.deepdots-popup .magicfeedback-container,
|
|
356
|
+
.deepdots-popup .magicfeedback-form,
|
|
357
|
+
.deepdots-popup .magicfeedback-div{padding-left:0;padding-right:0}
|
|
319
358
|
#dd-logo{max-height:40px;max-width:100%;object-fit:contain;display:block;margin:12px 0 0 0}
|
|
320
359
|
.deepdots-error-hint{display:none;margin:12px 0 0 0;padding:10px 12px;border-radius:6px;background:#FEF3C7;color:#92400E;border:1px solid #FCD34D;font-size:13px}
|
|
321
360
|
/* Pantalla final: el HTML del editor de la plataforma (imagen + texto), centrado. */
|
|
@@ -323,7 +362,7 @@ body{display:${bodyDisplay};justify-content:${pos.justifyContent};align-items:${
|
|
|
323
362
|
.deepdots-success img{max-width:100%;height:auto;margin:0 auto 16px auto;display:block}
|
|
324
363
|
.deepdots-success p{margin:0;font-size:16px;font-weight:600;line-height:1.4}
|
|
325
364
|
.deepdots-popup-footer{display:flex;flex-direction:row-reverse;justify-content:space-between;align-items:center;gap:4px;margin-top:auto;width:100%;padding-top:12px}
|
|
326
|
-
.dd-nav-btn{display:none;border:none;min-height:44px;padding:12px 24px;border-radius:
|
|
365
|
+
.dd-nav-btn{display:none;border:none;min-height:44px;padding:12px 24px;border-radius:999px;cursor:pointer;font-size:15px;font-weight:600;align-items:center;justify-content:center;text-align:center}
|
|
327
366
|
/* Secundario como bot\xF3n de texto: sin borde ni fondo, para que el primario sea la \xFAnica CTA. */
|
|
328
367
|
#dd-back{background:transparent;color:${textMuted};border:none}
|
|
329
368
|
#dd-start,#dd-complete,#dd-submit{background:#1E293B;color:#fff;border:none}
|
|
@@ -548,9 +587,23 @@ ${customCss}
|
|
|
548
587
|
}
|
|
549
588
|
}
|
|
550
589
|
|
|
590
|
+
${PRIMARY_COLOR_JS}
|
|
591
|
+
${REVEAL_JS}
|
|
592
|
+
// El popup se ense\xF1a cuando el survey est\xE1 pintado; \`ready\` deja al host abrir su propio
|
|
593
|
+
// Modal en ese momento en vez de montar un WebView en blanco.
|
|
594
|
+
var ddReveal=ddCreateReveal(popup, function(){
|
|
595
|
+
document.body.classList.add('dd-ready');
|
|
596
|
+
emitJSON('ready');
|
|
597
|
+
}, ${REVEAL_TIMEOUT_MS});
|
|
598
|
+
|
|
551
599
|
function setLoading(isLoading){
|
|
552
600
|
spinner.style.display=isLoading?'flex':'none';
|
|
553
|
-
if(!isLoading){
|
|
601
|
+
if(!isLoading){
|
|
602
|
+
formHost.style.visibility='visible';
|
|
603
|
+
// Fin de la carga, sea por survey cargado o por error. Entre p\xE1ginas es no-op: la
|
|
604
|
+
// revelaci\xF3n solo act\xFAa la primera vez.
|
|
605
|
+
ddReveal.whenPainted();
|
|
606
|
+
}
|
|
554
607
|
footer.style.display=isLoading?'none':'flex';
|
|
555
608
|
backBtn.disabled=isLoading; startBtn.disabled=isLoading; completeBtn.disabled=isLoading; submitBtn.disabled=isLoading;
|
|
556
609
|
if(isLoading){ updateButtons(null); }
|
|
@@ -593,6 +646,10 @@ ${customCss}
|
|
|
593
646
|
if(s.loadingBarColor){ progressBar.style.background=s.loadingBarColor; progressFollowUp.style.background=s.loadingBarColor; }
|
|
594
647
|
if(chromeOn && s.boxBackgroundColor){ popup.style.background=s.boxBackgroundColor; }
|
|
595
648
|
if(s.contentAlign){ main.style.justifyContent = s.contentAlign==='center' ? 'center' : 'flex-start'; }
|
|
649
|
+
// El survey pinta sus controles con --mf-primary, que sale de primaryColor; si la
|
|
650
|
+
// integraci\xF3n solo configura el del bot\xF3n, el popup mezclar\xEDa el color de la marca
|
|
651
|
+
// con el gris azulado por defecto del paquete.
|
|
652
|
+
ddApplySurveyPrimaryColor(popup, s);
|
|
596
653
|
if(s.buttonPrimaryColor){
|
|
597
654
|
submitBtn.style.background=s.buttonPrimaryColor; submitBtn.style.border='none'; submitBtn.style.color='#fff';
|
|
598
655
|
startBtn.style.background=s.buttonPrimaryColor; startBtn.style.border='none'; startBtn.style.color='#fff';
|
|
@@ -684,6 +741,7 @@ ${customCss}
|
|
|
684
741
|
}
|
|
685
742
|
|
|
686
743
|
// src/platform/react-native-renderer.ts
|
|
744
|
+
var NATIVE_READY_GRACE_MS = 500;
|
|
687
745
|
var ReactNativePopupRenderer = class {
|
|
688
746
|
constructor(options = {}) {
|
|
689
747
|
this.options = options;
|
|
@@ -691,6 +749,8 @@ var ReactNativePopupRenderer = class {
|
|
|
691
749
|
this.onCloseFn = null;
|
|
692
750
|
this.currentSurveyId = null;
|
|
693
751
|
this.partialEmitted = false;
|
|
752
|
+
this.readyEmitted = false;
|
|
753
|
+
this.readyTimer = null;
|
|
694
754
|
}
|
|
695
755
|
init() {
|
|
696
756
|
}
|
|
@@ -699,6 +759,8 @@ var ReactNativePopupRenderer = class {
|
|
|
699
759
|
this.onCloseFn = onClose;
|
|
700
760
|
this.currentSurveyId = surveyId;
|
|
701
761
|
this.partialEmitted = false;
|
|
762
|
+
this.readyEmitted = false;
|
|
763
|
+
this.armReadyFallback(surveyId);
|
|
702
764
|
const { profile, metadata } = buildSurveyIdentity(userId ?? null, sessionId ?? null, miniService ?? null, analyticsFeedbackSessionId ?? null);
|
|
703
765
|
const html = buildSurveyHtml({
|
|
704
766
|
surveyId,
|
|
@@ -726,9 +788,32 @@ var ReactNativePopupRenderer = class {
|
|
|
726
788
|
}
|
|
727
789
|
}
|
|
728
790
|
hide() {
|
|
791
|
+
this.clearReadyFallback();
|
|
729
792
|
this.options.onHide?.();
|
|
730
793
|
this.currentSurveyId = null;
|
|
731
794
|
}
|
|
795
|
+
/** Respaldo del aviso de listo por si el WebView no llega a ejecutar el HTML. */
|
|
796
|
+
armReadyFallback(surveyId) {
|
|
797
|
+
this.clearReadyFallback();
|
|
798
|
+
if (!this.options.onReady) return;
|
|
799
|
+
this.readyTimer = setTimeout(() => {
|
|
800
|
+
this.readyTimer = null;
|
|
801
|
+
this.emitReady(surveyId);
|
|
802
|
+
}, REVEAL_TIMEOUT_MS + NATIVE_READY_GRACE_MS);
|
|
803
|
+
}
|
|
804
|
+
clearReadyFallback() {
|
|
805
|
+
if (this.readyTimer !== null) {
|
|
806
|
+
clearTimeout(this.readyTimer);
|
|
807
|
+
this.readyTimer = null;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
/** Idempotente: el WebView puede avisar a la vez que vence el respaldo. */
|
|
811
|
+
emitReady(surveyId) {
|
|
812
|
+
if (this.readyEmitted) return;
|
|
813
|
+
this.readyEmitted = true;
|
|
814
|
+
this.clearReadyFallback();
|
|
815
|
+
this.options.onReady?.({ surveyId });
|
|
816
|
+
}
|
|
732
817
|
/**
|
|
733
818
|
* El host la conecta al `onMessage` del WebView. Traduce los mensajes del survey a
|
|
734
819
|
* eventos del SDK: primera interacción → `popup_clicked` (PARTIAL), completado →
|
|
@@ -744,6 +829,9 @@ var ReactNativePopupRenderer = class {
|
|
|
744
829
|
name = raw;
|
|
745
830
|
}
|
|
746
831
|
switch (name) {
|
|
832
|
+
case "ready":
|
|
833
|
+
this.emitReady(surveyId);
|
|
834
|
+
break;
|
|
747
835
|
case "loaded":
|
|
748
836
|
case "before_submit":
|
|
749
837
|
case "after_submit":
|
|
@@ -771,6 +859,33 @@ function createReactNativeRenderer() {
|
|
|
771
859
|
return new ReactNativePopupRenderer();
|
|
772
860
|
}
|
|
773
861
|
|
|
862
|
+
// src/ui/preload.ts
|
|
863
|
+
var defaultLoader = () => import("./renderPopup-5QXL6EJW.mjs");
|
|
864
|
+
var pending = null;
|
|
865
|
+
function preloadRenderPopup(loader = defaultLoader) {
|
|
866
|
+
if (pending) return pending;
|
|
867
|
+
try {
|
|
868
|
+
pending = loader().then(() => void 0).catch(() => {
|
|
869
|
+
pending = null;
|
|
870
|
+
});
|
|
871
|
+
} catch {
|
|
872
|
+
pending = null;
|
|
873
|
+
return Promise.resolve();
|
|
874
|
+
}
|
|
875
|
+
return pending;
|
|
876
|
+
}
|
|
877
|
+
function schedulePreloadRenderPopup(loader) {
|
|
878
|
+
const run = () => {
|
|
879
|
+
void preloadRenderPopup(loader);
|
|
880
|
+
};
|
|
881
|
+
const idle = globalThis.requestIdleCallback;
|
|
882
|
+
if (typeof idle === "function") {
|
|
883
|
+
idle(run, { timeout: 2e3 });
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
setTimeout(run, 0);
|
|
887
|
+
}
|
|
888
|
+
|
|
774
889
|
// src/platform/renderer.ts
|
|
775
890
|
var NoopPopupRenderer = class {
|
|
776
891
|
show() {
|
|
@@ -805,13 +920,20 @@ var BrowserPopupRenderer = class {
|
|
|
805
920
|
document.body.appendChild(this.container);
|
|
806
921
|
}
|
|
807
922
|
}
|
|
923
|
+
/**
|
|
924
|
+
* Descarga el chunk de `renderPopup` antes de que ningún trigger dispare, para que abrir el
|
|
925
|
+
* popup no empiece por esperar ~238 KB de JS.
|
|
926
|
+
*/
|
|
927
|
+
preload() {
|
|
928
|
+
schedulePreloadRenderPopup();
|
|
929
|
+
}
|
|
808
930
|
show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId, _renderChrome, options) {
|
|
809
931
|
if (this.visible) return;
|
|
810
932
|
if (!this.container || !document.body.contains(this.container)) this.init();
|
|
811
933
|
if (!this.container) return;
|
|
812
934
|
this.visible = true;
|
|
813
935
|
const container = this.container;
|
|
814
|
-
void import("./renderPopup-
|
|
936
|
+
void import("./renderPopup-5QXL6EJW.mjs").then(({ renderPopup }) => {
|
|
815
937
|
renderPopup(container, surveyId, productId, actions, emit, onClose, env, userId, style, sessionId, miniService, analyticsFeedbackSessionId, options);
|
|
816
938
|
});
|
|
817
939
|
}
|
|
@@ -819,6 +941,8 @@ var BrowserPopupRenderer = class {
|
|
|
819
941
|
this.visible = false;
|
|
820
942
|
if (this.container) {
|
|
821
943
|
this.container.style.display = "none";
|
|
944
|
+
this.container.style.visibility = "";
|
|
945
|
+
this.container.style.pointerEvents = "";
|
|
822
946
|
this.container.innerHTML = "";
|
|
823
947
|
}
|
|
824
948
|
}
|
|
@@ -1563,6 +1687,75 @@ var CrashReporter = class {
|
|
|
1563
1687
|
}
|
|
1564
1688
|
};
|
|
1565
1689
|
|
|
1690
|
+
// src/core/popup-state-store.ts
|
|
1691
|
+
var POPUP_STATE_STORAGE_KEY = "deepdots.popups.state";
|
|
1692
|
+
var MAX_TRACKED_POPUP_ENTRIES = 200;
|
|
1693
|
+
function emptyState() {
|
|
1694
|
+
return { lastShown: {}, progress: {} };
|
|
1695
|
+
}
|
|
1696
|
+
function isValidTimestamp(value) {
|
|
1697
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
1698
|
+
}
|
|
1699
|
+
function capEntries(entries, timestampOf) {
|
|
1700
|
+
if (entries.length <= MAX_TRACKED_POPUP_ENTRIES) return entries;
|
|
1701
|
+
return [...entries].sort((a, b) => timestampOf(b[1]) - timestampOf(a[1])).slice(0, MAX_TRACKED_POPUP_ENTRIES);
|
|
1702
|
+
}
|
|
1703
|
+
function readPopupState(storage) {
|
|
1704
|
+
let raw = null;
|
|
1705
|
+
try {
|
|
1706
|
+
raw = storage.getItem(POPUP_STATE_STORAGE_KEY);
|
|
1707
|
+
} catch {
|
|
1708
|
+
return emptyState();
|
|
1709
|
+
}
|
|
1710
|
+
if (!raw) return emptyState();
|
|
1711
|
+
let parsed;
|
|
1712
|
+
try {
|
|
1713
|
+
parsed = JSON.parse(raw);
|
|
1714
|
+
} catch {
|
|
1715
|
+
return emptyState();
|
|
1716
|
+
}
|
|
1717
|
+
if (typeof parsed !== "object" || parsed === null) return emptyState();
|
|
1718
|
+
const source = parsed;
|
|
1719
|
+
const state = emptyState();
|
|
1720
|
+
if (typeof source.lastShown === "object" && source.lastShown !== null) {
|
|
1721
|
+
const valid = Object.entries(source.lastShown).filter((entry) => isValidTimestamp(entry[1]));
|
|
1722
|
+
capEntries(valid, (timestamp) => timestamp).forEach(([popupId, timestamp]) => {
|
|
1723
|
+
state.lastShown[popupId] = timestamp;
|
|
1724
|
+
});
|
|
1725
|
+
}
|
|
1726
|
+
if (typeof source.progress === "object" && source.progress !== null) {
|
|
1727
|
+
const valid = Object.entries(source.progress).filter((entry) => {
|
|
1728
|
+
const value = entry[1];
|
|
1729
|
+
if (typeof value !== "object" || value === null) return false;
|
|
1730
|
+
const candidate = value;
|
|
1731
|
+
return POPUP_TRIGGER_CONDITION_STATUSES.includes(candidate.status) && isValidTimestamp(candidate.timestamp);
|
|
1732
|
+
});
|
|
1733
|
+
capEntries(valid, (entry) => entry.timestamp).forEach(([surveyId, entry]) => {
|
|
1734
|
+
state.progress[surveyId] = { status: entry.status, timestamp: entry.timestamp };
|
|
1735
|
+
});
|
|
1736
|
+
}
|
|
1737
|
+
return state;
|
|
1738
|
+
}
|
|
1739
|
+
function writePopupState(storage, state) {
|
|
1740
|
+
const capped = emptyState();
|
|
1741
|
+
capEntries(Object.entries(state.lastShown), (timestamp) => timestamp).forEach(([popupId, timestamp]) => {
|
|
1742
|
+
capped.lastShown[popupId] = timestamp;
|
|
1743
|
+
});
|
|
1744
|
+
capEntries(Object.entries(state.progress), (entry) => entry.timestamp).forEach(([surveyId, entry]) => {
|
|
1745
|
+
capped.progress[surveyId] = entry;
|
|
1746
|
+
});
|
|
1747
|
+
try {
|
|
1748
|
+
storage.setItem(POPUP_STATE_STORAGE_KEY, JSON.stringify(capped));
|
|
1749
|
+
} catch {
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
function clearPopupState(storage) {
|
|
1753
|
+
try {
|
|
1754
|
+
storage.removeItem(POPUP_STATE_STORAGE_KEY);
|
|
1755
|
+
} catch {
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1566
1759
|
// src/analytics/messaging.ts
|
|
1567
1760
|
var MESSAGE_CHANNELS = ["push", "in_app"];
|
|
1568
1761
|
function buildMessageParams(stage, o) {
|
|
@@ -1650,6 +1843,10 @@ var DeepdotsPopups = class {
|
|
|
1650
1843
|
this.popupDefinitions = [];
|
|
1651
1844
|
this.popupsLoaded = false;
|
|
1652
1845
|
this.pendingAutoLaunch = false;
|
|
1846
|
+
// Estado que alimenta las reglas de redisplay. Copia de trabajo en memoria; la fuente
|
|
1847
|
+
// duradera es el storage (`popup-state-store`), rehidratada en `init()` — sin eso, una
|
|
1848
|
+
// recarga reiniciaba el cooldown y el popup volvía a salir. `answeredSurveys` se deriva
|
|
1849
|
+
// de las entradas COMPLETED de `surveyProgress` (los dos los escribe `markSurveyAnswered`).
|
|
1653
1850
|
this.answeredSurveys = /* @__PURE__ */ new Set();
|
|
1654
1851
|
this.surveyProgress = /* @__PURE__ */ new Map();
|
|
1655
1852
|
this.lastShown = /* @__PURE__ */ new Map();
|
|
@@ -1725,6 +1922,7 @@ var DeepdotsPopups = class {
|
|
|
1725
1922
|
};
|
|
1726
1923
|
const storage = config.storage ?? createDefaultStorage();
|
|
1727
1924
|
this.storage = storage;
|
|
1925
|
+
this.loadPopupState(storage);
|
|
1728
1926
|
this.tracking = new TrackingManager({
|
|
1729
1927
|
storage,
|
|
1730
1928
|
clientUserId: this.config.userId,
|
|
@@ -1814,6 +2012,7 @@ var DeepdotsPopups = class {
|
|
|
1814
2012
|
this.popupDefinitions = this.validatePopupDefinitions(defs);
|
|
1815
2013
|
this.popupsLoaded = true;
|
|
1816
2014
|
this.log("Popups loaded from API", this.popupDefinitions);
|
|
2015
|
+
if (this.popupDefinitions.length) this.renderer.preload?.();
|
|
1817
2016
|
this.configureTriggersFromDefinitions();
|
|
1818
2017
|
this.processDeferredExitQueue();
|
|
1819
2018
|
if (this.pendingAutoLaunch) {
|
|
@@ -1992,6 +2191,10 @@ var DeepdotsPopups = class {
|
|
|
1992
2191
|
}) : null;
|
|
1993
2192
|
this.analytics?.resetUserScope();
|
|
1994
2193
|
this.messageGuard.reset();
|
|
2194
|
+
this.lastShown.clear();
|
|
2195
|
+
this.surveyProgress.clear();
|
|
2196
|
+
this.answeredSurveys.clear();
|
|
2197
|
+
clearPopupState(storage);
|
|
1995
2198
|
this.log("tracking \xB7 user_change \xB7 user_id:", this.tracking.getUserId());
|
|
1996
2199
|
this.openSession();
|
|
1997
2200
|
}
|
|
@@ -2163,6 +2366,7 @@ var DeepdotsPopups = class {
|
|
|
2163
2366
|
this.log("Showing popup (definition)", def);
|
|
2164
2367
|
this.surveyToPopupId.set(def.surveyId, def.id);
|
|
2165
2368
|
this.lastShown.set(def.id, Date.now());
|
|
2369
|
+
this.persistPopupState();
|
|
2166
2370
|
this.renderPopup(def.surveyId, def.productId, def.actions, def.style, def.title);
|
|
2167
2371
|
this.emitEvent("popup_shown", def.surveyId, { popupId: def.id });
|
|
2168
2372
|
}
|
|
@@ -2296,7 +2500,7 @@ var DeepdotsPopups = class {
|
|
|
2296
2500
|
processDeferredExitQueue() {
|
|
2297
2501
|
const queue = this.getDeferredExitQueue();
|
|
2298
2502
|
if (!queue.length) return;
|
|
2299
|
-
const
|
|
2503
|
+
const pending2 = [];
|
|
2300
2504
|
const now = Date.now();
|
|
2301
2505
|
queue.forEach((item) => {
|
|
2302
2506
|
const def = this.popupDefinitions.find((popup) => popup.id === item.id && popup.surveyId === item.surveyId);
|
|
@@ -2307,10 +2511,10 @@ var DeepdotsPopups = class {
|
|
|
2307
2511
|
this.tryShowDeferredExitPopup(item);
|
|
2308
2512
|
return;
|
|
2309
2513
|
}
|
|
2310
|
-
|
|
2514
|
+
pending2.push(item);
|
|
2311
2515
|
this.scheduleDeferredExitPopup(item);
|
|
2312
2516
|
});
|
|
2313
|
-
this.setDeferredExitQueue(
|
|
2517
|
+
this.setDeferredExitQueue(pending2);
|
|
2314
2518
|
}
|
|
2315
2519
|
scheduleDeferredExitPopup(item) {
|
|
2316
2520
|
if (typeof window === "undefined") return;
|
|
@@ -2383,7 +2587,7 @@ var DeepdotsPopups = class {
|
|
|
2383
2587
|
async fetchPopupsFromServer() {
|
|
2384
2588
|
const apiKey = this.config?.apiKey;
|
|
2385
2589
|
const baseUrl = this.baseUrl;
|
|
2386
|
-
const userId = this.config?.userId;
|
|
2590
|
+
const userId = this.tracking?.getUserId() ?? this.config?.userId;
|
|
2387
2591
|
if (!apiKey || !baseUrl) {
|
|
2388
2592
|
this.log("Missing apiKey or baseUrl. Skipping popups fetch.");
|
|
2389
2593
|
return [];
|
|
@@ -2527,7 +2731,7 @@ var DeepdotsPopups = class {
|
|
|
2527
2731
|
}
|
|
2528
2732
|
if (!this.popupContainer) return;
|
|
2529
2733
|
const container = this.popupContainer;
|
|
2530
|
-
void import("./renderPopup-
|
|
2734
|
+
void import("./renderPopup-5QXL6EJW.mjs").then(({ renderPopup }) => {
|
|
2531
2735
|
renderPopup(
|
|
2532
2736
|
container,
|
|
2533
2737
|
surveyId,
|
|
@@ -2642,6 +2846,29 @@ var DeepdotsPopups = class {
|
|
|
2642
2846
|
return;
|
|
2643
2847
|
}
|
|
2644
2848
|
this.surveyProgress.set(surveyId, { status, timestamp: Date.now() });
|
|
2849
|
+
this.persistPopupState();
|
|
2850
|
+
}
|
|
2851
|
+
/**
|
|
2852
|
+
* Rehidrata el estado de redisplay desde el storage. `answeredSurveys` se reconstruye
|
|
2853
|
+
* desde las entradas COMPLETED: es exactamente lo que persiste `markSurveyAnswered`.
|
|
2854
|
+
*/
|
|
2855
|
+
loadPopupState(storage) {
|
|
2856
|
+
const state = readPopupState(storage);
|
|
2857
|
+
Object.entries(state.lastShown).forEach(([popupId, timestamp]) => {
|
|
2858
|
+
this.lastShown.set(popupId, timestamp);
|
|
2859
|
+
});
|
|
2860
|
+
Object.entries(state.progress).forEach(([surveyId, entry]) => {
|
|
2861
|
+
this.surveyProgress.set(surveyId, { status: entry.status, timestamp: entry.timestamp });
|
|
2862
|
+
if (entry.status === "COMPLETED") this.answeredSurveys.add(surveyId);
|
|
2863
|
+
});
|
|
2864
|
+
}
|
|
2865
|
+
/** Vuelca el estado de redisplay al storage. Sin storage (o bloqueado) degrada a memoria. */
|
|
2866
|
+
persistPopupState() {
|
|
2867
|
+
if (!this.storage) return;
|
|
2868
|
+
writePopupState(this.storage, {
|
|
2869
|
+
lastShown: Object.fromEntries(this.lastShown),
|
|
2870
|
+
progress: Object.fromEntries(this.surveyProgress)
|
|
2871
|
+
});
|
|
2645
2872
|
}
|
|
2646
2873
|
isPopupDefinition(value) {
|
|
2647
2874
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -2759,8 +2986,10 @@ export {
|
|
|
2759
2986
|
MAX_TRACKED_MESSAGES,
|
|
2760
2987
|
MESSAGE_CHANNELS,
|
|
2761
2988
|
MessageGuard,
|
|
2989
|
+
NATIVE_READY_GRACE_MS,
|
|
2762
2990
|
NoopPopupRenderer,
|
|
2763
2991
|
POPUP_TRIGGER_CONDITION_STATUSES,
|
|
2992
|
+
REVEAL_TIMEOUT_MS,
|
|
2764
2993
|
RTL_LOCALES,
|
|
2765
2994
|
ReactNativePopupRenderer,
|
|
2766
2995
|
SUPPORTED_LANGUAGES,
|