@magicfeedback/popup-sdk 1.7.0 → 1.7.1

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.
@@ -576,6 +576,13 @@ declare class DeepdotsPopups {
576
576
  private safeParseUrl;
577
577
  private mapPopupTriggerType;
578
578
  private markSurveyProgress;
579
+ /**
580
+ * Rehidrata el estado de redisplay desde el storage. `answeredSurveys` se reconstruye
581
+ * desde las entradas COMPLETED: es exactamente lo que persiste `markSurveyAnswered`.
582
+ */
583
+ private loadPopupState;
584
+ /** Vuelca el estado de redisplay al storage. Sin storage (o bloqueado) degrada a memoria. */
585
+ private persistPopupState;
579
586
  private isPopupDefinition;
580
587
  private isPopupTrigger;
581
588
  private isPopupTriggerType;
@@ -576,6 +576,13 @@ declare class DeepdotsPopups {
576
576
  private safeParseUrl;
577
577
  private mapPopupTriggerType;
578
578
  private markSurveyProgress;
579
+ /**
580
+ * Rehidrata el estado de redisplay desde el storage. `answeredSurveys` se reconstruye
581
+ * desde las entradas COMPLETED: es exactamente lo que persiste `markSurveyAnswered`.
582
+ */
583
+ private loadPopupState;
584
+ /** Vuelca el estado de redisplay al storage. Sin storage (o bloqueado) degrada a memoria. */
585
+ private persistPopupState;
579
586
  private isPopupDefinition;
580
587
  private isPopupTrigger;
581
588
  private isPopupTriggerType;
@@ -4580,6 +4580,75 @@ var CrashReporter = class {
4580
4580
  }
4581
4581
  };
4582
4582
 
4583
+ // src/core/popup-state-store.ts
4584
+ var POPUP_STATE_STORAGE_KEY = "deepdots.popups.state";
4585
+ var MAX_TRACKED_POPUP_ENTRIES = 200;
4586
+ function emptyState() {
4587
+ return { lastShown: {}, progress: {} };
4588
+ }
4589
+ function isValidTimestamp(value) {
4590
+ return typeof value === "number" && Number.isFinite(value);
4591
+ }
4592
+ function capEntries(entries, timestampOf) {
4593
+ if (entries.length <= MAX_TRACKED_POPUP_ENTRIES) return entries;
4594
+ return [...entries].sort((a, b) => timestampOf(b[1]) - timestampOf(a[1])).slice(0, MAX_TRACKED_POPUP_ENTRIES);
4595
+ }
4596
+ function readPopupState(storage) {
4597
+ let raw = null;
4598
+ try {
4599
+ raw = storage.getItem(POPUP_STATE_STORAGE_KEY);
4600
+ } catch {
4601
+ return emptyState();
4602
+ }
4603
+ if (!raw) return emptyState();
4604
+ let parsed;
4605
+ try {
4606
+ parsed = JSON.parse(raw);
4607
+ } catch {
4608
+ return emptyState();
4609
+ }
4610
+ if (typeof parsed !== "object" || parsed === null) return emptyState();
4611
+ const source = parsed;
4612
+ const state = emptyState();
4613
+ if (typeof source.lastShown === "object" && source.lastShown !== null) {
4614
+ const valid = Object.entries(source.lastShown).filter((entry) => isValidTimestamp(entry[1]));
4615
+ capEntries(valid, (timestamp) => timestamp).forEach(([popupId, timestamp]) => {
4616
+ state.lastShown[popupId] = timestamp;
4617
+ });
4618
+ }
4619
+ if (typeof source.progress === "object" && source.progress !== null) {
4620
+ const valid = Object.entries(source.progress).filter((entry) => {
4621
+ const value = entry[1];
4622
+ if (typeof value !== "object" || value === null) return false;
4623
+ const candidate = value;
4624
+ return POPUP_TRIGGER_CONDITION_STATUSES.includes(candidate.status) && isValidTimestamp(candidate.timestamp);
4625
+ });
4626
+ capEntries(valid, (entry) => entry.timestamp).forEach(([surveyId, entry]) => {
4627
+ state.progress[surveyId] = { status: entry.status, timestamp: entry.timestamp };
4628
+ });
4629
+ }
4630
+ return state;
4631
+ }
4632
+ function writePopupState(storage, state) {
4633
+ const capped = emptyState();
4634
+ capEntries(Object.entries(state.lastShown), (timestamp) => timestamp).forEach(([popupId, timestamp]) => {
4635
+ capped.lastShown[popupId] = timestamp;
4636
+ });
4637
+ capEntries(Object.entries(state.progress), (entry) => entry.timestamp).forEach(([surveyId, entry]) => {
4638
+ capped.progress[surveyId] = entry;
4639
+ });
4640
+ try {
4641
+ storage.setItem(POPUP_STATE_STORAGE_KEY, JSON.stringify(capped));
4642
+ } catch {
4643
+ }
4644
+ }
4645
+ function clearPopupState(storage) {
4646
+ try {
4647
+ storage.removeItem(POPUP_STATE_STORAGE_KEY);
4648
+ } catch {
4649
+ }
4650
+ }
4651
+
4583
4652
  // src/core/deepdots-popups.ts
4584
4653
  init_logger();
4585
4654
 
@@ -4670,6 +4739,10 @@ var DeepdotsPopups = class {
4670
4739
  this.popupDefinitions = [];
4671
4740
  this.popupsLoaded = false;
4672
4741
  this.pendingAutoLaunch = false;
4742
+ // Estado que alimenta las reglas de redisplay. Copia de trabajo en memoria; la fuente
4743
+ // duradera es el storage (`popup-state-store`), rehidratada en `init()` — sin eso, una
4744
+ // recarga reiniciaba el cooldown y el popup volvía a salir. `answeredSurveys` se deriva
4745
+ // de las entradas COMPLETED de `surveyProgress` (los dos los escribe `markSurveyAnswered`).
4673
4746
  this.answeredSurveys = /* @__PURE__ */ new Set();
4674
4747
  this.surveyProgress = /* @__PURE__ */ new Map();
4675
4748
  this.lastShown = /* @__PURE__ */ new Map();
@@ -4745,6 +4818,7 @@ var DeepdotsPopups = class {
4745
4818
  };
4746
4819
  const storage = config.storage ?? createDefaultStorage();
4747
4820
  this.storage = storage;
4821
+ this.loadPopupState(storage);
4748
4822
  this.tracking = new TrackingManager({
4749
4823
  storage,
4750
4824
  clientUserId: this.config.userId,
@@ -5012,6 +5086,10 @@ var DeepdotsPopups = class {
5012
5086
  }) : null;
5013
5087
  this.analytics?.resetUserScope();
5014
5088
  this.messageGuard.reset();
5089
+ this.lastShown.clear();
5090
+ this.surveyProgress.clear();
5091
+ this.answeredSurveys.clear();
5092
+ clearPopupState(storage);
5015
5093
  this.log("tracking \xB7 user_change \xB7 user_id:", this.tracking.getUserId());
5016
5094
  this.openSession();
5017
5095
  }
@@ -5183,6 +5261,7 @@ var DeepdotsPopups = class {
5183
5261
  this.log("Showing popup (definition)", def);
5184
5262
  this.surveyToPopupId.set(def.surveyId, def.id);
5185
5263
  this.lastShown.set(def.id, Date.now());
5264
+ this.persistPopupState();
5186
5265
  this.renderPopup(def.surveyId, def.productId, def.actions, def.style, def.title);
5187
5266
  this.emitEvent("popup_shown", def.surveyId, { popupId: def.id });
5188
5267
  }
@@ -5662,6 +5741,29 @@ var DeepdotsPopups = class {
5662
5741
  return;
5663
5742
  }
5664
5743
  this.surveyProgress.set(surveyId, { status, timestamp: Date.now() });
5744
+ this.persistPopupState();
5745
+ }
5746
+ /**
5747
+ * Rehidrata el estado de redisplay desde el storage. `answeredSurveys` se reconstruye
5748
+ * desde las entradas COMPLETED: es exactamente lo que persiste `markSurveyAnswered`.
5749
+ */
5750
+ loadPopupState(storage) {
5751
+ const state = readPopupState(storage);
5752
+ Object.entries(state.lastShown).forEach(([popupId, timestamp]) => {
5753
+ this.lastShown.set(popupId, timestamp);
5754
+ });
5755
+ Object.entries(state.progress).forEach(([surveyId, entry]) => {
5756
+ this.surveyProgress.set(surveyId, { status: entry.status, timestamp: entry.timestamp });
5757
+ if (entry.status === "COMPLETED") this.answeredSurveys.add(surveyId);
5758
+ });
5759
+ }
5760
+ /** Vuelca el estado de redisplay al storage. Sin storage (o bloqueado) degrada a memoria. */
5761
+ persistPopupState() {
5762
+ if (!this.storage) return;
5763
+ writePopupState(this.storage, {
5764
+ lastShown: Object.fromEntries(this.lastShown),
5765
+ progress: Object.fromEntries(this.surveyProgress)
5766
+ });
5665
5767
  }
5666
5768
  isPopupDefinition(value) {
5667
5769
  if (typeof value !== "object" || value === null) return false;