Files
nahodka-front/src/pages/PostOnFeed.jsx
2026-08-24 01:47:28 +03:00

207 lines
7.0 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {useRef, useState, useEffect} from "react";
import PinIcon from "../components/icons/PinIcon";
import CalendarIcon from "../components/icons/CalendarIcon";
import PhoneIcon from "../components/icons/PhoneIcon";
import HeartIcon from "../components/icons/HeartIcon";
import TrashIcon from "../components/icons/TrashIcon";
import {useContactSeller} from "../hooks/useContactSeller";
import {useAuthContext} from "../context/AuthContext";
import {getImageUrl} from "../config/authConstants";
import DateCalendarPopup from "../components/DateCalendarPopup";
import Toast from "../components/Toast";
import {useTranslation} from 'react-i18next';
export default function PostOnFeed({ ad, liked, likeCountAdj, onLike, onEdit, onDelete, onMarkFound, isOwner, onImageClick, onLocationClick, onAuthRequired }) {
const { t } = useTranslation();
const [showCalendar, setShowCalendar] = useState(false);
const [descExpanded, setDescExpanded] = useState(false);
const [descTruncated, setDescTruncated] = useState(false);
const dateAnchorRef = useRef(null);
const descRef = useRef(null);
const { isAuthenticated } = useAuthContext();
const { contactSeller, error: contactError, clearError } = useContactSeller(ad);
useEffect(() => {
const check = () => {
if (!descExpanded && descRef.current) {
setDescTruncated(descRef.current.scrollHeight > descRef.current.clientHeight + 1);
}
};
check();
window.addEventListener("resize", check);
return () => window.removeEventListener("resize", check);
}, [descExpanded]);
const handleContact = (e) => {
e.stopPropagation();
if (!isAuthenticated) {
if (onAuthRequired) onAuthRequired();
return;
}
contactSeller();
};
const imageUrl = getImageUrl(ad.images?.[0]);
const address = ad.city;
const createdAt = ad.createdAt
? new Date(ad.createdAt).toLocaleDateString("ru-RU")
: null;
const isClosed = ad.status === 'CLOSED';
const headAccentClass = ad.type === "found" ? " f-head-found" : " f-head-loss";
const typeBadge = (
<div className={`f-type-badge ${ad.type === "loss" ? "f-type-loss" : "f-type-found"}`}>
{ad.type === "loss" ? t('postCard.loss') : t('postCard.found')}
</div>
);
return (
<div className={`f-card${isClosed ? ' f-card-closed' : ''}`}>
<div className={`f-card-head${headAccentClass}`}>
<div className="f-card-author">
<div
className="f-card-author-name"
>
{ad.title}
</div>
</div>
<div className="f-card-head-side">
{typeBadge}
{isClosed && (
<span className="f-status-badge f-status-closed">{t('postCard.foundStatus')}</span>
)}
</div>
</div>
<div className="f-card-img" onClick={e => { e.stopPropagation(); if (onImageClick) onImageClick(ad.images, 0, ad); }}>
{imageUrl
? <img src={imageUrl} alt={ad.title} />
: <span className="f-card-emoji">{ad.emoji || "📋"}</span>
}
</div>
<div className="f-card-actions">
<div className="f-card-actions-left">
<button type="button"
className={`f-card-icon-btn${liked ? " liked" : ""}`}
onClick={e => {
e.stopPropagation();
onLike(ad.id);
}}>
<HeartIcon filled={liked}/>
</button>
<span className="f-card-like-count">{(ad.likeCount || 0) + likeCountAdj}</span>
</div>
<div className="f-card-actions-right">
{onEdit && isOwner && !isClosed && (
<button type="button" className="f-card-edit-btn"
onClick={e => {
e.stopPropagation();
onEdit(ad);
}}>
✏️
</button>
)}
{onDelete && isOwner && (
<button type="button" className="f-card-delete-btn"
onClick={e => {
e.stopPropagation();
onDelete(ad.id);
}}>
<TrashIcon/>
</button>
)}
{ad.reward && (
<span className="f-card-reward-text">
{t("postCard.reward")}
</span>
)}
</div>
</div>
<div className="f-card-caption">
<span
ref={descRef}
className={`f-card-caption-text${descTruncated ? " f-truncatable" : ""}${descExpanded ? " f-expanded" : ""}`}
onClick={descTruncated ? () => setDescExpanded(v => !v) : undefined}
>
{ad.description}
</span>
</div>
<div className="f-card-meta">
{address && (
<div
className="f-meta-pill f-loc-pill"
title={t('postCard.showOnMap', { address })}
onClick={e => {
e.stopPropagation();
onLocationClick?.(ad);
}}
>
<PinIcon/> {address}
</div>
)}
{createdAt && (
<div
className="f-meta-pill f-date-pill"
ref={dateAnchorRef}
onClick={e => {
e.stopPropagation();
setShowCalendar(s => !s);
}}
>
<CalendarIcon/> {createdAt}
{showCalendar && (
<DateCalendarPopup
date={ad.createdAt}
onClose={() => setShowCalendar(false)}
anchorRef={dateAnchorRef}
/>
)}
</div>
)}
</div>
<div className="f-card-foot">
<div className="f-card-foot-left">
{ad.phone && (
<a className="f-card-phone" href={`tel:${ad.phone}`}
onClick={e => e.stopPropagation()}>
<PhoneIcon/> {ad.phone}
</a>
)}
</div>
<div className="f-card-foot-right">
{!isOwner && !isClosed && (
<button type="button" className="f-card-contact"
onClick={handleContact}>
{t('postCard.write')}
</button>
)}
{onMarkFound && isOwner && !isClosed && (
<button type="button" className="f-card-found-btn"
onClick={e => {
e.stopPropagation();
onMarkFound(ad.id);
}}>
{t('postCard.itemFound')}
</button>
)}
</div>
</div>
{contactError && (
<Toast
message={contactError}
type="error"
onDismiss={clearError}
/>
)}
</div>
);
}