From 11e4ec561bff95077e5d9fca9413f2e024f6e68a Mon Sep 17 00:00:00 2001 From: Cesar Alcazar Date: Mon, 19 Oct 2026 22:13:28 -0600 Subject: [PATCH] 8zbhzr21j: Restyle post cards, comments and the own-post menu to the redesign spec --- src/app/layout.tsx | 1 + src/components/Buttons.tsx | 2 +- src/components/CommentsPanel.tsx | 29 ++-- src/components/PostCard.tsx | 150 +++++++++++++++---- src/dev/FeedPreview.tsx | 23 ++- src/dev/fixtures.ts | 15 +- src/lib/feed-model.ts | 15 +- src/styles/legacy.css | 38 ----- src/styles/post.css | 243 +++++++++++++++++++++++++++++++ 9 files changed, 432 insertions(+), 84 deletions(-) create mode 100644 src/styles/post.css diff --git a/src/app/layout.tsx b/src/app/layout.tsx index d0c2484..49c42a8 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -8,6 +8,7 @@ import "../styles/layout.css"; import "../styles/buttons.css"; import "../styles/avatar.css"; import "../styles/signin.css"; +import "../styles/post.css"; import "../styles/legacy.css"; const manrope = Manrope({ diff --git a/src/components/Buttons.tsx b/src/components/Buttons.tsx index 8ce2346..41ac174 100644 --- a/src/components/Buttons.tsx +++ b/src/components/Buttons.tsx @@ -227,8 +227,8 @@ export interface IconButtonProps extends NativeButton { export function IconButton({ label, icon, shape = "square", size = "md", className, type = "button", ...rest }: IconButtonProps) { return ( : null} + {p.onRetry ? : null}

) : null}
{ e.preventDefault(); if (!empty && !p.busy) p.onSubmit?.(); @@ -54,7 +59,7 @@ export function CommentsPanel(p: CommentsPanelProps) { maxLength={5000} onChange={(e) => p.onChange?.(e.target.value)} /> - + } /> ); diff --git a/src/components/PostCard.tsx b/src/components/PostCard.tsx index d49d3a4..64189e8 100644 --- a/src/components/PostCard.tsx +++ b/src/components/PostCard.tsx @@ -1,10 +1,11 @@ "use client"; -import { useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { HexAvatar } from "@/components/HexAvatar"; import { CommentsSection } from "@/components/CommentsSection"; import { VideoPlayer } from "@/components/VideoPlayer"; -import { relativeTime, type CardPost } from "@/lib/feed-model"; +import { IconButton, SweepButton } from "@/components/Buttons"; +import { formatDuration, relativeTime, type CardMedia, type CardPost } from "@/lib/feed-model"; export interface PostCardProps { post: CardPost; @@ -20,9 +21,19 @@ export interface PostCardProps { deleteError?: boolean; } +const icon = { + viewBox: "0 0 24 24", + fill: "none", + stroke: "currentColor", + strokeWidth: 2, + strokeLinecap: "round" as const, + strokeLinejoin: "round" as const, + "aria-hidden": true, +}; + function Heart({ filled }: { filled: boolean }) { return ( -
+ + {started ? null : ( + <> + + {duration ? {duration} : null} + + )} +
+ ); +} + export function PostCard({ post, now, onToggleLike, likeBusy, likeError, onSignedOut, canDelete, onDelete, deleteBusy, deleteError }: PostCardProps) { const [open, setOpen] = useState(false); const [menu, setMenu] = useState(false); const [confirming, setConfirming] = useState(false); + const [beat, setBeat] = useState(false); + const menuBox = useRef(null); + const own = !!(canDelete && onDelete); + + useEffect(() => { + if (!menu) return; + const close = () => { setMenu(false); setConfirming(false); }; + const onDown = (e: MouseEvent) => { if (!menuBox.current?.contains(e.target as Node)) close(); }; + const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") close(); }; + document.addEventListener("mousedown", onDown); + document.addEventListener("keydown", onKey); + return () => { + document.removeEventListener("mousedown", onDown); + document.removeEventListener("keydown", onKey); + }; + }, [menu]); + return (
- {post.authorName} + + {post.authorName} + {own ? You : null} +
- {canDelete && onDelete ? ( -
+ {own ? ( +
{menu ? ( -
+
{confirming ? ( - <> - Delete this post? - - - +
+

Delete this post?

+

This cannot be undone.

+
+ { setConfirming(false); setMenu(false); }} + /> + } + size="sm" + disabled={deleteBusy} + onClick={() => { setMenu(false); setConfirming(false); onDelete?.(post); }} + /> +
+
) : ( - )} @@ -78,13 +169,11 @@ export function PostCard({ post, now, onToggleLike, likeBusy, likeError, onSigne
) : null}
- {deleteError ?

Could not delete the post.

: null} + {deleteError ?

Could not delete the post.

: null} {post.text ?

{post.text}

: null} {post.media.map((m, i) => m.kind === "video" && (m.mp4Url || m.hlsUrl) ? ( -
- -
+ ) : m.src ? (
{/* eslint-disable-next-line @next/next/no-img-element */} @@ -96,28 +185,29 @@ export function PostCard({ post, now, onToggleLike, likeBusy, likeError, onSigne
) : null, )} -