Guest PWA vollständig lokalisiert
This commit is contained in:
@@ -22,6 +22,9 @@ import SlideshowPage from './pages/SlideshowPage';
|
||||
import SettingsPage from './pages/SettingsPage';
|
||||
import LegalPage from './pages/LegalPage';
|
||||
import NotFoundPage from './pages/NotFoundPage';
|
||||
import { LocaleProvider } from './i18n/LocaleContext';
|
||||
import { DEFAULT_LOCALE, isLocaleCode } from './i18n/messages';
|
||||
import { useTranslation, type TranslateFn } from './i18n/useTranslation';
|
||||
|
||||
function HomeLayout() {
|
||||
const { token } = useParams();
|
||||
@@ -85,41 +88,52 @@ function EventBoundary({ token }: { token: string }) {
|
||||
return <EventErrorView code={errorCode} message={error} />;
|
||||
}
|
||||
|
||||
const eventLocale = isLocaleCode(event.default_locale) ? event.default_locale : DEFAULT_LOCALE;
|
||||
const localeStorageKey = `guestLocale_event_${event.id ?? token}`;
|
||||
|
||||
return (
|
||||
<EventStatsProvider eventKey={token}>
|
||||
<div className="pb-16">
|
||||
<Header slug={token} />
|
||||
<div className="px-4 py-3">
|
||||
<Outlet />
|
||||
<LocaleProvider defaultLocale={eventLocale} storageKey={localeStorageKey}>
|
||||
<EventStatsProvider eventKey={token}>
|
||||
<div className="pb-16">
|
||||
<Header slug={token} />
|
||||
<div className="px-4 py-3">
|
||||
<Outlet />
|
||||
</div>
|
||||
<BottomNav />
|
||||
</div>
|
||||
<BottomNav />
|
||||
</div>
|
||||
</EventStatsProvider>
|
||||
</EventStatsProvider>
|
||||
</LocaleProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function SetupLayout() {
|
||||
const { token } = useParams<{ token: string }>();
|
||||
if (!token) return null;
|
||||
const { event } = useEventData();
|
||||
const eventLocale = event && isLocaleCode(event.default_locale) ? event.default_locale : DEFAULT_LOCALE;
|
||||
const localeStorageKey = event ? `guestLocale_event_${event.id}` : `guestLocale_event_${token}`;
|
||||
return (
|
||||
<GuestIdentityProvider eventKey={token}>
|
||||
<EventStatsProvider eventKey={token}>
|
||||
<div className="pb-0">
|
||||
<Header slug={token} />
|
||||
<Outlet />
|
||||
</div>
|
||||
</EventStatsProvider>
|
||||
<LocaleProvider defaultLocale={eventLocale} storageKey={localeStorageKey}>
|
||||
<EventStatsProvider eventKey={token}>
|
||||
<div className="pb-0">
|
||||
<Header slug={token} />
|
||||
<Outlet />
|
||||
</div>
|
||||
</EventStatsProvider>
|
||||
</LocaleProvider>
|
||||
</GuestIdentityProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function EventLoadingView() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col items-center justify-center gap-4 px-6 text-center">
|
||||
<Loader2 className="h-10 w-10 animate-spin text-muted-foreground" aria-hidden />
|
||||
<div className="space-y-1">
|
||||
<p className="text-lg font-semibold text-foreground">Wir prüfen deinen Zugang...</p>
|
||||
<p className="text-sm text-muted-foreground">Einen Moment bitte.</p>
|
||||
<p className="text-lg font-semibold text-foreground">{t('eventAccess.loading.title')}</p>
|
||||
<p className="text-sm text-muted-foreground">{t('eventAccess.loading.subtitle')}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -131,7 +145,8 @@ interface EventErrorViewProps {
|
||||
}
|
||||
|
||||
function EventErrorView({ code, message }: EventErrorViewProps) {
|
||||
const content = getErrorContent(code, message);
|
||||
const { t } = useTranslation();
|
||||
const content = getErrorContent(t, code, message);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col items-center justify-center gap-6 px-6 text-center">
|
||||
@@ -155,50 +170,39 @@ function EventErrorView({ code, message }: EventErrorViewProps) {
|
||||
}
|
||||
|
||||
function getErrorContent(
|
||||
t: TranslateFn,
|
||||
code: FetchEventErrorCode | null,
|
||||
message: string | null,
|
||||
) {
|
||||
const base = (fallbackTitle: string, fallbackDescription: string, options?: { ctaLabel?: string; ctaHref?: string; hint?: string }) => ({
|
||||
title: fallbackTitle,
|
||||
description: message ?? fallbackDescription,
|
||||
ctaLabel: options?.ctaLabel,
|
||||
ctaHref: options?.ctaHref,
|
||||
hint: options?.hint ?? null,
|
||||
});
|
||||
const build = (key: string, options?: { ctaHref?: string }) => {
|
||||
const ctaLabel = t(`eventAccess.error.${key}.ctaLabel`, '');
|
||||
const hint = t(`eventAccess.error.${key}.hint`, '');
|
||||
return {
|
||||
title: t(`eventAccess.error.${key}.title`),
|
||||
description: message ?? t(`eventAccess.error.${key}.description`),
|
||||
ctaLabel: ctaLabel.trim().length > 0 ? ctaLabel : undefined,
|
||||
ctaHref: options?.ctaHref,
|
||||
hint: hint.trim().length > 0 ? hint : null,
|
||||
};
|
||||
};
|
||||
|
||||
switch (code) {
|
||||
case 'invalid_token':
|
||||
return base('Zugriffscode ungültig', 'Der eingegebene Code konnte nicht verifiziert werden.', {
|
||||
ctaLabel: 'Neuen Code anfordern',
|
||||
ctaHref: '/event',
|
||||
});
|
||||
return build('invalid_token', { ctaHref: '/event' });
|
||||
case 'token_revoked':
|
||||
return base('Zugriffscode deaktiviert', 'Dieser Code wurde zurückgezogen. Bitte fordere einen neuen Code an.', {
|
||||
ctaLabel: 'Neuen Code anfordern',
|
||||
ctaHref: '/event',
|
||||
});
|
||||
return build('token_revoked', { ctaHref: '/event' });
|
||||
case 'token_expired':
|
||||
return base('Zugriffscode abgelaufen', 'Der Code ist nicht mehr gültig. Aktualisiere deinen Code, um fortzufahren.', {
|
||||
ctaLabel: 'Code aktualisieren',
|
||||
ctaHref: '/event',
|
||||
});
|
||||
return build('token_expired', { ctaHref: '/event' });
|
||||
case 'token_rate_limited':
|
||||
return base('Zu viele Versuche', 'Es gab zu viele Eingaben in kurzer Zeit. Warte kurz und versuche es erneut.', {
|
||||
hint: 'Tipp: Eine erneute Eingabe ist in wenigen Minuten möglich.',
|
||||
});
|
||||
return build('token_rate_limited');
|
||||
case 'event_not_public':
|
||||
return base('Event nicht öffentlich', 'Dieses Event ist aktuell nicht öffentlich zugänglich.', {
|
||||
hint: 'Nimm Kontakt mit den Veranstalter:innen auf, um Zugang zu erhalten.',
|
||||
});
|
||||
return build('event_not_public');
|
||||
case 'network_error':
|
||||
return base('Verbindungsproblem', 'Wir konnten keine Verbindung zum Server herstellen. Prüfe deine Internetverbindung und versuche es erneut.');
|
||||
return build('network_error');
|
||||
case 'server_error':
|
||||
return base('Server nicht erreichbar', 'Der Server reagiert derzeit nicht. Versuche es später erneut.');
|
||||
return build('server_error');
|
||||
default:
|
||||
return base('Event nicht erreichbar', 'Wir konnten dein Event nicht laden. Bitte versuche es erneut.', {
|
||||
ctaLabel: 'Zur Code-Eingabe',
|
||||
ctaHref: '/event',
|
||||
});
|
||||
return build('default', { ctaHref: '/event' });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user