import React from 'react'; import { useParams } from 'react-router-dom'; import { YStack, XStack } from '@tamagui/stacks'; import { SizableText as Text } from '@tamagui/text'; import { Button } from '@tamagui/button'; import { AlertCircle, Download } from 'lucide-react'; import StandaloneShell from '../components/StandaloneShell'; import SurfaceCard from '../components/SurfaceCard'; import EventLogo from '../components/EventLogo'; import { fetchPhotoShare } from '@/guest/services/photosApi'; import type { EventBrandingPayload } from '@/guest/services/eventApi'; import { useTranslation } from '@/guest/i18n/useTranslation'; import { EventBrandingProvider } from '@/guest/context/EventBrandingContext'; import { mapEventBranding } from '../lib/eventBranding'; import { BrandingTheme } from '../lib/brandingTheme'; import { useGuestThemeVariant } from '../lib/guestTheme'; interface ShareResponse { slug: string; expires_at?: string; photo: { id: number; title?: string; likes_count?: number; emotion?: { name?: string; emoji?: string | null } | null; created_at?: string | null; image_urls: { full: string; thumbnail: string }; }; event?: { id: number; name?: string | null } | null; branding?: EventBrandingPayload | null; } export default function SharedPhotoScreen() { const { slug } = useParams<{ slug: string }>(); const { t } = useTranslation(); const [state, setState] = React.useState<{ loading: boolean; error: string | null; data: ShareResponse | null }>({ loading: true, error: null, data: null, }); const branding = React.useMemo(() => { if (!state.data?.branding) { return null; } return mapEventBranding(state.data.branding); }, [state.data]); const { isDark } = useGuestThemeVariant(branding); const mutedText = isDark ? 'rgba(248, 250, 252, 0.7)' : 'rgba(15, 23, 42, 0.65)'; React.useEffect(() => { let active = true; if (!slug) return; setState({ loading: true, error: null, data: null }); fetchPhotoShare(slug) .then((data) => { if (active) setState({ loading: false, error: null, data }); }) .catch(() => { if (active) { setState({ loading: false, error: t('share.expiredDescription', 'Dieser Link ist nicht mehr verfügbar.'), data: null, }); } }); return () => { active = false; }; }, [slug]); if (state.loading) { return ( {t('share.loading', 'Moment wird geladen …')} ); } if (state.error || !state.data) { return ( {t('share.expiredTitle', 'Link abgelaufen')} {state.error ?? t('share.expiredDescription', 'Dieses Foto ist nicht mehr verfügbar.')} ); } const { data } = state; const chips = buildChips(data, t); const content = ( {t('share.title', 'Geteiltes Foto')} {data.event?.name ?? t('share.defaultEvent', 'Ein besonderer Moment')} {data.photo.title ? ( {data.photo.title} ) : null} {chips.length > 0 ? ( {chips.map((chip) => ( {chip.icon ? {chip.icon} : null} {chip.label} {chip.value} ))} ) : null} ); if (!branding) { return content; } return ( {content} ); } function buildChips( data: ShareResponse, t: (key: string, fallback?: string) => string ): { id: string; label: string; value: string; icon?: string }[] { const list: { id: string; label: string; value: string; icon?: string }[] = []; if (data.photo.emotion?.name) { list.push({ id: 'emotion', label: t('share.chips.emotion', 'Emotion'), value: data.photo.emotion.name, icon: data.photo.emotion.emoji ?? '★', }); } if (data.photo.title) { list.push({ id: 'task', label: t('share.chips.task', 'Aufgabe'), value: data.photo.title }); } if (data.photo.created_at) { const date = formatDate(data.photo.created_at); list.push({ id: 'date', label: t('share.chips.date', 'Aufgenommen'), value: date }); } return list; } function formatDate(value: string): string { const parsed = new Date(value); if (Number.isNaN(parsed.getTime())) return ''; return parsed.toLocaleDateString(undefined, { day: '2-digit', month: 'short', year: 'numeric' }); }