refactor(core): centraliza hooks, providers e base compartilhada

This commit is contained in:
Felipe Coutinho
2026-03-09 17:11:55 +00:00
parent 2de5101058
commit 3e06a1d056
76 changed files with 3271 additions and 709 deletions

View File

@@ -0,0 +1,66 @@
"use client";
import { createContext, useContext, useEffect, useMemo, useState } from "react";
import { getFontVariable } from "@/public/fonts/font_index";
type FontContextValue = {
systemFont: string;
moneyFont: string;
setSystemFont: (key: string) => void;
setMoneyFont: (key: string) => void;
};
const FontContext = createContext<FontContextValue | null>(null);
export function FontProvider({
systemFont: initialSystemFont,
moneyFont: initialMoneyFont,
children,
}: {
systemFont: string;
moneyFont: string;
children: React.ReactNode;
}) {
const [systemFont, setSystemFontState] = useState(initialSystemFont);
const [moneyFont, setMoneyFontState] = useState(initialMoneyFont);
useEffect(() => {
document.documentElement.style.setProperty(
"--font-app",
getFontVariable(systemFont),
);
document.documentElement.style.setProperty(
"--font-money",
getFontVariable(moneyFont),
);
}, [systemFont, moneyFont]);
const value = useMemo(
() => ({
systemFont,
moneyFont,
setSystemFont: setSystemFontState,
setMoneyFont: setMoneyFontState,
}),
[systemFont, moneyFont],
);
return (
<>
<style
dangerouslySetInnerHTML={{
__html: `:root { --font-app: ${getFontVariable(initialSystemFont)}; --font-money: ${getFontVariable(initialMoneyFont)}; }`,
}}
/>
<FontContext value={value}>{children}</FontContext>
</>
);
}
export function useFont() {
const ctx = useContext(FontContext);
if (!ctx) {
throw new Error("useFont must be used within FontProvider");
}
return ctx;
}

View File

@@ -0,0 +1,3 @@
export { FontProvider } from "./font-provider";
export { PrivacyProvider } from "./privacy-provider";
export { ThemeProvider } from "./theme-provider";

View File

@@ -0,0 +1,91 @@
"use client";
import type React from "react";
import {
createContext,
useCallback,
useContext,
useMemo,
useSyncExternalStore,
} from "react";
interface PrivacyContextType {
privacyMode: boolean;
toggle: () => void;
set: (value: boolean) => void;
}
const PrivacyContext = createContext<PrivacyContextType | undefined>(undefined);
const STORAGE_KEY = "app:privacyMode";
const PRIVACY_MODE_EVENT = "openmonetis:privacy-mode";
// Read from localStorage safely (returns false on server)
function getStoredValue(): boolean {
if (typeof window === "undefined") return false;
return localStorage.getItem(STORAGE_KEY) === "true";
}
function notifyPrivacyModeChange() {
window.dispatchEvent(new Event(PRIVACY_MODE_EVENT));
}
// Subscribe to storage changes
function subscribeToStorage(callback: () => void) {
window.addEventListener("storage", callback);
window.addEventListener(PRIVACY_MODE_EVENT, callback);
return () => {
window.removeEventListener("storage", callback);
window.removeEventListener(PRIVACY_MODE_EVENT, callback);
};
}
export function PrivacyProvider({ children }: { children: React.ReactNode }) {
// useSyncExternalStore handles hydration safely
const privacyMode = useSyncExternalStore(
subscribeToStorage,
getStoredValue,
() => false, // Server snapshot
);
const setPrivacyMode = useCallback((value: boolean) => {
if (typeof window === "undefined") {
return;
}
const nextValue = String(value);
if (localStorage.getItem(STORAGE_KEY) === nextValue) {
return;
}
localStorage.setItem(STORAGE_KEY, nextValue);
notifyPrivacyModeChange();
}, []);
const toggle = useCallback(() => {
setPrivacyMode(!privacyMode);
}, [privacyMode, setPrivacyMode]);
const contextValue = useMemo(
() => ({
privacyMode,
toggle,
set: setPrivacyMode,
}),
[privacyMode, toggle, setPrivacyMode],
);
return (
<PrivacyContext.Provider value={contextValue}>
{children}
</PrivacyContext.Provider>
);
}
export function usePrivacyMode() {
const context = useContext(PrivacyContext);
if (context === undefined) {
throw new Error("usePrivacyMode must be used within a PrivacyProvider");
}
return context;
}

View File

@@ -0,0 +1,11 @@
"use client";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import type * as React from "react";
export function ThemeProvider({
children,
...props
}: React.ComponentProps<typeof NextThemesProvider>) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}