a tool for shared writing and social publishing
0

Configure Feed

Select the types of activity you want to include in your feed.

fixed missing handles, added some fake posts to pub preview if the user doesn't have any

celine (Apr 9, 2026, 10:08 PM EDT) 8a92feea 7ab56b9c

+162 -85
+92 -74
app/lish/[did]/[publication]/PublicationContent.tsx
··· 20 20 PublicationPostItem, 21 21 } from "./PublicationPageContent"; 22 22 23 + type FakePost = { 24 + title: string; 25 + description: string; 26 + date: React.ReactNode; 27 + }; 28 + 23 29 export const PublicationContent = ({ 24 30 record, 25 31 publication, 26 32 did, 27 33 profile, 28 34 showPageBackground, 35 + fakePosts, 29 36 }: { 30 37 record: ReturnType<typeof normalizePublicationRecord>; 31 38 publication: { ··· 47 54 did: string; 48 55 profile: { did: string; displayName?: string; handle: string } | undefined; 49 56 showPageBackground: boolean | undefined; 57 + fakePosts?: FakePost[]; 50 58 }) => { 51 59 return ( 52 60 <> ··· 85 93 } 86 94 /> 87 95 <div className="publicationPostList w-full flex flex-col gap-4"> 88 - {publication.documents_in_publications 89 - .filter((d) => !!d?.documents) 90 - .sort((a, b) => { 91 - const aRecord = normalizeDocumentRecord(a.documents?.data); 92 - const bRecord = normalizeDocumentRecord(b.documents?.data); 93 - const aDate = aRecord?.publishedAt 94 - ? new Date(aRecord.publishedAt) 95 - : new Date(0); 96 - const bDate = bRecord?.publishedAt 97 - ? new Date(bRecord.publishedAt) 98 - : new Date(0); 99 - return bDate.getTime() - aDate.getTime(); // Sort by most recent first 100 - }) 101 - .map((doc) => { 102 - if (!doc.documents) return null; 103 - const doc_record = normalizeDocumentRecord(doc.documents.data); 104 - if (!doc_record) return null; 105 - let uri = new AtUri(doc.documents.uri); 106 - let quotes = 107 - doc.documents.document_mentions_in_bsky[0].count || 0; 108 - let comments = 109 - record?.preferences?.showComments === false 110 - ? 0 111 - : doc.documents.comments_on_documents[0].count || 0; 112 - let recommends = 113 - doc.documents.recommends_on_documents?.[0]?.count || 0; 114 - let tags = doc_record.tags || []; 96 + {fakePosts && 97 + fakePosts.map((post, i) => ( 98 + <PublicationPostItem 99 + key={i} 100 + title={post.title} 101 + description={post.description} 102 + date={post.date} 103 + /> 104 + ))} 105 + {!fakePosts && 106 + publication.documents_in_publications 107 + .filter((d) => !!d?.documents) 108 + .sort((a, b) => { 109 + const aRecord = normalizeDocumentRecord(a.documents?.data); 110 + const bRecord = normalizeDocumentRecord(b.documents?.data); 111 + const aDate = aRecord?.publishedAt 112 + ? new Date(aRecord.publishedAt) 113 + : new Date(0); 114 + const bDate = bRecord?.publishedAt 115 + ? new Date(bRecord.publishedAt) 116 + : new Date(0); 117 + return bDate.getTime() - aDate.getTime(); // Sort by most recent first 118 + }) 119 + .map((doc) => { 120 + if (!doc.documents) return null; 121 + const doc_record = normalizeDocumentRecord(doc.documents.data); 122 + if (!doc_record) return null; 123 + let uri = new AtUri(doc.documents.uri); 124 + let quotes = 125 + doc.documents.document_mentions_in_bsky[0].count || 0; 126 + let comments = 127 + record?.preferences?.showComments === false 128 + ? 0 129 + : doc.documents.comments_on_documents[0].count || 0; 130 + let recommends = 131 + doc.documents.recommends_on_documents?.[0]?.count || 0; 132 + let tags = doc_record.tags || []; 115 133 116 - const docUrl = getDocumentURL( 117 - doc_record, 118 - doc.documents.uri, 119 - publication, 120 - ); 121 - return ( 122 - <React.Fragment key={doc.documents?.uri}> 123 - <PublicationPostItem 124 - href={docUrl} 125 - title={doc_record.title} 126 - description={ 127 - doc_record.description || getFirstParagraph(doc_record) 128 - } 129 - date={ 130 - doc_record.publishedAt ? ( 131 - <LocalizedDate 132 - dateString={doc_record.publishedAt} 133 - options={{ 134 - year: "numeric", 135 - month: "long", 136 - day: "2-digit", 137 - }} 134 + const docUrl = getDocumentURL( 135 + doc_record, 136 + doc.documents.uri, 137 + publication, 138 + ); 139 + return ( 140 + <React.Fragment key={doc.documents?.uri}> 141 + <PublicationPostItem 142 + href={docUrl} 143 + title={doc_record.title} 144 + description={ 145 + doc_record.description || getFirstParagraph(doc_record) 146 + } 147 + date={ 148 + doc_record.publishedAt ? ( 149 + <LocalizedDate 150 + dateString={doc_record.publishedAt} 151 + options={{ 152 + year: "numeric", 153 + month: "long", 154 + day: "2-digit", 155 + }} 156 + /> 157 + ) : undefined 158 + } 159 + interactions={ 160 + <InteractionPreview 161 + quotesCount={quotes} 162 + commentsCount={comments} 163 + recommendsCount={recommends} 164 + documentUri={doc.documents.uri} 165 + tags={tags} 166 + postUrl={docUrl} 167 + showComments={ 168 + record?.preferences?.showComments !== false 169 + } 170 + showMentions={ 171 + record?.preferences?.showMentions !== false 172 + } 173 + showRecommends={ 174 + record?.preferences?.showRecommends !== false 175 + } 138 176 /> 139 - ) : undefined 140 - } 141 - interactions={ 142 - <InteractionPreview 143 - quotesCount={quotes} 144 - commentsCount={comments} 145 - recommendsCount={recommends} 146 - documentUri={doc.documents.uri} 147 - tags={tags} 148 - postUrl={docUrl} 149 - showComments={ 150 - record?.preferences?.showComments !== false 151 - } 152 - showMentions={ 153 - record?.preferences?.showMentions !== false 154 - } 155 - showRecommends={ 156 - record?.preferences?.showRecommends !== false 157 - } 158 - /> 159 - } 160 - /> 161 - </React.Fragment> 162 - ); 163 - })} 177 + } 178 + /> 179 + </React.Fragment> 180 + ); 181 + })} 164 182 </div> 165 183 </PublicationHomeLayout> 166 184 </>
+5 -6
app/lish/[did]/[publication]/dashboard/settings/SettingsContent.tsx
··· 164 164 /> 165 165 166 166 {cardBorderHidden && <hr className="border-border-light my-2" />} 167 + <DashboardContainer> 168 + <ThemeSettings /> 169 + </DashboardContainer> 170 + 171 + {cardBorderHidden && <hr className="border-border-light my-2" />} 167 172 168 173 {/* ── Post Settings ── */} 169 174 <PostSettings ··· 178 183 showInDiscover={showInDiscover} 179 184 setShowInDiscover={setShowInDiscover} 180 185 /> 181 - 182 - {cardBorderHidden && <hr className="border-border-light my-2" />} 183 - 184 - <DashboardContainer> 185 - <ThemeSettings /> 186 - </DashboardContainer> 187 186 188 187 {cardBorderHidden && <hr className="border-border-light my-2" />} 189 188
+59 -4
app/lish/[did]/[publication]/theme-settings/PubPreview.tsx
··· 7 7 import { useIdentityData } from "components/IdentityProvider"; 8 8 import { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs"; 9 9 import { PublicationContent } from "../PublicationContent"; 10 + import { LocalizedDate } from "../LocalizedDate"; 10 11 11 12 export function PubPreview(props: { 12 13 showPageBackground: boolean; ··· 22 23 23 24 let did = publication?.identity_did || ""; 24 25 25 - let profile = profileRecord 26 + let profile = identity?.bsky_profiles 26 27 ? { 27 - did: profileRecord.did, 28 - displayName: profileRecord.displayName, 29 - handle: profileRecord.handle, 28 + did: identity.bsky_profiles.did, 29 + displayName: profileRecord?.displayName, 30 + handle: identity.bsky_profiles.handle || "", 30 31 } 31 32 : undefined; 32 33 33 34 if (!publication) return null; 34 35 36 + const hasPosts = publication.documents_in_publications.some( 37 + (d) => !!d?.documents, 38 + ); 39 + const today = new Date(); 40 + const yesterday = new Date(today); 41 + yesterday.setDate(today.getDate() - 1); 42 + const dayBefore = new Date(today); 43 + dayBefore.setDate(today.getDate() - 2); 44 + 45 + const dateOptions: Intl.DateTimeFormatOptions = { 46 + year: "numeric", 47 + month: "long", 48 + day: "2-digit", 49 + }; 50 + 51 + const fakePosts = !hasPosts 52 + ? undefined 53 + : [ 54 + { 55 + title: "Hello!", 56 + description: 57 + "So excited to have you! This is how your posts will appear in your publication", 58 + date: ( 59 + <LocalizedDate 60 + dateString={today.toISOString()} 61 + options={dateOptions} 62 + /> 63 + ), 64 + }, 65 + { 66 + title: "Welcome to your Leaflet Publication", 67 + description: 68 + "Leaflet is an open platform for writing and reading long form posts. Everything you write here, and everyone that follows you is data that belongs to YOU.", 69 + date: ( 70 + <LocalizedDate 71 + dateString={yesterday.toISOString()} 72 + options={dateOptions} 73 + /> 74 + ), 75 + }, 76 + { 77 + title: "I can't wait to see what you do here :)", 78 + description: 79 + "There's so much cool stuff happening here, including this publication!", 80 + date: ( 81 + <LocalizedDate 82 + dateString={dayBefore.toISOString()} 83 + options={dateOptions} 84 + /> 85 + ), 86 + }, 87 + ]; 88 + 35 89 return ( 36 90 <PublicationContent 37 91 record={record} ··· 39 93 did={did} 40 94 profile={profile} 41 95 showPageBackground={props.showPageBackground} 96 + fakePosts={fakePosts} 42 97 /> 43 98 ); 44 99 }
+6 -1
app/lish/[did]/[publication]/theme-settings/ThemeSettingsContent.tsx
··· 173 173 Discard and Leave 174 174 </ButtonPrimary> 175 175 176 - <button onClick={() => setOpen(false)}>Nevermind</button> 176 + <button 177 + className="text-accent-contrast font-bold" 178 + onClick={() => setOpen(false)} 179 + > 180 + Nevermind 181 + </button> 177 182 </div> 178 183 </div> 179 184 </BaseThemeProvider>