Add lightbox retries and queue removal
This commit is contained in:
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { YStack, XStack } from '@tamagui/stacks';
|
||||
import { SizableText as Text } from '@tamagui/text';
|
||||
import { Button } from '@tamagui/button';
|
||||
import { Camera, ChevronLeft, ChevronRight, Heart, Share2, Sparkles, X } from 'lucide-react';
|
||||
import { Camera, ChevronLeft, ChevronRight, Heart, Loader2, Share2, Sparkles, X } from 'lucide-react';
|
||||
import AppShell from '../components/AppShell';
|
||||
import PhotoFrameTile from '../components/PhotoFrameTile';
|
||||
import ShareSheet from '../components/ShareSheet';
|
||||
@@ -84,15 +84,20 @@ export default function GalleryScreen() {
|
||||
const [likedIds, setLikedIds] = React.useState<Set<number>>(new Set());
|
||||
const touchStartX = React.useRef<number | null>(null);
|
||||
const fallbackAttemptedRef = React.useRef(false);
|
||||
const pendingNotFoundRef = React.useRef(false);
|
||||
const photosRef = React.useRef<GalleryTile[]>([]);
|
||||
const galleryLoadingRef = React.useRef(Boolean(token));
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!token) {
|
||||
setPhotos([]);
|
||||
galleryLoadingRef.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
let active = true;
|
||||
setLoading(true);
|
||||
galleryLoadingRef.current = true;
|
||||
|
||||
fetchGallery(token, { limit: 18, locale })
|
||||
.then((response) => {
|
||||
@@ -133,6 +138,7 @@ export default function GalleryScreen() {
|
||||
if (active) {
|
||||
setLoading(false);
|
||||
}
|
||||
galleryLoadingRef.current = false;
|
||||
});
|
||||
|
||||
return () => {
|
||||
@@ -140,6 +146,10 @@ export default function GalleryScreen() {
|
||||
};
|
||||
}, [token, locale]);
|
||||
|
||||
React.useEffect(() => {
|
||||
photosRef.current = photos;
|
||||
}, [photos]);
|
||||
|
||||
const myPhotoIds = React.useMemo(() => {
|
||||
try {
|
||||
const raw = localStorage.getItem('my-photo-ids');
|
||||
@@ -295,6 +305,7 @@ export default function GalleryScreen() {
|
||||
setLightboxPhoto(null);
|
||||
setLightboxLoading(false);
|
||||
setLightboxError(null);
|
||||
pendingNotFoundRef.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -304,32 +315,61 @@ export default function GalleryScreen() {
|
||||
if (seed) {
|
||||
setLightboxPhoto(seed);
|
||||
}
|
||||
const hasSeed = Boolean(seed);
|
||||
|
||||
if (hasSeed) {
|
||||
setLightboxLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let active = true;
|
||||
const maxRetryMs = 10_000;
|
||||
const retryDelayMs = 1500;
|
||||
let timedOut = false;
|
||||
const timeoutId = window.setTimeout(() => {
|
||||
timedOut = true;
|
||||
}, maxRetryMs);
|
||||
setLightboxLoading(true);
|
||||
setLightboxError(null);
|
||||
fetchPhoto(selectedPhotoId, locale)
|
||||
.then((photo) => {
|
||||
if (!active || !photo) return;
|
||||
const mapped = mapFullPhoto(photo as Record<string, unknown>);
|
||||
if (mapped) {
|
||||
setLightboxPhoto(mapped);
|
||||
setLikesById((prev) => ({ ...prev, [mapped.id]: mapped.likes }));
|
||||
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
const loadPhoto = async () => {
|
||||
let lastError: unknown = null;
|
||||
|
||||
while (active && !timedOut) {
|
||||
try {
|
||||
const photo = await fetchPhoto(selectedPhotoId, locale);
|
||||
if (!active) return;
|
||||
if (photo) {
|
||||
const mapped = mapFullPhoto(photo as Record<string, unknown>);
|
||||
if (mapped) {
|
||||
setLightboxPhoto(mapped);
|
||||
setLikesById((prev) => ({ ...prev, [mapped.id]: mapped.likes }));
|
||||
setLightboxLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
lastError = { status: 404 };
|
||||
} catch (error) {
|
||||
console.error('Lightbox photo load failed', error);
|
||||
lastError = error;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Lightbox photo load failed', error);
|
||||
if (!active) return;
|
||||
setLightboxError(error?.status === 404 ? 'notFound' : 'loadFailed');
|
||||
})
|
||||
.finally(() => {
|
||||
if (active) {
|
||||
setLightboxLoading(false);
|
||||
}
|
||||
});
|
||||
|
||||
if (!active || timedOut) break;
|
||||
await sleep(retryDelayMs);
|
||||
}
|
||||
|
||||
if (!active) return;
|
||||
const status = (lastError as { status?: number } | null)?.status;
|
||||
setLightboxError(status === 404 ? 'notFound' : 'loadFailed');
|
||||
setLightboxLoading(false);
|
||||
};
|
||||
|
||||
void loadPhoto();
|
||||
|
||||
return () => {
|
||||
active = false;
|
||||
window.clearTimeout(timeoutId);
|
||||
};
|
||||
}, [lightboxOpen, lightboxSelected, locale, selectedPhotoId]);
|
||||
|
||||
@@ -351,6 +391,17 @@ export default function GalleryScreen() {
|
||||
};
|
||||
}, [lightboxOpen]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!pendingNotFoundRef.current) return;
|
||||
if (loading || galleryLoadingRef.current) return;
|
||||
if (lightboxSelected || photosRef.current.some((photo) => photo.id === selectedPhotoId)) {
|
||||
pendingNotFoundRef.current = false;
|
||||
return;
|
||||
}
|
||||
pendingNotFoundRef.current = false;
|
||||
setLightboxError('notFound');
|
||||
}, [lightboxSelected, loading]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!lightboxOpen || !lightboxError) {
|
||||
return;
|
||||
@@ -897,9 +948,20 @@ export default function GalleryScreen() {
|
||||
</Button>
|
||||
</YStack>
|
||||
) : (
|
||||
<Text fontSize="$2" color="$color" opacity={0.7}>
|
||||
{lightboxLoading ? t('galleryPage.loading', 'Loading…') : t('lightbox.errors.notFound', 'Photo not found')}
|
||||
</Text>
|
||||
<YStack alignItems="center" gap="$2">
|
||||
{lightboxLoading ? (
|
||||
<>
|
||||
<Loader2 size={24} className="animate-spin" color={isDark ? '#F8FAFF' : '#0F172A'} />
|
||||
<Text fontSize="$2" color="$color" opacity={0.7}>
|
||||
{t('galleryPage.loading', 'Loading…')}
|
||||
</Text>
|
||||
</>
|
||||
) : (
|
||||
<Text fontSize="$2" color="$color" opacity={0.7}>
|
||||
{t('lightbox.errors.notFound', 'Photo not found')}
|
||||
</Text>
|
||||
)}
|
||||
</YStack>
|
||||
)}
|
||||
</YStack>
|
||||
<XStack
|
||||
|
||||
Reference in New Issue
Block a user