124 lines
3.6 KiB
TypeScript
124 lines
3.6 KiB
TypeScript
import React, { useState, useCallback, useEffect } from "react";
|
|
import { BrandingContext } from "../src/hooks/useBrandingConfig";
|
|
import { BrandingConfig, defaultBrandingConfig } from "../src/config/branding.config";
|
|
|
|
interface CustomizationDecoratorProps {
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
const CUSTOMIZATION_STORAGE_KEY = "email-branding-config";
|
|
|
|
export const CustomizationDecorator = ({ children }: CustomizationDecoratorProps) => {
|
|
const [config, setConfig] = useState<BrandingConfig>(() => {
|
|
// Load from localStorage if available
|
|
if (typeof window !== "undefined") {
|
|
const saved = localStorage.getItem(CUSTOMIZATION_STORAGE_KEY);
|
|
if (saved) {
|
|
try {
|
|
return { ...defaultBrandingConfig, ...JSON.parse(saved) };
|
|
} catch (e) {
|
|
return defaultBrandingConfig;
|
|
}
|
|
}
|
|
}
|
|
return defaultBrandingConfig;
|
|
});
|
|
|
|
const updateConfig = useCallback((updates: Partial<BrandingConfig>) => {
|
|
setConfig((prev) => {
|
|
const newConfig = {
|
|
...prev,
|
|
...updates,
|
|
colors: {
|
|
...prev.colors,
|
|
...(updates.colors || {}),
|
|
},
|
|
font: {
|
|
...prev.font,
|
|
...(updates.font || {}),
|
|
},
|
|
};
|
|
|
|
// Save to localStorage
|
|
if (typeof window !== "undefined") {
|
|
localStorage.setItem(CUSTOMIZATION_STORAGE_KEY, JSON.stringify(newConfig));
|
|
}
|
|
|
|
return newConfig;
|
|
});
|
|
}, []);
|
|
|
|
// Listen for customization updates from panel
|
|
useEffect(() => {
|
|
const handleStorageChange = (e: StorageEvent) => {
|
|
if (e.key === CUSTOMIZATION_STORAGE_KEY && e.newValue) {
|
|
try {
|
|
const newConfig = { ...defaultBrandingConfig, ...JSON.parse(e.newValue) };
|
|
setConfig(newConfig);
|
|
} catch (e) {
|
|
// Ignore parse errors
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleCustomEvent = (e: CustomEvent) => {
|
|
if (e.detail?.config) {
|
|
setConfig((prev) => ({
|
|
...prev,
|
|
...e.detail.config,
|
|
colors: {
|
|
...prev.colors,
|
|
...(e.detail.config.colors || {}),
|
|
},
|
|
font: {
|
|
...prev.font,
|
|
...(e.detail.config.font || {}),
|
|
},
|
|
}));
|
|
}
|
|
};
|
|
|
|
const handleMessage = (event: MessageEvent) => {
|
|
if (event.data?.type === "CUSTOMIZATION_UPDATE") {
|
|
updateConfig(event.data.config);
|
|
}
|
|
};
|
|
|
|
// Poll localStorage for changes (for same-tab updates)
|
|
const pollInterval = setInterval(() => {
|
|
const saved = localStorage.getItem(CUSTOMIZATION_STORAGE_KEY);
|
|
if (saved) {
|
|
try {
|
|
const savedConfig = JSON.parse(saved);
|
|
setConfig((prev) => {
|
|
const prevStr = JSON.stringify(prev);
|
|
if (prevStr !== saved) {
|
|
return { ...defaultBrandingConfig, ...savedConfig };
|
|
}
|
|
return prev;
|
|
});
|
|
} catch (e) {
|
|
// Ignore parse errors
|
|
}
|
|
}
|
|
}, 500);
|
|
|
|
window.addEventListener("storage", handleStorageChange);
|
|
window.addEventListener("message", handleMessage);
|
|
window.addEventListener("customization-update" as any, handleCustomEvent as EventListener);
|
|
|
|
return () => {
|
|
clearInterval(pollInterval);
|
|
window.removeEventListener("storage", handleStorageChange);
|
|
window.removeEventListener("message", handleMessage);
|
|
window.removeEventListener("customization-update" as any, handleCustomEvent as EventListener);
|
|
};
|
|
}, [updateConfig]);
|
|
|
|
return (
|
|
<BrandingContext.Provider value={{ config, updateConfig }}>
|
|
{children}
|
|
</BrandingContext.Provider>
|
|
);
|
|
};
|