import '@fontsource-variable/nunito-sans'; import './styles.css'; import { mountAccountMenu, type AccountMenu } from './account/account-menu'; import { client } from './account/appwrite'; import { getCurrentUser } from './account/auth'; import { DONATE_URL } from './config'; import { currentLang, onLangChange, setLang } from './i18n/i18n'; import { legacyRedirect, rerender, startRouter } from './pages/router'; import { mountCardsPage } from './pages/cards-page'; import { mountCommunityPage } from './pages/community-page'; import { mountHomePage } from './pages/home-page'; import { mountResetPage } from './pages/reset-page'; import { mountEditor } from './editor/editor-page'; import { bindEditorToAuth } from './editor/auth-reset'; import { createStore, initialEditorState } from './editor/state'; import { mountDonateBanner } from './shell/donate-banner'; import { mountFooter } from './shell/footer'; import { mountHeader } from './shell/header'; import { initTheme } from './shell/theme'; // Hell/Dunkel: das Skript in index.html hat es vor dem ersten Paint gesetzt; hier wird nur die Browserleiste angeglichen. initTheme(); // Sprache: das Skript in index.html hat schon vor dem ersten Paint gesetzt; hier wird sie übernommen (ohne sie zu speichern: // gespeichert wird nur eine Wahl per Sprachknopf) und dem Appwrite-Client mitgegeben, damit dessen Mails (Passwort) in ihr ankommen. setLang(currentLang(), { persist: false }); client.setLocale(currentLang()); // Alte Editor-Links (`/?karte=`) zeigen jetzt auf `/gestalten?karte=`; der Verlauf bekommt keinen Eintrag dazu. const legacyTarget = legacyRedirect(new URL(location.href)); if (legacyTarget) history.replaceState(null, '', legacyTarget); // Der Editor-Zustand lebt außerhalb der Seiten und überlebt Seitenwechsel. const editorStore = createStore(initialEditorState()); // … und gehört zum angemeldeten Konto: beim Abmelden oder Kontowechsel wird er geleert bzw. entkoppelt. bindEditorToAuth(editorStore); // Der Rahmen der Seite: Spenden-Banner (ohne DONATE_URL bleibt der Platz leer), Kopfzeile mit Navigation, Suche und Knöpfen, // Konto-Menü im Seitenkopf und Fußzeile. Alle tragen Texte der gewählten Sprache und werden bei einem Sprachwechsel neu aufgebaut. const bannerSlot = document.getElementById('donate-banner'); const header = document.getElementById('app-header'); const accountSlot = document.getElementById('account-slot'); const footer = document.getElementById('app-footer'); let unmountHeader: (() => void) | null = null; let accountMenu: AccountMenu | null = null; // Ob der Anmeldestatus schon geholt wurde: ein später neu gebautes Konto-Menü zeigt sich dann sofort (kein Aufblitzen von „Anmelden“). let authKnown = false; function mountShell(): void { if (bannerSlot) mountDonateBanner(bannerSlot, DONATE_URL); if (header) { unmountHeader?.(); unmountHeader = mountHeader(header, { donateUrl: DONATE_URL }); } if (accountSlot) { accountMenu?.dispose(); accountMenu = mountAccountMenu(accountSlot); if (authKnown) accountMenu.ready(); } if (footer) mountFooter(footer); } mountShell(); // Der Anmeldestatus wird einmal beim Start geholt; schlägt das fehl (außer 401, das getCurrentUser selbst als „abgemeldet“ meldet), // gilt der Nutzer als abgemeldet. if (accountSlot) { getCurrentUser() .catch((error: unknown) => { console.error('Anmeldestatus konnte nicht geprüft werden', error); }) .finally(() => { authKnown = true; accountMenu?.ready(); }); } // Sprachwechsel ohne Neuladen: Der Appwrite-Client bekommt die neue Sprache, der Rahmen wird neu gebaut und die aktuelle Seite baut // sich neu auf (der Router setzt auch den Titel neu). Der Editor-Store bleibt dasselbe Objekt, ein Entwurf geht also nicht verloren. onLangChange((lang) => { client.setLocale(lang); // Die Kopfzeile wird ersetzt, der Sprachknopf darunter ebenso: wer ihn per Tastatur bedient hat, behält den Fokus auf dem neuen. const hadFocus = document.activeElement?.matches('.lang-btn') ?? false; mountShell(); if (hadFocus) header?.querySelector('.lang-btn')?.focus(); rerender(); }); // Der Router setzt `document.title` je Seite (titleFor) und ruft für jede Route die passende Mount-Funktion. startRouter((route, root) => { switch (route.id) { case 'gestalten': return mountEditor(root, editorStore); case 'cards': return mountCardsPage(root, editorStore); case 'community': return mountCommunityPage(root, route.params.id ?? ''); case 'reset': return mountResetPage(root); case 'home': return mountHomePage(root); } });