@loafmarkets/ui 0.1.423 → 0.1.425

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
@@ -2850,7 +2850,6 @@ function MobileDepthRow({
2850
2850
  );
2851
2851
  }
2852
2852
  var isHlsManifest = (url) => /\.m3u8(\?|$)/i.test(url);
2853
- var HLS_MIME = "application/vnd.apple.mpegurl";
2854
2853
  var MANIFEST_FAILURES = /* @__PURE__ */ new Set(["manifestLoadError", "manifestLoadTimeOut", "manifestParsingError"]);
2855
2854
  var HlsVideo = React9.forwardRef(
2856
2855
  ({ src, capLevelToPlayerSize = true, onError, ...videoProps }, ref) => {
@@ -2868,7 +2867,7 @@ var HlsVideo = React9.forwardRef(
2868
2867
  React9.useEffect(() => {
2869
2868
  const video = videoRef.current;
2870
2869
  if (!video || !activeSrc) return;
2871
- if (!isHlsManifest(activeSrc) || video.canPlayType(HLS_MIME)) {
2870
+ if (!isHlsManifest(activeSrc)) {
2872
2871
  video.src = activeSrc;
2873
2872
  return;
2874
2873
  }
@@ -3016,6 +3015,74 @@ var PropertyTour = React9.forwardRef(
3016
3015
  );
3017
3016
  PropertyTour.displayName = "PropertyTour";
3018
3017
 
3018
+ // src/lib/breaking-dismissals.ts
3019
+ var BREAKING_WINDOW_HOURS = 6;
3020
+ var BREAKING_MEMORY_CAP = 50;
3021
+ var BREAKING_MEMORY_STORAGE_KEY = "loaf:dismissed-breaking";
3022
+ var emptyBreakingMemory = () => ({ dismissed: {}, pinned: {}, watermark: null });
3023
+ var isTimestampMap = (v) => typeof v === "object" && v !== null && !Array.isArray(v) && Object.values(v).every((t) => typeof t === "number" && Number.isFinite(t));
3024
+ var pruneMap = (map, cutoff) => {
3025
+ const fresh = Object.entries(map).filter(([, t]) => t >= cutoff);
3026
+ fresh.sort((a, b) => b[1] - a[1]);
3027
+ return Object.fromEntries(fresh.slice(0, BREAKING_MEMORY_CAP));
3028
+ };
3029
+ var prune = (mem, now) => {
3030
+ const cutoff = now - BREAKING_WINDOW_HOURS * 36e5;
3031
+ return {
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
3036
+ };
3037
+ };
3038
+ function parseBreakingMemory(raw, now) {
3039
+ if (!raw) return emptyBreakingMemory();
3040
+ try {
3041
+ 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))) {
3043
+ return emptyBreakingMemory();
3044
+ }
3045
+ return prune({ dismissed: v.dismissed, pinned: v.pinned, watermark: v.watermark ?? null }, now);
3046
+ } catch {
3047
+ return emptyBreakingMemory();
3048
+ }
3049
+ }
3050
+ 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);
3066
+ }
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;
3071
+ }
3072
+ function loadBreakingMemory(now) {
3073
+ try {
3074
+ return parseBreakingMemory(window.localStorage.getItem(BREAKING_MEMORY_STORAGE_KEY), now);
3075
+ } catch {
3076
+ return emptyBreakingMemory();
3077
+ }
3078
+ }
3079
+ function saveBreakingMemory(mem) {
3080
+ try {
3081
+ window.localStorage.setItem(BREAKING_MEMORY_STORAGE_KEY, serializeBreakingMemory(mem));
3082
+ } catch {
3083
+ }
3084
+ }
3085
+
3019
3086
  // src/lib/isSafeUrl.ts
3020
3087
  var CONTROL_CHARS = /[\u0000-\u001F\u007F]/g;
3021
3088
  function isSafeUrl(url) {
@@ -3036,13 +3103,27 @@ function matchesTab(item, tab) {
3036
3103
  if (item.types && item.types.length > 0) return item.types.includes(tab);
3037
3104
  return item.type === tab;
3038
3105
  }
3039
- var BREAKING_WINDOW_HOURS = 6;
3040
3106
  var MAX_PINNED_BREAKING = 2;
3041
3107
  var BREAKING_IMPACT_FLOOR = 8;
3042
3108
  var breakingImpact = (it) => typeof it.impactScore === "number" && Number.isFinite(it.impactScore) ? it.impactScore : BREAKING_IMPACT_FLOOR;
3043
- var dismissedBreakingKeys = /* @__PURE__ */ new Set();
3044
- var __resetDismissedBreaking = () => dismissedBreakingKeys.clear();
3109
+ var breakingMemory = null;
3110
+ var getBreakingMemory = () => breakingMemory ??= loadBreakingMemory(Date.now());
3111
+ var commitBreakingMemory = (mem) => {
3112
+ breakingMemory = mem;
3113
+ saveBreakingMemory(mem);
3114
+ };
3115
+ var __resetDismissedBreaking = () => {
3116
+ breakingMemory = null;
3117
+ try {
3118
+ window.localStorage.removeItem(BREAKING_MEMORY_STORAGE_KEY);
3119
+ } catch {
3120
+ }
3121
+ };
3122
+ var __reloadBreakingMemory = () => {
3123
+ breakingMemory = null;
3124
+ };
3045
3125
  var breakingKey = (it) => String(it.id ?? it.title);
3126
+ var breakingPublishedMs = (it) => new Date(it.publishedAt).getTime();
3046
3127
  var BREAKING_SWIPE_MIN_PX = 80;
3047
3128
  var ITEMS_PER_PAGE = 5;
3048
3129
  var ITEMS_PER_PAGE_MOBILE = 5;
@@ -3570,8 +3651,19 @@ var PropertyNewsUpdates = React9.forwardRef(
3570
3651
  });
3571
3652
  };
3572
3653
  const pinnedBreaking = React9.useMemo(() => {
3573
- return breakingEligible.filter((it) => !dismissedBreakingKeys.has(breakingKey(it))).sort((a, b) => breakingImpact(b) - breakingImpact(a)).slice(0, MAX_PINNED_BREAKING);
3654
+ 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);
3574
3656
  }, [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]);
3575
3667
  const breakingRowKeys = React9.useMemo(
3576
3668
  () => new Set(breakingEligible.map(breakingKey)),
3577
3669
  [breakingEligible]
@@ -3676,8 +3768,13 @@ var PropertyNewsUpdates = React9.forwardRef(
3676
3768
  const sentiment = getSentimentInfo(item.sentimentScore);
3677
3769
  const k = breakingKey(item);
3678
3770
  const dx = breakingSwipe?.key === k ? breakingSwipe.dx : 0;
3771
+ const persistDismissal = () => {
3772
+ commitBreakingMemory(
3773
+ recordBreakingDismissal(getBreakingMemory(), k, breakingPublishedMs(item), Date.now())
3774
+ );
3775
+ };
3679
3776
  const finishDismiss = () => {
3680
- dismissedBreakingKeys.add(k);
3777
+ persistDismissal();
3681
3778
  setDismissingBreakingKey(null);
3682
3779
  setDismissedVersion((v) => v + 1);
3683
3780
  };
@@ -3725,7 +3822,7 @@ var PropertyNewsUpdates = React9.forwardRef(
3725
3822
  const drag = Math.max(0, e.clientX - breakingSwipeStartXRef.current);
3726
3823
  const width = e.currentTarget.offsetWidth || 0;
3727
3824
  if (drag >= Math.max(BREAKING_SWIPE_MIN_PX, width * 0.3)) {
3728
- dismissedBreakingKeys.add(k);
3825
+ persistDismissal();
3729
3826
  setDismissedVersion((v) => v + 1);
3730
3827
  }
3731
3828
  },
@@ -21947,6 +22044,6 @@ var SparklineChart = React9__default.memo(function SparklineChart2({ data, width
21947
22044
  );
21948
22045
  });
21949
22046
 
21950
- export { AssetSelectorBar, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ContactPopup, Header, HlsVideo, HousePositionSlider, HousePositionSliderMobile, LoafLiquidityBadge, LoafLiquidityLogo, LoginPopup, MobileTradeNav, OfferingProgressCard, OnboardingGuide, Orderbook, owner_booking_default as OwnerBooking, PaymentPopup, PortfolioActivityPanel, PortfolioSummary, PriceChart, PropertyBuy, PropertyCompareBar, PropertyDocuments, PropertyHeroHeader, PropertyHistory, PropertyInspectionTimes, PropertyMediaRow, PropertyNewsUpdates, PropertyOffers, PropertyOverview, PropertyPhotoGallery, PropertySubheader, PropertyTour, PropertyValuation, SentimentBar, SiteFooter, Skeleton, SlideDigit, SparklineChart, ToastProvider, TradeConfirmationModal, TradingSlider, YourOrders, __resetDismissedBreaking, badgeVariants, buttonVariants, extractSparkline, formatAge, hasPendingActivity, matchesTab, rowAgeLabel, useAdaptivePolling, useToast };
22047
+ export { AssetSelectorBar, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ContactPopup, Header, HlsVideo, HousePositionSlider, HousePositionSliderMobile, LoafLiquidityBadge, LoafLiquidityLogo, LoginPopup, MobileTradeNav, OfferingProgressCard, OnboardingGuide, Orderbook, owner_booking_default as OwnerBooking, PaymentPopup, PortfolioActivityPanel, PortfolioSummary, PriceChart, PropertyBuy, PropertyCompareBar, PropertyDocuments, PropertyHeroHeader, PropertyHistory, PropertyInspectionTimes, PropertyMediaRow, PropertyNewsUpdates, PropertyOffers, PropertyOverview, PropertyPhotoGallery, PropertySubheader, PropertyTour, PropertyValuation, SentimentBar, SiteFooter, Skeleton, SlideDigit, SparklineChart, ToastProvider, TradeConfirmationModal, TradingSlider, YourOrders, __reloadBreakingMemory, __resetDismissedBreaking, badgeVariants, buttonVariants, extractSparkline, formatAge, hasPendingActivity, matchesTab, rowAgeLabel, useAdaptivePolling, useToast };
21951
22048
  //# sourceMappingURL=index.mjs.map
21952
22049
  //# sourceMappingURL=index.mjs.map