mirror of
https://github.com/orangecoding/fredy.git
synced 2026-06-16 12:31:07 +00:00
possibility to display distance (#262)
This commit is contained in:
committed by
GitHub
parent
28a3a7f372
commit
9dde377fe6
@@ -7,6 +7,7 @@ import React, { useEffect, useRef, useState } from 'react';
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import 'maplibre-gl/dist/maplibre-gl.css';
|
||||
import { useSelector, useActions } from '../../services/state/store.js';
|
||||
import { distanceMeters, generateCircleCoords, getBoundsFromCenter } from './mapUtils.js';
|
||||
import { Select, Space, Typography, Button, Popover, Divider, Switch, Banner } from '@douyinfe/semi-ui-19';
|
||||
import { IconFilter } from '@douyinfe/semi-icons';
|
||||
import no_image from '../../assets/no_image.jpg';
|
||||
@@ -65,8 +66,10 @@ export default function MapView() {
|
||||
const mapContainer = useRef(null);
|
||||
const map = useRef(null);
|
||||
const markers = useRef([]);
|
||||
const homeMarker = useRef(null);
|
||||
const actions = useActions();
|
||||
const listings = useSelector((state) => state.listingsData.mapListings);
|
||||
const homeAddress = useSelector((state) => state.userSettings.settings.home_address);
|
||||
const [style, setStyle] = useState('STANDARD');
|
||||
const [show3dBuildings, setShow3dBuildings] = useState(false);
|
||||
|
||||
@@ -74,6 +77,7 @@ export default function MapView() {
|
||||
const [jobId, setJobId] = useState(null);
|
||||
const [priceRange, setPriceRange] = useState([0, 0]);
|
||||
const [showFilterBar, setShowFilterBar] = useState(false);
|
||||
const [distanceFilter, setDistanceFilter] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
setPriceRange([0, getMaxPrice()]);
|
||||
@@ -150,6 +154,7 @@ export default function MapView() {
|
||||
if (!map.current) return;
|
||||
|
||||
const add3dLayer = () => {
|
||||
if (!map.current || !map.current.isStyleLoaded()) return;
|
||||
if (show3dBuildings) {
|
||||
if (!map.current.getSource('openfreemap')) {
|
||||
map.current.addSource('openfreemap', {
|
||||
@@ -201,11 +206,7 @@ export default function MapView() {
|
||||
}
|
||||
};
|
||||
|
||||
if (map.current.isStyleLoaded()) {
|
||||
add3dLayer();
|
||||
} else {
|
||||
map.current.once('styledata', add3dLayer);
|
||||
}
|
||||
add3dLayer();
|
||||
}, [show3dBuildings, style]);
|
||||
|
||||
const setMapStyle = (value) => {
|
||||
@@ -225,12 +226,94 @@ export default function MapView() {
|
||||
fetchListings();
|
||||
}, [jobId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!map.current || !homeAddress?.coords) return;
|
||||
|
||||
// We only want to zoom/fly when distanceFilter OR homeAddress actually change,
|
||||
// not on every render. useEffect dependency array handles this.
|
||||
if (distanceFilter > 0) {
|
||||
const bounds = getBoundsFromCenter([homeAddress.coords.lng, homeAddress.coords.lat], distanceFilter);
|
||||
|
||||
map.current.fitBounds(bounds, {
|
||||
padding: 20,
|
||||
maxZoom: 15,
|
||||
duration: 1000,
|
||||
});
|
||||
} else {
|
||||
map.current.flyTo({
|
||||
center: [homeAddress.coords.lng, homeAddress.coords.lat],
|
||||
zoom: 12,
|
||||
duration: 1000,
|
||||
});
|
||||
}
|
||||
}, [homeAddress?.address, distanceFilter]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!map.current) return;
|
||||
|
||||
markers.current.forEach((marker) => marker.remove());
|
||||
markers.current = [];
|
||||
|
||||
if (homeMarker.current) {
|
||||
homeMarker.current.remove();
|
||||
homeMarker.current = null;
|
||||
}
|
||||
|
||||
if (homeAddress?.coords) {
|
||||
homeMarker.current = new maplibregl.Marker({ color: 'red' })
|
||||
.setLngLat([homeAddress.coords.lng, homeAddress.coords.lat])
|
||||
.setPopup(
|
||||
new maplibregl.Popup({ offset: 25 }).setHTML(
|
||||
`<div class="map-popup-content"><h4>Home Address</h4><p>${homeAddress.address}</p></div>`,
|
||||
),
|
||||
)
|
||||
.addTo(map.current);
|
||||
}
|
||||
|
||||
const addCircleLayer = () => {
|
||||
if (!map.current || !map.current.isStyleLoaded()) return;
|
||||
if (map.current.getLayer('distance-circle')) map.current.removeLayer('distance-circle');
|
||||
if (map.current.getLayer('distance-circle-outline')) map.current.removeLayer('distance-circle-outline');
|
||||
if (map.current.getSource('distance-circle-source')) map.current.removeSource('distance-circle-source');
|
||||
|
||||
if (distanceFilter > 0 && homeAddress?.coords) {
|
||||
const ret = generateCircleCoords([homeAddress.coords.lng, homeAddress.coords.lat], distanceFilter);
|
||||
|
||||
map.current.addSource('distance-circle-source', {
|
||||
type: 'geojson',
|
||||
data: {
|
||||
type: 'Feature',
|
||||
geometry: {
|
||||
type: 'Polygon',
|
||||
coordinates: [ret],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
map.current.addLayer({
|
||||
id: 'distance-circle',
|
||||
type: 'fill',
|
||||
source: 'distance-circle-source',
|
||||
paint: {
|
||||
'fill-color': '#90EE90',
|
||||
'fill-opacity': 0.3,
|
||||
},
|
||||
});
|
||||
|
||||
map.current.addLayer({
|
||||
id: 'distance-circle-outline',
|
||||
type: 'line',
|
||||
source: 'distance-circle-source',
|
||||
paint: {
|
||||
'line-color': '#006400',
|
||||
'line-width': 1,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
addCircleLayer();
|
||||
|
||||
filterListings().forEach((listing) => {
|
||||
if (
|
||||
listing.latitude != null &&
|
||||
@@ -256,7 +339,20 @@ export default function MapView() {
|
||||
</div>`,
|
||||
);
|
||||
|
||||
const marker = new maplibregl.Marker()
|
||||
let color = '#3FB1CE'; // Default blue-ish
|
||||
if (distanceFilter > 0 && homeAddress?.coords) {
|
||||
const dist = distanceMeters(
|
||||
homeAddress.coords.lat,
|
||||
homeAddress.coords.lng,
|
||||
listing.latitude,
|
||||
listing.longitude,
|
||||
);
|
||||
if (dist <= distanceFilter * 1000) {
|
||||
color = 'orange';
|
||||
}
|
||||
}
|
||||
|
||||
const marker = new maplibregl.Marker({ color })
|
||||
.setLngLat([listing.longitude, listing.latitude])
|
||||
.setPopup(popup)
|
||||
.addTo(map.current);
|
||||
@@ -264,7 +360,7 @@ export default function MapView() {
|
||||
markers.current.push(marker);
|
||||
}
|
||||
});
|
||||
}, [listings, priceRange]);
|
||||
}, [listings, priceRange, homeAddress, distanceFilter]);
|
||||
|
||||
return (
|
||||
<div className="map-view-container">
|
||||
@@ -318,6 +414,29 @@ export default function MapView() {
|
||||
</div>
|
||||
</div>
|
||||
<Divider layout="vertical" />
|
||||
<div className="listingsGrid__toolbar__card">
|
||||
<div>
|
||||
<Text strong>Distance:</Text>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '.3rem', alignItems: 'center' }}>
|
||||
<Select
|
||||
placeholder="Distance"
|
||||
style={{ width: 100 }}
|
||||
onChange={(val) => {
|
||||
setDistanceFilter(val);
|
||||
}}
|
||||
value={distanceFilter}
|
||||
>
|
||||
<Select.Option value={0}>---</Select.Option>
|
||||
<Select.Option value={5}>5 km</Select.Option>
|
||||
<Select.Option value={10}>10 km</Select.Option>
|
||||
<Select.Option value={15}>15 km</Select.Option>
|
||||
<Select.Option value={20}>20 km</Select.Option>
|
||||
<Select.Option value={25}>25 km</Select.Option>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<Divider layout="vertical" />
|
||||
<div className="listingsGrid__toolbar__card">
|
||||
<div>
|
||||
<Text strong>Price Range (€):</Text>
|
||||
@@ -343,6 +462,16 @@ export default function MapView() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!homeAddress && (
|
||||
<Banner
|
||||
fullMode={true}
|
||||
type="warning"
|
||||
bordered
|
||||
closeIcon={null}
|
||||
description="You have not set your home address yet. Please do so in the settings to use the distance filter."
|
||||
/>
|
||||
)}
|
||||
|
||||
<Banner
|
||||
fullMode={true}
|
||||
type="info"
|
||||
|
||||
Reference in New Issue
Block a user