From ac75a6f0cc7f1842dddfcce46502541c4130a8bc Mon Sep 17 00:00:00 2001 From: Jared Pereira Date: Thu, 21 May 2026 20:48:19 -0400 Subject: [PATCH] show theme for standard site blocks --- .../[publication]/PublicationPostItem.tsx | 4 + .../[publication]/[rkey]/PostContent.tsx | 12 ++- .../StandardSitePostItem.tsx | 96 +++++++++++++++++-- .../Blocks/StandardSitePostBlock/index.tsx | 35 ++++++- components/ThemeManager/ThemeProvider.tsx | 2 + lexicons/api/lexicons.ts | 3 + .../pub/leaflet/blocks/standardSitePost.ts | 1 + .../pub/leaflet/blocks/standardSitePost.json | 3 + lexicons/src/blocks.ts | 1 + src/replicache/attributes.ts | 4 + src/utils/factsToPagesRecord.ts | 7 ++ 11 files changed, 157 insertions(+), 11 deletions(-) diff --git a/app/lish/[did]/[publication]/PublicationPostItem.tsx b/app/lish/[did]/[publication]/PublicationPostItem.tsx index 6c9d5975..5b47b0f8 100644 --- a/app/lish/[did]/[publication]/PublicationPostItem.tsx +++ b/app/lish/[did]/[publication]/PublicationPostItem.tsx @@ -8,6 +8,7 @@ type CommonProps = { author?: React.ReactNode; date?: React.ReactNode; interactions?: React.ReactNode; + footer?: React.ReactNode; }; type LargeProps = CommonProps & { @@ -90,6 +91,7 @@ export function PublicationPostItemSmall(props: CommonProps) { textClassName="text-sm" /> + {props.footer}
); @@ -126,6 +128,7 @@ export function PublicationPostItemMedium(props: MediumProps) { )} + {props.footer}
); @@ -177,6 +180,7 @@ export function PublicationPostItemLarge(props: LargeProps) { )} {body} + {props.footer}
); diff --git a/app/lish/[did]/[publication]/[rkey]/PostContent.tsx b/app/lish/[did]/[publication]/[rkey]/PostContent.tsx index fa40bf56..3c056487 100644 --- a/app/lish/[did]/[publication]/[rkey]/PostContent.tsx +++ b/app/lish/[did]/[publication]/[rkey]/PostContent.tsx @@ -8,7 +8,6 @@ import { PubLeafletBlocksUnorderedList, PubLeafletBlocksOrderedList, PubLeafletBlocksWebsite, - PubLeafletDocument, PubLeafletPagesLinearDocument, PubLeafletPagesCanvas, PubLeafletBlocksHorizontalRule, @@ -47,7 +46,7 @@ import { slugify } from "src/utils/slugify"; import { PostNotAvailable } from "components/Blocks/BlueskyPostBlock/BlueskyEmbed"; import { useIframeChannel } from "src/hooks/useIframeChannel"; import { usePubTheme } from "components/ThemeManager/PublicationThemeProvider"; -import { useDocument } from "contexts/DocumentContext"; +import { useDocument, useDocumentOptional } from "contexts/DocumentContext"; import { openPage as openPageAction } from "./postPageState"; import { CheckboxChecked } from "components/Icons/CheckboxChecked"; import { CheckboxEmpty } from "components/Icons/CheckboxEmpty"; @@ -155,6 +154,8 @@ export let Block = ({ isLast?: boolean; }) => { let b = block; + let currentPublicationUri = + useDocumentOptional()?.publication?.uri ?? null; let blockProps = { style: { scrollMarginTop: "4rem", @@ -250,7 +251,12 @@ export let Block = ({ : "small"; return (
- +
); } diff --git a/components/Blocks/StandardSitePostBlock/StandardSitePostItem.tsx b/components/Blocks/StandardSitePostBlock/StandardSitePostItem.tsx index c59c3b5f..a553e4cd 100644 --- a/components/Blocks/StandardSitePostBlock/StandardSitePostItem.tsx +++ b/components/Blocks/StandardSitePostBlock/StandardSitePostItem.tsx @@ -1,17 +1,23 @@ "use client"; import { AtUri } from "@atproto/api"; +import Link from "next/link"; import { PublicationPostItemSmall, PublicationPostItemMedium, PublicationPostItemLarge, } from "app/lish/[did]/[publication]/PublicationPostItem"; import { LocalizedDate } from "app/lish/[did]/[publication]/LocalizedDate"; -import { getDocumentURL } from "app/lish/createPub/getPublicationURL"; +import { + getDocumentURL, + getPublicationURL, +} from "app/lish/createPub/getPublicationURL"; import { getFirstParagraph } from "src/utils/getFirstParagraph"; import { blobRefToSrc } from "src/utils/blobRefToSrc"; import { useStandardSitePost } from "components/StandardSitePostDataProvider"; import { useEntity, useReplicache } from "src/replicache"; import { InteractionPreview } from "components/InteractionsPreview"; +import { PubIcon } from "components/ActionBar/Publications"; +import { PublicationThemeProvider } from "components/ThemeManager/PublicationThemeProvider"; import type { StandardSitePostData } from "app/api/rpc/[command]/get_standard_site_posts"; export type StandardSitePostSize = "large" | "medium" | "small"; @@ -19,9 +25,13 @@ export type StandardSitePostSize = "large" | "medium" | "small"; export function StandardSitePostItem({ uri, size = "medium", + showPubTheme = true, + currentPublicationUri, }: { uri: string; size?: StandardSitePostSize; + showPubTheme?: boolean; + currentPublicationUri?: string | null; }) { const { data, isLoading } = useStandardSitePost(uri); const { rootEntity } = useReplicache(); @@ -39,7 +49,41 @@ export function StandardSitePostItem({ ); } - return ; + return ( + + ); +} + +export function WithStandardSitePostPublicationTheme({ + post, + enabled, + children, +}: { + post: StandardSitePostData; + enabled: boolean; + children: React.ReactNode; +}) { + if (!enabled || !post.publication?.record?.theme) return <>{children}; + let pubCreator: string; + try { + pubCreator = new AtUri(post.publication.uri).host; + } catch { + return <>{children}; + } + return ( + + {children} + + ); } function StandardSitePostItemPlaceholder({ @@ -116,9 +160,13 @@ function StandardSitePostItemPlaceholder({ export function StandardSitePostItemView({ post, size = "medium", + showPubTheme = true, + currentPublicationUri, }: { post: StandardSitePostData; size?: StandardSitePostSize; + showPubTheme?: boolean; + currentPublicationUri?: string | null; }) { const docUrl = getDocumentURL( post.record, @@ -170,16 +218,26 @@ export function StandardSitePostItemView({ /> ); + const showPubFooter = + !!post.publication?.record && + (!currentPublicationUri || post.publication.uri !== currentPublicationUri); + + const pubFooter = showPubFooter && post.publication ? ( + + ) : null; + const commonProps = { href: docUrl, title: post.record.title, author: authorLabel, date, interactions, + footer: pubFooter, }; + let item: React.ReactNode; if (size === "large") { - return ( + item = ( ); - } - if (size === "medium") { - return ( + } else if (size === "medium") { + item = ( ); + } else { + item = ; } - return ; + + return ( + + {item} + + ); +} + +function PubFooter({ + publication, +}: { + publication: NonNullable; +}) { + if (!publication.record) return null; + const pubUrl = getPublicationURL(publication); + return ( + + + {publication.record.name} + + ); } diff --git a/components/Blocks/StandardSitePostBlock/index.tsx b/components/Blocks/StandardSitePostBlock/index.tsx index 208787f4..93a817f8 100644 --- a/components/Blocks/StandardSitePostBlock/index.tsx +++ b/components/Blocks/StandardSitePostBlock/index.tsx @@ -2,11 +2,13 @@ import { BlockProps, BlockLayout } from "../Block"; import { useEntity, useReplicache } from "src/replicache"; import { useUIState } from "src/useUIState"; import { Popover } from "components/Popover"; +import { Toggle } from "components/Toggle"; import { SettingsTriggerButton } from "../SettingsTriggerButton"; import { StandardSitePostItem, type StandardSitePostSize, } from "./StandardSitePostItem"; +import { useLeafletPublicationData } from "components/PageSWRDataProvider"; export const StandardSitePostBlock = ( props: BlockProps & { preview?: boolean }, @@ -17,6 +19,13 @@ export const StandardSitePostBlock = ( let uri = useEntity(props.entityID, "block/standard-site-post")?.data.value; let sizeFact = useEntity(props.entityID, "standard-site-post/size"); let size: StandardSitePostSize = sizeFact?.data.value ?? "medium"; + let showPubThemeFact = useEntity( + props.entityID, + "standard-site-post/show-publication-theme", + ); + let showPubTheme = showPubThemeFact?.data.value !== false; + let editorPub = useLeafletPublicationData(); + let currentPublicationUri = editorPub.data?.publications?.uri ?? null; if (!uri) return null; @@ -30,7 +39,12 @@ export const StandardSitePostBlock = ( } > - + ); }; @@ -39,6 +53,11 @@ function StandardSitePostSettingsButton(props: { entityID: string }) { let { rep } = useReplicache(); let sizeFact = useEntity(props.entityID, "standard-site-post/size"); let size: StandardSitePostSize = sizeFact?.data.value ?? "medium"; + let showPubThemeFact = useEntity( + props.entityID, + "standard-site-post/show-publication-theme", + ); + let showPubTheme = showPubThemeFact?.data.value !== false; return ( +
+ { + if (!rep) return; + rep.mutate.assertFact({ + entity: props.entityID, + attribute: "standard-site-post/show-publication-theme", + data: { type: "boolean", value: !showPubTheme }, + }); + }} + > +
Use Publication Theme
+
); } diff --git a/components/ThemeManager/ThemeProvider.tsx b/components/ThemeManager/ThemeProvider.tsx index ce109b51..b66828cc 100644 --- a/components/ThemeManager/ThemeProvider.tsx +++ b/components/ThemeManager/ThemeProvider.tsx @@ -243,6 +243,7 @@ export const BaseThemeProvider = ({ // Dynamically load Google Fonts when fonts change useEffect(() => { + if (local) return; const loadGoogleFont = (url: string | null, fontFamily: string) => { if (!url) return; @@ -281,6 +282,7 @@ export const BaseThemeProvider = ({ loadGoogleFont(headingGoogleFontsUrl, headingFontConfig.fontFamily); loadGoogleFont(bodyGoogleFontsUrl, bodyFontConfig.fontFamily); }, [ + local, headingGoogleFontsUrl, bodyGoogleFontsUrl, headingFontConfig.fontFamily, diff --git a/lexicons/api/lexicons.ts b/lexicons/api/lexicons.ts index 3afca7b0..6cb98832 100644 --- a/lexicons/api/lexicons.ts +++ b/lexicons/api/lexicons.ts @@ -1609,6 +1609,9 @@ export const schemaDict = { type: 'string', knownValues: ['large', 'medium', 'small'], }, + showPublicationTheme: { + type: 'boolean', + }, }, }, }, diff --git a/lexicons/api/types/pub/leaflet/blocks/standardSitePost.ts b/lexicons/api/types/pub/leaflet/blocks/standardSitePost.ts index 9f4d9e86..2de04fbb 100644 --- a/lexicons/api/types/pub/leaflet/blocks/standardSitePost.ts +++ b/lexicons/api/types/pub/leaflet/blocks/standardSitePost.ts @@ -19,6 +19,7 @@ export interface Main { uri: string cid?: string size?: 'large' | 'medium' | 'small' | (string & {}) + showPublicationTheme?: boolean } const hashMain = 'main' diff --git a/lexicons/pub/leaflet/blocks/standardSitePost.json b/lexicons/pub/leaflet/blocks/standardSitePost.json index d88f57bb..0febeaa5 100644 --- a/lexicons/pub/leaflet/blocks/standardSitePost.json +++ b/lexicons/pub/leaflet/blocks/standardSitePost.json @@ -22,6 +22,9 @@ "medium", "small" ] + }, + "showPublicationTheme": { + "type": "boolean" } } } diff --git a/lexicons/src/blocks.ts b/lexicons/src/blocks.ts index 9ae64709..28baef5c 100644 --- a/lexicons/src/blocks.ts +++ b/lexicons/src/blocks.ts @@ -60,6 +60,7 @@ export const PubLeafletBlocksStandardSitePost: LexiconDoc = { uri: { type: "string", format: "at-uri" }, cid: { type: "string" }, size: { type: "string", knownValues: ["large", "medium", "small"] }, + showPublicationTheme: { type: "boolean" }, }, }, }, diff --git a/src/replicache/attributes.ts b/src/replicache/attributes.ts index 37182432..a3b0a0a3 100644 --- a/src/replicache/attributes.ts +++ b/src/replicache/attributes.ts @@ -95,6 +95,10 @@ const BlockAttributes = { type: "standard-site-post-size-union", cardinality: "one", }, + "standard-site-post/show-publication-theme": { + type: "boolean", + cardinality: "one", + }, "block/math": { type: "string", cardinality: "one", diff --git a/src/utils/factsToPagesRecord.ts b/src/utils/factsToPagesRecord.ts index a6b2af67..dbc251fb 100644 --- a/src/utils/factsToPagesRecord.ts +++ b/src/utils/factsToPagesRecord.ts @@ -381,10 +381,17 @@ export async function processBlocksToPages(opts: { const [uri] = scan.eav(b.value, "block/standard-site-post"); if (!uri) return; const [sizeFact] = scan.eav(b.value, "standard-site-post/size"); + const [showPubThemeFact] = scan.eav( + b.value, + "standard-site-post/show-publication-theme", + ); const block: $Typed = { $type: ids.PubLeafletBlocksStandardSitePost, uri: uri.data.value, ...(sizeFact && { size: sizeFact.data.value }), + ...(showPubThemeFact?.data.value === false && { + showPublicationTheme: false, + }), }; return block; } -- 2.51.2