EventBuzz_Website/src/pages/PhotoWallPresentPage.tsx

114 lines
3.8 KiB
TypeScript

import { useCallback, useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { getPhotoWallPresent, type PhotoWallPresentData } from "../lib/api";
const POLL_MS = 4000;
export function PhotoWallPresentPage() {
const { presentToken } = useParams<{ presentToken: string }>();
const [data, setData] = useState<PhotoWallPresentData | null>(null);
const [err, setErr] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
const refresh = useCallback(async () => {
if (!presentToken) return;
try {
const next = await getPhotoWallPresent(presentToken);
setData(next);
setErr(null);
} catch (e) {
setErr(e instanceof Error ? e.message : "Photo wall not available");
setData(null);
} finally {
setLoading(false);
}
}, [presentToken]);
useEffect(() => {
void refresh();
const id = window.setInterval(() => void refresh(), POLL_MS);
return () => window.clearInterval(id);
}, [refresh]);
if (loading) {
return (
<div className="eb-gradient-auth flex min-h-dvh items-center justify-center text-white/80">
Loading wall…
</div>
);
}
if (!data) {
return (
<div className="eb-gradient-auth flex min-h-dvh flex-col items-center justify-center gap-2 px-6 text-center text-white">
<p className="text-lg font-semibold">Photo wall unavailable</p>
<p className="text-sm text-white/80">{err}</p>
</div>
);
}
const cols = Math.max(1, Math.min(12, data.photosPerRow || 4));
const maxW = data.maxPhotoWidth || 320;
const maxH = data.maxPhotoHeight || 320;
const gap = data.gapPx ?? 8;
const showNames = Boolean(data.showUploaderNameOnWall);
const frameEnabled = Boolean(data.frameEnabled);
const frameW = data.frameCanvasWidth || 1080;
const frameH = data.frameCanvasHeight || 1080;
const aspectRatio = frameEnabled ? `${frameW} / ${frameH}` : `${maxW} / ${maxH}`;
return (
<div className="eb-gradient-auth min-h-dvh text-white">
<div className="px-4 pb-3 pt-4 text-center card-bg rounded-lg shadow-sm max-w-[400px] mx-auto mb-3">
<p className="text-xs uppercase tracking-[0.2em] text-[var(--theme-card-text-color)]/70">Photo Wall</p>
<h1 className="text-xl font-semibold sm:text-2xl text-[var(--theme-card-text-color)]">{data.eventName}</h1>
</div>
{data.photos.length === 0 ? (
<div className="flex min-h-[60dvh] items-center justify-center px-6 text-center text-white/80">
Waiting for photos…
</div>
) : (
<div
className="mx-auto w-full max-w-[100vw] px-3 pb-6"
style={{
display: "grid",
gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))`,
gap: `${gap}px`,
justifyItems: "center",
alignItems: "start",
}}
>
{data.photos.map((p) => (
<div
key={p._id}
className="flex w-full flex-col items-center"
style={{ maxWidth: maxW }}
>
<img
src={p.url}
alt=""
className="w-full rounded-md object-cover shadow-lg"
style={{
maxWidth: maxW,
maxHeight: maxH,
aspectRatio,
objectFit: "cover",
}}
/>
{showNames && p.displayName ? (
<p
className="mt- w-full truncate text-center text-xs font-medium text-[var(--theme-card-text-color)]/90 bg-[var(--theme-card-color)] rounded-md p-1 sm:text-sm"
title={p.displayName}
>
{p.displayName}
</p>
) : null}
</div>
))}
</div>
)}
</div>
);
}