@activecollab/components
Version:
ActiveCollab Components
89 lines • 3.72 kB
TypeScript
/**
* Mock unfurl service for the LinkCard concept.
*
* A link preview is a LIFECYCLE, not two static layouts. The real service is
* server-side (SSRF-guarded, oEmbed → OpenGraph → Twitter Cards → <title>,
* cached ~24h with a stored `fetchedAt` and a manual re-unfurl affordance — see
* note 52104 §B3). This is a promise-based fake with a configurable delay and a
* failure toggle so the story can drive the whole lifecycle without a network.
*
* The data arrives on a PROGRESSIVE LADDER of three tiers, each carrying more
* than the last:
* url → just the raw URL (nothing fetched yet)
* basic → favicon + title + domain
* full → + description, hero image, media kind / duration
* The card reserves the next tier's space at every tier, so climbing the ladder
* never reflows the card.
*/
export type UnfurlTier = "url" | "basic" | "full";
export type UnfurlStatus = "resolving" | "resolved" | "stale" | "error";
/** The cumulative data shape. Fields fill in as the ladder climbs. */
export interface UnfurlData {
/** Always present — the raw pasted URL. */
url: string;
/** Tier `basic` and up: a stand-in favicon colour. */
faviconColor?: string;
/** Tier `basic` and up: page title. */
title?: string;
/** Tier `basic` and up: the display domain. */
domain?: string;
/** Tier `full`: short description / summary. */
description?: string;
/** Tier `full`: hero image src (a real <img> in production). */
heroSrc?: string;
/** Tier `full`: hero fill colour, used when there is no hero image. */
heroColor?: string;
/** Tier `full`: media kind chip (Video, Article, …). */
kind?: string;
/** Tier `full`: duration chip for playable media. */
duration?: string;
/** Tier `full`: whether the media is playable (shows the cover play badge). */
playable?: boolean;
}
/** A seed link — what the fake "server" would resolve the URL to at tier full. */
export interface MockLink {
url: string;
faviconColor: string;
title: string;
domain: string;
description: string;
heroColor: string;
heroSrc?: string;
kind: string;
duration?: string;
playable?: boolean;
}
export declare const MOCK_LINKS: Record<string, MockLink>;
export interface UnfurlConfig {
/** Which seed link to resolve. */
link: MockLink;
/** Per-tier delay in ms (url→basic and basic→full each wait this long). */
delayMs?: number;
/** When true, the unfurl fails partway and lands in the error state. */
shouldFail?: boolean;
}
export interface UnfurlController {
status: UnfurlStatus;
tier: UnfurlTier;
data: UnfurlData;
/** Epoch ms of the last successful resolve (drives the stale affordance). */
fetchedAt: number | null;
/** Start (or restart) the unfurl ladder from scratch. */
start: () => void;
/** Retry after an error — re-runs the simulation. */
retry: () => void;
/** Re-unfurl a resolved/stale card — re-runs the simulation. */
reunfurl: () => void;
/** Force the resolved card into the stale state (demo affordance). */
markStale: () => void;
}
/**
* Drives one link through the unfurl lifecycle. Climbs url → basic → full on a
* timer, or lands in `error` if `shouldFail`. `retry` / `reunfurl` re-run it;
* `markStale` moves a resolved card to the stale state so the re-unfurl
* affordance can be shown.
*/
export declare const useUnfurl: ({ link, delayMs, shouldFail, }: UnfurlConfig) => UnfurlController;
/** Human-readable "fetched N ago" for the stale affordance. */
export declare const formatFetchedAt: (fetchedAt: number | null) => string;
//# sourceMappingURL=unfurl.d.ts.map