added map on post

This commit is contained in:
SlimusMinus
2026-08-24 01:26:06 +03:00
parent e13e80101b
commit 8326866393
5 changed files with 50 additions and 33 deletions

View File

@@ -15,6 +15,7 @@ export default function FeedContent({
onDelete, onDelete,
onMarkFound, onMarkFound,
onImageClick, onImageClick,
onLocationClick,
onAuthRequired, onAuthRequired,
t, t,
}) { }) {
@@ -79,6 +80,7 @@ export default function FeedContent({
onMarkFound={activeNav === "myads" ? onMarkFound : undefined} onMarkFound={activeNav === "myads" ? onMarkFound : undefined}
isOwner={activeNav === "myads"} isOwner={activeNav === "myads"}
onImageClick={onImageClick} onImageClick={onImageClick}
onLocationClick={onLocationClick}
onAuthRequired={onAuthRequired} onAuthRequired={onAuthRequired}
/> />
)); ));

View File

@@ -54,6 +54,11 @@ export default function MainFeed() {
const isSearchView = new URLSearchParams(location.search).get("view") === "search"; const isSearchView = new URLSearchParams(location.search).get("view") === "search";
const isMapView = new URLSearchParams(location.search).get("view") === "map"; const isMapView = new URLSearchParams(location.search).get("view") === "map";
// параметры фокуса карты на конкретном объявлении (?view=map&post=ID&lat=..&lng=..)
const mapFocusParams = new URLSearchParams(location.search);
const focusPostId = isMapView ? mapFocusParams.get("post") : null;
const focusLat = parseFloat(mapFocusParams.get("lat"));
const focusLng = parseFloat(mapFocusParams.get("lng"));
const hasActiveFilters = searchQuery.trim().length > 0 || selectedCities.length > 0; const hasActiveFilters = searchQuery.trim().length > 0 || selectedCities.length > 0;
const hasSearchParams = searchQuery.trim().length > 0 || selectedCities.length > 0; const hasSearchParams = searchQuery.trim().length > 0 || selectedCities.length > 0;
const searchPrompt = isSearchView && !hasSearchParams; const searchPrompt = isSearchView && !hasSearchParams;
@@ -201,6 +206,18 @@ export default function MainFeed() {
feedRef.current?.scrollTo({ top: 0, behavior: "smooth" }); feedRef.current?.scrollTo({ top: 0, behavior: "smooth" });
}, [isMapView, navigate]); }, [isMapView, navigate]);
// Клик по локации на карточке — карта с фокусом на этом объявлении
const handleLocationClick = useCallback((ad) => {
const params = new URLSearchParams({ view: "map", post: ad.id });
if (ad.latitude != null && ad.longitude != null) {
params.set("lat", ad.latitude);
params.set("lng", ad.longitude);
}
setActiveNav("map");
navigate(`/feed?${params.toString()}`);
feedRef.current?.scrollTo({ top: 0, behavior: "smooth" });
}, [navigate]);
const handleProfileClick = useCallback(() => { const handleProfileClick = useCallback(() => {
if (!isAuthenticated) modals.open("auth"); if (!isAuthenticated) modals.open("auth");
else modals.open("profile"); else modals.open("profile");
@@ -276,7 +293,7 @@ export default function MainFeed() {
<div className="f-wrap"> <div className="f-wrap">
{isMapView ? ( {isMapView ? (
<MapView /> <MapView focusPostId={focusPostId} focusLat={focusLat} focusLng={focusLng} />
) : ( ) : (
<> <>
<div className="f-feed" ref={feedRef}> <div className="f-feed" ref={feedRef}>
@@ -295,6 +312,7 @@ export default function MainFeed() {
onDelete={actions.handleDelete} onDelete={actions.handleDelete}
onMarkFound={actions.handleMarkFound} onMarkFound={actions.handleMarkFound}
onImageClick={handleImageClick} onImageClick={handleImageClick}
onLocationClick={handleLocationClick}
onAuthRequired={() => modals.open("auth")} onAuthRequired={() => modals.open("auth")}
t={t} t={t}
/> />

View File

@@ -11,7 +11,7 @@ import DateCalendarPopup from "../components/DateCalendarPopup";
import Toast from "../components/Toast"; import Toast from "../components/Toast";
import {useTranslation} from 'react-i18next'; import {useTranslation} from 'react-i18next';
export default function PostOnFeed({ ad, liked, likeCountAdj, onLike, onEdit, onDelete, onMarkFound, isOwner, onImageClick, onAuthRequired }) { export default function PostOnFeed({ ad, liked, likeCountAdj, onLike, onEdit, onDelete, onMarkFound, isOwner, onImageClick, onLocationClick, onAuthRequired }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [showCalendar, setShowCalendar] = useState(false); const [showCalendar, setShowCalendar] = useState(false);
const [descExpanded, setDescExpanded] = useState(false); const [descExpanded, setDescExpanded] = useState(false);
@@ -131,7 +131,18 @@ export default function PostOnFeed({ ad, liked, likeCountAdj, onLike, onEdit, on
</div> </div>
<div className="f-card-meta"> <div className="f-card-meta">
{address && <div className="f-meta-pill"><PinIcon/> {address}</div>} {address && (
<div
className="f-meta-pill f-loc-pill"
title={t('postCard.showOnMap', { address })}
onClick={e => {
e.stopPropagation();
onLocationClick?.(ad);
}}
>
<PinIcon/> {address}
</div>
)}
{createdAt && ( {createdAt && (
<div <div
className="f-meta-pill f-date-pill" className="f-meta-pill f-date-pill"

View File

@@ -139,11 +139,19 @@
} }
.f-meta-pill { .f-meta-pill {
display: inline-flex; align-items: center; gap: 4px; display: inline-flex; align-items: center; gap: 4px;
background: var(--hover-bg); background: rgba(var(--teal-rgb), .1);
border: 1px solid rgba(var(--teal-rgb), .3);
border-radius: 999px; padding: 3px 10px; border-radius: 999px; padding: 3px 10px;
font-size: .74rem; color: var(--muted); font-family: var(--font);
border: 1px solid transparent; font-size: .74rem; color: var(--teal);
transition: all .2s;
} }
.f-meta-pill svg { width: 1em; height: 1em; flex-shrink: 0; }
.f-meta-pill:hover {
background: rgba(var(--teal-rgb), .18);
border-color: rgba(var(--teal-rgb), .5);
}
.f-loc-pill { cursor: pointer; }
.f-cat-badge { .f-cat-badge {
display: inline-flex; align-items: center; display: inline-flex; align-items: center;
background: rgba(var(--teal-rgb), .08); background: rgba(var(--teal-rgb), .08);
@@ -208,7 +216,7 @@
display: flex; align-items: center; gap: 5px; display: flex; align-items: center; gap: 5px;
background: rgba(var(--teal-rgb), .1); background: rgba(var(--teal-rgb), .1);
border: 1px solid rgba(var(--teal-rgb), .3); border: 1px solid rgba(var(--teal-rgb), .3);
border-radius: 999px; padding: 12px 32px; border-radius: 999px; padding: 12px 36px;
cursor: pointer; font-family: var(--font); cursor: pointer; font-family: var(--font);
font-size: 1.05rem; font-weight: 700; font-size: 1.05rem; font-weight: 700;
color: var(--teal); transition: all .2s; color: var(--teal); transition: all .2s;
@@ -359,11 +367,11 @@
cursor: pointer; cursor: pointer;
position: relative; position: relative;
} }
.f-date-pill:hover { background: var(--hover-bg); }
@media (max-width: 600px) { @media (max-width: 600px) {
.f-card-contact { .f-card-contact {
padding: 20px 56px; padding: 10px 40px;
font-size: 1.5rem; font-size: .95rem;
white-space: nowrap;
} }
} }

View File

@@ -300,25 +300,7 @@
} }
/* ── Contact Button on Card ── */ /* ── Contact Button on Card ── */
.f-card-contact { /* Стили .f-card-contact живут в styles/card.css (PostOnFeed) */
display: flex;
align-items: center;
gap: 5px;
background: rgba(var(--teal-rgb), .1);
border: 1px solid rgba(var(--teal-rgb), .3);
border-radius: 999px;
padding: 5px 14px;
cursor: pointer;
font-family: var(--font);
font-size: .8rem;
font-weight: 700;
color: var(--teal);
transition: all .2s;
}
.f-card-contact:hover {
background: rgba(var(--teal-rgb), .18);
border-color: rgba(var(--teal-rgb), .5);
}
.chat-list-delete-btn { .chat-list-delete-btn {
background: rgba(255, 71, 87, .08); background: rgba(255, 71, 87, .08);
@@ -354,8 +336,4 @@
.chat-bubble { .chat-bubble {
max-width: 85%; max-width: 85%;
} }
.f-card-contact {
padding: 10px 28px;
font-size: 0.9rem;
}
} }