a tool for shared writing and social publishing
at feature/profiles 141 lines 5.1 kB view raw
1"use client"; 2import { AtUri } from "@atproto/api"; 3import { PubIcon } from "components/ActionBar/Publications"; 4import { CommentTiny } from "components/Icons/CommentTiny"; 5import { QuoteTiny } from "components/Icons/QuoteTiny"; 6import { Separator } from "components/Layout"; 7import { usePubTheme } from "components/ThemeManager/PublicationThemeProvider"; 8import { BaseThemeProvider } from "components/ThemeManager/ThemeProvider"; 9import { useSmoker } from "components/Toast"; 10import { PubLeafletDocument, PubLeafletPublication } from "lexicons/api"; 11import { blobRefToSrc } from "src/utils/blobRefToSrc"; 12import type { Post } from "app/(home-pages)/reader/getReaderFeed"; 13 14import Link from "next/link"; 15import { InteractionPreview } from "./InteractionsPreview"; 16import { useLocalizedDate } from "src/hooks/useLocalizedDate"; 17 18export const PostListing = (props: Post) => { 19 let pubRecord = props.publication?.pubRecord as 20 | PubLeafletPublication.Record 21 | undefined; 22 23 let postRecord = props.documents.data as PubLeafletDocument.Record; 24 let postUri = new AtUri(props.documents.uri); 25 26 let theme = usePubTheme(pubRecord?.theme || postRecord?.theme); 27 let backgroundImage = 28 pubRecord?.theme?.backgroundImage?.image?.ref && props.publication 29 ? blobRefToSrc( 30 pubRecord.theme.backgroundImage.image.ref, 31 new AtUri(props.publication.uri).host, 32 ) 33 : null; 34 35 let backgroundImageRepeat = pubRecord?.theme?.backgroundImage?.repeat; 36 let backgroundImageSize = pubRecord?.theme?.backgroundImage?.width || 500; 37 38 let showPageBackground = pubRecord?.theme?.showPageBackground; 39 40 let quotes = props.documents.document_mentions_in_bsky?.[0]?.count || 0; 41 let comments = 42 pubRecord?.preferences?.showComments === false 43 ? 0 44 : props.documents.comments_on_documents?.[0]?.count || 0; 45 let tags = (postRecord?.tags as string[] | undefined) || []; 46 47 // For standalone posts, link directly to the document 48 let postHref = props.publication 49 ? `${props.publication.href}/${postUri.rkey}` 50 : `/p/${postUri.host}/${postUri.rkey}`; 51 52 return ( 53 <BaseThemeProvider {...theme} local> 54 <div 55 style={{ 56 backgroundImage: backgroundImage 57 ? `url(${backgroundImage})` 58 : undefined, 59 backgroundRepeat: backgroundImageRepeat ? "repeat" : "no-repeat", 60 backgroundSize: `${backgroundImageRepeat ? `${backgroundImageSize}px` : "cover"}`, 61 }} 62 className={`no-underline! flex flex-row gap-2 w-full relative 63 bg-bg-leaflet 64 border border-border-light rounded-lg 65 sm:p-2 p-2 selected-outline 66 hover:outline-accent-contrast hover:border-accent-contrast 67 `} 68 > 69 <Link className="h-full w-full absolute top-0 left-0" href={postHref} /> 70 <div 71 className={`${showPageBackground ? "bg-bg-page " : "bg-transparent"} rounded-md w-full px-[10px] pt-2 pb-2`} 72 style={{ 73 backgroundColor: showPageBackground 74 ? "rgba(var(--bg-page), var(--bg-page-alpha))" 75 : "transparent", 76 }} 77 > 78 <h3 className="text-primary truncate">{postRecord.title}</h3> 79 80 <p className="text-secondary italic">{postRecord.description}</p> 81 <div className="flex flex-col-reverse md:flex-row md gap-2 text-sm text-tertiary items-center justify-start pt-1.5 md:pt-3 w-full"> 82 {props.publication && pubRecord && ( 83 <PubInfo 84 href={props.publication.href} 85 pubRecord={pubRecord} 86 uri={props.publication.uri} 87 /> 88 )} 89 <div className="flex flex-row justify-between gap-2 items-center w-full"> 90 <PostInfo publishedAt={postRecord.publishedAt} /> 91 <InteractionPreview 92 postUrl={postHref} 93 quotesCount={quotes} 94 commentsCount={comments} 95 tags={tags} 96 showComments={pubRecord?.preferences?.showComments} 97 share 98 /> 99 </div> 100 </div> 101 </div> 102 </div> 103 </BaseThemeProvider> 104 ); 105}; 106 107const PubInfo = (props: { 108 href: string; 109 pubRecord: PubLeafletPublication.Record; 110 uri: string; 111}) => { 112 return ( 113 <div className="flex flex-col md:w-auto shrink-0 w-full"> 114 <hr className="md:hidden block border-border-light mb-2" /> 115 <Link 116 href={props.href} 117 className="text-accent-contrast font-bold no-underline text-sm flex gap-1 items-center md:w-fit relative shrink-0" 118 > 119 <PubIcon small record={props.pubRecord} uri={props.uri} /> 120 {props.pubRecord.name} 121 </Link> 122 </div> 123 ); 124}; 125 126const PostInfo = (props: { publishedAt: string | undefined }) => { 127 let localizedDate = useLocalizedDate(props.publishedAt || "", { 128 year: "numeric", 129 month: "short", 130 day: "numeric", 131 }); 132 return ( 133 <div className="flex gap-2 items-center shrink-0 self-start"> 134 {props.publishedAt && ( 135 <> 136 <div className="shrink-0">{localizedDate}</div> 137 </> 138 )} 139 </div> 140 ); 141};