Merge pull request #55 from SlimusMinus/fix-post_on_feed

added border on post on feed
This commit is contained in:
SlimusMinus
2026-08-26 01:15:00 +03:00
committed by GitHub
5 changed files with 56 additions and 41 deletions

View File

@@ -1,6 +1,6 @@
// src/App.jsx // src/App.jsx
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom'; import { BrowserRouter, Routes, Route, useLocation } from 'react-router-dom';
import { AuthProvider } from './context/AuthContext'; import { AuthProvider } from './context/AuthContext';
import { ThemeProvider } from './context/ThemeContext'; import { ThemeProvider } from './context/ThemeContext';
import { useAuthContext } from './context/AuthContext'; import { useAuthContext } from './context/AuthContext';
@@ -13,6 +13,12 @@ import ChatListPage from './pages/ChatListPage';
import ReviewsPage from './pages/ReviewsPage'; import ReviewsPage from './pages/ReviewsPage';
import './styles/chat.css'; import './styles/chat.css';
function ScrollToTop() {
const { pathname } = useLocation();
useEffect(() => { window.scrollTo(0, 0); }, [pathname]);
return null;
}
function UnreadListener() { function UnreadListener() {
const { user } = useAuthContext(); const { user } = useAuthContext();
const { resetUnread } = useUnread(); const { resetUnread } = useUnread();
@@ -38,6 +44,7 @@ function App() {
<AuthProvider> <AuthProvider>
<UnreadListener /> <UnreadListener />
<BrowserRouter> <BrowserRouter>
<ScrollToTop />
<Routes> <Routes>
<Route path="/" element={<LandingPage />} /> <Route path="/" element={<LandingPage />} />
<Route path="/feed" element={<MainFeed />} /> <Route path="/feed" element={<MainFeed />} />

View File

@@ -73,7 +73,6 @@ export async function apiFetch(url, options = {}) {
res = await doFetch(newToken); res = await doFetch(newToken);
} catch { } catch {
clearTokens(); clearTokens();
window.location.href = "/";
throw new Error("Session expired"); throw new Error("Session expired");
} }
} }

View File

@@ -1,5 +1,5 @@
// src/components/AuthModal.jsx // src/components/AuthModal.jsx
import { useState } from "react"; import { useState, useEffect } from "react";
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import CloseIcon from "./icons/CloseIcon"; import CloseIcon from "./icons/CloseIcon";
import { useAuthContext } from "../context/AuthContext"; import { useAuthContext } from "../context/AuthContext";
@@ -14,6 +14,12 @@ export default function AuthModal({ onClose }) {
const [mode, setMode] = useState("login"); // "login" | "register" const [mode, setMode] = useState("login"); // "login" | "register"
const { login: saveAuth } = useAuthContext(); const { login: saveAuth } = useAuthContext();
useEffect(() => {
const prev = document.body.style.overflow;
document.body.style.overflow = "hidden";
return () => { document.body.style.overflow = prev; };
}, []);
return ( return (
<div className="f-modal-bg auth-modal-bg" onClick={e => e.target === e.currentTarget && onClose()}> <div className="f-modal-bg auth-modal-bg" onClick={e => e.target === e.currentTarget && onClose()}>
<div className="f-modal auth-modal"> <div className="f-modal auth-modal">

View File

@@ -58,7 +58,7 @@ export default function PostOnFeed({ ad, liked, likeCountAdj, onLike, onEdit, on
); );
return ( return (
<div className={`f-card${isClosed ? ' f-card-closed' : ''}`}> <div className={`f-card${isClosed ? ' f-card-closed' : ''}${ad.type === "found" ? ' f-card-found' : ' f-card-loss'}`}>
<div className={`f-card-head${headAccentClass}`}> <div className={`f-card-head${headAccentClass}`}>
<div className="f-card-author"> <div className="f-card-author">
@@ -114,10 +114,24 @@ export default function PostOnFeed({ ad, liked, likeCountAdj, onLike, onEdit, on
<TrashIcon/> <TrashIcon/>
</button> </button>
)} )}
{ad.reward && ( {createdAt && (
<span className="f-card-reward-text"> <div
{t("postCard.reward")} className="f-meta-pill f-date-pill"
</span> ref={dateAnchorRef}
onClick={e => {
e.stopPropagation();
setShowCalendar(s => !s);
}}
>
<CalendarIcon/> {createdAt}
{showCalendar && (
<DateCalendarPopup
date={ad.createdAt}
onClose={() => setShowCalendar(false)}
anchorRef={dateAnchorRef}
/>
)}
</div>
)} )}
</div> </div>
</div> </div>
@@ -145,24 +159,10 @@ export default function PostOnFeed({ ad, liked, likeCountAdj, onLike, onEdit, on
<PinIcon/> {address} <PinIcon/> {address}
</div> </div>
)} )}
{createdAt && ( {ad.reward && (
<div <span className="f-card-reward-text">
className="f-meta-pill f-date-pill" {t("postCard.reward")}
ref={dateAnchorRef} </span>
onClick={e => {
e.stopPropagation();
setShowCalendar(s => !s);
}}
>
<CalendarIcon/> {createdAt}
{showCalendar && (
<DateCalendarPopup
date={ad.createdAt}
onClose={() => setShowCalendar(false)}
anchorRef={dateAnchorRef}
/>
)}
</div>
)} )}
</div> </div>

View File

@@ -7,28 +7,28 @@
cursor: pointer; cursor: pointer;
} }
.f-card:hover { border-color: var(--muted2); } .f-card:hover { border-color: var(--muted2); }
.f-card-loss {
border-color: transparent;
box-shadow: 0 0 0 2px rgba(237,73,86,.7);
}
.f-card-found {
border-color: transparent;
box-shadow: 0 0 0 2px rgba(48,168,94,.7);
}
/* ── Post header ── */ /* ── Post header ── */
.f-card-head { .f-card-head {
display: flex; align-items: center; gap: 12px; display: flex; align-items: center; gap: 12px;
padding: 12px 14px 12px 17px; padding: 12px 14px 12px 17px;
position: relative;
} }
.f-head-loss,
/* Тонкая цветовая идентификация шапки: потеря / находка */
.f-card-head::before {
content: "";
position: absolute; left: 0; top: 0; bottom: 0;
width: 3px;
}
.f-head-loss {
background: rgba(237,73,86,.05);
}
.f-head-loss::before { background: rgba(237,73,86,.45); }
.f-head-found { .f-head-found {
background: rgba(48,168,94,.07); color: var(--text-primary);
} }
.f-head-found::before { background: rgba(48,168,94,.5); } .f-head-loss .f-card-author-name { color: var(--text-primary); }
.f-head-loss .f-type-badge { background: rgba(237,73,86,.1); color: #ed4956; border-color: rgba(237,73,86,.4); }
.f-head-found .f-card-author-name { color: var(--text-primary); }
.f-head-found .f-type-badge { background: rgba(48,168,94,.1); color: #30a85e; border-color: rgba(48,168,94,.4); }
.f-card-avatar-ring { .f-card-avatar-ring {
width: 42px; height: 42px; border-radius: 50%; width: 42px; height: 42px; border-radius: 50%;
@@ -63,11 +63,13 @@
background: var(--bg-card-img); background: var(--bg-card-img);
display: flex; align-items: center; justify-content: center; display: flex; align-items: center; justify-content: center;
overflow: hidden; overflow: hidden;
border: none; outline: none;
} }
.f-card-img img { .f-card-img img {
width: 100%; height: 100%; width: 100%; height: 100%;
object-fit: cover; object-position: center; object-fit: cover; object-position: center;
display: block; display: block;
border: none; outline: none;
} }
.f-card-emoji { font-size: 4rem; opacity: .85; filter: drop-shadow(0 4px 12px rgba(0,0,0,.3)); } .f-card-emoji { font-size: 4rem; opacity: .85; filter: drop-shadow(0 4px 12px rgba(0,0,0,.3)); }
.f-card-reward { .f-card-reward {
@@ -150,7 +152,8 @@
display: flex; flex-wrap: wrap; align-items: center; display: flex; flex-wrap: wrap; align-items: center;
gap: 6px; padding: 8px 16px 4px; gap: 6px; padding: 8px 16px 4px;
} }
.f-card-meta .f-date-pill { .f-card-meta .f-date-pill,
.f-card-meta .f-card-reward-text {
margin-left: auto; margin-left: auto;
} }
.f-meta-pill { .f-meta-pill {