@loafmarkets/ui 0.1.428 → 0.1.430

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();
@@ -3504,10 +3563,45 @@ function NewsArticleModal({ item, onClose }) {
3504
3563
  },
3505
3564
  children: item.type === "property" ? catStyle.label : "Market News"
3506
3565
  }
3507
- )
3566
+ ),
3567
+ /* @__PURE__ */ jsx(NewsMetaNotes, { item, fontSize: "0.65rem" })
3508
3568
  ] }),
3509
3569
  /* @__PURE__ */ jsx("div", { style: { height: "1px", background: "rgba(255,255,255,0.08)", marginBottom: "1.25rem" } }),
3510
3570
  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." }),
3571
+ thread && threadEntries.length > 0 && // Lands after the popup is already open, so it is announced rather than silent.
3572
+ /* @__PURE__ */ jsxs("div", { "aria-live": "polite", style: { marginTop: "1.25rem", paddingTop: "1rem", borderTop: "1px solid rgba(255,255,255,0.08)" }, children: [
3573
+ /* @__PURE__ */ jsxs("p", { style: { fontSize: "0.75rem", fontWeight: 600, color: "rgba(255,255,255,0.75)", margin: "0 0 0.75rem" }, children: [
3574
+ "Reported by ",
3575
+ thread.reportedBy.length > 0 ? thread.reportedBy.length : threadEntries.length + 1,
3576
+ " outlets"
3577
+ ] }),
3578
+ /* @__PURE__ */ jsx("ol", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: "0.6rem" }, children: threadEntries.map((entry) => /* @__PURE__ */ jsxs(
3579
+ "li",
3580
+ {
3581
+ "data-testid": "news-story-entry",
3582
+ "data-entry-id": entry.id,
3583
+ "data-relation": entry.relation,
3584
+ style: { borderLeft: "1px solid rgba(255,255,255,0.14)", paddingLeft: "0.7rem" },
3585
+ children: [
3586
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.4rem", alignItems: "baseline", flexWrap: "wrap", marginBottom: "0.15rem" }, children: [
3587
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "0.65rem", color: "#848e9c" }, children: formatDateShort(entry.publishedAt) }),
3588
+ entry.source && /* @__PURE__ */ jsx("span", { style: { fontSize: "0.65rem", color: "rgba(255,255,255,0.5)" }, children: entry.source })
3589
+ ] }),
3590
+ entry.url && isSafeUrl(entry.url) ? /* @__PURE__ */ jsx(
3591
+ "a",
3592
+ {
3593
+ href: entry.url,
3594
+ target: "_blank",
3595
+ rel: "noopener noreferrer",
3596
+ style: { fontSize: "0.8rem", color: "rgba(255,255,255,0.8)", textDecoration: "none", lineHeight: 1.5, overflowWrap: "anywhere" },
3597
+ children: entry.title
3598
+ }
3599
+ ) : /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8rem", color: "rgba(255,255,255,0.8)", lineHeight: 1.5, overflowWrap: "anywhere" }, children: entry.title })
3600
+ ]
3601
+ },
3602
+ entry.id
3603
+ )) })
3604
+ ] }),
3511
3605
  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
3606
  "a",
3513
3607
  {
@@ -3567,6 +3661,7 @@ var PropertyNewsUpdates = React9.forwardRef(
3567
3661
  pinBreaking = false,
3568
3662
  onAssetOpen,
3569
3663
  showAssetBadges = true,
3664
+ fetchStory,
3570
3665
  ...props
3571
3666
  }, ref) => {
3572
3667
  const isPurchaseVariant = variant === "purchases";
@@ -4103,6 +4198,7 @@ var PropertyNewsUpdates = React9.forwardRef(
4103
4198
  },
4104
4199
  badgeAsset.ticker
4105
4200
  )),
4201
+ /* @__PURE__ */ jsx(NewsMetaNotes, { item, fontSize: isMobile ? "0.52rem" : "0.62rem" }),
4106
4202
  /* @__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
4203
  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
4204
  sentimentInfo.arrow ? `${sentimentInfo.arrow} ` : "",
@@ -4157,7 +4253,10 @@ var PropertyNewsUpdates = React9.forwardRef(
4157
4253
  children: [
4158
4254
  /* @__PURE__ */ jsx("p", { style: { fontSize: "0.9375rem", fontWeight: 500, marginBottom: "0.4rem", color: "#fff", lineHeight: 1.4 }, children: item.title }),
4159
4255
  /* @__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 }) }),
4256
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", minWidth: 0, flexWrap: "wrap" }, children: [
4257
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "0.75rem", color: isHighlighted ? "#0ecb81" : "#b5b8c5", whiteSpace: "nowrap" }, children: dateLabel }),
4258
+ /* @__PURE__ */ jsx(NewsMetaNotes, { item, fontSize: "0.65rem" })
4259
+ ] }),
4161
4260
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-end", gap: "0.3rem", flexShrink: 0 }, children: [
4162
4261
  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
4262
  sentimentInfo.arrow ? `${sentimentInfo.arrow} ` : "",
@@ -4236,7 +4335,7 @@ var PropertyNewsUpdates = React9.forwardRef(
4236
4335
  }
4237
4336
  )
4238
4337
  ] }),
4239
- selectedItem && !isPurchaseVariant && /* @__PURE__ */ jsx(NewsArticleModal, { item: selectedItem, onClose: () => setSelectedItem(null) }),
4338
+ selectedItem && !isPurchaseVariant && /* @__PURE__ */ jsx(NewsArticleModal, { item: selectedItem, onClose: () => setSelectedItem(null), fetchStory }),
4240
4339
  badgeTip && ReactDOM.createPortal(
4241
4340
  /* @__PURE__ */ jsx(
4242
4341
  "span",
@@ -22045,6 +22144,6 @@ var SparklineChart = React9__default.memo(function SparklineChart2({ data, width
22045
22144
  );
22046
22145
  });
22047
22146
 
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 };
22147
+ 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
22148
  //# sourceMappingURL=index.mjs.map
22050
22149
  //# sourceMappingURL=index.mjs.map