import React, { useState } from "react";
import { formatDistanceToNow } from "date-fns";
import { clsx } from "clsx";
import {
MessageSquare,
Heart,
Repeat2,
ExternalLink,
Play,
} from "lucide-react";
import RichText from "./RichText";
import { Avatar } from "../ui";
import { segmentByFacets } from "../../lib/socialPost";
import type { SocialPost, SocialPostFacet } from "../../lib/socialPost";
import { displayHandle } from "../../lib/handle";
interface SocialPostEmbedProps {
post: SocialPost;
postUrl: string;
host: string;
className?: string;
onOpen: (e: React.MouseEvent, url: string) => void;
}
function shortTimestamp(createdAt?: string): string {
if (!createdAt) return "";
try {
return formatDistanceToNow(new Date(createdAt), { addSuffix: false })
.replace("less than a minute", "now")
.replace("about ", "")
.replace(/ hours?/, "h")
.replace(/ minutes?/, "m")
.replace(/ months?/, "mo")
.replace(/ days?/, "d")
.replace(/ years?/, "y");
} catch {
return "";
}
}
function formatCount(count?: number): string | null {
if (!count) return null;
if (count >= 10000) return `${Math.round(count / 1000)}K`;
if (count >= 1000) return `${(count / 1000).toFixed(1).replace(/\.0$/, "")}K`;
return String(count);
}
const FACET_LINK_CLASS =
"text-primary-600 dark:text-primary-400 hover:underline";
function PostText({
text,
facets,
host,
onOpen,
}: {
text: string;
facets?: SocialPostFacet[];
host: string;
onOpen: (e: React.MouseEvent, url: string) => void;
}) {
if (!facets?.length) return ;
return (
<>
{segmentByFacets(text, facets).map((segment, i) => {
const facet = segment.facet;
if (facet?.link) {
const link = facet.link;
return (
onOpen(e, link)}
>
{segment.text}
);
}
if (facet?.mention) {
return (
e.stopPropagation()}
>
{segment.text}
);
}
if (facet?.tag) {
const tagUrl = `https://${host}/hashtag/${encodeURIComponent(facet.tag)}`;
return (
onOpen(e, tagUrl)}
>
{segment.text}
);
}
return {segment.text};
})}
>
);
}
export default function SocialPostEmbed({
post,
postUrl,
host,
className,
onOpen,
}: SocialPostEmbedProps) {
const [videoThumbError, setVideoThumbError] = useState(false);
const timestamp = shortTimestamp(post.createdAt);
const counts = [
{ icon: MessageSquare, value: formatCount(post.replyCount) },
{ icon: Repeat2, value: formatCount(post.repostCount) },
{ icon: Heart, value: formatCount(post.likeCount) },
].filter((c) => c.value);
return (
{
e.preventDefault();
onOpen(e, postUrl);
}}
role="button"
tabIndex={0}
className={clsx(
"block bg-surface-50 dark:bg-surface-800 rounded-xl border border-surface-200 dark:border-surface-700 hover:border-surface-300 dark:hover:border-surface-600 transition-colors group overflow-hidden cursor-pointer p-3 font-sans",
className,
)}
>
{post.author.displayName ||
displayHandle(post.author.handle, post.author.did)}
@{displayHandle(post.author.handle, post.author.did)}
{timestamp && (
ยท {timestamp}
)}
{host}
{post.text && (
)}
{post.images.length > 0 && (
{post.images.slice(0, 4).map((img, i) => (

))}
)}
{post.video && (
{post.video.thumbnail && !videoThumbError ? (

setVideoThumbError(true)}
/>
) : (
)}
)}
{post.gif && (
{post.gif.isVideo ? (
) : (

)}
)}
{post.external && (
{post.external.thumb && (

)}
{post.external.title || post.external.uri}
{(() => {
try {
return new URL(post.external.uri).hostname.replace(
/^www\./,
"",
);
} catch {
return post.external.uri;
}
})()}
)}
{post.quote && (
{post.quote.author.displayName ||
displayHandle(post.quote.author.handle, post.quote.author.did)}
@{displayHandle(post.quote.author.handle, post.quote.author.did)}
{post.quote.text && (
)}
)}
{counts.length > 0 && (
{counts.map(({ icon: Icon, value }, i) => (
{value}
))}
)}
);
}