diff --git a/package.json b/package.json index adaa8a45..874adc73 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "dayjs": "1.11.8", "dotenv": "16.1.4", "emoji-mart": "5.5.2", - "fast-average-color": "9.3.0", + "fast-average-color-node": "2.6.0", "fast-deep-equal": "3.1.3", "hast-util-from-html": "1.0.2", "hast-util-to-html": "8.0.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8d6e9786..c2994b8e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -140,9 +140,9 @@ dependencies: emoji-mart: specifier: 5.5.2 version: 5.5.2 - fast-average-color: - specifier: 9.3.0 - version: 9.3.0 + fast-average-color-node: + specifier: 2.6.0 + version: 2.6.0 fast-deep-equal: specifier: 3.1.3 version: 3.1.3 @@ -7979,6 +7979,17 @@ packages: resolution: {integrity: sha512-oNvBekbhsm/0PNSOWca5raHNAi6dG960Bx6LJgxDPNF59WpuspgQ17bN5MKwOr7JcFdQYc7StW3VZ28DBZLavQ==} dev: false + /fast-average-color-node@2.6.0: + resolution: {integrity: sha512-dn7ps7AhcsO8Gq4468aAeSVMtYWAGjTIjQ6J83+QueA6UYZ22YeKfIrfKjQiYAjBt+oBWskZ5e1fXtmsm24xMg==} + engines: {node: '>= 12'} + dependencies: + fast-average-color: 9.3.0 + node-fetch: 2.6.11 + sharp: 0.31.3 + transitivePeerDependencies: + - encoding + dev: false + /fast-average-color@9.3.0: resolution: {integrity: sha512-FlPROSqDMOnoBgkFhWMHJODPvpS0Od0WDpedcKq4U/t0JVapGAkblNwxOr75qT+ZNd0dQM4qlgqrtnXbCJ8cNg==} engines: {node: '>= 12'} @@ -10609,6 +10620,10 @@ packages: resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==} dev: false + /node-addon-api@5.1.0: + resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} + dev: false + /node-addon-api@6.1.0: resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} dev: false @@ -12228,6 +12243,21 @@ packages: safe-buffer: 5.2.1 dev: false + /sharp@0.31.3: + resolution: {integrity: sha512-XcR4+FCLBFKw1bdB+GEhnUNXNXvnt0tDo4WsBsraKymuo/IAuPuCBVAL2wIkUw2r/dwFW5Q5+g66Kwl2dgDFVg==} + engines: {node: '>=14.15.0'} + requiresBuild: true + dependencies: + color: 4.2.3 + detect-libc: 2.0.1 + node-addon-api: 5.1.0 + prebuild-install: 7.1.1 + semver: 7.5.1 + simple-get: 4.0.1 + tar-fs: 2.1.1 + tunnel-agent: 0.6.0 + dev: false + /sharp@0.32.1: resolution: {integrity: sha512-kQTFtj7ldpUqSe8kDxoGLZc1rnMFU0AO2pqbX6pLy3b7Oj8ivJIdoKNwxHVQG2HN6XpHPJqCSM2nsma2gOXvOg==} engines: {node: '>=14.15.0'} diff --git a/src/app/site/[site]/layout.tsx b/src/app/site/[site]/layout.tsx index 3017998b..5792e879 100644 --- a/src/app/site/[site]/layout.tsx +++ b/src/app/site/[site]/layout.tsx @@ -18,6 +18,7 @@ import { cn } from "~/lib/utils" import { fetchGetPage } from "~/queries/page.server" import { fetchGetSite, + getCharacterColors, prefetchGetSiteSubscriptions, prefetchGetSiteToSubscriptions, } from "~/queries/site.server" @@ -156,12 +157,34 @@ export default async function SiteLayout({ const dehydratedState = dehydrate(queryClient) + const colors = await getCharacterColors(site) + return (
+ )} {site && !onlyContent && }
-
+
cover void } -const fac = new FastAverageColor() - const HeaderLink: React.FC<{ link: HeaderLinkType }> = ({ link }) => { const pathname = usePathname() const { t } = useTranslation("site") @@ -135,68 +130,9 @@ export const SiteHeader: React.FC<{ const [searchOpen, setSearchOpen] = useState(false) - const avatarRef = useRef(null) - const bannerRef = useRef(null) - - const isDark = useIsDark() - const [averageColor, setAverageColor] = useState() - const [autoHoverColor, setAutoHoverColor] = useState() - const [autoThemeColor, setAutoThemeColor] = useState() - - useEffect(() => { - if (bannerRef?.current) { - fac - .getColorAsync(bannerRef.current) - .then((color) => { - if (isDark) { - setAverageColor(chroma(color.hex).luminance(0.007).hex()) - setAutoHoverColor(chroma(color.hex).luminance(0.02).hex()) - } else { - setAverageColor(chroma(color.hex).hex()) - setAutoHoverColor(chroma(color.hex).luminance(0.8).hex()) - } - setAutoThemeColor(chroma(color.hex).saturate(3).luminance(0.3).hex()) - }) - .catch((e) => { - console.warn(e) - }) - } else if (avatarRef?.current) { - fac - .getColorAsync(avatarRef.current) - .then((color) => { - if (isDark) { - setAverageColor(chroma(color.hex).luminance(0.007).hex()) - setAutoHoverColor(chroma(color.hex).luminance(0.02).hex()) - } else { - setAverageColor(chroma(color.hex).luminance(0.95).hex()) - setAutoHoverColor(chroma(color.hex).luminance(0.8).hex()) - } - setAutoThemeColor(chroma(color.hex).saturate(3).luminance(0.3).hex()) - }) - .catch((e) => { - console.warn(e) - }) - } - }, [bannerRef, avatarRef, isDark]) - return (
- {averageColor && ( - - )} -
+
{(() => { switch ( site.data?.metadata?.content?.banners?.[0]?.mime_type?.split("/")[0] @@ -208,7 +144,6 @@ export const SiteHeader: React.FC<{ src={site.data?.metadata?.content?.banners?.[0]?.address} alt="banner" fill - imageRef={bannerRef as MutableRefObject} /> ) case "video": @@ -219,7 +154,6 @@ export const SiteHeader: React.FC<{ autoPlay muted playsInline - ref={bannerRef as RefObject} crossOrigin="anonymous" /> ) @@ -245,7 +179,6 @@ export const SiteHeader: React.FC<{ images={site.data?.metadata?.content?.avatars} size={120} name={site.data?.metadata?.content?.name} - imageRef={avatarRef as MutableRefObject} /> )}
diff --git a/src/queries/site.server.ts b/src/queries/site.server.ts index bbd18044..71dc5991 100644 --- a/src/queries/site.server.ts +++ b/src/queries/site.server.ts @@ -1,9 +1,12 @@ +import chroma from "chroma-js" +import { getAverageColor } from "fast-average-color-node" import { Asset } from "unidata.js" import { QueryClient } from "@tanstack/react-query" import { IPFS_GATEWAY } from "~/lib/env" import { cacheGet, getRedis } from "~/lib/redis.server" +import { ExpandedCharacter } from "~/lib/types" import * as siteModel from "~/models/site.model" export const prefetchGetSite = async ( @@ -153,3 +156,69 @@ export const getNFTs = async (address?: string) => { return result } } + +export const getCharacterColors = async (character?: ExpandedCharacter) => { + const key = ["getThemeColors", character?.handle] + if (!character) { + return null + } + return cacheGet({ + key, + getValueFun: async () => { + let colors = { + dark: {}, + light: {}, + } + + if ( + character.metadata?.content?.banners?.[0]?.mime_type?.split("/")[0] === + "image" + ) { + const color = await getAverageColor( + character.metadata.content.banners[0].address, + ) + colors = { + dark: { + averageColor: chroma(color.hex).luminance(0.007).hex(), + autoHoverColor: chroma(color.hex).luminance(0.02).hex(), + autoThemeColor: chroma(color.hex).saturate(3).luminance(0.3).hex(), + }, + light: { + averageColor: chroma(color.hex).hex(), + autoHoverColor: chroma(color.hex).luminance(0.8).hex(), + autoThemeColor: chroma(color.hex).saturate(3).luminance(0.3).hex(), + }, + } + } else if (character.metadata?.content?.avatars?.[0]) { + const color = await getAverageColor( + character.metadata.content.avatars[0], + ) + colors = { + dark: { + averageColor: chroma(color.hex).luminance(0.007).hex(), + autoHoverColor: chroma(color.hex).luminance(0.02).hex(), + autoThemeColor: chroma(color.hex).saturate(3).luminance(0.3).hex(), + }, + light: { + averageColor: chroma(color.hex).luminance(0.95).hex(), + autoHoverColor: chroma(color.hex).luminance(0.8).hex(), + autoThemeColor: chroma(color.hex).saturate(3).luminance(0.3).hex(), + }, + } + } + + return colors + }, + }) as Promise<{ + dark: { + averageColor?: string + autoHoverColor?: string + autoThemeColor?: string + } + light: { + averageColor?: string + autoHoverColor?: string + autoThemeColor?: string + } + }> +}