121 lines
3.9 KiB
TypeScript
121 lines
3.9 KiB
TypeScript
"use client";
|
|
|
|
import React, { createContext, useContext, useEffect, useState, useRef } from 'react';
|
|
import { useRouter, usePathname } from 'next/navigation';
|
|
|
|
type Theme = 'dark' | 'light';
|
|
type Currency = 'Rp' | 'USD' | 'Crypto';
|
|
type Locale = 'id' | 'en';
|
|
|
|
interface OmniContextProps {
|
|
theme: Theme;
|
|
setTheme: (t: Theme) => void;
|
|
currency: Currency;
|
|
setCurrency: (c: Currency) => void;
|
|
locale: Locale;
|
|
setLocale: (l: Locale) => void;
|
|
}
|
|
|
|
const OmniContext = createContext<OmniContextProps | null>(null);
|
|
|
|
export const useOmni = () => {
|
|
const ctx = useContext(OmniContext);
|
|
if (!ctx) throw new Error("useOmni must be used within OmniSyncProvider");
|
|
return ctx;
|
|
};
|
|
|
|
export function OmniSyncProvider({ children, initialLocale }: { children: React.ReactNode, initialLocale: Locale }) {
|
|
const [theme, setThemeState] = useState<Theme>('dark');
|
|
const [currency, setCurrencyState] = useState<Currency>('Rp');
|
|
const [locale, setLocaleState] = useState<Locale>(initialLocale);
|
|
const router = useRouter();
|
|
const pathname = usePathname();
|
|
|
|
const setCookie = (name: string, value: string) => {
|
|
const host = window.location.hostname;
|
|
const cookieDomain = process.env.NEXT_PUBLIC_COOKIE_DOMAIN || (host === 'localhost' || host === '127.0.0.1' ? host : `.${host}`);
|
|
document.cookie = `${name}=${value}; path=/; domain=${cookieDomain}; max-age=31536000`;
|
|
// For local dev fallback
|
|
if (window.location.hostname === 'localhost') {
|
|
document.cookie = `${name}=${value}; path=/; max-age=31536000`;
|
|
}
|
|
};
|
|
|
|
const getCookie = (name: string) => {
|
|
const value = `; ${document.cookie}`;
|
|
const parts = value.split(`; ${name}=`);
|
|
if (parts.length === 2) return parts.pop()?.split(';').shift();
|
|
return null;
|
|
};
|
|
|
|
const setTheme = (t: Theme) => {
|
|
setThemeState(t);
|
|
setCookie('omni_theme', t);
|
|
document.documentElement.setAttribute('data-theme', t);
|
|
channelRef.current?.postMessage({ type: 'SYNC_THEME', payload: t });
|
|
};
|
|
|
|
const setCurrency = (c: Currency) => {
|
|
setCurrencyState(c);
|
|
setCookie('omni_currency', c);
|
|
channelRef.current?.postMessage({ type: 'SYNC_CURRENCY', payload: c });
|
|
};
|
|
|
|
const setLocale = (l: Locale) => {
|
|
setLocaleState(l);
|
|
setCookie('NEXT_LOCALE', l); // next-intl standard cookie
|
|
channelRef.current?.postMessage({ type: 'SYNC_LOCALE', payload: l });
|
|
};
|
|
|
|
// Broadcast Channel for Cross-Tab Sync
|
|
const channelRef = useRef<BroadcastChannel | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (typeof window !== 'undefined') {
|
|
const bc = new BroadcastChannel('omni_sync_channel');
|
|
channelRef.current = bc;
|
|
return () => bc.close();
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
// Initialize from cookies
|
|
const savedTheme = getCookie('omni_theme') as Theme;
|
|
if (savedTheme) {
|
|
document.documentElement.setAttribute('data-theme', savedTheme);
|
|
setTimeout(() => setThemeState(savedTheme), 0);
|
|
} else {
|
|
document.documentElement.setAttribute('data-theme', 'dark');
|
|
}
|
|
|
|
const savedCurrency = getCookie('omni_currency') as Currency;
|
|
if (savedCurrency) setTimeout(() => setCurrencyState(savedCurrency), 0);
|
|
|
|
const bc = channelRef.current;
|
|
if (bc) {
|
|
const handleMessage = (event: MessageEvent) => {
|
|
const { type, payload } = event.data;
|
|
if (type === 'SYNC_THEME') {
|
|
setThemeState(payload);
|
|
document.documentElement.setAttribute('data-theme', payload);
|
|
}
|
|
if (type === 'SYNC_CURRENCY') {
|
|
setCurrencyState(payload);
|
|
}
|
|
if (type === 'SYNC_LOCALE') {
|
|
setLocaleState(payload);
|
|
// For VC and C, we just trigger a state change or soft reload
|
|
}
|
|
};
|
|
bc.addEventListener('message', handleMessage);
|
|
return () => bc.removeEventListener('message', handleMessage);
|
|
}
|
|
}, [pathname, router]);
|
|
|
|
return (
|
|
<OmniContext.Provider value={{ theme, setTheme, currency, setCurrency, locale, setLocale }}>
|
|
{children}
|
|
</OmniContext.Provider>
|
|
);
|
|
}
|