's'
This commit is contained in:
122
app/context/DevDebugContext.tsx
Normal file
122
app/context/DevDebugContext.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
import {
|
||||
DEBUG_FEATURES,
|
||||
getDebugFeatureDefaultVisible,
|
||||
type DebugFeature,
|
||||
} from "@/config/debug-features";
|
||||
|
||||
const STORAGE_KEY = "nomadcna:dev-debug-overrides";
|
||||
const IS_DEV = process.env.NODE_ENV === "development";
|
||||
|
||||
type Overrides = Record<string, boolean>;
|
||||
|
||||
interface DevDebugContextValue {
|
||||
isDev: boolean;
|
||||
panelOpen: boolean;
|
||||
setPanelOpen: (open: boolean) => void;
|
||||
features: DebugFeature[];
|
||||
overrides: Overrides;
|
||||
isFeatureVisible: (id: string) => boolean;
|
||||
setFeatureVisible: (id: string, visible: boolean) => void;
|
||||
resetAll: () => void;
|
||||
}
|
||||
|
||||
const DevDebugContext = createContext<DevDebugContextValue | null>(null);
|
||||
|
||||
function readOverrides(): Overrides {
|
||||
if (!IS_DEV || typeof window === "undefined") return {};
|
||||
try {
|
||||
const raw = localStorage.getItem(STORAGE_KEY);
|
||||
if (!raw) return {};
|
||||
const parsed = JSON.parse(raw) as Overrides;
|
||||
return parsed && typeof parsed === "object" ? parsed : {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function writeOverrides(overrides: Overrides) {
|
||||
if (!IS_DEV || typeof window === "undefined") return;
|
||||
try {
|
||||
if (Object.keys(overrides).length === 0) {
|
||||
localStorage.removeItem(STORAGE_KEY);
|
||||
} else {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(overrides));
|
||||
}
|
||||
} catch {
|
||||
// ignore quota / private mode
|
||||
}
|
||||
}
|
||||
|
||||
export function DevDebugProvider({ children }: { children: ReactNode }) {
|
||||
const [panelOpen, setPanelOpen] = useState(false);
|
||||
const [overrides, setOverrides] = useState<Overrides>({});
|
||||
|
||||
useEffect(() => {
|
||||
setOverrides(readOverrides());
|
||||
}, []);
|
||||
|
||||
const isFeatureVisible = useCallback(
|
||||
(id: string) => {
|
||||
if (!IS_DEV) return getDebugFeatureDefaultVisible(id);
|
||||
if (id in overrides) return overrides[id];
|
||||
return getDebugFeatureDefaultVisible(id);
|
||||
},
|
||||
[overrides]
|
||||
);
|
||||
|
||||
const setFeatureVisible = useCallback((id: string, visible: boolean) => {
|
||||
if (!IS_DEV) return;
|
||||
setOverrides((prev) => {
|
||||
const defaultVisible = getDebugFeatureDefaultVisible(id);
|
||||
const next = { ...prev };
|
||||
if (visible === defaultVisible) {
|
||||
delete next[id];
|
||||
} else {
|
||||
next[id] = visible;
|
||||
}
|
||||
writeOverrides(next);
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const resetAll = useCallback(() => {
|
||||
if (!IS_DEV) return;
|
||||
setOverrides({});
|
||||
writeOverrides({});
|
||||
}, []);
|
||||
|
||||
const value = useMemo<DevDebugContextValue>(
|
||||
() => ({
|
||||
isDev: IS_DEV,
|
||||
panelOpen,
|
||||
setPanelOpen,
|
||||
features: DEBUG_FEATURES,
|
||||
overrides,
|
||||
isFeatureVisible,
|
||||
setFeatureVisible,
|
||||
resetAll,
|
||||
}),
|
||||
[panelOpen, overrides, isFeatureVisible, setFeatureVisible, resetAll]
|
||||
);
|
||||
|
||||
return <DevDebugContext.Provider value={value}>{children}</DevDebugContext.Provider>;
|
||||
}
|
||||
|
||||
export function useDevDebug() {
|
||||
const context = useContext(DevDebugContext);
|
||||
if (!context) {
|
||||
throw new Error("useDevDebug must be used within DevDebugProvider");
|
||||
}
|
||||
return context;
|
||||
}
|
||||
Reference in New Issue
Block a user