Something went wrong. Try again.
a tool for shared writing and social publishing
Something went wrong. Try again.
5.9 kB · 195 lines
TSX
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196"use client";import { useMemo, useState } from "react";import { parseColor } from "react-aria-components";import { useEntity } from "src/replicache";import { getColorContrast } from "./ThemeProvider";import { useColorAttribute, colorToString } from "./useColorAttribute";import { BaseThemeProvider } from "./ThemeProvider";import { PubLeafletPublication, PubLeafletThemeColor } from "lexicons/api";import { usePublicationData } from "app/lish/[did]/[publication]/dashboard/PublicationSWRProvider";import { blobRefToSrc } from "src/utils/blobRefToSrc";
const PubThemeDefaults = { backgroundColor: "#FDFCFA", pageBackground: "#FDFCFA", primary: "#272727", accentText: "#FFFFFF", accentBackground: "#0000FF",};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 = ( record: PubLeafletPublication.Record | null | undefined, c: keyof typeof PubThemeDefaults,) => { return useMemo(() => { let v = record?.theme?.[c]; if (isColor(v)) { return parseThemeColor(v); } else return parseColor(PubThemeDefaults[c]); }, [record?.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 || {}; return ( <PublicationThemeProvider pub_creator={pub?.identity_did || ""} record={pub?.record as PubLeafletPublication.Record} > <PublicationBackgroundProvider record={pub?.record as PubLeafletPublication.Record} pub_creator={pub?.identity_did || ""} > {props.children} </PublicationBackgroundProvider> </PublicationThemeProvider> );}
export function PublicationBackgroundProvider(props: { record?: PubLeafletPublication.Record | null; pub_creator: string; className?: string; children: React.ReactNode;}) { let backgroundImage = props.record?.theme?.backgroundImage?.image?.ref ? blobRefToSrc( props.record?.theme?.backgroundImage?.image?.ref, props.pub_creator, ) : null;
let backgroundImageRepeat = props.record?.theme?.backgroundImage?.repeat; let backgroundImageSize = props.record?.theme?.backgroundImage?.width || 500; return ( <div className="PubBackgroundWrapper w-full bg-bg-leaflet text-primary h-full flex flex-col bg-cover bg-center bg-no-repeat items-stretch" style={{ backgroundImage: `url(${backgroundImage})`, backgroundRepeat: backgroundImageRepeat ? "repeat" : "no-repeat", backgroundSize: `${backgroundImageRepeat ? `${backgroundImageSize}px` : "cover"}`, }} > {props.children} </div> );}export function PublicationThemeProvider(props: { local?: boolean; children: React.ReactNode; record?: PubLeafletPublication.Record | null; pub_creator: string;}) { let colors = usePubTheme(props.record); return ( <BaseThemeProvider local={props.local} {...colors}> {props.children} </BaseThemeProvider> );}
export const usePubTheme = (record?: PubLeafletPublication.Record | null) => { let bgLeaflet = useColor(record, "backgroundColor"); let bgPage = useColor(record, "pageBackground"); bgPage = record?.theme?.pageBackground ? bgPage : bgLeaflet; let showPageBackground = record?.theme?.showPageBackground;
let primary = useColor(record, "primary");
let accent1 = useColor(record, "accentBackground"); let accent2 = useColor(record, "accentText");
let highlight1 = useEntity(null, "theme/highlight-1")?.data.value; let highlight2 = useColorAttribute(null, "theme/highlight-2"); let highlight3 = useColorAttribute(null, "theme/highlight-3");
return { bgLeaflet, bgPage, primary, accent1, accent2, highlight1, highlight2, highlight3, showPageBackground, };};
export const useLocalPubTheme = ( record: PubLeafletPublication.Record | undefined, showPageBackground?: boolean,) => { const pubTheme = usePubTheme(record); const [localOverrides, setTheme] = useState<Partial<typeof pubTheme>>({});
const mergedTheme = useMemo(() => { let newTheme = { ...pubTheme, ...localOverrides, showPageBackground, }; let newAccentContrast; let sortedAccents = [newTheme.accent1, newTheme.accent2].sort((a, b) => { return ( getColorContrast( colorToString(b, "rgb"), colorToString( showPageBackground ? newTheme.bgPage : newTheme.bgLeaflet, "rgb", ), ) - getColorContrast( colorToString(a, "rgb"), colorToString( showPageBackground ? newTheme.bgPage : newTheme.bgLeaflet, "rgb", ), ) ); }); if ( getColorContrast( colorToString(sortedAccents[0], "rgb"), colorToString(newTheme.primary, "rgb"), ) < 30 && getColorContrast( colorToString(sortedAccents[1], "rgb"), colorToString( showPageBackground ? newTheme.bgPage : newTheme.bgLeaflet, "rgb", ), ) > 12 ) { newAccentContrast = sortedAccents[1]; } else newAccentContrast = sortedAccents[0]; return { ...newTheme, accentContrast: newAccentContrast, }; }, [pubTheme, localOverrides, showPageBackground]); return { theme: mergedTheme, setTheme, changes: Object.keys(localOverrides).length > 0, };};