@loafmarkets/ui 0.1.427 → 0.1.428

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.mjs CHANGED
@@ -3019,7 +3019,7 @@ PropertyTour.displayName = "PropertyTour";
3019
3019
  var BREAKING_WINDOW_HOURS = 6;
3020
3020
  var BREAKING_MEMORY_CAP = 50;
3021
3021
  var BREAKING_MEMORY_STORAGE_KEY = "loaf:dismissed-breaking";
3022
- var emptyBreakingMemory = () => ({ dismissed: {}, pinned: {}, watermark: null });
3022
+ var emptyBreakingMemory = () => ({ dismissed: {}, blocked: {} });
3023
3023
  var isTimestampMap = (v) => typeof v === "object" && v !== null && !Array.isArray(v) && Object.values(v).every((t) => typeof t === "number" && Number.isFinite(t));
3024
3024
  var pruneMap = (map, cutoff) => {
3025
3025
  const fresh = Object.entries(map).filter(([, t]) => t >= cutoff);
@@ -3030,44 +3030,30 @@ var prune = (mem, now) => {
3030
3030
  const cutoff = now - BREAKING_WINDOW_HOURS * 36e5;
3031
3031
  return {
3032
3032
  dismissed: pruneMap(mem.dismissed, cutoff),
3033
- pinned: pruneMap(mem.pinned, cutoff),
3034
- // An expired watermark can't block anything (every in-window story is newer).
3035
- watermark: mem.watermark !== null && mem.watermark >= cutoff ? mem.watermark : null
3033
+ blocked: pruneMap(mem.blocked, cutoff)
3036
3034
  };
3037
3035
  };
3038
3036
  function parseBreakingMemory(raw, now) {
3039
3037
  if (!raw) return emptyBreakingMemory();
3040
3038
  try {
3041
3039
  const v = JSON.parse(raw);
3042
- if (typeof v !== "object" || v === null || Array.isArray(v) || !isTimestampMap(v.dismissed) || !isTimestampMap(v.pinned) || !(v.watermark === null || typeof v.watermark === "number" && Number.isFinite(v.watermark))) {
3040
+ if (typeof v !== "object" || v === null || Array.isArray(v) || !isTimestampMap(v.dismissed)) {
3043
3041
  return emptyBreakingMemory();
3044
3042
  }
3045
- return prune({ dismissed: v.dismissed, pinned: v.pinned, watermark: v.watermark ?? null }, now);
3043
+ const blocked = isTimestampMap(v.blocked) ? v.blocked : {};
3044
+ return prune({ dismissed: v.dismissed, blocked }, now);
3046
3045
  } catch {
3047
3046
  return emptyBreakingMemory();
3048
3047
  }
3049
3048
  }
3050
3049
  var serializeBreakingMemory = (mem) => JSON.stringify(mem);
3051
- function recordBreakingDismissal(mem, key, publishedAtMs, now) {
3052
- return prune(
3053
- {
3054
- dismissed: { ...mem.dismissed, [key]: publishedAtMs },
3055
- pinned: mem.pinned,
3056
- watermark: Math.max(mem.watermark ?? -Infinity, publishedAtMs)
3057
- },
3058
- now
3059
- );
3060
- }
3061
- function recordBreakingPins(mem, pins, now) {
3062
- if (pins.every((p) => mem.pinned[p.key] !== void 0)) return mem;
3063
- const pinned = { ...mem.pinned };
3064
- for (const p of pins) pinned[p.key] = p.publishedAtMs;
3065
- return prune({ dismissed: mem.dismissed, pinned, watermark: mem.watermark }, now);
3050
+ function recordBreakingDismissal(mem, key, publishedAtMs, now, waiting = []) {
3051
+ const blocked = { ...mem.blocked };
3052
+ for (const w of waiting) blocked[w.key] = w.publishedAtMs;
3053
+ return prune({ dismissed: { ...mem.dismissed, [key]: publishedAtMs }, blocked }, now);
3066
3054
  }
3067
- function mayPinBreaking(mem, key, publishedAtMs) {
3068
- if (mem.dismissed[key] !== void 0) return false;
3069
- if (mem.watermark === null) return true;
3070
- return publishedAtMs > mem.watermark || mem.pinned[key] !== void 0;
3055
+ function mayPinBreaking(mem, key) {
3056
+ return mem.dismissed[key] === void 0 && mem.blocked[key] === void 0;
3071
3057
  }
3072
3058
  function loadBreakingMemory(now) {
3073
3059
  try {
@@ -3652,18 +3638,8 @@ var PropertyNewsUpdates = React9.forwardRef(
3652
3638
  };
3653
3639
  const pinnedBreaking = React9.useMemo(() => {
3654
3640
  const mem = getBreakingMemory();
3655
- return breakingEligible.filter((it) => mayPinBreaking(mem, breakingKey(it), breakingPublishedMs(it))).sort((a, b) => breakingImpact(b) - breakingImpact(a)).slice(0, MAX_PINNED_BREAKING);
3641
+ return breakingEligible.filter((it) => mayPinBreaking(mem, breakingKey(it))).sort((a, b) => breakingImpact(b) - breakingImpact(a)).slice(0, MAX_PINNED_BREAKING);
3656
3642
  }, [breakingEligible, dismissedVersion]);
3657
- React9.useEffect(() => {
3658
- if (pinnedBreaking.length === 0) return;
3659
- const mem = getBreakingMemory();
3660
- const next = recordBreakingPins(
3661
- mem,
3662
- pinnedBreaking.map((it) => ({ key: breakingKey(it), publishedAtMs: breakingPublishedMs(it) })),
3663
- Date.now()
3664
- );
3665
- if (next !== mem) commitBreakingMemory(next);
3666
- }, [pinnedBreaking]);
3667
3643
  const breakingRowKeys = React9.useMemo(
3668
3644
  () => new Set(breakingEligible.map(breakingKey)),
3669
3645
  [breakingEligible]
@@ -3769,8 +3745,10 @@ var PropertyNewsUpdates = React9.forwardRef(
3769
3745
  const k = breakingKey(item);
3770
3746
  const dx = breakingSwipe?.key === k ? breakingSwipe.dx : 0;
3771
3747
  const persistDismissal = () => {
3748
+ const holding = new Set(pinnedBreaking);
3749
+ const waiting = breakingEligible.filter((c) => !holding.has(c)).map((c) => ({ key: breakingKey(c), publishedAtMs: breakingPublishedMs(c) }));
3772
3750
  commitBreakingMemory(
3773
- recordBreakingDismissal(getBreakingMemory(), k, breakingPublishedMs(item), Date.now())
3751
+ recordBreakingDismissal(getBreakingMemory(), k, breakingPublishedMs(item), Date.now(), waiting)
3774
3752
  );
3775
3753
  };
3776
3754
  const finishDismiss = () => {