import React from 'react'; import { YStack, XStack } from '@tamagui/stacks'; import { SizableText as Text } from '@tamagui/text'; import { Button } from '@tamagui/button'; import { Camera, CheckCircle2, Heart, RefreshCw, Sparkles, Timer as TimerIcon } from 'lucide-react'; import PhotoFrameTile from './PhotoFrameTile'; import { useTranslation } from '@/guest/i18n/useTranslation'; import { getEmotionIcon, getEmotionTheme, type EmotionIdentity } from '@/guest/lib/emotionTheme'; import { useGuestThemeVariant } from '../lib/guestTheme'; import { getBentoSurfaceTokens } from '../lib/bento'; type TaskHeroEmotion = EmotionIdentity & { emoji?: string | null }; export type TaskHero = { id: number; title: string; description?: string | null; instructions?: string | null; duration?: number | null; emotion?: TaskHeroEmotion | null; }; export type TaskHeroPhoto = { id: number; imageUrl: string; likesCount?: number; }; type TaskHeroCardProps = { task: TaskHero | null; loading: boolean; error?: string | null; hasSwiped: boolean; onSwiped: () => void; onStart: () => void; onShuffle: () => void; onViewSimilar: () => void; onRetry: () => void; onOpenPhoto: (photoId: number) => void; isCompleted: boolean; photos: TaskHeroPhoto[]; photosLoading: boolean; photosError?: string | null; }; const SWIPE_THRESHOLD_PX = 40; export default function TaskHeroCard({ task, loading, error, hasSwiped, onSwiped, onStart, onShuffle, onViewSimilar, onRetry, onOpenPhoto, isCompleted, photos, photosLoading, photosError, }: TaskHeroCardProps) { const { t } = useTranslation(); const heroCardRef = React.useRef(null); const theme = getEmotionTheme(task?.emotion ?? null); const emotionIcon = getEmotionIcon(task?.emotion ?? null); const { isDark } = useGuestThemeVariant(); const bentoSurface = getBentoSurfaceTokens(isDark); React.useEffect(() => { const card = heroCardRef.current; if (!card) return; let startX: number | null = null; let startY: number | null = null; const onTouchStart = (event: TouchEvent) => { const touch = event.touches[0]; startX = touch.clientX; startY = touch.clientY; }; const onTouchEnd = (event: TouchEvent) => { if (startX === null || startY === null) return; const touch = event.changedTouches[0]; const deltaX = touch.clientX - startX; const deltaY = touch.clientY - startY; if (Math.abs(deltaX) > SWIPE_THRESHOLD_PX && Math.abs(deltaY) < 60) { if (deltaX < 0) { onShuffle(); } else { onViewSimilar(); } onSwiped(); } startX = null; startY = null; }; card.addEventListener('touchstart', onTouchStart, { passive: true }); card.addEventListener('touchend', onTouchEnd); return () => { card.removeEventListener('touchstart', onTouchStart); card.removeEventListener('touchend', onTouchEnd); }; }, [onShuffle, onSwiped, onViewSimilar]); if (loading && !task) { return ( {t('common.actions.loading', 'Loading...')} ); } if (error && !task) { return ( {error} ); } if (!task) { return ( {t('tasks.page.emptyTitle', 'No matching task found')} ); } return ( {emotionIcon} {task.emotion?.name ?? t('tasks.page.eyebrow', 'New mission')} {task.duration ? ( {task.duration} min ) : null} {task.title} {task.description ? ( {task.description} ) : null} {!hasSwiped ? ( {t('tasks.page.swipeHint', 'Swipe for more missions')} ) : null} {task.instructions ? ( {task.instructions} ) : null} {isCompleted ? ( {t('tasks.page.completedLabel', 'Completed')} ) : null} {(photosLoading || photosError || photos.length > 0) && ( {t('tasks.page.inspirationTitle', 'Inspiration')} {photosLoading ? ( {t('tasks.page.inspirationLoading', 'Loading')} ) : null} {photosError && photos.length === 0 ? ( {photosError} ) : photos.length > 0 ? ( {photos.map((photo) => ( ))} ) : ( )} )} ); }