@loafmarkets/ui 0.1.427 → 0.1.429
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.d.mts +52 -2
- package/dist/index.d.ts +52 -2
- package/dist/index.js +117 -40
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +117 -41
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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: {},
|
|
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
|
-
|
|
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)
|
|
3040
|
+
if (typeof v !== "object" || v === null || Array.isArray(v) || !isTimestampMap(v.dismissed)) {
|
|
3043
3041
|
return emptyBreakingMemory();
|
|
3044
3042
|
}
|
|
3045
|
-
|
|
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
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
pinned: mem.pinned,
|
|
3056
|
-
watermark: Math.max(mem.watermark ?? -Infinity, publishedAtMs)
|
|
3057
|
-
},
|
|
3058
|
-
now
|
|
3059
|
-
);
|
|
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);
|
|
3060
3054
|
}
|
|
3061
|
-
function
|
|
3062
|
-
|
|
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;
|
|
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 {
|
|
@@ -3332,6 +3318,47 @@ var formatTimeAgo = (timestamp) => {
|
|
|
3332
3318
|
if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
|
|
3333
3319
|
return `${Math.floor(diff / 3600)}h ago`;
|
|
3334
3320
|
};
|
|
3321
|
+
function freshnessNote(freshness) {
|
|
3322
|
+
if (freshness === "catching_up") return { label: "Catching up", title: "Reported promptly; we picked it up late" };
|
|
3323
|
+
if (freshness === "retrospective") return { label: "Older event", title: "The event is materially older than this report" };
|
|
3324
|
+
return null;
|
|
3325
|
+
}
|
|
3326
|
+
function NewsMetaNotes({ item, fontSize }) {
|
|
3327
|
+
const note = freshnessNote(item.freshness);
|
|
3328
|
+
const outlets = typeof item.reportedByCount === "number" && item.reportedByCount > 1 ? item.reportedByCount : null;
|
|
3329
|
+
if (!note && outlets === null) return null;
|
|
3330
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3331
|
+
note && /* @__PURE__ */ jsx(
|
|
3332
|
+
"span",
|
|
3333
|
+
{
|
|
3334
|
+
"data-testid": "news-freshness",
|
|
3335
|
+
title: note.title,
|
|
3336
|
+
style: {
|
|
3337
|
+
fontSize,
|
|
3338
|
+
fontWeight: 500,
|
|
3339
|
+
color: "rgba(255,255,255,0.5)",
|
|
3340
|
+
border: "1px solid rgba(255,255,255,0.14)",
|
|
3341
|
+
borderRadius: "3px",
|
|
3342
|
+
padding: "0.1rem 0.35rem",
|
|
3343
|
+
whiteSpace: "nowrap"
|
|
3344
|
+
},
|
|
3345
|
+
children: note.label
|
|
3346
|
+
}
|
|
3347
|
+
),
|
|
3348
|
+
outlets !== null && /* @__PURE__ */ jsxs(
|
|
3349
|
+
"span",
|
|
3350
|
+
{
|
|
3351
|
+
"data-testid": "news-outlets",
|
|
3352
|
+
title: `Reported by ${outlets} publishers`,
|
|
3353
|
+
style: { fontSize, fontWeight: 500, color: "rgba(255,255,255,0.5)", whiteSpace: "nowrap" },
|
|
3354
|
+
children: [
|
|
3355
|
+
outlets,
|
|
3356
|
+
" outlets"
|
|
3357
|
+
]
|
|
3358
|
+
}
|
|
3359
|
+
)
|
|
3360
|
+
] });
|
|
3361
|
+
}
|
|
3335
3362
|
function getSentimentInfo(score) {
|
|
3336
3363
|
if (score == null) return null;
|
|
3337
3364
|
if (score > 0.15) return { arrow: "\u25B2", label: "Bullish", color: "#0ecb81" };
|
|
@@ -3394,9 +3421,27 @@ function SentimentBar({ score, color }) {
|
|
|
3394
3421
|
}
|
|
3395
3422
|
);
|
|
3396
3423
|
}
|
|
3397
|
-
function NewsArticleModal({
|
|
3424
|
+
function NewsArticleModal({
|
|
3425
|
+
item,
|
|
3426
|
+
onClose,
|
|
3427
|
+
fetchStory
|
|
3428
|
+
}) {
|
|
3398
3429
|
const sentimentInfo = getSentimentInfo(item.sentimentScore);
|
|
3399
3430
|
const catStyle = categoryStyles[item.type] ?? categoryStyles.market;
|
|
3431
|
+
const [thread, setThread] = React9.useState(null);
|
|
3432
|
+
const threadEntries = thread ? thread.timeline.filter((entry) => entry.id !== item.id) : [];
|
|
3433
|
+
const storyId = item.storyId;
|
|
3434
|
+
const threaded = storyId !== void 0 && (typeof item.reportedByCount === "number" && item.reportedByCount > 1 || storyId !== item.id);
|
|
3435
|
+
React9.useEffect(() => {
|
|
3436
|
+
if (!fetchStory || !threaded || storyId === void 0) return;
|
|
3437
|
+
let dropped = false;
|
|
3438
|
+
void fetchStory(storyId).then((t) => {
|
|
3439
|
+
if (!dropped) setThread(t);
|
|
3440
|
+
});
|
|
3441
|
+
return () => {
|
|
3442
|
+
dropped = true;
|
|
3443
|
+
};
|
|
3444
|
+
}, [fetchStory, threaded, storyId]);
|
|
3400
3445
|
React9.useEffect(() => {
|
|
3401
3446
|
const handler = (e) => {
|
|
3402
3447
|
if (e.key === "Escape") onClose();
|
|
@@ -3522,6 +3567,40 @@ function NewsArticleModal({ item, onClose }) {
|
|
|
3522
3567
|
] }),
|
|
3523
3568
|
/* @__PURE__ */ jsx("div", { style: { height: "1px", background: "rgba(255,255,255,0.08)", marginBottom: "1.25rem" } }),
|
|
3524
3569
|
item.summary ? /* @__PURE__ */ jsx("p", { style: { fontSize: "0.875rem", color: "rgba(255,255,255,0.8)", lineHeight: 1.75, margin: 0 }, children: item.summary }) : /* @__PURE__ */ jsx("p", { style: { fontSize: "0.8rem", color: "rgba(255,255,255,0.35)", fontStyle: "italic", margin: 0 }, children: "Summary not available for this article." }),
|
|
3570
|
+
thread && threadEntries.length > 0 && // Lands after the popup is already open, so it is announced rather than silent.
|
|
3571
|
+
/* @__PURE__ */ jsxs("div", { "aria-live": "polite", style: { marginTop: "1.25rem", paddingTop: "1rem", borderTop: "1px solid rgba(255,255,255,0.08)" }, children: [
|
|
3572
|
+
/* @__PURE__ */ jsxs("p", { style: { fontSize: "0.75rem", fontWeight: 600, color: "rgba(255,255,255,0.75)", margin: "0 0 0.75rem" }, children: [
|
|
3573
|
+
"Reported by ",
|
|
3574
|
+
thread.reportedBy.length > 0 ? thread.reportedBy.length : threadEntries.length + 1,
|
|
3575
|
+
" outlets"
|
|
3576
|
+
] }),
|
|
3577
|
+
/* @__PURE__ */ jsx("ol", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: "0.6rem" }, children: threadEntries.map((entry) => /* @__PURE__ */ jsxs(
|
|
3578
|
+
"li",
|
|
3579
|
+
{
|
|
3580
|
+
"data-testid": "news-story-entry",
|
|
3581
|
+
"data-entry-id": entry.id,
|
|
3582
|
+
"data-relation": entry.relation,
|
|
3583
|
+
style: { borderLeft: "1px solid rgba(255,255,255,0.14)", paddingLeft: "0.7rem" },
|
|
3584
|
+
children: [
|
|
3585
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.4rem", alignItems: "baseline", flexWrap: "wrap", marginBottom: "0.15rem" }, children: [
|
|
3586
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "0.65rem", color: "#848e9c" }, children: formatDateShort(entry.publishedAt) }),
|
|
3587
|
+
entry.source && /* @__PURE__ */ jsx("span", { style: { fontSize: "0.65rem", color: "rgba(255,255,255,0.5)" }, children: entry.source })
|
|
3588
|
+
] }),
|
|
3589
|
+
entry.url && isSafeUrl(entry.url) ? /* @__PURE__ */ jsx(
|
|
3590
|
+
"a",
|
|
3591
|
+
{
|
|
3592
|
+
href: entry.url,
|
|
3593
|
+
target: "_blank",
|
|
3594
|
+
rel: "noopener noreferrer",
|
|
3595
|
+
style: { fontSize: "0.8rem", color: "rgba(255,255,255,0.8)", textDecoration: "none", lineHeight: 1.5, overflowWrap: "anywhere" },
|
|
3596
|
+
children: entry.title
|
|
3597
|
+
}
|
|
3598
|
+
) : /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8rem", color: "rgba(255,255,255,0.8)", lineHeight: 1.5, overflowWrap: "anywhere" }, children: entry.title })
|
|
3599
|
+
]
|
|
3600
|
+
},
|
|
3601
|
+
entry.id
|
|
3602
|
+
)) })
|
|
3603
|
+
] }),
|
|
3525
3604
|
item.url && isSafeUrl(item.url) && /* @__PURE__ */ jsx("div", { style: { marginTop: "1.25rem", paddingTop: "1rem", borderTop: "1px solid rgba(255,255,255,0.08)" }, children: /* @__PURE__ */ jsxs(
|
|
3526
3605
|
"a",
|
|
3527
3606
|
{
|
|
@@ -3581,6 +3660,7 @@ var PropertyNewsUpdates = React9.forwardRef(
|
|
|
3581
3660
|
pinBreaking = false,
|
|
3582
3661
|
onAssetOpen,
|
|
3583
3662
|
showAssetBadges = true,
|
|
3663
|
+
fetchStory,
|
|
3584
3664
|
...props
|
|
3585
3665
|
}, ref) => {
|
|
3586
3666
|
const isPurchaseVariant = variant === "purchases";
|
|
@@ -3652,18 +3732,8 @@ var PropertyNewsUpdates = React9.forwardRef(
|
|
|
3652
3732
|
};
|
|
3653
3733
|
const pinnedBreaking = React9.useMemo(() => {
|
|
3654
3734
|
const mem = getBreakingMemory();
|
|
3655
|
-
return breakingEligible.filter((it) => mayPinBreaking(mem, breakingKey(it)
|
|
3735
|
+
return breakingEligible.filter((it) => mayPinBreaking(mem, breakingKey(it))).sort((a, b) => breakingImpact(b) - breakingImpact(a)).slice(0, MAX_PINNED_BREAKING);
|
|
3656
3736
|
}, [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
3737
|
const breakingRowKeys = React9.useMemo(
|
|
3668
3738
|
() => new Set(breakingEligible.map(breakingKey)),
|
|
3669
3739
|
[breakingEligible]
|
|
@@ -3769,8 +3839,10 @@ var PropertyNewsUpdates = React9.forwardRef(
|
|
|
3769
3839
|
const k = breakingKey(item);
|
|
3770
3840
|
const dx = breakingSwipe?.key === k ? breakingSwipe.dx : 0;
|
|
3771
3841
|
const persistDismissal = () => {
|
|
3842
|
+
const holding = new Set(pinnedBreaking);
|
|
3843
|
+
const waiting = breakingEligible.filter((c) => !holding.has(c)).map((c) => ({ key: breakingKey(c), publishedAtMs: breakingPublishedMs(c) }));
|
|
3772
3844
|
commitBreakingMemory(
|
|
3773
|
-
recordBreakingDismissal(getBreakingMemory(), k, breakingPublishedMs(item), Date.now())
|
|
3845
|
+
recordBreakingDismissal(getBreakingMemory(), k, breakingPublishedMs(item), Date.now(), waiting)
|
|
3774
3846
|
);
|
|
3775
3847
|
};
|
|
3776
3848
|
const finishDismiss = () => {
|
|
@@ -4125,6 +4197,7 @@ var PropertyNewsUpdates = React9.forwardRef(
|
|
|
4125
4197
|
},
|
|
4126
4198
|
badgeAsset.ticker
|
|
4127
4199
|
)),
|
|
4200
|
+
/* @__PURE__ */ jsx(NewsMetaNotes, { item, fontSize: isMobile ? "0.52rem" : "0.62rem" }),
|
|
4128
4201
|
/* @__PURE__ */ jsx("span", { style: { padding: isMobile ? "0.1rem 0.35rem" : "0.15rem 0.4rem", borderRadius: "2px", fontSize: isMobile ? "0.52rem" : "0.65rem", fontWeight: 500, backgroundColor: isProperty ? "rgba(14,203,129,0.1)" : "rgba(240,185,11,0.1)", color: isProperty ? "#0ecb81" : "#E6C87E" }, children: isProperty ? "Property Update" : "Market News" }),
|
|
4129
4202
|
sentimentInfo && /* @__PURE__ */ jsx("span", { style: { fontSize: isMobile ? "0.52rem" : "0.65rem", fontWeight: 600, color: sentimentInfo.color }, children: /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.3rem" }, children: [
|
|
4130
4203
|
sentimentInfo.arrow ? `${sentimentInfo.arrow} ` : "",
|
|
@@ -4179,7 +4252,10 @@ var PropertyNewsUpdates = React9.forwardRef(
|
|
|
4179
4252
|
children: [
|
|
4180
4253
|
/* @__PURE__ */ jsx("p", { style: { fontSize: "0.9375rem", fontWeight: 500, marginBottom: "0.4rem", color: "#fff", lineHeight: 1.4 }, children: item.title }),
|
|
4181
4254
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-end", gap: "0.5rem" }, children: [
|
|
4182
|
-
/* @__PURE__ */
|
|
4255
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", minWidth: 0, flexWrap: "wrap" }, children: [
|
|
4256
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "0.75rem", color: isHighlighted ? "#0ecb81" : "#b5b8c5", whiteSpace: "nowrap" }, children: dateLabel }),
|
|
4257
|
+
/* @__PURE__ */ jsx(NewsMetaNotes, { item, fontSize: "0.65rem" })
|
|
4258
|
+
] }),
|
|
4183
4259
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-end", gap: "0.3rem", flexShrink: 0 }, children: [
|
|
4184
4260
|
sentimentInfo && /* @__PURE__ */ jsx("span", { style: { fontSize: "0.65rem", fontWeight: 700, color: sentimentInfo.color, whiteSpace: "nowrap" }, children: /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.3rem" }, children: [
|
|
4185
4261
|
sentimentInfo.arrow ? `${sentimentInfo.arrow} ` : "",
|
|
@@ -4258,7 +4334,7 @@ var PropertyNewsUpdates = React9.forwardRef(
|
|
|
4258
4334
|
}
|
|
4259
4335
|
)
|
|
4260
4336
|
] }),
|
|
4261
|
-
selectedItem && !isPurchaseVariant && /* @__PURE__ */ jsx(NewsArticleModal, { item: selectedItem, onClose: () => setSelectedItem(null) }),
|
|
4337
|
+
selectedItem && !isPurchaseVariant && /* @__PURE__ */ jsx(NewsArticleModal, { item: selectedItem, onClose: () => setSelectedItem(null), fetchStory }),
|
|
4262
4338
|
badgeTip && ReactDOM.createPortal(
|
|
4263
4339
|
/* @__PURE__ */ jsx(
|
|
4264
4340
|
"span",
|
|
@@ -22067,6 +22143,6 @@ var SparklineChart = React9__default.memo(function SparklineChart2({ data, width
|
|
|
22067
22143
|
);
|
|
22068
22144
|
});
|
|
22069
22145
|
|
|
22070
|
-
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 };
|
|
22146
|
+
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, freshnessNote, hasPendingActivity, matchesTab, rowAgeLabel, useAdaptivePolling, useToast };
|
|
22071
22147
|
//# sourceMappingURL=index.mjs.map
|
|
22072
22148
|
//# sourceMappingURL=index.mjs.map
|