@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.d.mts
CHANGED
|
@@ -324,7 +324,43 @@ type PropertyNewsItem = {
|
|
|
324
324
|
* can concern several properties, user call 2026-08-20). Takes precedence
|
|
325
325
|
* over the singular `asset`. */
|
|
326
326
|
assets?: NewsBadgeAsset[];
|
|
327
|
-
|
|
327
|
+
/** Is this old news? Derived server-side on every read (loaf-news-service
|
|
328
|
+
* src/freshness.ts:41). Only `catching_up` and `retrospective` are badged —
|
|
329
|
+
* `fresh` is the default class and badging it is noise. */
|
|
330
|
+
freshness?: NewsFreshness;
|
|
331
|
+
/** When the EVENT happened, not when it was published (schemas/article.ts:91).
|
|
332
|
+
* Absent is common and correct — never stand `publishedAt` in for it. */
|
|
333
|
+
eventTime?: string;
|
|
334
|
+
/** `month`/`quarter`/`year` are PERIODS, not timestamps (freshness.ts:21). */
|
|
335
|
+
eventTimePrecision?: NewsEventTimePrecision;
|
|
336
|
+
/** How many publishers ran the story; ≥1 (article.ts:190). >1 earns the
|
|
337
|
+
* "Reported by N outlets" trust line. */
|
|
338
|
+
reportedByCount?: number;
|
|
339
|
+
/** Storyline id — equals `id` for a standalone story. */
|
|
340
|
+
storyId?: string;
|
|
341
|
+
storyRole?: 'canonical' | 'follow_up';
|
|
342
|
+
publishedAtSource?: 'publisher' | 'ingest_fallback' | 'clamped_future';
|
|
343
|
+
};
|
|
344
|
+
type NewsFreshness = 'breaking' | 'fresh' | 'catching_up' | 'retrospective' | 'evergreen';
|
|
345
|
+
/** One report in a storyline — loaf-news-service GET /v1/stories/:story_id. */
|
|
346
|
+
type NewsStoryEntry = {
|
|
347
|
+
id: string;
|
|
348
|
+
title: string;
|
|
349
|
+
publishedAt: string;
|
|
350
|
+
source?: string;
|
|
351
|
+
url?: string;
|
|
352
|
+
relation?: 'duplicate' | 'follow_up';
|
|
353
|
+
};
|
|
354
|
+
type NewsStoryThread = {
|
|
355
|
+
storyId: string;
|
|
356
|
+
canonical: NewsStoryEntry;
|
|
357
|
+
/** Oldest → newest. */
|
|
358
|
+
timeline: NewsStoryEntry[];
|
|
359
|
+
reportedBy: string[];
|
|
360
|
+
firstReportedAt?: string;
|
|
361
|
+
lastUpdatedAt?: string;
|
|
362
|
+
};
|
|
363
|
+
type NewsEventTimePrecision = 'instant' | 'day' | 'month' | 'quarter' | 'year' | 'unknown';
|
|
328
364
|
/** The badge's tooltip is name and ticker only (user call 2026-08-24: no price or % change). */
|
|
329
365
|
type NewsBadgeAsset = {
|
|
330
366
|
ticker: string;
|
|
@@ -373,6 +409,10 @@ interface PropertyNewsUpdatesProps extends React$1.HTMLAttributes<HTMLDivElement
|
|
|
373
409
|
price?: number;
|
|
374
410
|
changePercent?: number;
|
|
375
411
|
}) => void;
|
|
412
|
+
/** Loads one storyline for the article popup (DI, like SharePositionModal's
|
|
413
|
+
* `fetchCard`): loaf-ui owns no transport, and the consumer already knows its
|
|
414
|
+
* news base URL. Omitted → the popup shows the article alone. */
|
|
415
|
+
fetchStory?: (storyId: string) => Promise<NewsStoryThread | null>;
|
|
376
416
|
/** Show the square asset thumbnail(s) on property rows: which platform
|
|
377
417
|
* asset(s) the story is about. ON by default (user call 2026-08-24, which
|
|
378
418
|
* reversed the 22 Aug "hide for now"); pass `false` to hide them. */
|
|
@@ -387,6 +427,16 @@ declare function rowAgeLabel(publishedAt: string | undefined, now: number): {
|
|
|
387
427
|
label: string;
|
|
388
428
|
fresh: boolean;
|
|
389
429
|
} | null;
|
|
430
|
+
/**
|
|
431
|
+
* The two freshness classes worth saying out loud. `fresh` is the default class, so
|
|
432
|
+
* badging it would mark every row; `breaking` already has the gold pin and `evergreen`
|
|
433
|
+
* (an explainer with no datable event) has nothing to date. Wording is what the class
|
|
434
|
+
* MEANS to a reader, not the wire value — loaf-news-service src/freshness.ts:41-51.
|
|
435
|
+
*/
|
|
436
|
+
declare function freshnessNote(freshness?: string): {
|
|
437
|
+
label: string;
|
|
438
|
+
title: string;
|
|
439
|
+
} | null;
|
|
390
440
|
declare function SentimentBar({ score, color }: {
|
|
391
441
|
score: number;
|
|
392
442
|
color: string;
|
|
@@ -1552,4 +1602,4 @@ declare function extractSparkline(candles: ReadonlyArray<{
|
|
|
1552
1602
|
}>, maxPoints?: number): number[];
|
|
1553
1603
|
declare const SparklineChart: React__default.NamedExoticComponent<SparklineChartProps>;
|
|
1554
1604
|
|
|
1555
|
-
export { type ActivityTabId, AssetSelectorBar, type AssetSelectorBarProps, type AssetSelectorMetric, Badge, type BadgeProps, Button, type ButtonProps, Card, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, ContactPopup, type ContactPopupProps, type FinancialItem, type FundWalletParams, type FundWalletResult, Header, type HeaderNavItem, type HeaderPortfolioData, type HeaderProps, HlsVideo, type HlsVideoProps, type HlsVideoSource, type HousePositionOrderbook, type HousePositionPendingOrder, HousePositionSlider, HousePositionSliderMobile, type HousePositionSliderMobileOrderPayload, type HousePositionSliderMobileOrderbook, type HousePositionSliderMobileProps, type HousePositionSliderOrderPayload, type HousePositionSliderProps, type InspectionSlot, LoafLiquidityBadge, type LoafLiquidityDotState, LoafLiquidityLogo, type LoafLiquidityLogoProps, LoginPopup, type LoginPopupProps, MobileTradeNav, type MobileTradeNavItem, type MobileTradeNavProps, type NewsBadgeAsset, type OfferStatus, OfferingProgressCard, type OfferingProgressCardProps, OnboardingGuide, type OnboardingGuideProps, Orderbook, type OrderbookLevel, type OrderbookProps, type OrderbookSide, type OrderbookTrade, type OverviewImage, type OverviewResponse, OwnerBooking, type OwnerBookingProps, PaymentPopup, type PaymentPopupProps, PortfolioActivityPanel, type PortfolioActivityPanelProps, type PortfolioPosition, PortfolioSummary, type PortfolioSummaryProps, PriceChart, type PriceChartCandle, type PriceChartProps, type PriceChartRange, type PropertyAddressOption, PropertyBuy, PropertyCompareBar, type PropertyCompareBarProps, PropertyDocuments, type PropertyGalleryHotspot, type PropertyGalleryImage, PropertyHeroHeader, type PropertyHeroHeaderProps, PropertyHistory, type PropertyInfoItem, PropertyInspectionTimes, PropertyMediaRow, type PropertyMediaRowProps, type PropertyNewsItem, type PropertyNewsType, PropertyNewsUpdates, type PropertyNewsUpdatesProps, type PropertyOffer, PropertyOffers, type PropertyOffersProps, PropertyOverview, type PropertyOverviewProps, PropertyPhotoGallery, type PropertyPriceSummary, PropertySubheader, type PropertySubheaderAction, type PropertySubheaderProps, type PropertySubheaderTab, PropertyTour, type PropertyTourProps, PropertyValuation, type PropertyValuationProps, type PropertyValueSummary, type PurchaseFeedItem, type SelectorItem, SentimentBar, SiteFooter, Skeleton, type SkeletonProps, SlideDigit, SparklineChart, type SparklineChartProps, type ToastData, ToastProvider, type ToastVariant, TradeConfirmationModal, type TradeConfirmationModalDetails, type TradeConfirmationModalProps, TradingSlider, type TradingSliderProps, type YourOrder, type YourOrderSide, YourOrders, type YourOrdersProps, __reloadBreakingMemory, __resetDismissedBreaking, badgeVariants, buttonVariants, extractSparkline, formatAge, hasPendingActivity, matchesTab, rowAgeLabel, useAdaptivePolling, useToast };
|
|
1605
|
+
export { type ActivityTabId, AssetSelectorBar, type AssetSelectorBarProps, type AssetSelectorMetric, Badge, type BadgeProps, Button, type ButtonProps, Card, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, ContactPopup, type ContactPopupProps, type FinancialItem, type FundWalletParams, type FundWalletResult, Header, type HeaderNavItem, type HeaderPortfolioData, type HeaderProps, HlsVideo, type HlsVideoProps, type HlsVideoSource, type HousePositionOrderbook, type HousePositionPendingOrder, HousePositionSlider, HousePositionSliderMobile, type HousePositionSliderMobileOrderPayload, type HousePositionSliderMobileOrderbook, type HousePositionSliderMobileProps, type HousePositionSliderOrderPayload, type HousePositionSliderProps, type InspectionSlot, LoafLiquidityBadge, type LoafLiquidityDotState, LoafLiquidityLogo, type LoafLiquidityLogoProps, LoginPopup, type LoginPopupProps, MobileTradeNav, type MobileTradeNavItem, type MobileTradeNavProps, type NewsBadgeAsset, type NewsEventTimePrecision, type NewsFreshness, type NewsStoryEntry, type NewsStoryThread, type OfferStatus, OfferingProgressCard, type OfferingProgressCardProps, OnboardingGuide, type OnboardingGuideProps, Orderbook, type OrderbookLevel, type OrderbookProps, type OrderbookSide, type OrderbookTrade, type OverviewImage, type OverviewResponse, OwnerBooking, type OwnerBookingProps, PaymentPopup, type PaymentPopupProps, PortfolioActivityPanel, type PortfolioActivityPanelProps, type PortfolioPosition, PortfolioSummary, type PortfolioSummaryProps, PriceChart, type PriceChartCandle, type PriceChartProps, type PriceChartRange, type PropertyAddressOption, PropertyBuy, PropertyCompareBar, type PropertyCompareBarProps, PropertyDocuments, type PropertyGalleryHotspot, type PropertyGalleryImage, PropertyHeroHeader, type PropertyHeroHeaderProps, PropertyHistory, type PropertyInfoItem, PropertyInspectionTimes, PropertyMediaRow, type PropertyMediaRowProps, type PropertyNewsItem, type PropertyNewsType, PropertyNewsUpdates, type PropertyNewsUpdatesProps, type PropertyOffer, PropertyOffers, type PropertyOffersProps, PropertyOverview, type PropertyOverviewProps, PropertyPhotoGallery, type PropertyPriceSummary, PropertySubheader, type PropertySubheaderAction, type PropertySubheaderProps, type PropertySubheaderTab, PropertyTour, type PropertyTourProps, PropertyValuation, type PropertyValuationProps, type PropertyValueSummary, type PurchaseFeedItem, type SelectorItem, SentimentBar, SiteFooter, Skeleton, type SkeletonProps, SlideDigit, SparklineChart, type SparklineChartProps, type ToastData, ToastProvider, type ToastVariant, TradeConfirmationModal, type TradeConfirmationModalDetails, type TradeConfirmationModalProps, TradingSlider, type TradingSliderProps, type YourOrder, type YourOrderSide, YourOrders, type YourOrdersProps, __reloadBreakingMemory, __resetDismissedBreaking, badgeVariants, buttonVariants, extractSparkline, formatAge, freshnessNote, hasPendingActivity, matchesTab, rowAgeLabel, useAdaptivePolling, useToast };
|
package/dist/index.d.ts
CHANGED
|
@@ -324,7 +324,43 @@ type PropertyNewsItem = {
|
|
|
324
324
|
* can concern several properties, user call 2026-08-20). Takes precedence
|
|
325
325
|
* over the singular `asset`. */
|
|
326
326
|
assets?: NewsBadgeAsset[];
|
|
327
|
-
|
|
327
|
+
/** Is this old news? Derived server-side on every read (loaf-news-service
|
|
328
|
+
* src/freshness.ts:41). Only `catching_up` and `retrospective` are badged —
|
|
329
|
+
* `fresh` is the default class and badging it is noise. */
|
|
330
|
+
freshness?: NewsFreshness;
|
|
331
|
+
/** When the EVENT happened, not when it was published (schemas/article.ts:91).
|
|
332
|
+
* Absent is common and correct — never stand `publishedAt` in for it. */
|
|
333
|
+
eventTime?: string;
|
|
334
|
+
/** `month`/`quarter`/`year` are PERIODS, not timestamps (freshness.ts:21). */
|
|
335
|
+
eventTimePrecision?: NewsEventTimePrecision;
|
|
336
|
+
/** How many publishers ran the story; ≥1 (article.ts:190). >1 earns the
|
|
337
|
+
* "Reported by N outlets" trust line. */
|
|
338
|
+
reportedByCount?: number;
|
|
339
|
+
/** Storyline id — equals `id` for a standalone story. */
|
|
340
|
+
storyId?: string;
|
|
341
|
+
storyRole?: 'canonical' | 'follow_up';
|
|
342
|
+
publishedAtSource?: 'publisher' | 'ingest_fallback' | 'clamped_future';
|
|
343
|
+
};
|
|
344
|
+
type NewsFreshness = 'breaking' | 'fresh' | 'catching_up' | 'retrospective' | 'evergreen';
|
|
345
|
+
/** One report in a storyline — loaf-news-service GET /v1/stories/:story_id. */
|
|
346
|
+
type NewsStoryEntry = {
|
|
347
|
+
id: string;
|
|
348
|
+
title: string;
|
|
349
|
+
publishedAt: string;
|
|
350
|
+
source?: string;
|
|
351
|
+
url?: string;
|
|
352
|
+
relation?: 'duplicate' | 'follow_up';
|
|
353
|
+
};
|
|
354
|
+
type NewsStoryThread = {
|
|
355
|
+
storyId: string;
|
|
356
|
+
canonical: NewsStoryEntry;
|
|
357
|
+
/** Oldest → newest. */
|
|
358
|
+
timeline: NewsStoryEntry[];
|
|
359
|
+
reportedBy: string[];
|
|
360
|
+
firstReportedAt?: string;
|
|
361
|
+
lastUpdatedAt?: string;
|
|
362
|
+
};
|
|
363
|
+
type NewsEventTimePrecision = 'instant' | 'day' | 'month' | 'quarter' | 'year' | 'unknown';
|
|
328
364
|
/** The badge's tooltip is name and ticker only (user call 2026-08-24: no price or % change). */
|
|
329
365
|
type NewsBadgeAsset = {
|
|
330
366
|
ticker: string;
|
|
@@ -373,6 +409,10 @@ interface PropertyNewsUpdatesProps extends React$1.HTMLAttributes<HTMLDivElement
|
|
|
373
409
|
price?: number;
|
|
374
410
|
changePercent?: number;
|
|
375
411
|
}) => void;
|
|
412
|
+
/** Loads one storyline for the article popup (DI, like SharePositionModal's
|
|
413
|
+
* `fetchCard`): loaf-ui owns no transport, and the consumer already knows its
|
|
414
|
+
* news base URL. Omitted → the popup shows the article alone. */
|
|
415
|
+
fetchStory?: (storyId: string) => Promise<NewsStoryThread | null>;
|
|
376
416
|
/** Show the square asset thumbnail(s) on property rows: which platform
|
|
377
417
|
* asset(s) the story is about. ON by default (user call 2026-08-24, which
|
|
378
418
|
* reversed the 22 Aug "hide for now"); pass `false` to hide them. */
|
|
@@ -387,6 +427,16 @@ declare function rowAgeLabel(publishedAt: string | undefined, now: number): {
|
|
|
387
427
|
label: string;
|
|
388
428
|
fresh: boolean;
|
|
389
429
|
} | null;
|
|
430
|
+
/**
|
|
431
|
+
* The two freshness classes worth saying out loud. `fresh` is the default class, so
|
|
432
|
+
* badging it would mark every row; `breaking` already has the gold pin and `evergreen`
|
|
433
|
+
* (an explainer with no datable event) has nothing to date. Wording is what the class
|
|
434
|
+
* MEANS to a reader, not the wire value — loaf-news-service src/freshness.ts:41-51.
|
|
435
|
+
*/
|
|
436
|
+
declare function freshnessNote(freshness?: string): {
|
|
437
|
+
label: string;
|
|
438
|
+
title: string;
|
|
439
|
+
} | null;
|
|
390
440
|
declare function SentimentBar({ score, color }: {
|
|
391
441
|
score: number;
|
|
392
442
|
color: string;
|
|
@@ -1552,4 +1602,4 @@ declare function extractSparkline(candles: ReadonlyArray<{
|
|
|
1552
1602
|
}>, maxPoints?: number): number[];
|
|
1553
1603
|
declare const SparklineChart: React__default.NamedExoticComponent<SparklineChartProps>;
|
|
1554
1604
|
|
|
1555
|
-
export { type ActivityTabId, AssetSelectorBar, type AssetSelectorBarProps, type AssetSelectorMetric, Badge, type BadgeProps, Button, type ButtonProps, Card, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, ContactPopup, type ContactPopupProps, type FinancialItem, type FundWalletParams, type FundWalletResult, Header, type HeaderNavItem, type HeaderPortfolioData, type HeaderProps, HlsVideo, type HlsVideoProps, type HlsVideoSource, type HousePositionOrderbook, type HousePositionPendingOrder, HousePositionSlider, HousePositionSliderMobile, type HousePositionSliderMobileOrderPayload, type HousePositionSliderMobileOrderbook, type HousePositionSliderMobileProps, type HousePositionSliderOrderPayload, type HousePositionSliderProps, type InspectionSlot, LoafLiquidityBadge, type LoafLiquidityDotState, LoafLiquidityLogo, type LoafLiquidityLogoProps, LoginPopup, type LoginPopupProps, MobileTradeNav, type MobileTradeNavItem, type MobileTradeNavProps, type NewsBadgeAsset, type OfferStatus, OfferingProgressCard, type OfferingProgressCardProps, OnboardingGuide, type OnboardingGuideProps, Orderbook, type OrderbookLevel, type OrderbookProps, type OrderbookSide, type OrderbookTrade, type OverviewImage, type OverviewResponse, OwnerBooking, type OwnerBookingProps, PaymentPopup, type PaymentPopupProps, PortfolioActivityPanel, type PortfolioActivityPanelProps, type PortfolioPosition, PortfolioSummary, type PortfolioSummaryProps, PriceChart, type PriceChartCandle, type PriceChartProps, type PriceChartRange, type PropertyAddressOption, PropertyBuy, PropertyCompareBar, type PropertyCompareBarProps, PropertyDocuments, type PropertyGalleryHotspot, type PropertyGalleryImage, PropertyHeroHeader, type PropertyHeroHeaderProps, PropertyHistory, type PropertyInfoItem, PropertyInspectionTimes, PropertyMediaRow, type PropertyMediaRowProps, type PropertyNewsItem, type PropertyNewsType, PropertyNewsUpdates, type PropertyNewsUpdatesProps, type PropertyOffer, PropertyOffers, type PropertyOffersProps, PropertyOverview, type PropertyOverviewProps, PropertyPhotoGallery, type PropertyPriceSummary, PropertySubheader, type PropertySubheaderAction, type PropertySubheaderProps, type PropertySubheaderTab, PropertyTour, type PropertyTourProps, PropertyValuation, type PropertyValuationProps, type PropertyValueSummary, type PurchaseFeedItem, type SelectorItem, SentimentBar, SiteFooter, Skeleton, type SkeletonProps, SlideDigit, SparklineChart, type SparklineChartProps, type ToastData, ToastProvider, type ToastVariant, TradeConfirmationModal, type TradeConfirmationModalDetails, type TradeConfirmationModalProps, TradingSlider, type TradingSliderProps, type YourOrder, type YourOrderSide, YourOrders, type YourOrdersProps, __reloadBreakingMemory, __resetDismissedBreaking, badgeVariants, buttonVariants, extractSparkline, formatAge, hasPendingActivity, matchesTab, rowAgeLabel, useAdaptivePolling, useToast };
|
|
1605
|
+
export { type ActivityTabId, AssetSelectorBar, type AssetSelectorBarProps, type AssetSelectorMetric, Badge, type BadgeProps, Button, type ButtonProps, Card, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, ContactPopup, type ContactPopupProps, type FinancialItem, type FundWalletParams, type FundWalletResult, Header, type HeaderNavItem, type HeaderPortfolioData, type HeaderProps, HlsVideo, type HlsVideoProps, type HlsVideoSource, type HousePositionOrderbook, type HousePositionPendingOrder, HousePositionSlider, HousePositionSliderMobile, type HousePositionSliderMobileOrderPayload, type HousePositionSliderMobileOrderbook, type HousePositionSliderMobileProps, type HousePositionSliderOrderPayload, type HousePositionSliderProps, type InspectionSlot, LoafLiquidityBadge, type LoafLiquidityDotState, LoafLiquidityLogo, type LoafLiquidityLogoProps, LoginPopup, type LoginPopupProps, MobileTradeNav, type MobileTradeNavItem, type MobileTradeNavProps, type NewsBadgeAsset, type NewsEventTimePrecision, type NewsFreshness, type NewsStoryEntry, type NewsStoryThread, type OfferStatus, OfferingProgressCard, type OfferingProgressCardProps, OnboardingGuide, type OnboardingGuideProps, Orderbook, type OrderbookLevel, type OrderbookProps, type OrderbookSide, type OrderbookTrade, type OverviewImage, type OverviewResponse, OwnerBooking, type OwnerBookingProps, PaymentPopup, type PaymentPopupProps, PortfolioActivityPanel, type PortfolioActivityPanelProps, type PortfolioPosition, PortfolioSummary, type PortfolioSummaryProps, PriceChart, type PriceChartCandle, type PriceChartProps, type PriceChartRange, type PropertyAddressOption, PropertyBuy, PropertyCompareBar, type PropertyCompareBarProps, PropertyDocuments, type PropertyGalleryHotspot, type PropertyGalleryImage, PropertyHeroHeader, type PropertyHeroHeaderProps, PropertyHistory, type PropertyInfoItem, PropertyInspectionTimes, PropertyMediaRow, type PropertyMediaRowProps, type PropertyNewsItem, type PropertyNewsType, PropertyNewsUpdates, type PropertyNewsUpdatesProps, type PropertyOffer, PropertyOffers, type PropertyOffersProps, PropertyOverview, type PropertyOverviewProps, PropertyPhotoGallery, type PropertyPriceSummary, PropertySubheader, type PropertySubheaderAction, type PropertySubheaderProps, type PropertySubheaderTab, PropertyTour, type PropertyTourProps, PropertyValuation, type PropertyValuationProps, type PropertyValueSummary, type PurchaseFeedItem, type SelectorItem, SentimentBar, SiteFooter, Skeleton, type SkeletonProps, SlideDigit, SparklineChart, type SparklineChartProps, type ToastData, ToastProvider, type ToastVariant, TradeConfirmationModal, type TradeConfirmationModalDetails, type TradeConfirmationModalProps, TradingSlider, type TradingSliderProps, type YourOrder, type YourOrderSide, YourOrders, type YourOrdersProps, __reloadBreakingMemory, __resetDismissedBreaking, badgeVariants, buttonVariants, extractSparkline, formatAge, freshnessNote, hasPendingActivity, matchesTab, rowAgeLabel, useAdaptivePolling, useToast };
|
package/dist/index.js
CHANGED
|
@@ -3044,7 +3044,7 @@ PropertyTour.displayName = "PropertyTour";
|
|
|
3044
3044
|
var BREAKING_WINDOW_HOURS = 6;
|
|
3045
3045
|
var BREAKING_MEMORY_CAP = 50;
|
|
3046
3046
|
var BREAKING_MEMORY_STORAGE_KEY = "loaf:dismissed-breaking";
|
|
3047
|
-
var emptyBreakingMemory = () => ({ dismissed: {},
|
|
3047
|
+
var emptyBreakingMemory = () => ({ dismissed: {}, blocked: {} });
|
|
3048
3048
|
var isTimestampMap = (v) => typeof v === "object" && v !== null && !Array.isArray(v) && Object.values(v).every((t) => typeof t === "number" && Number.isFinite(t));
|
|
3049
3049
|
var pruneMap = (map, cutoff) => {
|
|
3050
3050
|
const fresh = Object.entries(map).filter(([, t]) => t >= cutoff);
|
|
@@ -3055,44 +3055,30 @@ var prune = (mem, now) => {
|
|
|
3055
3055
|
const cutoff = now - BREAKING_WINDOW_HOURS * 36e5;
|
|
3056
3056
|
return {
|
|
3057
3057
|
dismissed: pruneMap(mem.dismissed, cutoff),
|
|
3058
|
-
|
|
3059
|
-
// An expired watermark can't block anything (every in-window story is newer).
|
|
3060
|
-
watermark: mem.watermark !== null && mem.watermark >= cutoff ? mem.watermark : null
|
|
3058
|
+
blocked: pruneMap(mem.blocked, cutoff)
|
|
3061
3059
|
};
|
|
3062
3060
|
};
|
|
3063
3061
|
function parseBreakingMemory(raw, now) {
|
|
3064
3062
|
if (!raw) return emptyBreakingMemory();
|
|
3065
3063
|
try {
|
|
3066
3064
|
const v = JSON.parse(raw);
|
|
3067
|
-
if (typeof v !== "object" || v === null || Array.isArray(v) || !isTimestampMap(v.dismissed)
|
|
3065
|
+
if (typeof v !== "object" || v === null || Array.isArray(v) || !isTimestampMap(v.dismissed)) {
|
|
3068
3066
|
return emptyBreakingMemory();
|
|
3069
3067
|
}
|
|
3070
|
-
|
|
3068
|
+
const blocked = isTimestampMap(v.blocked) ? v.blocked : {};
|
|
3069
|
+
return prune({ dismissed: v.dismissed, blocked }, now);
|
|
3071
3070
|
} catch {
|
|
3072
3071
|
return emptyBreakingMemory();
|
|
3073
3072
|
}
|
|
3074
3073
|
}
|
|
3075
3074
|
var serializeBreakingMemory = (mem) => JSON.stringify(mem);
|
|
3076
|
-
function recordBreakingDismissal(mem, key, publishedAtMs, now) {
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
pinned: mem.pinned,
|
|
3081
|
-
watermark: Math.max(mem.watermark ?? -Infinity, publishedAtMs)
|
|
3082
|
-
},
|
|
3083
|
-
now
|
|
3084
|
-
);
|
|
3075
|
+
function recordBreakingDismissal(mem, key, publishedAtMs, now, waiting = []) {
|
|
3076
|
+
const blocked = { ...mem.blocked };
|
|
3077
|
+
for (const w of waiting) blocked[w.key] = w.publishedAtMs;
|
|
3078
|
+
return prune({ dismissed: { ...mem.dismissed, [key]: publishedAtMs }, blocked }, now);
|
|
3085
3079
|
}
|
|
3086
|
-
function
|
|
3087
|
-
|
|
3088
|
-
const pinned = { ...mem.pinned };
|
|
3089
|
-
for (const p of pins) pinned[p.key] = p.publishedAtMs;
|
|
3090
|
-
return prune({ dismissed: mem.dismissed, pinned, watermark: mem.watermark }, now);
|
|
3091
|
-
}
|
|
3092
|
-
function mayPinBreaking(mem, key, publishedAtMs) {
|
|
3093
|
-
if (mem.dismissed[key] !== void 0) return false;
|
|
3094
|
-
if (mem.watermark === null) return true;
|
|
3095
|
-
return publishedAtMs > mem.watermark || mem.pinned[key] !== void 0;
|
|
3080
|
+
function mayPinBreaking(mem, key) {
|
|
3081
|
+
return mem.dismissed[key] === void 0 && mem.blocked[key] === void 0;
|
|
3096
3082
|
}
|
|
3097
3083
|
function loadBreakingMemory(now) {
|
|
3098
3084
|
try {
|
|
@@ -3357,6 +3343,47 @@ var formatTimeAgo = (timestamp) => {
|
|
|
3357
3343
|
if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
|
|
3358
3344
|
return `${Math.floor(diff / 3600)}h ago`;
|
|
3359
3345
|
};
|
|
3346
|
+
function freshnessNote(freshness) {
|
|
3347
|
+
if (freshness === "catching_up") return { label: "Catching up", title: "Reported promptly; we picked it up late" };
|
|
3348
|
+
if (freshness === "retrospective") return { label: "Older event", title: "The event is materially older than this report" };
|
|
3349
|
+
return null;
|
|
3350
|
+
}
|
|
3351
|
+
function NewsMetaNotes({ item, fontSize }) {
|
|
3352
|
+
const note = freshnessNote(item.freshness);
|
|
3353
|
+
const outlets = typeof item.reportedByCount === "number" && item.reportedByCount > 1 ? item.reportedByCount : null;
|
|
3354
|
+
if (!note && outlets === null) return null;
|
|
3355
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3356
|
+
note && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3357
|
+
"span",
|
|
3358
|
+
{
|
|
3359
|
+
"data-testid": "news-freshness",
|
|
3360
|
+
title: note.title,
|
|
3361
|
+
style: {
|
|
3362
|
+
fontSize,
|
|
3363
|
+
fontWeight: 500,
|
|
3364
|
+
color: "rgba(255,255,255,0.5)",
|
|
3365
|
+
border: "1px solid rgba(255,255,255,0.14)",
|
|
3366
|
+
borderRadius: "3px",
|
|
3367
|
+
padding: "0.1rem 0.35rem",
|
|
3368
|
+
whiteSpace: "nowrap"
|
|
3369
|
+
},
|
|
3370
|
+
children: note.label
|
|
3371
|
+
}
|
|
3372
|
+
),
|
|
3373
|
+
outlets !== null && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3374
|
+
"span",
|
|
3375
|
+
{
|
|
3376
|
+
"data-testid": "news-outlets",
|
|
3377
|
+
title: `Reported by ${outlets} publishers`,
|
|
3378
|
+
style: { fontSize, fontWeight: 500, color: "rgba(255,255,255,0.5)", whiteSpace: "nowrap" },
|
|
3379
|
+
children: [
|
|
3380
|
+
outlets,
|
|
3381
|
+
" outlets"
|
|
3382
|
+
]
|
|
3383
|
+
}
|
|
3384
|
+
)
|
|
3385
|
+
] });
|
|
3386
|
+
}
|
|
3360
3387
|
function getSentimentInfo(score) {
|
|
3361
3388
|
if (score == null) return null;
|
|
3362
3389
|
if (score > 0.15) return { arrow: "\u25B2", label: "Bullish", color: "#0ecb81" };
|
|
@@ -3419,9 +3446,27 @@ function SentimentBar({ score, color }) {
|
|
|
3419
3446
|
}
|
|
3420
3447
|
);
|
|
3421
3448
|
}
|
|
3422
|
-
function NewsArticleModal({
|
|
3449
|
+
function NewsArticleModal({
|
|
3450
|
+
item,
|
|
3451
|
+
onClose,
|
|
3452
|
+
fetchStory
|
|
3453
|
+
}) {
|
|
3423
3454
|
const sentimentInfo = getSentimentInfo(item.sentimentScore);
|
|
3424
3455
|
const catStyle = categoryStyles[item.type] ?? categoryStyles.market;
|
|
3456
|
+
const [thread, setThread] = React9__namespace.useState(null);
|
|
3457
|
+
const threadEntries = thread ? thread.timeline.filter((entry) => entry.id !== item.id) : [];
|
|
3458
|
+
const storyId = item.storyId;
|
|
3459
|
+
const threaded = storyId !== void 0 && (typeof item.reportedByCount === "number" && item.reportedByCount > 1 || storyId !== item.id);
|
|
3460
|
+
React9__namespace.useEffect(() => {
|
|
3461
|
+
if (!fetchStory || !threaded || storyId === void 0) return;
|
|
3462
|
+
let dropped = false;
|
|
3463
|
+
void fetchStory(storyId).then((t) => {
|
|
3464
|
+
if (!dropped) setThread(t);
|
|
3465
|
+
});
|
|
3466
|
+
return () => {
|
|
3467
|
+
dropped = true;
|
|
3468
|
+
};
|
|
3469
|
+
}, [fetchStory, threaded, storyId]);
|
|
3425
3470
|
React9__namespace.useEffect(() => {
|
|
3426
3471
|
const handler = (e) => {
|
|
3427
3472
|
if (e.key === "Escape") onClose();
|
|
@@ -3547,6 +3592,40 @@ function NewsArticleModal({ item, onClose }) {
|
|
|
3547
3592
|
] }),
|
|
3548
3593
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: "1px", background: "rgba(255,255,255,0.08)", marginBottom: "1.25rem" } }),
|
|
3549
3594
|
item.summary ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.875rem", color: "rgba(255,255,255,0.8)", lineHeight: 1.75, margin: 0 }, children: item.summary }) : /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.8rem", color: "rgba(255,255,255,0.35)", fontStyle: "italic", margin: 0 }, children: "Summary not available for this article." }),
|
|
3595
|
+
thread && threadEntries.length > 0 && // Lands after the popup is already open, so it is announced rather than silent.
|
|
3596
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { "aria-live": "polite", style: { marginTop: "1.25rem", paddingTop: "1rem", borderTop: "1px solid rgba(255,255,255,0.08)" }, children: [
|
|
3597
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: { fontSize: "0.75rem", fontWeight: 600, color: "rgba(255,255,255,0.75)", margin: "0 0 0.75rem" }, children: [
|
|
3598
|
+
"Reported by ",
|
|
3599
|
+
thread.reportedBy.length > 0 ? thread.reportedBy.length : threadEntries.length + 1,
|
|
3600
|
+
" outlets"
|
|
3601
|
+
] }),
|
|
3602
|
+
/* @__PURE__ */ jsxRuntime.jsx("ol", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: "0.6rem" }, children: threadEntries.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3603
|
+
"li",
|
|
3604
|
+
{
|
|
3605
|
+
"data-testid": "news-story-entry",
|
|
3606
|
+
"data-entry-id": entry.id,
|
|
3607
|
+
"data-relation": entry.relation,
|
|
3608
|
+
style: { borderLeft: "1px solid rgba(255,255,255,0.14)", paddingLeft: "0.7rem" },
|
|
3609
|
+
children: [
|
|
3610
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "0.4rem", alignItems: "baseline", flexWrap: "wrap", marginBottom: "0.15rem" }, children: [
|
|
3611
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.65rem", color: "#848e9c" }, children: formatDateShort(entry.publishedAt) }),
|
|
3612
|
+
entry.source && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.65rem", color: "rgba(255,255,255,0.5)" }, children: entry.source })
|
|
3613
|
+
] }),
|
|
3614
|
+
entry.url && isSafeUrl(entry.url) ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3615
|
+
"a",
|
|
3616
|
+
{
|
|
3617
|
+
href: entry.url,
|
|
3618
|
+
target: "_blank",
|
|
3619
|
+
rel: "noopener noreferrer",
|
|
3620
|
+
style: { fontSize: "0.8rem", color: "rgba(255,255,255,0.8)", textDecoration: "none", lineHeight: 1.5, overflowWrap: "anywhere" },
|
|
3621
|
+
children: entry.title
|
|
3622
|
+
}
|
|
3623
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.8rem", color: "rgba(255,255,255,0.8)", lineHeight: 1.5, overflowWrap: "anywhere" }, children: entry.title })
|
|
3624
|
+
]
|
|
3625
|
+
},
|
|
3626
|
+
entry.id
|
|
3627
|
+
)) })
|
|
3628
|
+
] }),
|
|
3550
3629
|
item.url && isSafeUrl(item.url) && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: "1.25rem", paddingTop: "1rem", borderTop: "1px solid rgba(255,255,255,0.08)" }, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3551
3630
|
"a",
|
|
3552
3631
|
{
|
|
@@ -3606,6 +3685,7 @@ var PropertyNewsUpdates = React9__namespace.forwardRef(
|
|
|
3606
3685
|
pinBreaking = false,
|
|
3607
3686
|
onAssetOpen,
|
|
3608
3687
|
showAssetBadges = true,
|
|
3688
|
+
fetchStory,
|
|
3609
3689
|
...props
|
|
3610
3690
|
}, ref) => {
|
|
3611
3691
|
const isPurchaseVariant = variant === "purchases";
|
|
@@ -3677,18 +3757,8 @@ var PropertyNewsUpdates = React9__namespace.forwardRef(
|
|
|
3677
3757
|
};
|
|
3678
3758
|
const pinnedBreaking = React9__namespace.useMemo(() => {
|
|
3679
3759
|
const mem = getBreakingMemory();
|
|
3680
|
-
return breakingEligible.filter((it) => mayPinBreaking(mem, breakingKey(it)
|
|
3760
|
+
return breakingEligible.filter((it) => mayPinBreaking(mem, breakingKey(it))).sort((a, b) => breakingImpact(b) - breakingImpact(a)).slice(0, MAX_PINNED_BREAKING);
|
|
3681
3761
|
}, [breakingEligible, dismissedVersion]);
|
|
3682
|
-
React9__namespace.useEffect(() => {
|
|
3683
|
-
if (pinnedBreaking.length === 0) return;
|
|
3684
|
-
const mem = getBreakingMemory();
|
|
3685
|
-
const next = recordBreakingPins(
|
|
3686
|
-
mem,
|
|
3687
|
-
pinnedBreaking.map((it) => ({ key: breakingKey(it), publishedAtMs: breakingPublishedMs(it) })),
|
|
3688
|
-
Date.now()
|
|
3689
|
-
);
|
|
3690
|
-
if (next !== mem) commitBreakingMemory(next);
|
|
3691
|
-
}, [pinnedBreaking]);
|
|
3692
3762
|
const breakingRowKeys = React9__namespace.useMemo(
|
|
3693
3763
|
() => new Set(breakingEligible.map(breakingKey)),
|
|
3694
3764
|
[breakingEligible]
|
|
@@ -3794,8 +3864,10 @@ var PropertyNewsUpdates = React9__namespace.forwardRef(
|
|
|
3794
3864
|
const k = breakingKey(item);
|
|
3795
3865
|
const dx = breakingSwipe?.key === k ? breakingSwipe.dx : 0;
|
|
3796
3866
|
const persistDismissal = () => {
|
|
3867
|
+
const holding = new Set(pinnedBreaking);
|
|
3868
|
+
const waiting = breakingEligible.filter((c) => !holding.has(c)).map((c) => ({ key: breakingKey(c), publishedAtMs: breakingPublishedMs(c) }));
|
|
3797
3869
|
commitBreakingMemory(
|
|
3798
|
-
recordBreakingDismissal(getBreakingMemory(), k, breakingPublishedMs(item), Date.now())
|
|
3870
|
+
recordBreakingDismissal(getBreakingMemory(), k, breakingPublishedMs(item), Date.now(), waiting)
|
|
3799
3871
|
);
|
|
3800
3872
|
};
|
|
3801
3873
|
const finishDismiss = () => {
|
|
@@ -4150,6 +4222,7 @@ var PropertyNewsUpdates = React9__namespace.forwardRef(
|
|
|
4150
4222
|
},
|
|
4151
4223
|
badgeAsset.ticker
|
|
4152
4224
|
)),
|
|
4225
|
+
/* @__PURE__ */ jsxRuntime.jsx(NewsMetaNotes, { item, fontSize: isMobile ? "0.52rem" : "0.62rem" }),
|
|
4153
4226
|
/* @__PURE__ */ jsxRuntime.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" }),
|
|
4154
4227
|
sentimentInfo && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: isMobile ? "0.52rem" : "0.65rem", fontWeight: 600, color: sentimentInfo.color }, children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.3rem" }, children: [
|
|
4155
4228
|
sentimentInfo.arrow ? `${sentimentInfo.arrow} ` : "",
|
|
@@ -4204,7 +4277,10 @@ var PropertyNewsUpdates = React9__namespace.forwardRef(
|
|
|
4204
4277
|
children: [
|
|
4205
4278
|
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.9375rem", fontWeight: 500, marginBottom: "0.4rem", color: "#fff", lineHeight: 1.4 }, children: item.title }),
|
|
4206
4279
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-end", gap: "0.5rem" }, children: [
|
|
4207
|
-
/* @__PURE__ */ jsxRuntime.
|
|
4280
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", minWidth: 0, flexWrap: "wrap" }, children: [
|
|
4281
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.75rem", color: isHighlighted ? "#0ecb81" : "#b5b8c5", whiteSpace: "nowrap" }, children: dateLabel }),
|
|
4282
|
+
/* @__PURE__ */ jsxRuntime.jsx(NewsMetaNotes, { item, fontSize: "0.65rem" })
|
|
4283
|
+
] }),
|
|
4208
4284
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-end", gap: "0.3rem", flexShrink: 0 }, children: [
|
|
4209
4285
|
sentimentInfo && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.65rem", fontWeight: 700, color: sentimentInfo.color, whiteSpace: "nowrap" }, children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.3rem" }, children: [
|
|
4210
4286
|
sentimentInfo.arrow ? `${sentimentInfo.arrow} ` : "",
|
|
@@ -4283,7 +4359,7 @@ var PropertyNewsUpdates = React9__namespace.forwardRef(
|
|
|
4283
4359
|
}
|
|
4284
4360
|
)
|
|
4285
4361
|
] }),
|
|
4286
|
-
selectedItem && !isPurchaseVariant && /* @__PURE__ */ jsxRuntime.jsx(NewsArticleModal, { item: selectedItem, onClose: () => setSelectedItem(null) }),
|
|
4362
|
+
selectedItem && !isPurchaseVariant && /* @__PURE__ */ jsxRuntime.jsx(NewsArticleModal, { item: selectedItem, onClose: () => setSelectedItem(null), fetchStory }),
|
|
4287
4363
|
badgeTip && ReactDOM__namespace.createPortal(
|
|
4288
4364
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4289
4365
|
"span",
|
|
@@ -22147,6 +22223,7 @@ exports.badgeVariants = badgeVariants;
|
|
|
22147
22223
|
exports.buttonVariants = buttonVariants;
|
|
22148
22224
|
exports.extractSparkline = extractSparkline;
|
|
22149
22225
|
exports.formatAge = formatAge;
|
|
22226
|
+
exports.freshnessNote = freshnessNote;
|
|
22150
22227
|
exports.hasPendingActivity = hasPendingActivity;
|
|
22151
22228
|
exports.matchesTab = matchesTab;
|
|
22152
22229
|
exports.rowAgeLabel = rowAgeLabel;
|