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