Fortune-Email/stories/CustomizationDecorator.tsx

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>
);
};