a tool for shared writing and social publishing
0

Configure Feed

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

show theme for standard site blocks

Jared Pereira (May 21, 2026, 8:59 PM EDT) ac75a6f0 8f6d02e9

+157 -11
+4
app/lish/[did]/[publication]/PublicationPostItem.tsx
··· 8 8 author?: React.ReactNode; 9 9 date?: React.ReactNode; 10 10 interactions?: React.ReactNode; 11 + footer?: React.ReactNode; 11 12 }; 12 13 13 14 type LargeProps = CommonProps & { ··· 90 91 textClassName="text-sm" 91 92 /> 92 93 </div> 94 + {props.footer} 93 95 <hr className="last:hidden border-border-light" /> 94 96 </> 95 97 ); ··· 126 128 </div> 127 129 )} 128 130 </div> 131 + {props.footer} 129 132 <hr className="last:hidden border-border-light" /> 130 133 </> 131 134 ); ··· 177 180 )} 178 181 {body} 179 182 </div> 183 + {props.footer} 180 184 <hr className="last:hidden border-border-light" /> 181 185 </> 182 186 );
+9 -3
app/lish/[did]/[publication]/[rkey]/PostContent.tsx
··· 8 8 PubLeafletBlocksUnorderedList, 9 9 PubLeafletBlocksOrderedList, 10 10 PubLeafletBlocksWebsite, 11 - PubLeafletDocument, 12 11 PubLeafletPagesLinearDocument, 13 12 PubLeafletPagesCanvas, 14 13 PubLeafletBlocksHorizontalRule, ··· 47 46 import { PostNotAvailable } from "components/Blocks/BlueskyPostBlock/BlueskyEmbed"; 48 47 import { useIframeChannel } from "src/hooks/useIframeChannel"; 49 48 import { usePubTheme } from "components/ThemeManager/PublicationThemeProvider"; 50 - import { useDocument } from "contexts/DocumentContext"; 49 + import { useDocument, useDocumentOptional } from "contexts/DocumentContext"; 51 50 import { openPage as openPageAction } from "./postPageState"; 52 51 import { CheckboxChecked } from "components/Icons/CheckboxChecked"; 53 52 import { CheckboxEmpty } from "components/Icons/CheckboxEmpty"; ··· 155 154 isLast?: boolean; 156 155 }) => { 157 156 let b = block; 157 + let currentPublicationUri = 158 + useDocumentOptional()?.publication?.uri ?? null; 158 159 let blockProps = { 159 160 style: { 160 161 scrollMarginTop: "4rem", ··· 250 251 : "small"; 251 252 return ( 252 253 <div className={className} {...blockProps}> 253 - <StandardSitePostItemView post={post} size={size} /> 254 + <StandardSitePostItemView 255 + post={post} 256 + size={size} 257 + showPubTheme={b.block.showPublicationTheme !== false} 258 + currentPublicationUri={currentPublicationUri} 259 + /> 254 260 </div> 255 261 ); 256 262 }
+89 -7
components/Blocks/StandardSitePostBlock/StandardSitePostItem.tsx
··· 1 1 "use client"; 2 2 import { AtUri } from "@atproto/api"; 3 + import Link from "next/link"; 3 4 import { 4 5 PublicationPostItemSmall, 5 6 PublicationPostItemMedium, 6 7 PublicationPostItemLarge, 7 8 } from "app/lish/[did]/[publication]/PublicationPostItem"; 8 9 import { LocalizedDate } from "app/lish/[did]/[publication]/LocalizedDate"; 9 - import { getDocumentURL } from "app/lish/createPub/getPublicationURL"; 10 + import { 11 + getDocumentURL, 12 + getPublicationURL, 13 + } from "app/lish/createPub/getPublicationURL"; 10 14 import { getFirstParagraph } from "src/utils/getFirstParagraph"; 11 15 import { blobRefToSrc } from "src/utils/blobRefToSrc"; 12 16 import { useStandardSitePost } from "components/StandardSitePostDataProvider"; 13 17 import { useEntity, useReplicache } from "src/replicache"; 14 18 import { InteractionPreview } from "components/InteractionsPreview"; 19 + import { PubIcon } from "components/ActionBar/Publications"; 20 + import { PublicationThemeProvider } from "components/ThemeManager/PublicationThemeProvider"; 15 21 import type { StandardSitePostData } from "app/api/rpc/[command]/get_standard_site_posts"; 16 22 17 23 export type StandardSitePostSize = "large" | "medium" | "small"; ··· 19 25 export function StandardSitePostItem({ 20 26 uri, 21 27 size = "medium", 28 + showPubTheme = true, 29 + currentPublicationUri, 22 30 }: { 23 31 uri: string; 24 32 size?: StandardSitePostSize; 33 + showPubTheme?: boolean; 34 + currentPublicationUri?: string | null; 25 35 }) { 26 36 const { data, isLoading } = useStandardSitePost(uri); 27 37 const { rootEntity } = useReplicache(); ··· 39 49 ); 40 50 } 41 51 42 - return <StandardSitePostItemView post={data} size={size} />; 52 + return ( 53 + <StandardSitePostItemView 54 + post={data} 55 + size={size} 56 + showPubTheme={showPubTheme} 57 + currentPublicationUri={currentPublicationUri} 58 + /> 59 + ); 60 + } 61 + 62 + export function WithStandardSitePostPublicationTheme({ 63 + post, 64 + enabled, 65 + children, 66 + }: { 67 + post: StandardSitePostData; 68 + enabled: boolean; 69 + children: React.ReactNode; 70 + }) { 71 + if (!enabled || !post.publication?.record?.theme) return <>{children}</>; 72 + let pubCreator: string; 73 + try { 74 + pubCreator = new AtUri(post.publication.uri).host; 75 + } catch { 76 + return <>{children}</>; 77 + } 78 + return ( 79 + <PublicationThemeProvider 80 + local 81 + theme={post.publication.record.theme} 82 + pub_creator={pubCreator} 83 + > 84 + {children} 85 + </PublicationThemeProvider> 86 + ); 43 87 } 44 88 45 89 function StandardSitePostItemPlaceholder({ ··· 116 160 export function StandardSitePostItemView({ 117 161 post, 118 162 size = "medium", 163 + showPubTheme = true, 164 + currentPublicationUri, 119 165 }: { 120 166 post: StandardSitePostData; 121 167 size?: StandardSitePostSize; 168 + showPubTheme?: boolean; 169 + currentPublicationUri?: string | null; 122 170 }) { 123 171 const docUrl = getDocumentURL( 124 172 post.record, ··· 170 218 /> 171 219 ); 172 220 221 + const showPubFooter = 222 + !!post.publication?.record && 223 + (!currentPublicationUri || post.publication.uri !== currentPublicationUri); 224 + 225 + const pubFooter = showPubFooter && post.publication ? ( 226 + <PubFooter publication={post.publication} /> 227 + ) : null; 228 + 173 229 const commonProps = { 174 230 href: docUrl, 175 231 title: post.record.title, 176 232 author: authorLabel, 177 233 date, 178 234 interactions, 235 + footer: pubFooter, 179 236 }; 180 237 238 + let item: React.ReactNode; 181 239 if (size === "large") { 182 - return ( 240 + item = ( 183 241 <PublicationPostItemLarge 184 242 {...commonProps} 185 243 description={description} ··· 188 246 pageWidth={pageWidth} 189 247 /> 190 248 ); 191 - } 192 - if (size === "medium") { 193 - return ( 249 + } else if (size === "medium") { 250 + item = ( 194 251 <PublicationPostItemMedium 195 252 {...commonProps} 196 253 description={description} ··· 198 255 coverImageAlt={post.record.title} 199 256 /> 200 257 ); 258 + } else { 259 + item = <PublicationPostItemSmall {...commonProps} />; 201 260 } 202 - return <PublicationPostItemSmall {...commonProps} />; 261 + 262 + return ( 263 + <WithStandardSitePostPublicationTheme post={post} enabled={showPubTheme}> 264 + {item} 265 + </WithStandardSitePostPublicationTheme> 266 + ); 267 + } 268 + 269 + function PubFooter({ 270 + publication, 271 + }: { 272 + publication: NonNullable<StandardSitePostData["publication"]>; 273 + }) { 274 + if (!publication.record) return null; 275 + const pubUrl = getPublicationURL(publication); 276 + return ( 277 + <Link 278 + href={pubUrl} 279 + className="flex items-center gap-1.5 px-3 pt-1.5 pb-2 text-accent-contrast font-bold no-underline! text-sm" 280 + > 281 + <PubIcon tiny record={publication.record} uri={publication.uri} /> 282 + <span className="min-w-0 truncate">{publication.record.name}</span> 283 + </Link> 284 + ); 203 285 }
+34 -1
components/Blocks/StandardSitePostBlock/index.tsx
··· 2 2 import { useEntity, useReplicache } from "src/replicache"; 3 3 import { useUIState } from "src/useUIState"; 4 4 import { Popover } from "components/Popover"; 5 + import { Toggle } from "components/Toggle"; 5 6 import { SettingsTriggerButton } from "../SettingsTriggerButton"; 6 7 import { 7 8 StandardSitePostItem, 8 9 type StandardSitePostSize, 9 10 } from "./StandardSitePostItem"; 11 + import { useLeafletPublicationData } from "components/PageSWRDataProvider"; 10 12 11 13 export const StandardSitePostBlock = ( 12 14 props: BlockProps & { preview?: boolean }, ··· 17 19 let uri = useEntity(props.entityID, "block/standard-site-post")?.data.value; 18 20 let sizeFact = useEntity(props.entityID, "standard-site-post/size"); 19 21 let size: StandardSitePostSize = sizeFact?.data.value ?? "medium"; 22 + let showPubThemeFact = useEntity( 23 + props.entityID, 24 + "standard-site-post/show-publication-theme", 25 + ); 26 + let showPubTheme = showPubThemeFact?.data.value !== false; 27 + let editorPub = useLeafletPublicationData(); 28 + let currentPublicationUri = editorPub.data?.publications?.uri ?? null; 20 29 21 30 if (!uri) return null; 22 31 ··· 30 39 <StandardSitePostSettingsButton entityID={props.entityID} /> 31 40 } 32 41 > 33 - <StandardSitePostItem uri={uri} size={size} /> 42 + <StandardSitePostItem 43 + uri={uri} 44 + size={size} 45 + showPubTheme={showPubTheme} 46 + currentPublicationUri={currentPublicationUri} 47 + /> 34 48 </BlockLayout> 35 49 ); 36 50 }; ··· 39 53 let { rep } = useReplicache(); 40 54 let sizeFact = useEntity(props.entityID, "standard-site-post/size"); 41 55 let size: StandardSitePostSize = sizeFact?.data.value ?? "medium"; 56 + let showPubThemeFact = useEntity( 57 + props.entityID, 58 + "standard-site-post/show-publication-theme", 59 + ); 60 + let showPubTheme = showPubThemeFact?.data.value !== false; 42 61 43 62 return ( 44 63 <Popover ··· 89 108 ); 90 109 })} 91 110 </div> 111 + <hr className="border-border-light my-1" /> 112 + <Toggle 113 + toggle={showPubTheme} 114 + onToggle={() => { 115 + if (!rep) return; 116 + rep.mutate.assertFact({ 117 + entity: props.entityID, 118 + attribute: "standard-site-post/show-publication-theme", 119 + data: { type: "boolean", value: !showPubTheme }, 120 + }); 121 + }} 122 + > 123 + <div className="font-bold">Use Publication Theme</div> 124 + </Toggle> 92 125 </Popover> 93 126 ); 94 127 }
+2
components/ThemeManager/ThemeProvider.tsx
··· 243 243 244 244 // Dynamically load Google Fonts when fonts change 245 245 useEffect(() => { 246 + if (local) return; 246 247 const loadGoogleFont = (url: string | null, fontFamily: string) => { 247 248 if (!url) return; 248 249 ··· 281 282 loadGoogleFont(headingGoogleFontsUrl, headingFontConfig.fontFamily); 282 283 loadGoogleFont(bodyGoogleFontsUrl, bodyFontConfig.fontFamily); 283 284 }, [ 285 + local, 284 286 headingGoogleFontsUrl, 285 287 bodyGoogleFontsUrl, 286 288 headingFontConfig.fontFamily,
+3
lexicons/api/lexicons.ts
··· 1609 1609 type: 'string', 1610 1610 knownValues: ['large', 'medium', 'small'], 1611 1611 }, 1612 + showPublicationTheme: { 1613 + type: 'boolean', 1614 + }, 1612 1615 }, 1613 1616 }, 1614 1617 },
+1
lexicons/api/types/pub/leaflet/blocks/standardSitePost.ts
··· 19 19 uri: string 20 20 cid?: string 21 21 size?: 'large' | 'medium' | 'small' | (string & {}) 22 + showPublicationTheme?: boolean 22 23 } 23 24 24 25 const hashMain = 'main'
+3
lexicons/pub/leaflet/blocks/standardSitePost.json
··· 22 22 "medium", 23 23 "small" 24 24 ] 25 + }, 26 + "showPublicationTheme": { 27 + "type": "boolean" 25 28 } 26 29 } 27 30 }
+1
lexicons/src/blocks.ts
··· 60 60 uri: { type: "string", format: "at-uri" }, 61 61 cid: { type: "string" }, 62 62 size: { type: "string", knownValues: ["large", "medium", "small"] }, 63 + showPublicationTheme: { type: "boolean" }, 63 64 }, 64 65 }, 65 66 },
+4
src/replicache/attributes.ts
··· 95 95 type: "standard-site-post-size-union", 96 96 cardinality: "one", 97 97 }, 98 + "standard-site-post/show-publication-theme": { 99 + type: "boolean", 100 + cardinality: "one", 101 + }, 98 102 "block/math": { 99 103 type: "string", 100 104 cardinality: "one",
+7
src/utils/factsToPagesRecord.ts
··· 381 381 const [uri] = scan.eav(b.value, "block/standard-site-post"); 382 382 if (!uri) return; 383 383 const [sizeFact] = scan.eav(b.value, "standard-site-post/size"); 384 + const [showPubThemeFact] = scan.eav( 385 + b.value, 386 + "standard-site-post/show-publication-theme", 387 + ); 384 388 const block: $Typed<PubLeafletBlocksStandardSitePost.Main> = { 385 389 $type: ids.PubLeafletBlocksStandardSitePost, 386 390 uri: uri.data.value, 387 391 ...(sizeFact && { size: sizeFact.data.value }), 392 + ...(showPubThemeFact?.data.value === false && { 393 + showPublicationTheme: false, 394 + }), 388 395 }; 389 396 return block; 390 397 }