Files
nahodka-front/src/pages/FeedPage.jsx
2026-05-07 01:10:10 +03:00

140 lines
4.2 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.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {useState, useEffect, useCallback} from "react";
import "../styles/feed.css";
import {API_URL} from "../config/constants";
import Sidebar from "../components/Sidebar";
import RightSidebar from "../components/RightSidebar";
import AdCard from "../components/AdCard";
import PostModal from "../components/PostModal";
import MenuIcon from "../components/icons/MenuIcon"
export default function FeedPage() {
const [sidebarOpen, setSidebarOpen] = useState(false);
const [activeNav, setActiveNav] = useState("Главная");
const [modalOpen, setModalOpen] = useState(false);
const [ads, setAds] = useState([]);
const [liked, setLiked] = useState(new Set());
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
useEffect(() => {
const load = async () => {
setLoading(true);
setError("");
try {
const res = await fetch(`${API_URL}/api/v1/posts`);
if (!res.ok) {
throw new Error(`Ошибка ${res.status}`);
}
const data = await res.json();
// поддерживаем { items: [] } и просто []
setAds(Array.isArray(data) ? data : (data.items ?? []));
} catch (err) {
setError(err.message || "Не удалось загрузить объявления");
} finally {
setLoading(false);
}
};
load();
}, []);
const toggleLike = useCallback((id) => {
setLiked(prev => {
const next = new Set(prev);
next.has(id) ? next.delete(id) : next.add(id);
return next;
});
}, []);
// Новое объявление — добавляем в начало ленты
const handleNewAd = useCallback((ad) => {
setAds(prev => [ad, ...prev]);
}, []);
const renderContent = () => {
if (loading) {
return (
<div className="f-center">
<div className="f-spinner-wrap">
<div className="f-spinner"/>
Загружаем объявления...
</div>
</div>
);
}
if (error) {
return (
<div className="f-center">
<div className="f-center-icon">⚠️</div>
<div className="f-center-title">Не удалось загрузить ленту</div>
<div className="f-center-sub">{error}</div>
</div>
);
}
if (!ads.length) {
return (
<div className="f-center">
<div className="f-center-icon">🔍</div>
<div className="f-center-title">Объявлений пока нет</div>
<div className="f-center-sub">
Будьте первым — разместите объявление<br/>о потере или находке
</div>
</div>
);
}
return ads.map(ad => (
<AdCard key={ad.id} ad={ad} liked={liked.has(ad.id)}
onLike={toggleLike}/>
));
};
return (
<>
<div className="f-app">
<Sidebar
open={sidebarOpen}
activeNav={activeNav}
onClose={() => setSidebarOpen(false)}
onNavClick={setActiveNav}
/>
<div className="f-main">
<div className="f-topbar">
<button className="f-menu-btn"
onClick={() => setSidebarOpen(v => !v)}>
<MenuIcon/>
</button>
<div className="f-topbar-center">
<button className="f-post-btn"
onClick={() => setModalOpen(true)}>
<span style={{
fontSize: "1.15rem",
fontWeight: 900,
lineHeight: 1
}}>+</span>
Разместить объявление
</button>
</div>
<div className="f-topbar-spacer"/>
</div>
<div className="f-wrap">
<div className="f-feed">
{renderContent()}
</div>
<RightSidebar/>
</div>
</div>
{modalOpen && (
<PostModal
onClose={() => setModalOpen(false)}
onSuccess={handleNewAd}
/>
)}
</div>
</>
);
}