@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.
@@ -1565,6 +1565,75 @@ var CrashReporter = class {
1565
1565
  }
1566
1566
  };
1567
1567
 
1568
+ // src/core/popup-state-store.ts
1569
+ var POPUP_STATE_STORAGE_KEY = "deepdots.popups.state";
1570
+ var MAX_TRACKED_POPUP_ENTRIES = 200;
1571
+ function emptyState() {
1572
+ return { lastShown: {}, progress: {} };
1573
+ }
1574
+ function isValidTimestamp(value) {
1575
+ return typeof value === "number" && Number.isFinite(value);
1576
+ }
1577
+ function capEntries(entries, timestampOf) {
1578
+ if (entries.length <= MAX_TRACKED_POPUP_ENTRIES) return entries;
1579
+ return [...entries].sort((a, b) => timestampOf(b[1]) - timestampOf(a[1])).slice(0, MAX_TRACKED_POPUP_ENTRIES);
1580
+ }
1581
+ function readPopupState(storage) {
1582
+ let raw = null;
1583
+ try {
1584
+ raw = storage.getItem(POPUP_STATE_STORAGE_KEY);
1585
+ } catch {
1586
+ return emptyState();
1587
+ }
1588
+ if (!raw) return emptyState();
1589
+ let parsed;
1590
+ try {
1591
+ parsed = JSON.parse(raw);
1592
+ } catch {
1593
+ return emptyState();
1594
+ }
1595
+ if (typeof parsed !== "object" || parsed === null) return emptyState();
1596
+ const source = parsed;
1597
+ const state = emptyState();
1598
+ if (typeof source.lastShown === "object" && source.lastShown !== null) {
1599
+ const valid = Object.entries(source.lastShown).filter((entry) => isValidTimestamp(entry[1]));
1600
+ capEntries(valid, (timestamp) => timestamp).forEach(([popupId, timestamp]) => {
1601
+ state.lastShown[popupId] = timestamp;
1602
+ });
1603
+ }
1604
+ if (typeof source.progress === "object" && source.progress !== null) {
1605
+ const valid = Object.entries(source.progress).filter((entry) => {
1606
+ const value = entry[1];
1607
+ if (typeof value !== "object" || value === null) return false;
1608
+ const candidate = value;
1609
+ return POPUP_TRIGGER_CONDITION_STATUSES.includes(candidate.status) && isValidTimestamp(candidate.timestamp);
1610
+ });
1611
+ capEntries(valid, (entry) => entry.timestamp).forEach(([surveyId, entry]) => {
1612
+ state.progress[surveyId] = { status: entry.status, timestamp: entry.timestamp };
1613
+ });
1614
+ }
1615
+ return state;
1616
+ }
1617
+ function writePopupState(storage, state) {
1618
+ const capped = emptyState();
1619
+ capEntries(Object.entries(state.lastShown), (timestamp) => timestamp).forEach(([popupId, timestamp]) => {
1620
+ capped.lastShown[popupId] = timestamp;
1621
+ });
1622
+ capEntries(Object.entries(state.progress), (entry) => entry.timestamp).forEach(([surveyId, entry]) => {
1623
+ capped.progress[surveyId] = entry;
1624
+ });
1625
+ try {
1626
+ storage.setItem(POPUP_STATE_STORAGE_KEY, JSON.stringify(capped));
1627
+ } catch {
1628
+ }
1629
+ }
1630
+ function clearPopupState(storage) {
1631
+ try {
1632
+ storage.removeItem(POPUP_STATE_STORAGE_KEY);
1633
+ } catch {
1634
+ }
1635
+ }
1636
+
1568
1637
  // src/analytics/messaging.ts
1569
1638
  var MESSAGE_CHANNELS = ["push", "in_app"];
1570
1639
  function buildMessageParams(stage, o) {
@@ -1652,6 +1721,10 @@ var DeepdotsPopups = class {
1652
1721
  this.popupDefinitions = [];
1653
1722
  this.popupsLoaded = false;
1654
1723
  this.pendingAutoLaunch = false;
1724
+ // Estado que alimenta las reglas de redisplay. Copia de trabajo en memoria; la fuente
1725
+ // duradera es el storage (`popup-state-store`), rehidratada en `init()` — sin eso, una
1726
+ // recarga reiniciaba el cooldown y el popup volvía a salir. `answeredSurveys` se deriva
1727
+ // de las entradas COMPLETED de `surveyProgress` (los dos los escribe `markSurveyAnswered`).
1655
1728
  this.answeredSurveys = /* @__PURE__ */ new Set();
1656
1729
  this.surveyProgress = /* @__PURE__ */ new Map();
1657
1730
  this.lastShown = /* @__PURE__ */ new Map();
@@ -1727,6 +1800,7 @@ var DeepdotsPopups = class {
1727
1800
  };
1728
1801
  const storage = config.storage ?? createDefaultStorage();
1729
1802
  this.storage = storage;
1803
+ this.loadPopupState(storage);
1730
1804
  this.tracking = new TrackingManager({
1731
1805
  storage,
1732
1806
  clientUserId: this.config.userId,
@@ -1994,6 +2068,10 @@ var DeepdotsPopups = class {
1994
2068
  }) : null;
1995
2069
  this.analytics?.resetUserScope();
1996
2070
  this.messageGuard.reset();
2071
+ this.lastShown.clear();
2072
+ this.surveyProgress.clear();
2073
+ this.answeredSurveys.clear();
2074
+ clearPopupState(storage);
1997
2075
  this.log("tracking \xB7 user_change \xB7 user_id:", this.tracking.getUserId());
1998
2076
  this.openSession();
1999
2077
  }
@@ -2165,6 +2243,7 @@ var DeepdotsPopups = class {
2165
2243
  this.log("Showing popup (definition)", def);
2166
2244
  this.surveyToPopupId.set(def.surveyId, def.id);
2167
2245
  this.lastShown.set(def.id, Date.now());
2246
+ this.persistPopupState();
2168
2247
  this.renderPopup(def.surveyId, def.productId, def.actions, def.style, def.title);
2169
2248
  this.emitEvent("popup_shown", def.surveyId, { popupId: def.id });
2170
2249
  }
@@ -2644,6 +2723,29 @@ var DeepdotsPopups = class {
2644
2723
  return;
2645
2724
  }
2646
2725
  this.surveyProgress.set(surveyId, { status, timestamp: Date.now() });
2726
+ this.persistPopupState();
2727
+ }
2728
+ /**
2729
+ * Rehidrata el estado de redisplay desde el storage. `answeredSurveys` se reconstruye
2730
+ * desde las entradas COMPLETED: es exactamente lo que persiste `markSurveyAnswered`.
2731
+ */
2732
+ loadPopupState(storage) {
2733
+ const state = readPopupState(storage);
2734
+ Object.entries(state.lastShown).forEach(([popupId, timestamp]) => {
2735
+ this.lastShown.set(popupId, timestamp);
2736
+ });
2737
+ Object.entries(state.progress).forEach(([surveyId, entry]) => {
2738
+ this.surveyProgress.set(surveyId, { status: entry.status, timestamp: entry.timestamp });
2739
+ if (entry.status === "COMPLETED") this.answeredSurveys.add(surveyId);
2740
+ });
2741
+ }
2742
+ /** Vuelca el estado de redisplay al storage. Sin storage (o bloqueado) degrada a memoria. */
2743
+ persistPopupState() {
2744
+ if (!this.storage) return;
2745
+ writePopupState(this.storage, {
2746
+ lastShown: Object.fromEntries(this.lastShown),
2747
+ progress: Object.fromEntries(this.surveyProgress)
2748
+ });
2647
2749
  }
2648
2750
  isPopupDefinition(value) {
2649
2751
  if (typeof value !== "object" || value === null) return false;