"use client"; import { AtUri } from "@atproto/api"; import { PubIcon } from "components/ActionBar/Publications"; import { usePubTheme } from "components/ThemeManager/PublicationThemeProvider"; import { BaseThemeProvider } from "components/ThemeManager/ThemeProvider"; import { blobRefToSrc } from "src/utils/blobRefToSrc"; import type { NormalizedDocument, NormalizedPublication, } from "src/utils/normalizeRecords"; import { hasLeafletContent } from "lexicons/src/normalize"; import type { Post } from "app/(home-pages)/reader/getReaderFeed"; import Link from "next/link"; import { useEffect, useRef, useState } from "react"; import { TagPopover } from "./InteractionsPreview"; import { useLocalizedDate } from "src/hooks/useLocalizedDate"; import { useSmoker } from "./Toast"; import { CommentTiny } from "./Icons/CommentTiny"; import { QuoteTiny } from "./Icons/QuoteTiny"; import { ShareTiny } from "./Icons/ShareTiny"; import { useSelectedPostListing } from "src/useSelectedPostState"; import { mergePreferences } from "src/utils/mergePreferences"; import { ExternalLinkTiny } from "./Icons/ExternalLinkTiny"; import { getDocumentURL } from "app/lish/createPub/getPublicationURL"; import { RecommendButton } from "./RecommendButton"; import { getFirstParagraph } from "src/utils/getFirstParagraph"; export const PostListing = (props: Post & { selected?: boolean }) => { let pubRecord = props.publication?.pubRecord as | NormalizedPublication | undefined; let postRecord = props.documents.data as NormalizedDocument | null; // Don't render anything for records that can't be normalized (e.g., site.standard records without expected fields) if (!postRecord) { return null; } let postUri = new AtUri(props.documents.uri); let uri = props.publication ? props.publication?.uri : props.documents.uri; // For standalone documents (no publication), pass isStandalone to get correct defaults let isStandalone = !pubRecord; let theme = usePubTheme(pubRecord?.theme || postRecord?.theme, isStandalone); let themeRecord = pubRecord?.theme || postRecord?.theme; let elRef = useRef(null); let [hasBackgroundImage, setHasBackgroundImage] = useState(false); useEffect(() => { if (!themeRecord?.backgroundImage?.image || !elRef.current) { setHasBackgroundImage(false); return; } let alpha = Number( window .getComputedStyle(elRef.current) .getPropertyValue("--bg-page-alpha"), ); setHasBackgroundImage(alpha < 0.7); }, [themeRecord?.backgroundImage?.image]); let backgroundImage = themeRecord?.backgroundImage?.image?.ref && uri ? blobRefToSrc(themeRecord.backgroundImage.image.ref, new AtUri(uri).host) : null; let backgroundImageRepeat = themeRecord?.backgroundImage?.repeat; let backgroundImageSize = themeRecord?.backgroundImage?.width || 500; let showPageBackground = pubRecord ? pubRecord?.theme?.showPageBackground : postRecord.theme?.showPageBackground ?? true; let mergedPrefs = mergePreferences( postRecord?.preferences, pubRecord?.preferences, ); let quotes = props.documents.mentionsCount ?? props.documents.document_mentions_in_bsky?.[0]?.count ?? 0; let comments = mergedPrefs.showComments === false ? 0 : props.documents.comments_on_documents?.[0]?.count || 0; let recommends = props.documents.recommends_on_documents?.[0]?.count || 0; let tags = (postRecord?.tags as string[] | undefined) || []; // For standalone posts, link directly to the document let postUrl = getDocumentURL(postRecord, props.documents.uri, pubRecord); return (
{postRecord.coverImage && (
{postRecord.title
)}
{postRecord.title && (

{postRecord.title}

)}

{postRecord.description || getFirstParagraph(postRecord)}

{props.publication && pubRecord && ( )}
{tags.length === 0 ? null : }
); }; const PubInfo = (props: { href: string; pubRecord: NormalizedPublication; uri: string; postRecord: NormalizedDocument; }) => { let isLeaflet = hasLeafletContent(props.postRecord); let cleanUrl = props.pubRecord.url ?.replace(/^https?:\/\//, "") .replace(/^www\./, ""); return (

{props.pubRecord.name}
{!isLeaflet && (
{cleanUrl}
)}
); }; const PostDate = (props: { publishedAt: string | undefined }) => { let localizedDate = useLocalizedDate(props.publishedAt || "", { year: "numeric", month: "short", day: "numeric", }); if (props.publishedAt) { return
{localizedDate}
; } else return null; }; const Interactions = (props: { quotesCount: number; commentsCount: number; recommendsCount: number; tags?: string[]; postUrl: string; showComments: boolean; showMentions: boolean; documentUri: string; document: NormalizedDocument; publication?: NormalizedPublication; }) => { let setSelectedPostListing = useSelectedPostListing( (s) => s.setSelectedPostListing, ); let selectPostListing = (drawer: "quotes" | "comments") => { setSelectedPostListing({ document_uri: props.documentUri, document: props.document, publication: props.publication, drawer, }); }; return (
{!props.showMentions || props.quotesCount === 0 ? null : ( )} {!props.showComments || props.commentsCount === 0 ? null : ( )}
); }; const Share = (props: { postUrl: string }) => { let smoker = useSmoker(); return ( ); };