80 lines
2.7 KiB
TypeScript
80 lines
2.7 KiB
TypeScript
"use client";
|
|
|
|
/**
|
|
* A post shared into a chat, rendered as a card with its author.
|
|
*
|
|
* The contents are resolved by the BFF on every read, per viewer — never from
|
|
* a snapshot frozen at send time. So a post that has since been deleted or
|
|
* hidden arrives as `entity_unavailable` and renders as a tombstone rather
|
|
* than quietly showing content the reader is no longer allowed to see.
|
|
*/
|
|
|
|
import Link from "next/link";
|
|
import { ImageOff } from "lucide-react";
|
|
import { UserAvatar } from "@/components/ui/user-avatar";
|
|
import type { Attachment } from "@/lib/chat";
|
|
|
|
export function SharedPostCard({ attachment }: { attachment: Attachment }) {
|
|
const post = attachment.entity;
|
|
|
|
if (!post || attachment.entity_unavailable) {
|
|
return (
|
|
<div className="flex w-full items-center gap-2 rounded-lg border border-border bg-muted/30 px-3 py-2.5 sm:max-w-64">
|
|
<ImageOff className="size-4 shrink-0 text-muted-foreground" aria-hidden />
|
|
<span className="text-sm text-muted-foreground">
|
|
This post is no longer available
|
|
</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Link
|
|
href={`/u/${encodeURIComponent(post.author_handle)}/posts#post-${post.id}`}
|
|
// Capped on wide screens. The bubble allows 78% of the column, which on
|
|
// a phone is a sensible ~265px but on desktop is over 500px — and with
|
|
// a square image that makes a shared post taller than the viewport.
|
|
// Mobile keeps the bubble's own width; only sm+ needs the ceiling.
|
|
className="block w-full overflow-hidden rounded-lg border border-border bg-card transition-colors hover:bg-muted/40 sm:max-w-64"
|
|
>
|
|
<span className="flex items-center gap-2 px-3 py-2">
|
|
<UserAvatar
|
|
src={post.author_avatar_url ?? null}
|
|
name={post.author_name}
|
|
className="size-6 shrink-0"
|
|
/>
|
|
<span className="min-w-0 flex-1">
|
|
<span className="block truncate text-xs font-medium text-foreground">
|
|
{post.author_name}
|
|
</span>
|
|
<span className="block truncate text-[11px] text-muted-foreground">
|
|
@{post.author_handle}
|
|
</span>
|
|
</span>
|
|
</span>
|
|
|
|
{post.image_url && (
|
|
// eslint-disable-next-line @next/next/no-img-element
|
|
<img
|
|
src={post.image_url}
|
|
alt=""
|
|
loading="lazy"
|
|
className="aspect-square w-full object-cover"
|
|
/>
|
|
)}
|
|
|
|
{post.caption && (
|
|
<span className="block px-3 py-2 text-sm leading-relaxed text-foreground line-clamp-3">
|
|
{post.caption}
|
|
</span>
|
|
)}
|
|
|
|
{post.source_title && (
|
|
<span className="block truncate px-3 pb-2 text-xs text-muted-foreground">
|
|
{post.source_title}
|
|
</span>
|
|
)}
|
|
</Link>
|
|
);
|
|
}
|