Files
nahodka-front/src/components/ImageViewer.jsx
SlimusMinus 7f59ee1f38 fixed code
2026-08-11 00:24:07 +03:00

122 lines
3.9 KiB
JavaScript

import { useState, useCallback, useEffect, useRef, useMemo } from "react";
import { useTranslation } from 'react-i18next';
import CloseIcon from "./icons/CloseIcon";
import Toast from "./Toast";
import { getImageUrl } from "../config/authConstants";
import { useContactSeller } from "../hooks/useContactSeller";
import { useAuthContext } from "../context/AuthContext";
export default function ImageViewer({ images, initialIndex = 0, onClose, postData }) {
const { t } = useTranslation();
const [index, setIndex] = useState(initialIndex);
const touchStart = useRef(null);
const swiped = useRef(false);
const { isAuthenticated } = useAuthContext();
const { contactSeller, error: contactError, clearError } = useContactSeller(postData);
const urls = useMemo(() => images.map(u => getImageUrl(u)), [images]);
const next = useCallback(() => {
setIndex(i => (i + 1) % urls.length);
}, [urls.length]);
const prev = useCallback(() => {
setIndex(i => (i - 1 + urls.length) % urls.length);
}, [urls.length]);
const handleImgClick = useCallback(() => {
next();
}, [next]);
useEffect(() => {
const handleKey = (e) => {
if (e.key === "Escape") onClose();
if (e.key === "ArrowRight") next();
if (e.key === "ArrowLeft") prev();
};
window.addEventListener("keydown", handleKey);
return () => window.removeEventListener("keydown", handleKey);
}, [onClose, next, prev]);
useEffect(() => {
setIndex(initialIndex);
}, [initialIndex]);
const handleTouchStart = useCallback((e) => {
touchStart.current = { x: e.touches[0].clientX, y: e.touches[0].clientY };
}, []);
const handleTouchEnd = useCallback((e) => {
if (!touchStart.current) return;
const dx = e.changedTouches[0].clientX - touchStart.current.x;
const dy = e.changedTouches[0].clientY - touchStart.current.y;
const isHorizontal = Math.abs(dx) > Math.abs(dy);
if (isHorizontal && Math.abs(dx) > 50) {
swiped.current = true;
if (dx > 0) prev();
else next();
setTimeout(() => { swiped.current = false; }, 400);
}
touchStart.current = null;
}, [prev, next]);
const handleContact = useCallback(() => {
// Закрываем вьюер перед переходом в чат
contactSeller(onClose);
}, [contactSeller, onClose]);
const showContact = isAuthenticated && postData && !postData.isOwner && postData.status !== 'CLOSED';
if (!urls.length) return null;
return (
<div
className="f-viewer-bg"
onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd}
onClick={e => e.target === e.currentTarget && !swiped.current && onClose()}
>
<button type="button" className="f-viewer-close" onClick={onClose} aria-label={t('common.close')}>
<CloseIcon />
</button>
<div className="f-viewer">
<div className="f-viewer-img-wrap" onClick={handleImgClick}>
<img
src={urls[index]}
alt=""
className="f-viewer-img"
/>
</div>
{showContact && (
<button type="button" className="f-viewer-contact" onClick={handleContact}>
{t('postCard.write')}
</button>
)}
{urls.length > 1 && (
<div className="f-viewer-dots">
{urls.map((_, i) => (
<button
type="button"
key={i}
className={`f-viewer-dot${i === index ? " active" : ""}`}
onClick={() => setIndex(i)}
aria-label={t('viewer.goToImage', { number: i + 1 })}
/>
))}
</div>
)}
</div>
{contactError && (
<Toast
message={contactError}
type="error"
onDismiss={clearError}
/>
)}
</div>
);
}