terry-staging #16

Merged
terry merged 96 commits from terry-staging into main 2026-06-05 16:33:12 +00:00
Showing only changes of commit 724bfb8f24 - Show all commits

View File

@@ -147,12 +147,49 @@ function FileCard({ post, att }: { post: Post; att: Attachment }) {
);
}
function MediaGrid({ attachments }: { attachments: Attachment[] }) {
const visible = attachments.slice(0, 4);
const extra = Math.max(0, attachments.length - visible.length);
if (visible.length <= 1) {
const src = attachmentPreview(visible[0]);
return src ? (
function PillDownloadIcon() {
return (
<svg
width="12"
height="10"
viewBox="0 0 12 10"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M9.84528 3.61663C9.68928 1.5958 8.02426 0 6.00008 0C4.26863 0 2.78232 1.14503 2.30275 2.81502C0.934727 3.29852 0 4.6181 0 6.10918C0 8.034 1.53816 9.60013 3.42862 9.60013H9.00013C10.6544 9.60013 12.0002 8.22993 12.0002 6.54555C12.0002 5.17142 11.113 3.99191 9.84528 3.61663ZM8.0174 5.98132L6.30309 7.7268C6.21952 7.81189 6.1098 7.85465 6.00008 7.85465C5.89037 7.85465 5.78065 7.81189 5.69708 7.7268L3.98277 5.98132C3.8602 5.85652 3.82334 5.66888 3.88977 5.50568C3.9562 5.34291 4.11263 5.23644 4.28577 5.23644H5.14293V3.49096C5.14293 3.00921 5.52693 2.61822 6.00008 2.61822C6.47323 2.61822 6.85724 3.00921 6.85724 3.49096V5.23644H7.71439C7.88754 5.23644 8.04397 5.34291 8.1104 5.50568C8.17683 5.66888 8.13997 5.85652 8.0174 5.98132Z"
fill="#A8A9AE"
/>
</svg>
);
}
function MediaSizeChip({ att }: { att: Attachment }) {
return (
<div className="absolute left-3 top-2.5 z-20 flex h-6 w-[72px] items-center overflow-hidden rounded-full bg-black text-white">
<span className="grid h-6 w-6 shrink-0 place-items-center bg-[#545454]">
<PillDownloadIcon />
</span>
<span className="flex h-4 w-12 items-center justify-center text-[10px] font-medium leading-4">
{formatBytes(att.sizeBytes)}
</span>
</div>
);
}
function MediaTile({
att,
showExtra,
}: {
att: Attachment;
showExtra?: number;
}) {
const src = attachmentPreview(att);
const isVideo = att.kind === "video" || att.mime.startsWith("video/");
return (
<div className="relative h-full w-full overflow-hidden bg-black">
{src ? (
<img
src={src}
alt=""
@@ -160,44 +197,62 @@ function MediaGrid({ attachments }: { attachments: Attachment[] }) {
loading="lazy"
decoding="async"
/>
) : (
<div className="h-full w-full bg-black" />
) : null}
<MediaSizeChip att={att} />
{isVideo ? (
<div className="absolute inset-0 grid place-items-center">
<span className="grid h-16 w-16 place-items-center rounded-full bg-black/55 text-white backdrop-blur-sm">
<Play className="ml-1 h-8 w-8 fill-current" />
</span>
</div>
) : null}
{showExtra ? (
<div className="absolute inset-0 grid place-items-center bg-black/55 text-[24px] font-bold text-white backdrop-blur-sm">
+{showExtra}
</div>
) : null}
</div>
);
}
function MediaGrid({ attachments }: { attachments: Attachment[] }) {
const visible = attachments.slice(0, 4);
const extra = Math.max(0, attachments.length - visible.length);
if (visible.length === 0) return <div className="h-full w-full bg-black" />;
if (visible.length === 1) return <MediaTile att={visible[0]} />;
if (visible.length === 2) {
return (
<div className="grid h-full w-full grid-cols-2 gap-0">
{visible.map((att) => (
<MediaTile key={att.id} att={att} />
))}
</div>
);
}
if (visible.length === 3) {
return (
<div className="grid h-full w-full grid-cols-2 gap-0">
<MediaTile att={visible[0]} />
<div className="grid h-full grid-rows-2 gap-0">
<MediaTile att={visible[1]} />
<MediaTile att={visible[2]} />
</div>
</div>
);
}
return (
<div className="grid h-full w-full grid-cols-2 grid-rows-2 gap-0">
{visible.map((att, index) => (
<div key={att.id} className="relative overflow-hidden bg-black">
<img
src={attachmentPreview(att)}
alt=""
className="h-full w-full object-cover"
loading="lazy"
decoding="async"
<MediaTile
key={att.id}
att={att}
showExtra={extra > 0 && index === visible.length - 1 ? extra : 0}
/>
{extra > 0 && index === visible.length - 1 ? (
<div className="absolute inset-0 grid place-items-center bg-black/55 text-[24px] font-bold text-white backdrop-blur-sm">
+{extra}
</div>
) : null}
</div>
))}
</div>
);
}
function MediaBadge({ att }: { att?: Attachment }) {
if (!att) return null;
return (
<div className="absolute left-3 top-3 z-20 inline-flex h-8 items-center overflow-hidden rounded-full bg-black/75 text-[12px] font-medium text-white shadow-lg backdrop-blur">
<span className="grid h-8 w-8 place-items-center rounded-full bg-[#3a3a40]">
<DownloadCloudIcon />
</span>
<span className="px-3">{formatBytes(att.sizeBytes)}</span>
</div>
);
}
function VisualCard({ post }: { post: Post }) {
const { lang } = useI18n();
const att = post.attachments[0];
@@ -213,21 +268,13 @@ function VisualCard({ post }: { post: Post }) {
}`}
>
<MediaGrid attachments={post.attachments} />
<MediaBadge att={att} />
{isVideo ? (
<div className="absolute inset-0 grid place-items-center">
<span className="grid h-14 w-14 place-items-center rounded-full bg-black/55 text-white backdrop-blur-sm">
<Play className="ml-1 h-7 w-7 fill-current" />
</span>
</div>
) : null}
</div>
{text || post.title ? (
<div className="message-stream-copyable-text whitespace-pre-wrap break-words px-4 pt-3 text-[15px] font-medium leading-6 text-white">
{autolink(text || post.title || "")}
</div>
) : null}
<Footer post={post} attachment={att} />
<Footer post={post} />
</article>
);
}