"use client"; import { useMemo, useState } from "react"; import { parseColor } from "react-aria-components"; import { useEntity } from "src/replicache"; import { useColorAttribute, colorToString } from "./useColorAttribute"; import { BaseThemeProvider, CardBorderHiddenContext } from "./ThemeProvider"; import { PubLeafletPublication, PubLeafletThemeColor } from "lexicons/api"; import type * as SiteStandardThemeBasic from "lexicons/api/types/site/standard/theme/basic"; import { resolvePublicationTheme } from "lexicons/src/normalize"; import { usePublicationData, useNormalizedPublicationRecord, } from "app/(app)/lish/[did]/[publication]/dashboard/PublicationSWRProvider"; import { blobRefToSrc } from "src/utils/blobRefToSrc"; import { PubThemeDefaults } from "./themeDefaults"; export type PubThemeSource = | { theme?: PubLeafletPublication.Theme | null; basicTheme?: SiteStandardThemeBasic.Main | null; } | null | undefined; // Default page background for standalone leaflets (matches editor default) const StandalonePageBackground = "#FFFFFF"; function parseThemeColor( c: PubLeafletThemeColor.Rgb | PubLeafletThemeColor.Rgba, ) { if (c.$type === "pub.leaflet.theme.color#rgba") { return parseColor(`rgba(${c.r}, ${c.g}, ${c.b}, ${c.a / 100})`); } return parseColor(`rgb(${c.r}, ${c.g}, ${c.b})`); } let useColor = ( theme: PubLeafletPublication.Record["theme"] | null | undefined, c: keyof typeof PubThemeDefaults, ) => { return useMemo(() => { let v = theme?.[c]; if (isColor(v)) { return parseThemeColor(v); } else return parseColor(PubThemeDefaults[c]); }, [theme?.[c]]); }; let isColor = ( c: any, ): c is PubLeafletThemeColor.Rgb | PubLeafletThemeColor.Rgba => { return ( c?.$type === "pub.leaflet.theme.color#rgb" || c?.$type === "pub.leaflet.theme.color#rgba" ); }; export function PublicationThemeProviderDashboard(props: { children: React.ReactNode; }) { let { data } = usePublicationData(); let { publication: pub } = data || {}; const normalizedPub = useNormalizedPublicationRecord(); return ( {props.children} ); } export function PublicationBackgroundProvider(props: { record?: PubThemeSource; pub_creator: string; className?: string; children: React.ReactNode; localBgImage?: string | null; localBgImageRepeat?: number | null; }) { let backgroundImage = props.localBgImage !== undefined ? props.localBgImage : props.record?.theme?.backgroundImage?.image?.ref ? blobRefToSrc( props.record?.theme?.backgroundImage?.image?.ref, props.pub_creator, ) : null; let backgroundImageRepeat = props.localBgImageRepeat !== undefined ? !!props.localBgImageRepeat : props.record?.theme?.backgroundImage?.repeat; let backgroundImageSize = (props.localBgImageRepeat !== undefined ? props.localBgImageRepeat : props.record?.theme?.backgroundImage?.width) || 500; return (
{props.children}
); } export function PublicationThemeProvider(props: { local?: boolean; children: React.ReactNode; record?: PubThemeSource; pub_creator: string; isStandalone?: boolean; }) { let theme = usePubTheme(props.record, props.isStandalone); let cardBorderHidden = !theme.showPageBackground; let hasBackgroundImage = !!props.record?.theme?.backgroundImage?.image?.ref; return ( {props.children} ); } export const usePubTheme = ( source?: PubThemeSource, isStandalone?: boolean, ) => { const theme = useMemo(() => resolvePublicationTheme(source), [source]); let bgLeaflet = useColor(theme, "backgroundColor"); let bgPage = useColor(theme, "pageBackground"); // For standalone documents, use the editor default page background (#FFFFFF) // For publications without explicit pageBackground, use bgLeaflet if (isStandalone && !theme?.pageBackground) { bgPage = parseColor(StandalonePageBackground); } else if (theme && !theme.pageBackground) { bgPage = bgLeaflet; } // For standalone documents, default to showing page background // For publications, default to hiding it (borderless) let showPageBackground = isStandalone ? theme?.showPageBackground !== false : !!theme?.showPageBackground; let pageWidth = theme?.pageWidth; let primary = useColor(theme, "primary"); let accent1 = useColor(theme, "accentBackground"); let accent2 = useColor(theme, "accentText"); let highlight1 = useEntity(null, "theme/highlight-1")?.data.value; let highlight2 = useColorAttribute(null, "theme/highlight-2"); let highlight3 = useColorAttribute(null, "theme/highlight-3"); let headingFontId = theme?.headingFont; let bodyFontId = theme?.bodyFont; return { bgLeaflet, bgPage, primary, accent1, accent2, highlight1, highlight2, highlight3, showPageBackground, pageWidth, headingFontId, bodyFontId, }; }; export const useLocalPubTheme = ( theme: PubLeafletPublication.Record["theme"] | undefined, showPageBackground?: boolean, ) => { const pubTheme = usePubTheme({ theme }); const [localOverrides, setTheme] = useState>({}); const mergedTheme = useMemo(() => { let newTheme = { ...pubTheme, ...localOverrides, showPageBackground, }; return { ...newTheme, }; }, [pubTheme, localOverrides, showPageBackground]); return { theme: mergedTheme, setTheme, resetChanges: () => setTheme({}), changes: Object.keys(localOverrides).length > 0, }; };