233 lines
6.2 KiB
TypeScript
233 lines
6.2 KiB
TypeScript
"use client";
|
|
|
|
import { useGetAbout } from "@/app/[name]/(Main)/about/hooks/useAboutData";
|
|
import {
|
|
applyCustomImageCssVariables,
|
|
applyPatternCssVariables,
|
|
clearCustomImageCssVariables,
|
|
clearPatternCssVariables,
|
|
colorizeSvg,
|
|
hexToRgba,
|
|
isCustomImageBackground,
|
|
isPatternBackground,
|
|
normalizeBgNumber,
|
|
PATTERN_BACKGROUND_OPACITY,
|
|
} from "@/lib/helpers/backgroundUtils";
|
|
import {
|
|
applyCachedThemeToDom,
|
|
buildThemeCacheFromRestaurant,
|
|
getThemeCache,
|
|
removeCachedThemeBackground,
|
|
setThemeCache,
|
|
svgToDataUrl,
|
|
themeBackgroundMatches,
|
|
type CachedRestaurantTheme,
|
|
} from "@/lib/helpers/themeCache";
|
|
import { useParams } from "next/navigation";
|
|
import {
|
|
createContext,
|
|
useContext,
|
|
useEffect,
|
|
useLayoutEffect,
|
|
useMemo,
|
|
useState,
|
|
type CSSProperties,
|
|
type ReactNode,
|
|
} from "react";
|
|
|
|
const DEFAULT_MENU_COLOR = "#1E3A8A";
|
|
|
|
export type CustomImageSettings = {
|
|
bgUrl: string;
|
|
bgBlur: number | null;
|
|
bgOpacity: number | null;
|
|
bgOverlay: string | null;
|
|
};
|
|
|
|
type PatternBackgroundContextValue = {
|
|
isPattern: boolean;
|
|
isCustomImage: boolean;
|
|
backgroundStyle: CSSProperties;
|
|
customImageSettings: CustomImageSettings | null;
|
|
};
|
|
|
|
const PatternBackgroundContext = createContext<PatternBackgroundContextValue>({
|
|
isPattern: false,
|
|
isCustomImage: false,
|
|
backgroundStyle: {},
|
|
customImageSettings: null,
|
|
});
|
|
|
|
export function PatternBackgroundProvider({ children }: { children: ReactNode }) {
|
|
const { name: restaurantSlug } = useParams<{ name: string }>();
|
|
const { data: aboutData } = useGetAbout();
|
|
const restaurant = aboutData?.data;
|
|
const [hydratedCache, setHydratedCache] = useState<CachedRestaurantTheme | null>(null);
|
|
const [patternImageUrl, setPatternImageUrl] = useState<string | null>(null);
|
|
|
|
const isPattern = restaurant
|
|
? isPatternBackground(restaurant)
|
|
: (hydratedCache?.isPattern ?? false);
|
|
|
|
const isCustomImage = restaurant
|
|
? isCustomImageBackground(restaurant)
|
|
: (!hydratedCache?.isPattern && !!hydratedCache?.bgUrl);
|
|
const menuColor =
|
|
restaurant?.menuColor || hydratedCache?.menuColor || DEFAULT_MENU_COLOR;
|
|
const baseTint = hexToRgba(menuColor, PATTERN_BACKGROUND_OPACITY);
|
|
|
|
useLayoutEffect(() => {
|
|
if (!restaurantSlug) return;
|
|
|
|
const cached = getThemeCache(restaurantSlug);
|
|
setHydratedCache(cached);
|
|
|
|
if (!cached) return;
|
|
|
|
applyCachedThemeToDom(cached);
|
|
if (cached.patternDataUrl) {
|
|
setPatternImageUrl(cached.patternDataUrl);
|
|
}
|
|
}, [restaurantSlug]);
|
|
|
|
useEffect(() => {
|
|
if (!restaurantSlug) {
|
|
clearPatternCssVariables();
|
|
removeCachedThemeBackground();
|
|
setPatternImageUrl(null);
|
|
return;
|
|
}
|
|
|
|
if (!restaurant) return;
|
|
|
|
const themeBase = buildThemeCacheFromRestaurant(restaurant);
|
|
|
|
if (!themeBase.isPattern) {
|
|
clearPatternCssVariables();
|
|
setPatternImageUrl(null);
|
|
|
|
if (isCustomImageBackground(restaurant)) {
|
|
applyCustomImageCssVariables();
|
|
setThemeCache(restaurantSlug, { ...themeBase, patternDataUrl: null });
|
|
} else {
|
|
clearCustomImageCssVariables();
|
|
removeCachedThemeBackground();
|
|
setThemeCache(restaurantSlug, { ...themeBase, patternDataUrl: null });
|
|
}
|
|
return;
|
|
}
|
|
|
|
clearCustomImageCssVariables();
|
|
|
|
applyPatternCssVariables(menuColor);
|
|
|
|
if (!themeBase.bgUrl) {
|
|
setPatternImageUrl(null);
|
|
setThemeCache(restaurantSlug, {
|
|
...themeBase,
|
|
patternDataUrl: null,
|
|
});
|
|
const saved = getThemeCache(restaurantSlug);
|
|
if (saved) {
|
|
applyCachedThemeToDom(saved);
|
|
}
|
|
return;
|
|
}
|
|
|
|
const cached = getThemeCache(restaurantSlug);
|
|
const cacheHit =
|
|
cached &&
|
|
themeBackgroundMatches(cached, restaurant) &&
|
|
cached.isPattern &&
|
|
cached.patternDataUrl;
|
|
|
|
if (cacheHit) {
|
|
setPatternImageUrl(cached.patternDataUrl!);
|
|
setThemeCache(restaurantSlug, {
|
|
...themeBase,
|
|
patternDataUrl: cached.patternDataUrl,
|
|
});
|
|
applyCachedThemeToDom(cached);
|
|
return;
|
|
}
|
|
|
|
let cancelled = false;
|
|
const proxyUrl = `/${restaurantSlug}/api/proxy-svg?url=${encodeURIComponent(themeBase.bgUrl)}`;
|
|
|
|
fetch(proxyUrl)
|
|
.then((response) => {
|
|
if (!response.ok) {
|
|
throw new Error("Failed to fetch background pattern");
|
|
}
|
|
return response.text();
|
|
})
|
|
.then((svg) => {
|
|
if (cancelled) return;
|
|
|
|
const dataUrl = svgToDataUrl(colorizeSvg(svg, menuColor));
|
|
setPatternImageUrl(dataUrl);
|
|
|
|
const nextCache = {
|
|
...themeBase,
|
|
patternDataUrl: dataUrl,
|
|
};
|
|
setThemeCache(restaurantSlug, nextCache);
|
|
|
|
const saved = getThemeCache(restaurantSlug);
|
|
if (saved) {
|
|
applyCachedThemeToDom(saved);
|
|
}
|
|
})
|
|
.catch(() => {
|
|
if (!cancelled && cached?.patternDataUrl) {
|
|
setPatternImageUrl(cached.patternDataUrl);
|
|
applyCachedThemeToDom(cached);
|
|
}
|
|
});
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [menuColor, restaurant, restaurantSlug]);
|
|
|
|
const backgroundStyle = useMemo<CSSProperties>(
|
|
() => ({
|
|
backgroundColor: baseTint,
|
|
...(patternImageUrl
|
|
? {
|
|
backgroundImage: `url("${patternImageUrl}")`,
|
|
backgroundRepeat: "repeat",
|
|
backgroundSize: "auto",
|
|
}
|
|
: {}),
|
|
}),
|
|
[baseTint, patternImageUrl],
|
|
);
|
|
|
|
const customImageSettings = useMemo<CustomImageSettings | null>(() => {
|
|
const src = restaurant?.bgUrl || hydratedCache?.bgUrl;
|
|
if (!isCustomImage || !src) return null;
|
|
return {
|
|
bgUrl: src,
|
|
bgBlur: normalizeBgNumber(restaurant?.bgBlur ?? hydratedCache?.bgBlur),
|
|
bgOpacity: normalizeBgNumber(restaurant?.bgOpacity ?? hydratedCache?.bgOpacity),
|
|
bgOverlay: restaurant?.bgOverlay ?? hydratedCache?.bgOverlay ?? null,
|
|
};
|
|
}, [isCustomImage, restaurant, hydratedCache]);
|
|
|
|
const value = useMemo(
|
|
() => ({ isPattern, isCustomImage, backgroundStyle, customImageSettings }),
|
|
[backgroundStyle, isPattern, isCustomImage, customImageSettings],
|
|
);
|
|
|
|
return (
|
|
<PatternBackgroundContext.Provider value={value}>
|
|
{children}
|
|
</PatternBackgroundContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function usePatternBackground() {
|
|
return useContext(PatternBackgroundContext);
|
|
}
|