"use client"; /** * PostScrollCard — Instagram-style post used on the scroll feed. * * Layout: * * ┌───────────────────────────────────────┐ * │ ◯ name @handle ⋮ │ ← author header (TBD) * ├───────────────────────────────────────┤ * │ │ * │ image carousel │ ← square, swipeable * │ (or quote) │ (no-image variant * │ │ fills with gradient * │ • ○ ○ ○ │ panel + serif quote) * ├───────────────────────────────────────┤ * │ "Quoted passage shown as caption…" │ ← truncates with "more" * │ ŚRĪMAD-BHĀGAVATAM — CANTO 4, CH 5 │ ← citation * └───────────────────────────────────────┘ * * • Multi-image carousel uses native CSS scroll-snap with dot indicators * below; the same primitive CollectionScrollCard uses for posts. * • Long captions clamp to 3 lines until the user expands via "more". * • Owner action cluster (Update background / Delete) sits in the * top-right of the media area, fading in on hover (desktop) and * always visible on touch (mobile). */ import { useEffect, useRef, useState } from "react"; import Image from "next/image"; import Link from "next/link"; import { AtSign, ImageIcon, Repeat2 } from "lucide-react"; import { cn } from "@/lib/utils"; import type { Post, PostImage } from "@/lib/social"; import { displaySourceLabel } from "@/lib/source-reference"; import { useMediaQuery } from "@/components/selection-toolbar/shared"; import { UserAvatar } from "@/components/ui/user-avatar"; import { useAuth } from "@/components/auth/auth-provider"; import { postSourceHref, quoteCardBgStyle } from "./post-card-shared"; import { PostActionsFooter } from "./post-actions-footer"; import { PostCardMenu } from "./post-card-menu"; import { FeedFollowButton } from "./feed-follow-button"; import { UpdateBackgroundModal } from "../update-background-modal"; import { MentionsDrawer } from "../mentions-drawer"; export function PostScrollCard({ post, onDelete, onUpdated, enableFollow = false, }: { post: Post; onDelete?: (id: string) => void; onUpdated?: (updated: Post) => void; /** Home-feed only: show a Follow / Follow back button in the header for * authors the viewer doesn't already follow. Off everywhere else. */ enableFollow?: boolean; }) { const images = post.images ?? []; const hasImages = images.length > 0; const sourceHref = postSourceHref(post); const isMobile = useMediaQuery("(max-width: 767px)"); const { user } = useAuth(); const isAuthor = !!user && user.id === post.user_id; const [bgModal, setBgModal] = useState(false); const [expanded, setExpanded] = useState(false); const [mentionsOpen, setMentionsOpen] = useState(false); // Live mention count — seeded from the hydrated post, adjusted when the // author edits tags in the drawer, so the icon appears/disappears in place. const [mentionCount, setMentionCount] = useState(post.mentions_count ?? 0); // The author always gets the mentions entry point (to add/remove); everyone // else sees it only when the post actually tags someone. const showMentions = mentionCount > 0 || isAuthor; // Top-right media overlay: a Mentions icon and the owner-only // Update-background button. Delete lives in the header menu. const topRight = showMentions || onUpdated ? (
“{post.caption}”
. Once we've observed
// overflow we latch it on so "Show less" stays visible after expansion
// (where scrollHeight would equal clientHeight and we'd otherwise lose
// the way back).
const pRef = useRef
“{text}”