feat(private-room): migrate to album APIs
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef } from "react";
|
||||
import Image from "next/image";
|
||||
import { ChevronLeft, ChevronRight, X } from "lucide-react";
|
||||
|
||||
import type { PrivateAlbum } from "@/data/dto/private-room";
|
||||
|
||||
import styles from "../private-room-screen.module.css";
|
||||
|
||||
export interface PrivateAlbumGalleryProps {
|
||||
album: PrivateAlbum;
|
||||
imageIndex: number;
|
||||
onClose: () => void;
|
||||
onImageIndexChange: (index: number) => void;
|
||||
}
|
||||
|
||||
export function PrivateAlbumGallery({
|
||||
album,
|
||||
imageIndex,
|
||||
onClose,
|
||||
onImageIndexChange,
|
||||
}: PrivateAlbumGalleryProps) {
|
||||
const touchStartXRef = useRef<number | null>(null);
|
||||
const currentImage = album.images[imageIndex];
|
||||
const hasPrevious = imageIndex > 0;
|
||||
const hasNext = imageIndex < album.images.length - 1;
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape") onClose();
|
||||
if (event.key === "ArrowLeft" && hasPrevious) {
|
||||
onImageIndexChange(imageIndex - 1);
|
||||
}
|
||||
if (event.key === "ArrowRight" && hasNext) {
|
||||
onImageIndexChange(imageIndex + 1);
|
||||
}
|
||||
};
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
return () => document.removeEventListener("keydown", handleKeyDown);
|
||||
}, [hasNext, hasPrevious, imageIndex, onClose, onImageIndexChange]);
|
||||
|
||||
if (!currentImage?.url) return null;
|
||||
|
||||
const handleTouchEnd = (event: React.TouchEvent) => {
|
||||
const touchStartX = touchStartXRef.current;
|
||||
touchStartXRef.current = null;
|
||||
if (touchStartX === null) return;
|
||||
const delta = event.changedTouches[0]?.clientX ?? touchStartX;
|
||||
const distance = delta - touchStartX;
|
||||
if (distance > 48 && hasPrevious) onImageIndexChange(imageIndex - 1);
|
||||
if (distance < -48 && hasNext) onImageIndexChange(imageIndex + 1);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.galleryOverlay}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label={album.title || "Private album gallery"}
|
||||
onTouchStart={(event) => {
|
||||
touchStartXRef.current = event.touches[0]?.clientX ?? null;
|
||||
}}
|
||||
onTouchEnd={handleTouchEnd}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.galleryClose}
|
||||
onClick={onClose}
|
||||
aria-label="Close private album gallery"
|
||||
>
|
||||
<X size={24} aria-hidden="true" />
|
||||
</button>
|
||||
|
||||
<div className={styles.galleryImageFrame}>
|
||||
<Image
|
||||
src={currentImage.url}
|
||||
alt={`${album.title || "Private album"} photo ${imageIndex + 1}`}
|
||||
fill
|
||||
priority
|
||||
draggable={false}
|
||||
sizes="(max-width: 540px) 100vw, 540px"
|
||||
className={styles.galleryImage}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<span className={styles.galleryCounter}>
|
||||
{imageIndex + 1} / {album.images.length}
|
||||
</span>
|
||||
|
||||
{hasPrevious ? (
|
||||
<button
|
||||
type="button"
|
||||
className={`${styles.galleryNav} ${styles.galleryPrevious}`}
|
||||
onClick={() => onImageIndexChange(imageIndex - 1)}
|
||||
aria-label="Previous photo"
|
||||
>
|
||||
<ChevronLeft size={30} aria-hidden="true" />
|
||||
</button>
|
||||
) : null}
|
||||
{hasNext ? (
|
||||
<button
|
||||
type="button"
|
||||
className={`${styles.galleryNav} ${styles.galleryNext}`}
|
||||
onClick={() => onImageIndexChange(imageIndex + 1)}
|
||||
aria-label="Next photo"
|
||||
>
|
||||
<ChevronRight size={30} aria-hidden="true" />
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user