/**
* A link's card: what a row cannot hold. The header is the row's line (icon
* and title as the link; a ring or faces in the corner), then the key and
* what it is in, its summary, the items inside it, the newest thing said in
* it, and a footer: the pickers and the conversation count, or who and when.
* Words a person wrote draw as themselves, with their face and the time.
*
* While it loads it holds the card's own height, so what is around it does
* not jump; not available is one line.
*/
import { Avatar } from './Avatar'
import { AvatarGroup } from './AvatarGroup'
import { Card } from './Card'
import { Link } from './Link'
import type { Linked, LinkedKind, LinkState, LoadingShape } from './linked'
import { CommentCount, LinkedIcon, LinkKey, LinkPickers, ProgressRing, StatusMark } from './LinkParts'
import { LinkUnavailable } from './LinkRow'
import { SkeletonBar } from './Skeleton'
export interface LinkCardProps {
link: LinkState
/** The values read as words, never pickers: the hover preview. */
readOnly?: boolean
/** No frame of its own: inside a box that draws one, as the hover preview's. */
bare?: boolean
}
export function LinkCard({ link, readOnly = false, bare = false }: LinkCardProps) {
if (link.state === 'loading') return
if (link.state === 'unavailable') return
const { linked } = link
const body = linked.kind === 'message' && linked.author ? :
if (bare) return
{/* The status is the row's icon: a list needs no kind icon on every row. */}
{child.key && {child.key}}
{/* Each opens on its own; the link covers its row. */}
{child.title}
{child.owner ? : }
))}
)}
{linked.latest && (
{/* "Latest" in words: the card follows it, so this is not necessarily what the link was sent about. */}
Latest conversation
{linked.latest.author.name}{linked.latest.at}
{linked.latest.body}
)}
{(linked.status || linked.owner) && }
{/* Faces only: names would not fit once several people wrote it. */}
{linked.authors ? (
{linked.updated && {linked.updated}}
) : (
!linked.status && !linked.owner && linked.updated && Updated {linked.updated}
)}
{linked.conversation && (
)}
>
)
}
/** Words a person wrote, linked: their face, their name, the time as the link, and the words. */
function SaidBody({ linked }: { linked: Linked }) {
const author = linked.author!
return (
<>
{author.name}
{linked.updated ?? 'Open'}
{linked.body ?? linked.title}
>
)
}
/** Each kind's usual card, for a placeholder when the app remembers nothing of it. */
const KIND_SHAPE: Record = {
issue: { subtitle: true, summary: true, footer: 'pickers' },
project: { subtitle: false, summary: true, rows: 3, ring: true, footer: 'pickers' },
topic: { summary: true, latest: true, footer: 'line' },
page: { subtitle: true, summary: true, footer: 'people' },
file: { summary: false, footer: 'line' },
message: { summary: true, footer: 'none' },
}
export interface LinkCardLoadingProps {
kind: LinkedKind
/** What the card will hold (`loadingShapeOf` from the last time it drew); the kind's usual card otherwise. */
shape?: LoadingShape
}
/** The card it will become, drawn in bars at the card's own height. */
export function LinkCardLoading({ kind, shape }: LinkCardLoadingProps) {
const { subtitle = false, summary = true, rows = 0, latest = false, ring = false, footer = 'line' } = shape ?? KIND_SHAPE[kind]
return (