Match gallery layout to achievements spacing
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-01-14 15:44:32 +01:00
parent 3612c97e86
commit 0ef4b32bf6

View File

@@ -315,12 +315,8 @@ export default function GalleryPage() {
releaseLabel={t('common.releaseToRefresh')} releaseLabel={t('common.releaseToRefresh')}
refreshingLabel={t('common.refreshing')} refreshingLabel={t('common.refreshing')}
> >
<motion.section <motion.div className="space-y-4 px-2 pb-14" {...containerMotion}>
className="mx-0.5 space-y-2 rounded-3xl border border-pink-200/40 bg-white/90 p-2 shadow-sm backdrop-blur dark:border-white/10 dark:bg-slate-950/80" <motion.div className="space-y-2" style={bodyFont ? { fontFamily: bodyFont } : undefined} {...fadeUpMotion}>
style={bodyFont ? { fontFamily: bodyFont, borderRadius: radius } : { borderRadius: radius }}
{...containerMotion}
>
<motion.div {...fadeUpMotion}>
<div className="flex flex-wrap items-center justify-between gap-2"> <div className="flex flex-wrap items-center justify-between gap-2">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-pink-500/10 text-pink-500" style={{ borderRadius: radius }}> <div className="flex h-10 w-10 items-center justify-center rounded-full bg-pink-500/10 text-pink-500" style={{ borderRadius: radius }}>
@@ -351,40 +347,41 @@ export default function GalleryPage() {
</button> </button>
)} )}
</div> </div>
<div className="mt-2">
<FiltersBar <FiltersBar
value={filter} value={filter}
onChange={setFilter} onChange={setFilter}
className="mt-0" className="mt-0"
showPhotobooth={showPhotoboothFilter} showPhotobooth={showPhotoboothFilter}
styleOverride={{ borderRadius: radius, fontFamily: headingFont }} styleOverride={{ borderRadius: radius, fontFamily: headingFont }}
/> />
</div>
</motion.div> </motion.div>
{loading && ( {loading && (
<motion.p className="px-1" {...fadeUpMotion}> <motion.p className="px-1" {...fadeUpMotion}>
{t('galleryPage.loading', 'Lade…')} {t('galleryPage.loading', 'Lade…')}
</motion.p> </motion.p>
)} )}
<motion.div className="grid grid-cols-2 gap-3 pb-6 sm:grid-cols-3 lg:grid-cols-4" {...gridMotion}>
{list.map((p: GalleryPhoto) => {
const imageUrl = normalizeImageUrl(p.thumbnail_path || p.file_path);
const createdLabel = p.created_at
? new Date(p.created_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
: t('galleryPage.photo.justNow', 'Gerade eben');
const likeCount = counts[p.id] ?? (p.likes_count || 0);
const localizedTaskTitle = localizeTaskLabel(p.task_title ?? null, locale);
const altSuffix = localizedTaskTitle
? t('galleryPage.photo.altTaskSuffix', { task: localizedTaskTitle })
: '';
const altText = t('galleryPage.photo.alt', { id: p.id, suffix: altSuffix }, `Foto ${p.id}${altSuffix}`);
const openPhoto = () => { <motion.div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4" {...gridMotion}>
const index = list.findIndex((photo) => photo.id === p.id); {list.map((p: GalleryPhoto) => {
setCurrentPhotoIndex(index >= 0 ? index : null); const imageUrl = normalizeImageUrl(p.thumbnail_path || p.file_path);
}; const createdLabel = p.created_at
? new Date(p.created_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
: t('galleryPage.photo.justNow', 'Gerade eben');
const likeCount = counts[p.id] ?? (p.likes_count || 0);
const localizedTaskTitle = localizeTaskLabel(p.task_title ?? null, locale);
const altSuffix = localizedTaskTitle
? t('galleryPage.photo.altTaskSuffix', { task: localizedTaskTitle })
: '';
const altText = t('galleryPage.photo.alt', { id: p.id, suffix: altSuffix }, `Foto ${p.id}${altSuffix}`);
return ( const openPhoto = () => {
const index = list.findIndex((photo) => photo.id === p.id);
setCurrentPhotoIndex(index >= 0 ? index : null);
};
return (
<motion.div <motion.div
key={p.id} key={p.id}
role="button" role="button"
@@ -461,25 +458,25 @@ export default function GalleryPage() {
</div> </div>
</div> </div>
</motion.div> </motion.div>
); );
})} })}
{list.length === 0 && Array.from({ length: 6 }).map((_, idx) => ( {list.length === 0 && Array.from({ length: 6 }).map((_, idx) => (
<motion.div <motion.div
key={`placeholder-${idx}`} key={`placeholder-${idx}`}
className="relative overflow-hidden border border-muted/40 bg-white shadow-sm ring-1 ring-black/5 dark:bg-slate-950 dark:ring-white/10" className="relative overflow-hidden border border-muted/40 bg-white shadow-sm ring-1 ring-black/5 dark:bg-slate-950 dark:ring-white/10"
style={{ borderRadius: radius }} style={{ borderRadius: radius }}
{...fadeScaleMotion} {...fadeScaleMotion}
> >
<div className="absolute inset-0 bg-gradient-to-br from-white/60 via-white/30 to-transparent dark:from-white/5 dark:via-white/0" aria-hidden /> <div className="absolute inset-0 bg-gradient-to-br from-white/60 via-white/30 to-transparent dark:from-white/5 dark:via-white/0" aria-hidden />
<div className="flex aspect-[3/4] items-center justify-center gap-2 p-4 text-muted-foreground/70"> <div className="flex aspect-[3/4] items-center justify-center gap-2 p-4 text-muted-foreground/70">
<ImageIcon className="h-6 w-6" aria-hidden /> <ImageIcon className="h-6 w-6" aria-hidden />
<div className="h-2 w-10 rounded-full bg-muted/40" /> <div className="h-2 w-10 rounded-full bg-muted/40" />
</div> </div>
<div className="absolute inset-0 animate-pulse bg-white/30 dark:bg-white/5" aria-hidden /> <div className="absolute inset-0 animate-pulse bg-white/30 dark:bg-white/5" aria-hidden />
</motion.div> </motion.div>
))} ))}
</motion.div>
</motion.div> </motion.div>
</motion.section>
</PullToRefresh> </PullToRefresh>
</div> </div>
{currentPhotoIndex !== null && list.length > 0 && ( {currentPhotoIndex !== null && list.length > 0 && (