import { useEffect, useRef, useState } from 'react'; import { useI18n, LANGS } from '../i18n/I18nProvider'; import { ACCENTS, FONT_SETS, useTheme, type FontSetId } from '../theme/ThemeProvider'; import { Avatar } from './Avatar'; import type { User } from '../api'; // Header: the app's TopBar minus everything camera-only, plus the two controls // the web build adds — theme (light/dark + accent group) and language. export function TopBar({ recipeName, pro, user, exporting, savingPhoto, canUndo, canRedo, onUndo, onRedo, onReset, onSavePhoto, onExport, photoCount, photoMax, onAuth, onSignup, onLogout, }: { recipeName: string; // Saving into the account's own folder is the account's own act, so the // button wears the same PRO marker the chips do until the address is proven. pro: boolean; user: User | null; exporting: boolean; savingPhoto: boolean; canUndo: boolean; canRedo: boolean; onUndo: () => void; onRedo: () => void; onReset: () => void; onSavePhoto: () => void; onExport: () => void; // This account's own folder: the SAVE PHOTO button states the count, and the // API refuses past the cap. A guest sees 0/12 and is sent to sign in. null is // the admin, who the API lets past the cap — the button then just counts. photoCount: number; photoMax: number | null; onAuth: () => void; onSignup: () => void; onLogout: () => void; }) { const { t, lang, setLang } = useI18n(); const { mode, accent, fonts, setMode, setAccent, setFonts } = useTheme(); const [menu, setMenu] = useState<'theme' | 'lang' | 'account' | null>(null); const wrap = useRef(null); useEffect(() => { if (!menu) return; const away = (e: MouseEvent) => { if (!wrap.current?.contains(e.target as Node)) setMenu(null); }; document.addEventListener('mousedown', away); return () => document.removeEventListener('mousedown', away); }, [menu]); return (
RecipesCam {t('nav.studio')} {recipeName} {/* Both arrows are one icon wide, so the pair reads as one control: the counter-clockwise arrow steps back, the clockwise one forward. They sit with the name they would take back — after the mark and the preset. */} {/* The catalogue is a page of its own, open to guests as much as to members — it reads the visitor's own folders and never the server. */} {t('nav.library')}
{menu === 'theme' ? (
{ACCENTS.map((entry) => ( ))}
{/* A dropdown keeps the group compact — the labels are long and the current choice is the only one that needs to be visible. */}
) : null}
{menu === 'lang' ? (
{LANGS.map((entry) => ( ))}
) : null}
{menu === 'account' ? (
{user ? ( <>

{t('auth.loggedInAs', { email: user.email })}

{/* The account control opens the one page that account can act on: moderation for an admin, its own profile for a member. */} {user.admin ? ( {t('nav.admin')} ) : ( <> {t('nav.photos')} {t('nav.profile')} )} ) : ( <> )}
) : null}
); }