Refine guest gallery UI and add multi-photo upload flow
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-09 18:01:01 +01:00
parent e3bb1642db
commit 1f9a43806a
9 changed files with 369 additions and 159 deletions

View File

@@ -14,6 +14,7 @@ import { useGuestThemeVariant } from '../lib/guestTheme';
import { useTranslation } from '@/shared/guest/i18n/useTranslation';
import { useLocale } from '@/shared/guest/i18n/LocaleContext';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { createPortal } from 'react-dom';
import { buildEventPath } from '../lib/routes';
import { getBentoSurfaceTokens } from '../lib/bento';
import { usePollStats } from '../hooks/usePollStats';
@@ -982,7 +983,7 @@ export default function GalleryScreen() {
</Button>
</YStack>
) : isSingle ? (
<YStack gap="$3">
<YStack gap="$1.5">
<Button unstyled onPress={() => openLightbox(displayPhotos[0].id)}>
<PhotoFrameTile height={360} borderRadius="$bento">
<YStack flex={1} width="100%" height="100%" alignItems="center" justifyContent="center">
@@ -1021,8 +1022,8 @@ export default function GalleryScreen() {
</Button>
</YStack>
) : (
<XStack gap="$3">
<YStack flex={1} gap="$3">
<XStack gap="$1">
<YStack flex={1} gap="$1">
{(loading ? Array.from({ length: 5 }, (_, index) => index) : leftColumn).map((tile, index) => {
if (typeof tile === 'number') {
return <PhotoFrameTile key={`left-${tile}`} height={140 + (index % 3) * 24} shimmer shimmerDelayMs={200 + index * 120} />;
@@ -1049,7 +1050,7 @@ export default function GalleryScreen() {
);
})}
</YStack>
<YStack flex={1} gap="$3">
<YStack flex={1} gap="$1">
{(loading ? Array.from({ length: 5 }, (_, index) => index) : rightColumn).map((tile, index) => {
if (typeof tile === 'number') {
return <PhotoFrameTile key={`right-${tile}`} height={120 + (index % 3) * 28} shimmer shimmerDelayMs={260 + index * 140} />;
@@ -1105,7 +1106,7 @@ export default function GalleryScreen() {
)}
</YStack>
{lightboxOpen || lightboxMounted ? (
{typeof document !== 'undefined' && (lightboxOpen || lightboxMounted) ? createPortal((
<YStack
position="fixed"
top={0}
@@ -1328,8 +1329,8 @@ export default function GalleryScreen() {
) : null}
</XStack>
<XStack
gap="$1"
padding="$1"
gap="$1.5"
padding="$1.5"
borderRadius="$pill"
backgroundColor={mutedButton}
borderWidth={1}
@@ -1339,55 +1340,55 @@ export default function GalleryScreen() {
{lightboxPhoto ? (
<Button
unstyled
paddingHorizontal="$2.5"
paddingVertical="$1.5"
paddingHorizontal="$3"
paddingVertical="$2"
onPress={handleLike}
aria-label={t('galleryPage.photo.likeAria', 'Like')}
>
<Heart size={16} color={likedIds.has(lightboxPhoto.id) ? '#F43F5E' : (isDark ? '#F8FAFF' : '#0F172A')} />
<Heart size={18} color={likedIds.has(lightboxPhoto.id) ? '#F43F5E' : (isDark ? '#F8FAFF' : '#0F172A')} />
</Button>
) : null}
{lightboxPhoto ? (
<Button
unstyled
paddingHorizontal="$2"
paddingVertical="$1.5"
paddingHorizontal="$3"
paddingVertical="$2"
onPress={() => downloadPhoto(lightboxPhoto)}
aria-label={t('common.actions.download', 'Download')}
>
<Download size={14} color={isDark ? '#F8FAFF' : '#0F172A'} />
<Download size={18} color={isDark ? '#F8FAFF' : '#0F172A'} />
</Button>
) : null}
{lightboxPhoto && hasAiStylingAccess ? (
<Button
unstyled
paddingHorizontal="$2"
paddingVertical="$1.5"
paddingHorizontal="$3"
paddingVertical="$2"
onPress={openAiMagicEdit}
aria-label={t('galleryPage.lightbox.aiMagicEditAria', 'AI Magic Edit')}
>
<Sparkles size={14} color={isDark ? '#F8FAFF' : '#0F172A'} />
<Sparkles size={18} color={isDark ? '#F8FAFF' : '#0F172A'} />
</Button>
) : null}
{lightboxPhoto && canDelete ? (
<Button
unstyled
paddingHorizontal="$2"
paddingVertical="$1.5"
paddingHorizontal="$3"
paddingVertical="$2"
onPress={() => setDeleteConfirmOpen(true)}
disabled={deleteBusy}
aria-label={t('galleryPage.lightbox.deleteAria', 'Delete photo')}
>
<Trash2 size={14} color={isDark ? '#F8FAFF' : '#0F172A'} />
<Trash2 size={18} color={isDark ? '#F8FAFF' : '#0F172A'} />
</Button>
) : null}
<Button unstyled paddingHorizontal="$2" paddingVertical="$1.5" onPress={openShareSheet}>
<Share2 size={14} color={isDark ? '#F8FAFF' : '#0F172A'} />
<Button unstyled paddingHorizontal="$3" paddingVertical="$2" onPress={openShareSheet}>
<Share2 size={18} color={isDark ? '#F8FAFF' : '#0F172A'} />
</Button>
<Button
unstyled
paddingHorizontal="$2.5"
paddingVertical="$1.5"
paddingHorizontal="$3"
paddingVertical="$2"
onPress={() => {
transitionDirectionRef.current = 'prev';
goPrev();
@@ -1395,12 +1396,12 @@ export default function GalleryScreen() {
disabled={lightboxIndex <= 0}
opacity={lightboxIndex <= 0 ? 0.4 : 1}
>
<ChevronLeft size={16} color={isDark ? '#F8FAFF' : '#0F172A'} />
<ChevronLeft size={18} color={isDark ? '#F8FAFF' : '#0F172A'} />
</Button>
<Button
unstyled
paddingHorizontal="$2.5"
paddingVertical="$1.5"
paddingHorizontal="$3"
paddingVertical="$2"
onPress={() => {
transitionDirectionRef.current = 'next';
goNext();
@@ -1408,7 +1409,7 @@ export default function GalleryScreen() {
disabled={lightboxIndex < 0 || lightboxIndex >= displayPhotos.length - 1}
opacity={lightboxIndex < 0 || lightboxIndex >= displayPhotos.length - 1 ? 0.4 : 1}
>
<ChevronRight size={16} color={isDark ? '#F8FAFF' : '#0F172A'} />
<ChevronRight size={18} color={isDark ? '#F8FAFF' : '#0F172A'} />
</Button>
</XStack>
</XStack>
@@ -1522,7 +1523,7 @@ export default function GalleryScreen() {
</YStack>
</YStack>
</YStack>
) : null}
), document.body) : null}
</AppShell>
);
}