a tool for shared writing and social publishing
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};