background image
This commit is contained in:
@@ -4,11 +4,15 @@ import { usePatternBackground } from "@/components/background/PatternBackgroundP
|
||||
import { THEME_BG_ELEMENT_ID } from "@/lib/helpers/themeCache";
|
||||
import { useLayoutEffect } from "react";
|
||||
|
||||
const THEME_BG_IMG_ID = "cached-theme-bg-img";
|
||||
const THEME_BG_OVERLAY_ID = "cached-theme-bg-overlay";
|
||||
|
||||
export default function AppBackground() {
|
||||
const { isPattern, backgroundStyle } = usePatternBackground();
|
||||
const { isPattern, backgroundStyle, isCustomImage, customImageSettings } =
|
||||
usePatternBackground();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!isPattern) {
|
||||
if (!isPattern && !isCustomImage) {
|
||||
document.getElementById(THEME_BG_ELEMENT_ID)?.remove();
|
||||
return;
|
||||
}
|
||||
@@ -25,8 +29,53 @@ export default function AppBackground() {
|
||||
document.body.appendChild(element);
|
||||
}
|
||||
|
||||
Object.assign(element.style, backgroundStyle as Record<string, string>);
|
||||
}, [backgroundStyle, isPattern]);
|
||||
if (isPattern) {
|
||||
element.style.overflow = "";
|
||||
document.getElementById(THEME_BG_IMG_ID)?.remove();
|
||||
document.getElementById(THEME_BG_OVERLAY_ID)?.remove();
|
||||
Object.assign(element.style, backgroundStyle as Record<string, string>);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isCustomImage && customImageSettings) {
|
||||
const { bgUrl, bgBlur, bgOpacity, bgOverlay } = customImageSettings;
|
||||
|
||||
element.style.backgroundColor = "";
|
||||
element.style.backgroundImage = "";
|
||||
element.style.overflow = "hidden";
|
||||
|
||||
let imgLayer = document.getElementById(THEME_BG_IMG_ID);
|
||||
if (!imgLayer) {
|
||||
imgLayer = document.createElement("div");
|
||||
imgLayer.id = THEME_BG_IMG_ID;
|
||||
imgLayer.style.position = "absolute";
|
||||
imgLayer.style.inset = "-40px";
|
||||
imgLayer.style.backgroundSize = "cover";
|
||||
imgLayer.style.backgroundPosition = "center";
|
||||
imgLayer.style.backgroundRepeat = "no-repeat";
|
||||
element.appendChild(imgLayer);
|
||||
}
|
||||
|
||||
imgLayer.style.backgroundImage = `url("${bgUrl}")`;
|
||||
imgLayer.style.filter = bgBlur ? `blur(${bgBlur}px)` : "";
|
||||
imgLayer.style.opacity =
|
||||
bgOpacity != null ? String(bgOpacity / 100) : "1";
|
||||
|
||||
let overlayLayer = document.getElementById(THEME_BG_OVERLAY_ID);
|
||||
if (!overlayLayer) {
|
||||
overlayLayer = document.createElement("div");
|
||||
overlayLayer.id = THEME_BG_OVERLAY_ID;
|
||||
overlayLayer.style.position = "absolute";
|
||||
overlayLayer.style.inset = "0";
|
||||
element.appendChild(overlayLayer);
|
||||
}
|
||||
|
||||
const overlayOpacity =
|
||||
bgOverlay != null ? Number(bgOverlay) / 100 : 0;
|
||||
overlayLayer.style.backgroundColor =
|
||||
overlayOpacity > 0 ? `rgba(0,0,0,${overlayOpacity})` : "";
|
||||
}
|
||||
}, [backgroundStyle, isPattern, isCustomImage, customImageSettings]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
|
||||
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 {
|
||||
@@ -16,6 +20,7 @@ import {
|
||||
removeCachedThemeBackground,
|
||||
setThemeCache,
|
||||
svgToDataUrl,
|
||||
themeBackgroundMatches,
|
||||
type CachedRestaurantTheme,
|
||||
} from "@/lib/helpers/themeCache";
|
||||
import { useParams } from "next/navigation";
|
||||
@@ -32,14 +37,25 @@ import {
|
||||
|
||||
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 }) {
|
||||
@@ -52,6 +68,10 @@ export function PatternBackgroundProvider({ children }: { children: ReactNode })
|
||||
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);
|
||||
@@ -84,15 +104,21 @@ export function PatternBackgroundProvider({ children }: { children: ReactNode })
|
||||
|
||||
if (!themeBase.isPattern) {
|
||||
clearPatternCssVariables();
|
||||
removeCachedThemeBackground();
|
||||
setPatternImageUrl(null);
|
||||
setThemeCache(restaurantSlug, {
|
||||
...themeBase,
|
||||
patternDataUrl: 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) {
|
||||
@@ -110,9 +136,9 @@ export function PatternBackgroundProvider({ children }: { children: ReactNode })
|
||||
|
||||
const cached = getThemeCache(restaurantSlug);
|
||||
const cacheHit =
|
||||
cached?.isPattern &&
|
||||
cached.bgUrl === themeBase.bgUrl &&
|
||||
cached.menuColor === menuColor &&
|
||||
cached &&
|
||||
themeBackgroundMatches(cached, restaurant) &&
|
||||
cached.isPattern &&
|
||||
cached.patternDataUrl;
|
||||
|
||||
if (cacheHit) {
|
||||
@@ -178,9 +204,20 @@ export function PatternBackgroundProvider({ children }: { children: ReactNode })
|
||||
[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, backgroundStyle }),
|
||||
[backgroundStyle, isPattern],
|
||||
() => ({ isPattern, isCustomImage, backgroundStyle, customImageSettings }),
|
||||
[backgroundStyle, isPattern, isCustomImage, customImageSettings],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -131,10 +131,14 @@ function PreferenceWrapper({ children }: Props) {
|
||||
const restaurant = aboutData.data;
|
||||
const cached = getThemeCache(restaurantSlug);
|
||||
|
||||
const themeBase = buildThemeCacheFromRestaurant(restaurant);
|
||||
setThemeCache(restaurantSlug, {
|
||||
menuColor,
|
||||
bgUrl: restaurant.bgUrl,
|
||||
isPattern: buildThemeCacheFromRestaurant(restaurant).isPattern,
|
||||
bgBlur: themeBase.bgBlur,
|
||||
bgOpacity: themeBase.bgOpacity,
|
||||
bgOverlay: themeBase.bgOverlay,
|
||||
isPattern: themeBase.isPattern,
|
||||
patternDataUrl: cached?.patternDataUrl ?? null,
|
||||
});
|
||||
|
||||
@@ -155,8 +159,7 @@ function PreferenceWrapper({ children }: Props) {
|
||||
if (seenFingerprint === '|' && currentFingerprint !== '|') {
|
||||
localStorage.setItem(getSplashStorageKey(restaurantSlug), currentFingerprint);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [aboutData?.data?.menuColor, restaurantSlug]);
|
||||
}, [aboutData?.data, restaurantSlug]);
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.setAttribute('data-theme', nightMode ? 'dark' : 'light');
|
||||
|
||||
Reference in New Issue
Block a user