@loafmarkets/ui 0.1.422 → 0.1.424

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
@@ -3016,6 +3016,74 @@ var PropertyTour = React9.forwardRef(
3016
3016
  );
3017
3017
  PropertyTour.displayName = "PropertyTour";
3018
3018
 
3019
+ // src/lib/breaking-dismissals.ts
3020
+ var BREAKING_WINDOW_HOURS = 6;
3021
+ var BREAKING_MEMORY_CAP = 50;
3022
+ var BREAKING_MEMORY_STORAGE_KEY = "loaf:dismissed-breaking";
3023
+ var emptyBreakingMemory = () => ({ dismissed: {}, pinned: {}, watermark: null });
3024
+ var isTimestampMap = (v) => typeof v === "object" && v !== null && !Array.isArray(v) && Object.values(v).every((t) => typeof t === "number" && Number.isFinite(t));
3025
+ var pruneMap = (map, cutoff) => {
3026
+ const fresh = Object.entries(map).filter(([, t]) => t >= cutoff);
3027
+ fresh.sort((a, b) => b[1] - a[1]);
3028
+ return Object.fromEntries(fresh.slice(0, BREAKING_MEMORY_CAP));
3029
+ };
3030
+ var prune = (mem, now) => {
3031
+ const cutoff = now - BREAKING_WINDOW_HOURS * 36e5;
3032
+ return {
3033
+ dismissed: pruneMap(mem.dismissed, cutoff),
3034
+ pinned: pruneMap(mem.pinned, cutoff),
3035
+ // An expired watermark can't block anything (every in-window story is newer).
3036
+ watermark: mem.watermark !== null && mem.watermark >= cutoff ? mem.watermark : null
3037
+ };
3038
+ };
3039
+ function parseBreakingMemory(raw, now) {
3040
+ if (!raw) return emptyBreakingMemory();
3041
+ try {
3042
+ const v = JSON.parse(raw);
3043
+ 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))) {
3044
+ return emptyBreakingMemory();
3045
+ }
3046
+ return prune({ dismissed: v.dismissed, pinned: v.pinned, watermark: v.watermark ?? null }, now);
3047
+ } catch {
3048
+ return emptyBreakingMemory();
3049
+ }
3050
+ }
3051
+ var serializeBreakingMemory = (mem) => JSON.stringify(mem);
3052
+ function recordBreakingDismissal(mem, key, publishedAtMs, now) {
3053
+ return prune(
3054
+ {
3055
+ dismissed: { ...mem.dismissed, [key]: publishedAtMs },
3056
+ pinned: mem.pinned,
3057
+ watermark: Math.max(mem.watermark ?? -Infinity, publishedAtMs)
3058
+ },
3059
+ now
3060
+ );
3061
+ }
3062
+ function recordBreakingPins(mem, pins, now) {
3063
+ if (pins.every((p) => mem.pinned[p.key] !== void 0)) return mem;
3064
+ const pinned = { ...mem.pinned };
3065
+ for (const p of pins) pinned[p.key] = p.publishedAtMs;
3066
+ return prune({ dismissed: mem.dismissed, pinned, watermark: mem.watermark }, now);
3067
+ }
3068
+ function mayPinBreaking(mem, key, publishedAtMs) {
3069
+ if (mem.dismissed[key] !== void 0) return false;
3070
+ if (mem.watermark === null) return true;
3071
+ return publishedAtMs > mem.watermark || mem.pinned[key] !== void 0;
3072
+ }
3073
+ function loadBreakingMemory(now) {
3074
+ try {
3075
+ return parseBreakingMemory(window.localStorage.getItem(BREAKING_MEMORY_STORAGE_KEY), now);
3076
+ } catch {
3077
+ return emptyBreakingMemory();
3078
+ }
3079
+ }
3080
+ function saveBreakingMemory(mem) {
3081
+ try {
3082
+ window.localStorage.setItem(BREAKING_MEMORY_STORAGE_KEY, serializeBreakingMemory(mem));
3083
+ } catch {
3084
+ }
3085
+ }
3086
+
3019
3087
  // src/lib/isSafeUrl.ts
3020
3088
  var CONTROL_CHARS = /[\u0000-\u001F\u007F]/g;
3021
3089
  function isSafeUrl(url) {
@@ -3036,13 +3104,27 @@ function matchesTab(item, tab) {
3036
3104
  if (item.types && item.types.length > 0) return item.types.includes(tab);
3037
3105
  return item.type === tab;
3038
3106
  }
3039
- var BREAKING_WINDOW_HOURS = 6;
3040
3107
  var MAX_PINNED_BREAKING = 2;
3041
3108
  var BREAKING_IMPACT_FLOOR = 8;
3042
3109
  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();
3110
+ var breakingMemory = null;
3111
+ var getBreakingMemory = () => breakingMemory ??= loadBreakingMemory(Date.now());
3112
+ var commitBreakingMemory = (mem) => {
3113
+ breakingMemory = mem;
3114
+ saveBreakingMemory(mem);
3115
+ };
3116
+ var __resetDismissedBreaking = () => {
3117
+ breakingMemory = null;
3118
+ try {
3119
+ window.localStorage.removeItem(BREAKING_MEMORY_STORAGE_KEY);
3120
+ } catch {
3121
+ }
3122
+ };
3123
+ var __reloadBreakingMemory = () => {
3124
+ breakingMemory = null;
3125
+ };
3045
3126
  var breakingKey = (it) => String(it.id ?? it.title);
3127
+ var breakingPublishedMs = (it) => new Date(it.publishedAt).getTime();
3046
3128
  var BREAKING_SWIPE_MIN_PX = 80;
3047
3129
  var ITEMS_PER_PAGE = 5;
3048
3130
  var ITEMS_PER_PAGE_MOBILE = 5;
@@ -3570,8 +3652,19 @@ var PropertyNewsUpdates = React9.forwardRef(
3570
3652
  });
3571
3653
  };
3572
3654
  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);
3655
+ const mem = getBreakingMemory();
3656
+ return breakingEligible.filter((it) => mayPinBreaking(mem, breakingKey(it), breakingPublishedMs(it))).sort((a, b) => breakingImpact(b) - breakingImpact(a)).slice(0, MAX_PINNED_BREAKING);
3574
3657
  }, [breakingEligible, dismissedVersion]);
3658
+ React9.useEffect(() => {
3659
+ if (pinnedBreaking.length === 0) return;
3660
+ const mem = getBreakingMemory();
3661
+ const next = recordBreakingPins(
3662
+ mem,
3663
+ pinnedBreaking.map((it) => ({ key: breakingKey(it), publishedAtMs: breakingPublishedMs(it) })),
3664
+ Date.now()
3665
+ );
3666
+ if (next !== mem) commitBreakingMemory(next);
3667
+ }, [pinnedBreaking]);
3575
3668
  const breakingRowKeys = React9.useMemo(
3576
3669
  () => new Set(breakingEligible.map(breakingKey)),
3577
3670
  [breakingEligible]
@@ -3676,8 +3769,13 @@ var PropertyNewsUpdates = React9.forwardRef(
3676
3769
  const sentiment = getSentimentInfo(item.sentimentScore);
3677
3770
  const k = breakingKey(item);
3678
3771
  const dx = breakingSwipe?.key === k ? breakingSwipe.dx : 0;
3772
+ const persistDismissal = () => {
3773
+ commitBreakingMemory(
3774
+ recordBreakingDismissal(getBreakingMemory(), k, breakingPublishedMs(item), Date.now())
3775
+ );
3776
+ };
3679
3777
  const finishDismiss = () => {
3680
- dismissedBreakingKeys.add(k);
3778
+ persistDismissal();
3681
3779
  setDismissingBreakingKey(null);
3682
3780
  setDismissedVersion((v) => v + 1);
3683
3781
  };
@@ -3725,7 +3823,7 @@ var PropertyNewsUpdates = React9.forwardRef(
3725
3823
  const drag = Math.max(0, e.clientX - breakingSwipeStartXRef.current);
3726
3824
  const width = e.currentTarget.offsetWidth || 0;
3727
3825
  if (drag >= Math.max(BREAKING_SWIPE_MIN_PX, width * 0.3)) {
3728
- dismissedBreakingKeys.add(k);
3826
+ persistDismissal();
3729
3827
  setDismissedVersion((v) => v + 1);
3730
3828
  }
3731
3829
  },
@@ -7644,6 +7742,7 @@ var LoginPopup = ({
7644
7742
  onFundWallet,
7645
7743
  initialView,
7646
7744
  initialCode,
7745
+ defaultHandle,
7647
7746
  kycStatus: kycStatusProp,
7648
7747
  walletAddress,
7649
7748
  onSubmitReferralCode,
@@ -7654,6 +7753,12 @@ var LoginPopup = ({
7654
7753
  const [view, setView] = useState(() => initialView ?? "main");
7655
7754
  const [email, setEmail] = useState("");
7656
7755
  const [handle, setHandle] = useState("");
7756
+ const seededDefaultHandleRef = React9__default.useRef(null);
7757
+ useEffect(() => {
7758
+ if (!defaultHandle || seededDefaultHandleRef.current === defaultHandle) return;
7759
+ seededDefaultHandleRef.current = defaultHandle;
7760
+ setHandle((current) => current ? current : defaultHandle);
7761
+ }, [defaultHandle]);
7657
7762
  const [otp, setOtp] = useState(Array(OTP_INPUT_LENGTH).fill(""));
7658
7763
  const [error, setError] = useState("");
7659
7764
  const [copied, setCopied] = useState(false);
@@ -21940,6 +22045,6 @@ var SparklineChart = React9__default.memo(function SparklineChart2({ data, width
21940
22045
  );
21941
22046
  });
21942
22047
 
21943
- 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 };
22048
+ 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 };
21944
22049
  //# sourceMappingURL=index.mjs.map
21945
22050
  //# sourceMappingURL=index.mjs.map