fix: Ngăn bản đồ ExploreMap tự động nhảy về vị trí người dùng khi thay đổi zoom trên mobile
- Tách userPos (dùng cho marker) và mapCenter (dùng cho center bản đồ) thành 2 state riêng biệt
- MapContainer dùng center={mapCenter} cố định thay vì center={userPos} bị GPS thay đổi liên tục
- Xóa component RecenterMap tự động re-center khi userPos thay đổi
- Thêm nút 'Vị trí của tôi' để người dùng chủ động nhấn mới nhảy bản đồ đến vị trí của mình
- requestGpsPosition cập nhật cả mapCenter lẫn store để map thực sự nhảy khi người dùng chủ động yêu cầu
- Các thao tác khác như click gợi ý tìm kiếm, click ảnh trên bản đồ cũng cập nhật mapCenter để map nhảy đúng vị trí
- Áp dụng cho ExploreMap.tsx - bản đồ chính khi người dùng mở app
This commit is contained in:
@@ -39,15 +39,6 @@ const PHOTO_TAG_LABELS: { [key: string]: string } = {
|
||||
'thu-cung': '🐕 Thú cưng'
|
||||
};
|
||||
|
||||
// Component Helper để cập nhật tâm bản đồ khi vị trí người dùng thay đổi
|
||||
function RecenterMap({ position }: { position: [number, number] }) {
|
||||
const map = useMap();
|
||||
useEffect(() => {
|
||||
map.setView(position, map.getZoom());
|
||||
}, [position, map]);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Component Helper để đóng menu khi tương tác với bản đồ
|
||||
function MapEvents({ onMapAction }: { onMapAction: () => void }) {
|
||||
useMapEvents({
|
||||
@@ -202,7 +193,11 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour, onOpenMyPhotos,
|
||||
return null;
|
||||
});
|
||||
|
||||
const [userPos, setUserPos] = useState<[number, number]>(initialViewState?.center || [10.7769, 106.7009]);
|
||||
// Map dùng center cố định để không bị GPS tự nhảy vị trí người dùng
|
||||
const defaultCenter = initialViewState?.center || [10.7769, 106.7009];
|
||||
|
||||
const [userPos, setUserPos] = useState<[number, number]>(defaultCenter);
|
||||
const [mapCenter, setLocalMapCenter] = useState<[number, number]>(initialViewState?.center || defaultCenter);
|
||||
const [mapZoom] = useState(initialViewState?.zoom || 13);
|
||||
const [publicPhotos, setPublicPhotos] = useState<any[]>([]);
|
||||
const [selectedPhoto, setSelectedPhoto] = useState<any | null>(null);
|
||||
@@ -262,7 +257,7 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour, onOpenMyPhotos,
|
||||
const [blacklist, setBlacklist] = useState<any[]>([]);
|
||||
const [isReportModalOpen, setIsReportModalOpen] = useState(false);
|
||||
const [isBlacklistOpen, setIsBlacklistOpen] = useState(false);
|
||||
const mapCenter = useTourStore(state => state.mapCenter);
|
||||
const storeMapCenter = useTourStore(state => state.mapCenter);
|
||||
|
||||
// Recommendations and GPS States
|
||||
const [recommendedLocations, setRecommendedLocations] = useState<any[]>([]);
|
||||
@@ -342,6 +337,7 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour, onOpenMyPhotos,
|
||||
const posArray: [number, number] = [pos.coords.latitude, pos.coords.longitude];
|
||||
setUserGpsPos(posArray);
|
||||
setUserPos(posArray);
|
||||
setLocalMapCenter(posArray);
|
||||
setMapCenter(posArray);
|
||||
},
|
||||
() => {
|
||||
@@ -435,7 +431,28 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour, onOpenMyPhotos,
|
||||
fetchTrustedUsers();
|
||||
fetchBlacklist();
|
||||
fetchRecommendations();
|
||||
// Luôn tải danh sách ảnh công khai để hiển thị trên bản đồ cho tất cả mọi người
|
||||
fetchPublicPhotos();
|
||||
|
||||
// Chỉ tải danh sách tour khi người dùng đã đăng nhập và có token
|
||||
if (user || localStorage.getItem('token')) {
|
||||
fetchPublicTours();
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Chỉ lấy vị trí GPS ban đầu để hiển thị marker, KHÔNG tự động nhảy bản đồ đến vị trí đó
|
||||
// Người dùng phải chủ động nhấn nút định vị mới nhảy bản đồ đến vị trí của mình
|
||||
useEffect(() => {
|
||||
if (!initialViewState && navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(pos) => {
|
||||
const posArray: [number, number] = [pos.coords.latitude, pos.coords.longitude];
|
||||
setUserPos(posArray);
|
||||
},
|
||||
() => console.log("Không thể lấy vị trí người dùng")
|
||||
);
|
||||
}
|
||||
}, [initialViewState]);
|
||||
const [isFilterDropdownOpen, setIsFilterDropdownOpen] = useState(false); // State để điều khiển hiển thị dropdown lọc
|
||||
const [searchQuery, setSearchQuery] = useState(''); // State cho giá trị tìm kiếm
|
||||
const [suggestions, setSuggestions] = useState<{ type: 'tour' | 'location', id: string, name: string, lat?: number, lon?: number }[]>([]);
|
||||
@@ -576,6 +593,7 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour, onOpenMyPhotos,
|
||||
} else if (s.lat && s.lon) {
|
||||
const pos: [number, number] = [s.lat, s.lon];
|
||||
setUserPos(pos);
|
||||
setLocalMapCenter(pos);
|
||||
setMapCenter(pos);
|
||||
notify({
|
||||
title: 'Tìm thấy địa điểm',
|
||||
@@ -783,6 +801,18 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour, onOpenMyPhotos,
|
||||
|
||||
{/* Nhóm bên phải: Các thao tác người dùng */}
|
||||
<div className="flex items-center gap-2 pointer-events-auto">
|
||||
{/* Nút định vị người dùng */}
|
||||
<button
|
||||
onClick={requestGpsPosition}
|
||||
className="w-11 h-11 bg-[var(--surface)] rounded-full shadow-xl hover:bg-blue-100 text-blue-600 transition-all flex items-center justify-center border border-[var(--border)] shrink-0"
|
||||
title="Vị trí của tôi"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
<path d="M12 2v4M12 18v4M2 12h4M18 12h4" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{/* Nút Ảnh của tôi */}
|
||||
{isLoggedInOrGuest && (
|
||||
<button
|
||||
@@ -891,7 +921,7 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour, onOpenMyPhotos,
|
||||
</div>
|
||||
|
||||
<MapContainer
|
||||
center={userPos}
|
||||
center={mapCenter}
|
||||
zoom={mapZoom}
|
||||
className="h-full w-full"
|
||||
preferCanvas={true}
|
||||
@@ -906,9 +936,6 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour, onOpenMyPhotos,
|
||||
{/* Đóng menu và dropdown khi tương tác bản đồ */}
|
||||
<MapEvents onMapAction={() => { setShareMenu(null); setSuggestions([]); setIsFilterDropdownOpen(false); }} />
|
||||
|
||||
{/* Tự động di chuyển bản đồ đến vị trí người dùng khi tìm thấy tọa độ */}
|
||||
<RecenterMap position={userPos} />
|
||||
|
||||
<MarkerClusterGroup key={`cluster-${filteredTours.length}`} chunkedLoading>
|
||||
{filteredTours.map((tour) => {
|
||||
let startLoc = null;
|
||||
@@ -1403,6 +1430,7 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour, onOpenMyPhotos,
|
||||
<button
|
||||
onClick={() => {
|
||||
setUserPos([item.latitude, item.longitude]);
|
||||
setLocalMapCenter([item.latitude, item.longitude]);
|
||||
setMapCenter([item.latitude, item.longitude]);
|
||||
}}
|
||||
className="text-[9px] font-bold text-blue-600 dark:text-blue-400 text-left hover:underline flex items-center gap-1 mt-0.5 cursor-pointer bg-transparent border-none p-0"
|
||||
@@ -1481,6 +1509,7 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour, onOpenMyPhotos,
|
||||
<button
|
||||
onClick={() => {
|
||||
setUserPos([item.latitude, item.longitude]);
|
||||
setLocalMapCenter([item.latitude, item.longitude]);
|
||||
setMapCenter([item.latitude, item.longitude]);
|
||||
}}
|
||||
className="text-[9px] font-bold text-blue-600 dark:text-blue-400 text-left hover:underline flex items-center gap-1 mt-0.5 cursor-pointer bg-transparent border-none p-0"
|
||||
|
||||
Reference in New Issue
Block a user