import type { PooledTweet } from "@xtap-pool/shared";
import {
avatarColor,
displayName,
formatCount,
formatTweetDate,
isArticleTweet,
isRetweet,
photoMedia,
quotedTweetUrl,
tweetMetrics,
tweetTextHtml,
} from "../lib/format.js";
export type TweetCardProps = {
tweet: PooledTweet;
contributors: readonly string[];
now: Date;
};
/**
* Tweet text rendered to escaped HTML (URLs/mentions/hashtags linkified).
*/
function TweetText({ text }: { text: string }): React.JSX.Element {
return (
);
}
function TweetMedia({ tweet }: { tweet: PooledTweet }): React.JSX.Element | null {
const photos = photoMedia(tweet);
if (photos.length === 0) return null;
return (
{photos.map((photo) => (
))}
);
}
function Metrics({ tweet }: { tweet: PooledTweet }): React.JSX.Element {
const metrics = tweetMetrics(tweet);
const entries: [string, number][] = [
["replies", metrics.replies],
["reposts", metrics.retweets],
["likes", metrics.likes],
["views", metrics.views],
];
return (
{entries.map(([label, value]) => (
{formatCount(value)} {label}
))}
);
}
/** One tweet, rendered in the solmaz.io X-card style. */
export function TweetCard({ tweet, contributors, now }: TweetCardProps): React.JSX.Element {
const name = displayName(tweet);
const quoted = quotedTweetUrl(tweet);
return (
{name.slice(0, 1).toUpperCase()}
{name}
@{tweet.author.username}
·
View on X
{isRetweet(tweet) ?
reposted
: null}
{quoted === undefined ? null : (
View quoted post on X
)}
{isArticleTweet(tweet) ? article : null}
{contributors.map((contributor) => (
⛏ {contributor}
))}
);
}