import React from 'react'; import { ScrollView } from '@tamagui/scroll-view'; import { YStack, XStack } from '@tamagui/stacks'; import { SizableText as Text } from '@tamagui/text'; import { Button } from '@tamagui/button'; import { ArrowLeft, X } from 'lucide-react'; import SettingsContent from './SettingsContent'; import { useGuestThemeVariant } from '../lib/guestTheme'; import { useTranslation } from '@/guest/i18n/useTranslation'; import { useLocale } from '@/guest/i18n/LocaleContext'; import { LegalMarkdown } from '@/guest/components/legal-markdown'; import type { LocaleCode } from '@/guest/i18n/messages'; const legalLinks = [ { slug: 'impressum', labelKey: 'settings.legal.section.impressum', fallback: 'Impressum' }, { slug: 'datenschutz', labelKey: 'settings.legal.section.privacy', fallback: 'Datenschutz' }, { slug: 'agb', labelKey: 'settings.legal.section.terms', fallback: 'AGB' }, ] as const; type ViewState = | { mode: 'home' } | { mode: 'legal'; slug: (typeof legalLinks)[number]['slug']; labelKey: (typeof legalLinks)[number]['labelKey'] }; type LegalDocumentState = | { phase: 'idle'; title: string; markdown: string; html: string } | { phase: 'loading'; title: string; markdown: string; html: string } | { phase: 'ready'; title: string; markdown: string; html: string } | { phase: 'error'; title: string; markdown: string; html: string }; type SettingsSheetProps = { open: boolean; onOpenChange: (open: boolean) => void; }; export default function SettingsSheet({ open, onOpenChange }: SettingsSheetProps) { const { t } = useTranslation(); const { locale } = useLocale(); const { isDark } = useGuestThemeVariant(); const [view, setView] = React.useState({ mode: 'home' }); const isLegal = view.mode === 'legal'; const legalDocument = useLegalDocument(isLegal ? view.slug : null, locale); const handleBack = React.useCallback(() => setView({ mode: 'home' }), []); const handleOpenLegal = React.useCallback( (slug: (typeof legalLinks)[number]['slug'], labelKey: (typeof legalLinks)[number]['labelKey']) => { setView({ mode: 'legal', slug, labelKey }); }, [] ); React.useEffect(() => { if (!open) { setView({ mode: 'home' }); } }, [open]); return ( <> onOpenChange(false)} onClick={() => onOpenChange(false)} onMouseDown={() => onOpenChange(false)} onTouchStart={() => onOpenChange(false)} /> {isLegal ? ( {legalDocument.phase === 'ready' && legalDocument.title ? legalDocument.title : t(view.labelKey, 'Legal')} {legalDocument.phase === 'loading' ? t('common.actions.loading', 'Loading...') : t('settings.legal.description', 'Legal notice')} ) : ( {t('settings.title', 'Settings')} )} {isLegal ? ( ) : ( onOpenChange(false)} showHeader={false} onOpenLegal={handleOpenLegal} /> )} ); } function LegalView({ document, fallbackTitle }: { document: LegalDocumentState; fallbackTitle: string }) { const { t } = useTranslation(); const { isDark } = useGuestThemeVariant(); const mutedText = isDark ? 'rgba(226, 232, 240, 0.7)' : 'rgba(15, 23, 42, 0.6)'; if (document.phase === 'error') { return ( {t('settings.legal.error', 'Etwas ist schiefgelaufen.')} {t('settings.legal.loading', 'Lade...')} ); } if (document.phase === 'loading' || document.phase === 'idle') { return ( {t('settings.legal.loading', 'Lade...')} ); } return ( {document.title || fallbackTitle} ); } function useLegalDocument(slug: string | null, locale: LocaleCode): LegalDocumentState { const [state, setState] = React.useState({ phase: 'idle', title: '', markdown: '', html: '', }); React.useEffect(() => { if (!slug) { setState({ phase: 'idle', title: '', markdown: '', html: '' }); return; } const controller = new AbortController(); setState((prev) => ({ ...prev, phase: 'loading' })); fetch(`/api/v1/legal/${encodeURIComponent(slug)}?lang=${encodeURIComponent(locale)}`, { headers: { Accept: 'application/json' }, signal: controller.signal, }) .then(async (res) => { if (!res.ok) { throw new Error('Failed to load legal page'); } return res.json(); }) .then((data) => { setState({ phase: 'ready', title: data?.title ?? '', markdown: data?.body_markdown ?? '', html: data?.body_html ?? '', }); }) .catch((error) => { if (error?.name === 'AbortError') return; console.error('Failed to load legal page', error); setState((prev) => ({ ...prev, phase: 'error' })); }); return () => controller.abort(); }, [slug, locale]); return state; }