refactor(guest): retire legacy guest app and move shared modules
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-02-06 08:42:53 +01:00
parent b14435df8b
commit 0a08f2704f
191 changed files with 243 additions and 12631 deletions

View File

@@ -4,7 +4,7 @@ import { SizableText as Text } from '@tamagui/text';
import { Button } from '@tamagui/button';
import { Award, BarChart2, Camera, Flame, Sparkles, Trophy, Users } from 'lucide-react';
import AppShell from '../components/AppShell';
import PullToRefresh from '@/guest/components/PullToRefresh';
import PullToRefresh from '@/shared/guest/components/PullToRefresh';
import { useEventData } from '../context/EventDataContext';
import { useOptionalGuestIdentity } from '../context/GuestIdentityContext';
import {
@@ -17,11 +17,11 @@ import {
type TopPhotoHighlight,
type TrendingEmotionHighlight,
} from '../services/achievementsApi';
import { useTranslation } from '@/guest/i18n/useTranslation';
import { useLocale } from '@/guest/i18n/LocaleContext';
import { useTranslation } from '@/shared/guest/i18n/useTranslation';
import { useLocale } from '@/shared/guest/i18n/LocaleContext';
import { useGuestThemeVariant } from '../lib/guestTheme';
import { getBentoSurfaceTokens } from '../lib/bento';
import { localizeTaskLabel } from '@/guest/lib/localizeTaskLabel';
import { localizeTaskLabel } from '@/shared/guest/lib/localizeTaskLabel';
import { buildEventPath } from '../lib/routes';
import { useNavigate } from 'react-router-dom';
@@ -76,9 +76,10 @@ type LeaderboardProps = {
emptyCopy: string;
formatNumber: (value: number) => string;
guestFallback: string;
isDark: boolean;
};
function Leaderboard({ title, description, icon: Icon, entries, emptyCopy, formatNumber, guestFallback }: LeaderboardProps) {
function Leaderboard({ title, description, icon: Icon, entries, emptyCopy, formatNumber, guestFallback, isDark }: LeaderboardProps) {
return (
<YStack gap="$2">
<XStack alignItems="center" gap="$2">
@@ -114,9 +115,9 @@ function Leaderboard({ title, description, icon: Icon, entries, emptyCopy, forma
justifyContent="space-between"
padding="$2"
borderRadius="$card"
backgroundColor="rgba(15, 23, 42, 0.05)"
backgroundColor={isDark ? 'rgba(255, 255, 255, 0.08)' : 'rgba(15, 23, 42, 0.05)'}
borderWidth={1}
borderColor="rgba(15, 23, 42, 0.08)"
borderColor={isDark ? 'rgba(255, 255, 255, 0.16)' : 'rgba(15, 23, 42, 0.08)'}
>
<XStack alignItems="center" gap="$2">
<Text fontSize="$1" fontWeight="$7" color="$color" opacity={0.7}>
@@ -146,9 +147,10 @@ type BadgesGridProps = {
badges: AchievementBadge[];
emptyCopy: string;
completeCopy: string;
isDark: boolean;
};
function BadgesGrid({ badges, emptyCopy, completeCopy }: BadgesGridProps) {
function BadgesGrid({ badges, emptyCopy, completeCopy, isDark }: BadgesGridProps) {
if (badges.length === 0) {
return (
<Text fontSize="$2" color="$color" opacity={0.7}>
@@ -172,8 +174,8 @@ function BadgesGrid({ badges, emptyCopy, completeCopy }: BadgesGridProps) {
padding="$2"
borderRadius="$card"
borderWidth={1}
borderColor={badge.earned ? 'rgba(16, 185, 129, 0.4)' : 'rgba(15, 23, 42, 0.1)'}
backgroundColor={badge.earned ? 'rgba(16, 185, 129, 0.08)' : 'rgba(255, 255, 255, 0.85)'}
borderColor={badge.earned ? 'rgba(16, 185, 129, 0.4)' : isDark ? 'rgba(255, 255, 255, 0.16)' : 'rgba(15, 23, 42, 0.1)'}
backgroundColor={badge.earned ? 'rgba(16, 185, 129, 0.08)' : isDark ? 'rgba(255, 255, 255, 0.05)' : 'rgba(255, 255, 255, 0.85)'}
gap="$1"
>
<Text fontSize="$2" fontWeight="$7">
@@ -190,7 +192,7 @@ function BadgesGrid({ badges, emptyCopy, completeCopy }: BadgesGridProps) {
{badge.earned ? completeCopy : `${progress}/${target}`}
</Text>
</XStack>
<YStack height={6} borderRadius={999} backgroundColor="rgba(15, 23, 42, 0.08)">
<YStack height={6} borderRadius={999} backgroundColor={isDark ? 'rgba(255, 255, 255, 0.16)' : 'rgba(15, 23, 42, 0.08)'}>
<YStack
height={6}
borderRadius={999}
@@ -209,9 +211,10 @@ type TimelineProps = {
points: TimelinePoint[];
formatNumber: (value: number) => string;
emptyCopy: string;
isDark: boolean;
};
function Timeline({ points, formatNumber, emptyCopy }: TimelineProps) {
function Timeline({ points, formatNumber, emptyCopy, isDark }: TimelineProps) {
if (points.length === 0) {
return (
<Text fontSize="$2" color="$color" opacity={0.7}>
@@ -229,9 +232,9 @@ function Timeline({ points, formatNumber, emptyCopy }: TimelineProps) {
justifyContent="space-between"
padding="$2"
borderRadius="$card"
backgroundColor="rgba(15, 23, 42, 0.05)"
backgroundColor={isDark ? 'rgba(255, 255, 255, 0.08)' : 'rgba(15, 23, 42, 0.05)'}
borderWidth={1}
borderColor="rgba(15, 23, 42, 0.08)"
borderColor={isDark ? 'rgba(255, 255, 255, 0.16)' : 'rgba(15, 23, 42, 0.08)'}
>
<Text fontSize="$2" fontWeight="$6">
{point.date}
@@ -257,6 +260,7 @@ type HighlightsProps = {
topPhotoFallbackGuest: string;
trendingTitle: string;
trendingCountLabel: string;
isDark: boolean;
};
function Highlights({
@@ -271,6 +275,7 @@ function Highlights({
topPhotoFallbackGuest,
trendingTitle,
trendingCountLabel,
isDark,
}: HighlightsProps) {
if (!topPhoto && !trendingEmotion) {
return (
@@ -298,7 +303,13 @@ function Highlights({
style={{ width: '100%', height: 180, objectFit: 'cover', borderRadius: 16 }}
/>
) : (
<YStack height={180} borderRadius={16} backgroundColor="rgba(15, 23, 42, 0.08)" alignItems="center" justifyContent="center">
<YStack
height={180}
borderRadius={16}
backgroundColor={isDark ? 'rgba(255, 255, 255, 0.12)' : 'rgba(15, 23, 42, 0.08)'}
alignItems="center"
justifyContent="center"
>
<Text fontSize="$2" color="$color" opacity={0.7}>
{topPhotoNoPreview}
</Text>
@@ -347,9 +358,10 @@ type FeedProps = {
emptyCopy: string;
guestFallback: string;
likesLabel: string;
isDark: boolean;
};
function Feed({ feed, formatRelativeTime, locale, formatNumber, emptyCopy, guestFallback, likesLabel }: FeedProps) {
function Feed({ feed, formatRelativeTime, locale, formatNumber, emptyCopy, guestFallback, likesLabel, isDark }: FeedProps) {
if (feed.length === 0) {
return (
<Text fontSize="$2" color="$color" opacity={0.7}>
@@ -368,9 +380,9 @@ function Feed({ feed, formatRelativeTime, locale, formatNumber, emptyCopy, guest
gap="$2"
padding="$2"
borderRadius="$card"
backgroundColor="rgba(15, 23, 42, 0.05)"
backgroundColor={isDark ? 'rgba(255, 255, 255, 0.08)' : 'rgba(15, 23, 42, 0.05)'}
borderWidth={1}
borderColor="rgba(15, 23, 42, 0.08)"
borderColor={isDark ? 'rgba(255, 255, 255, 0.16)' : 'rgba(15, 23, 42, 0.08)'}
>
{item.thumbnail ? (
<img
@@ -379,8 +391,15 @@ function Feed({ feed, formatRelativeTime, locale, formatNumber, emptyCopy, guest
style={{ width: 64, height: 64, objectFit: 'cover', borderRadius: 12 }}
/>
) : (
<YStack width={64} height={64} borderRadius={12} backgroundColor="rgba(15, 23, 42, 0.08)" alignItems="center" justifyContent="center">
<Camera size={18} color="#0F172A" />
<YStack
width={64}
height={64}
borderRadius={12}
backgroundColor={isDark ? 'rgba(255, 255, 255, 0.12)' : 'rgba(15, 23, 42, 0.08)'}
alignItems="center"
justifyContent="center"
>
<Camera size={18} color={isDark ? '#F8FAFF' : '#0F172A'} />
</YStack>
)}
<YStack flex={1} gap="$1">
@@ -415,7 +434,6 @@ export default function AchievementsScreen() {
const { locale } = useLocale();
const { isDark } = useGuestThemeVariant();
const navigate = useNavigate();
const surface = getBentoSurfaceTokens(isDark);
const mutedButton = isDark ? 'rgba(255, 255, 255, 0.08)' : 'rgba(15, 23, 42, 0.06)';
const mutedButtonBorder = isDark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(15, 23, 42, 0.12)';
const [payload, setPayload] = React.useState<AchievementsPayload | null>(null);
@@ -576,6 +594,7 @@ export default function AchievementsScreen() {
badges={personal?.badges ?? []}
emptyCopy={t('achievements.badges.empty', 'No badges yet.')}
completeCopy={t('achievements.badges.complete', 'Complete')}
isDark={isDark}
/>
</YStack>
</BentoCard>
@@ -597,6 +616,7 @@ export default function AchievementsScreen() {
topPhotoFallbackGuest={t('achievements.leaderboard.guestFallback', 'Guest')}
trendingTitle={t('achievements.highlights.trendingTitle', 'Trending emotion')}
trendingCountLabel={t('achievements.highlights.trendingCount', { count: '{count}' }, '{count} photos')}
isDark={isDark}
/>
</BentoCard>
@@ -613,6 +633,7 @@ export default function AchievementsScreen() {
points={highlights?.timeline ?? []}
formatNumber={formatNumber}
emptyCopy={t('achievements.timeline.empty', 'No timeline data yet.')}
isDark={isDark}
/>
</BentoCard>
</YStack>
@@ -626,6 +647,7 @@ export default function AchievementsScreen() {
emptyCopy={t('achievements.leaderboard.uploadsEmpty', 'No uploads yet.')}
formatNumber={formatNumber}
guestFallback={t('achievements.leaderboard.guestFallback', 'Guest')}
isDark={isDark}
/>
</BentoCard>
<BentoCard isDark={isDark}>
@@ -637,6 +659,7 @@ export default function AchievementsScreen() {
emptyCopy={t('achievements.leaderboard.likesEmpty', 'No likes yet.')}
formatNumber={formatNumber}
guestFallback={t('achievements.leaderboard.guestFallback', 'Guest')}
isDark={isDark}
/>
</BentoCard>
</YStack>
@@ -654,6 +677,7 @@ export default function AchievementsScreen() {
emptyCopy={t('achievements.feed.empty', 'The feed is quiet for now.')}
guestFallback={t('achievements.leaderboard.guestFallback', 'Guest')}
likesLabel={t('achievements.feed.likesLabel', { count: '{count}' }, '{count} likes')}
isDark={isDark}
/>
</BentoCard>
) : null}