a tool for shared writing and social publishing
at feature/thread-viewer 132 lines 4.8 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"; 16 17export const PostListing = (props: Post) => { 18 let pubRecord = props.publication.pubRecord as PubLeafletPublication.Record; 19 20 let postRecord = props.documents.data as PubLeafletDocument.Record; 21 let postUri = new AtUri(props.documents.uri); 22 23 let theme = usePubTheme(pubRecord.theme); 24 let backgroundImage = pubRecord?.theme?.backgroundImage?.image?.ref 25 ? blobRefToSrc( 26 pubRecord?.theme?.backgroundImage?.image?.ref, 27 new AtUri(props.publication.uri).host, 28 ) 29 : null; 30 31 let backgroundImageRepeat = pubRecord?.theme?.backgroundImage?.repeat; 32 let backgroundImageSize = pubRecord?.theme?.backgroundImage?.width || 500; 33 34 let showPageBackground = pubRecord.theme?.showPageBackground; 35 36 let quotes = props.documents.document_mentions_in_bsky?.[0]?.count || 0; 37 let comments = 38 pubRecord.preferences?.showComments === false 39 ? 0 40 : props.documents.comments_on_documents?.[0]?.count || 0; 41 let tags = (postRecord?.tags as string[] | undefined) || []; 42 43 return ( 44 <BaseThemeProvider {...theme} local> 45 <div 46 style={{ 47 backgroundImage: `url(${backgroundImage})`, 48 backgroundRepeat: backgroundImageRepeat ? "repeat" : "no-repeat", 49 backgroundSize: `${backgroundImageRepeat ? `${backgroundImageSize}px` : "cover"}`, 50 }} 51 className={`no-underline! flex flex-row gap-2 w-full relative 52 bg-bg-leaflet 53 border border-border-light rounded-lg 54 sm:p-2 p-2 selected-outline 55 hover:outline-accent-contrast hover:border-accent-contrast 56 `} 57 > 58 <Link 59 className="h-full w-full absolute top-0 left-0" 60 href={`${props.publication.href}/${postUri.rkey}`} 61 /> 62 <div 63 className={`${showPageBackground ? "bg-bg-page " : "bg-transparent"} rounded-md w-full px-[10px] pt-2 pb-2`} 64 style={{ 65 backgroundColor: showPageBackground 66 ? "rgba(var(--bg-page), var(--bg-page-alpha))" 67 : "transparent", 68 }} 69 > 70 <h3 className="text-primary truncate">{postRecord.title}</h3> 71 72 <p className="text-secondary italic">{postRecord.description}</p> 73 <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"> 74 <PubInfo 75 href={props.publication.href} 76 pubRecord={pubRecord} 77 uri={props.publication.uri} 78 /> 79 <div className="flex flex-row justify-between gap-2 items-center w-full"> 80 <PostInfo publishedAt={postRecord.publishedAt} /> 81 <InteractionPreview 82 postUrl={`${props.publication.href}/${postUri.rkey}`} 83 quotesCount={quotes} 84 commentsCount={comments} 85 tags={tags} 86 showComments={pubRecord.preferences?.showComments} 87 share 88 /> 89 </div> 90 </div> 91 </div> 92 </div> 93 </BaseThemeProvider> 94 ); 95}; 96 97const PubInfo = (props: { 98 href: string; 99 pubRecord: PubLeafletPublication.Record; 100 uri: string; 101}) => { 102 return ( 103 <div className="flex flex-col md:w-auto shrink-0 w-full"> 104 <hr className="md:hidden block border-border-light mb-2" /> 105 <Link 106 href={props.href} 107 className="text-accent-contrast font-bold no-underline text-sm flex gap-1 items-center md:w-fit relative shrink-0" 108 > 109 <PubIcon small record={props.pubRecord} uri={props.uri} /> 110 {props.pubRecord.name} 111 </Link> 112 </div> 113 ); 114}; 115 116const PostInfo = (props: { publishedAt: string | undefined }) => { 117 return ( 118 <div className="flex gap-2 items-center shrink-0 self-start"> 119 {props.publishedAt && ( 120 <> 121 <div className="shrink-0"> 122 {new Date(props.publishedAt).toLocaleDateString("en-US", { 123 year: "numeric", 124 month: "short", 125 day: "numeric", 126 })} 127 </div> 128 </> 129 )} 130 </div> 131 ); 132};