Update media wall to Justified flex layout to guarantee no cropping, zero gaps, and dynamic sizing based on true aspect ratios

This commit is contained in:
Jannik committed 2026-09-24 04:40:22 +02:00
1 parent 9c66ea85b8
commit a5f40b60b2
5 files changed
+21 -32

No files matched your search

+14 -25
View File
@@ -9,7 +9,6 @@ type MediaItem = {
relativePath: string;
filename: string;
published: boolean;
wasOncePublished: boolean;
featured: boolean;
mediaType: string;
};
@@ -78,7 +77,7 @@ export default function AdminDashboard() {
const togglePublished = async (id: number, current: boolean) => {
// Optimistic UI
setItems((prev) =>
prev.map((i) => (i.id === id ? { ...i, published: !current, ...( !current ? { wasOncePublished: true } : {} ) } : i))
prev.map((i) => (i.id === id ? { ...i, published: !current } : i))
);
setStats((prev) => ({
...prev,
@@ -124,11 +123,7 @@ export default function AdminDashboard() {
// Optimistic UI
setItems((prev) =>
prev.map((i) => ({
...i,
published: isPublished,
...(isPublished ? { wasOncePublished: true } : {})
}))
prev.map((i) => ({ ...i, published: isPublished }))
);
setStats({
total: items.length,
@@ -207,28 +202,22 @@ export default function AdminDashboard() {
borderRadius: 'var(--radius)',
overflow: 'hidden',
border: '1px solid var(--color-border)',
opacity: (!item.wasOncePublished && !item.published) ? 1 : (item.published ? 1 : 0.6),
opacity: item.published ? 1 : 0.6,
transition: 'opacity 0.2s'
}}
>
<div style={{ position: 'relative', aspectRatio: '1/1', background: '#eee' }}>
{(!item.wasOncePublished && !item.published) ? (
<div style={{ width: '100%', height: '100%', background: '#fff' }} />
) : (
<>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={`/api/media/${item.relativePath}?type=thumb`}
alt={item.filename}
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
loading="lazy"
/>
{item.mediaType === 'VIDEO' && (
<div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(0,0,0,0.3)' }}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="white" stroke="none"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>
</div>
)}
</>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={`/api/media/${item.relativePath}?type=thumb`}
alt={item.filename}
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
loading="lazy"
/>
{item.mediaType === 'VIDEO' && (
<div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(0,0,0,0.3)' }}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="white" stroke="none"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>
</div>
)}
</div>
-1
View File
@@ -37,7 +37,6 @@ export async function PATCH(
where: { id },
data: {
...(published !== undefined && { published }),
...(published === true && { wasOncePublished: true }),
...(featured !== undefined && { featured }),
...(placementMode !== undefined && { placementMode }),
...(gridColumn !== undefined && { gridColumn }),
+1 -4
View File
@@ -24,10 +24,7 @@ export async function PATCH(request: NextRequest) {
where: {
id: { in: ids }
},
data: {
published,
...(published === true ? { wasOncePublished: true } : {})
}
data: { published }
});
if (result.count > 0) {
+1
View File
@@ -1,5 +1,6 @@
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/db';
import { validateMediaPath } from '@/lib/security/path';
import { validateSession } from '@/lib/auth/session';
import { getSafeContentType } from '@/lib/media/types';
import { getThumbnailPath, getGalleryPath } from '@/lib/processing/images';
+5 -2
View File
@@ -195,10 +195,13 @@ export default function MediaWall({
votingOpen={votingOpen}
/>
))}
{/* Ghost element to prevent the last row from stretching */}
<div style={{ flexGrow: 1000, height: 0 }} />
{hasMore && (
<div
ref={loadMoreRef}
style={{ height: '20px', width: '100%', gridColumn: '1 / -1' }}
style={{ height: '20px', width: '100%' }}
/>
)}
{loading && (
@@ -206,7 +209,7 @@ export default function MediaWall({
style={{
padding: '20px',
textAlign: 'center',
gridColumn: '1 / -1',
width: '100%',
color: 'var(--color-text-light)',
}}
>