Merge pull request #53 from SlimusMinus/add-map_on_post
added map on post
This commit is contained in:
@@ -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}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user