@loafmarkets/ui 0.1.428 → 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.mjs CHANGED
@@ -3318,6 +3318,47 @@ var formatTimeAgo = (timestamp) => {
3318
3318
  if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
3319
3319
  return `${Math.floor(diff / 3600)}h ago`;
3320
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
+ }
3321
3362
  function getSentimentInfo(score) {
3322
3363
  if (score == null) return null;
3323
3364
  if (score > 0.15) return { arrow: "\u25B2", label: "Bullish", color: "#0ecb81" };
@@ -3380,9 +3421,27 @@ function SentimentBar({ score, color }) {
3380
3421
  }
3381
3422
  );
3382
3423
  }
3383
- function NewsArticleModal({ item, onClose }) {
3424
+ function NewsArticleModal({
3425
+ item,
3426
+ onClose,
3427
+ fetchStory
3428
+ }) {
3384
3429
  const sentimentInfo = getSentimentInfo(item.sentimentScore);
3385
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]);
3386
3445
  React9.useEffect(() => {
3387
3446
  const handler = (e) => {
3388
3447
  if (e.key === "Escape") onClose();
@@ -3508,6 +3567,40 @@ function NewsArticleModal({ item, onClose }) {
3508
3567
  ] }),
3509
3568
  /* @__PURE__ */ jsx("div", { style: { height: "1px", background: "rgba(255,255,255,0.08)", marginBottom: "1.25rem" } }),
3510
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
+ ] }),
3511
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(
3512
3605
  "a",
3513
3606
  {
@@ -3567,6 +3660,7 @@ var PropertyNewsUpdates = React9.forwardRef(
3567
3660
  pinBreaking = false,
3568
3661
  onAssetOpen,
3569
3662
  showAssetBadges = true,
3663
+ fetchStory,
3570
3664
  ...props
3571
3665
  }, ref) => {
3572
3666
  const isPurchaseVariant = variant === "purchases";
@@ -4103,6 +4197,7 @@ var PropertyNewsUpdates = React9.forwardRef(
4103
4197
  },
4104
4198
  badgeAsset.ticker
4105
4199
  )),
4200
+ /* @__PURE__ */ jsx(NewsMetaNotes, { item, fontSize: isMobile ? "0.52rem" : "0.62rem" }),
4106
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" }),
4107
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: [
4108
4203
  sentimentInfo.arrow ? `${sentimentInfo.arrow} ` : "",
@@ -4157,7 +4252,10 @@ var PropertyNewsUpdates = React9.forwardRef(
4157
4252
  children: [
4158
4253
  /* @__PURE__ */ jsx("p", { style: { fontSize: "0.9375rem", fontWeight: 500, marginBottom: "0.4rem", color: "#fff", lineHeight: 1.4 }, children: item.title }),
4159
4254
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-end", gap: "0.5rem" }, children: [
4160
- /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", minWidth: 0, flexWrap: "wrap" }, children: /* @__PURE__ */ jsx("span", { style: { fontSize: "0.75rem", color: isHighlighted ? "#0ecb81" : "#b5b8c5", whiteSpace: "nowrap" }, children: dateLabel }) }),
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
+ ] }),
4161
4259
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-end", gap: "0.3rem", flexShrink: 0 }, children: [
4162
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: [
4163
4261
  sentimentInfo.arrow ? `${sentimentInfo.arrow} ` : "",
@@ -4236,7 +4334,7 @@ var PropertyNewsUpdates = React9.forwardRef(
4236
4334
  }
4237
4335
  )
4238
4336
  ] }),
4239
- selectedItem && !isPurchaseVariant && /* @__PURE__ */ jsx(NewsArticleModal, { item: selectedItem, onClose: () => setSelectedItem(null) }),
4337
+ selectedItem && !isPurchaseVariant && /* @__PURE__ */ jsx(NewsArticleModal, { item: selectedItem, onClose: () => setSelectedItem(null), fetchStory }),
4240
4338
  badgeTip && ReactDOM.createPortal(
4241
4339
  /* @__PURE__ */ jsx(
4242
4340
  "span",
@@ -22045,6 +22143,6 @@ var SparklineChart = React9__default.memo(function SparklineChart2({ data, width
22045
22143
  );
22046
22144
  });
22047
22145
 
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 };
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 };
22049
22147
  //# sourceMappingURL=index.mjs.map
22050
22148
  //# sourceMappingURL=index.mjs.map