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:
2026-06-25 11:58:32 +07:00
parent 8e984e609b
commit 90475d4130
+50 -21
View File
@@ -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
@@ -890,13 +920,13 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour, onOpenMyPhotos,
</div>
</div>
<MapContainer
center={userPos}
zoom={mapZoom}
className="h-full w-full"
preferCanvas={true}
attributionControl={false}
>
<MapContainer
center={mapCenter}
zoom={mapZoom}
className="h-full w-full"
preferCanvas={true}
attributionControl={false}
>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
@@ -905,9 +935,6 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour, onOpenMyPhotos,
<MapTracker />
{/* Đó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) => {
@@ -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"