added geolocation

This commit is contained in:
SlimusMinus
2026-09-01 01:19:59 +03:00
parent 0ce69c14c1
commit 98473d4b59
8 changed files with 196 additions and 54 deletions

View File

@@ -2,12 +2,15 @@ import { useCallback, useEffect, useRef, useState } from "react";
const YANDEX_GEOCODER_API_KEY = process.env.REACT_APP_YANDEX_GEOCODER_KEY;
const DEFAULT_CENTER = [55.751574, 37.573856];
export function useYandexMap(enabled, initialCoords = null, initialAddress = "") {
const mapRef = useRef(null);
const ymapRef = useRef(null);
const placemarkRef = useRef(null);
const initializedRef = useRef(false); // защита от двойного вызова StrictMode
const requestIdRef = useRef(0); // защита от гонки при быстрых кликах/драгах
const pendingFocusRef = useRef(null); // фокус, который ждёт, пока карта инициализируется
const [coords, setCoords] = useState(initialCoords);
const [address, setAddress] = useState(initialAddress ?? "");
@@ -48,60 +51,84 @@ export function useYandexMap(enabled, initialCoords = null, initialAddress = "")
});
}, []);
const attachPlacemarkDrag = useCallback((map, pm) => {
pm.events.add("dragend", () => {
const [lat, lng] = pm.geometry.getCoordinates();
setCoords({ latitude: lat, longitude: lng });
resolveAddress(lat, lng);
});
}, [resolveAddress]);
/**
* Устанавливает маркер и центр карты на указанные координаты.
* Если карта ещё не инициализирована — откладывает фокус до её создания.
*/
const moveTo = useCallback((lat, lng) => {
const map = ymapRef.current;
if (!map) {
pendingFocusRef.current = { lat, lng };
return;
}
pendingFocusRef.current = null;
setCoords({ latitude: lat, longitude: lng });
if (placemarkRef.current) {
placemarkRef.current.geometry.setCoordinates([lat, lng]);
} else {
const pm = new window.ymaps.Placemark([lat, lng], {}, { draggable: true });
map.geoObjects.add(pm);
placemarkRef.current = pm;
attachPlacemarkDrag(map, pm);
}
map.setCenter([lat, lng], 14);
resolveAddress(lat, lng);
}, [attachPlacemarkDrag, resolveAddress]);
const init = useCallback(() => {
if (ymapRef.current) return;
const startCoords = coordsRef.current;
const center = startCoords
? [startCoords.latitude, startCoords.longitude]
: DEFAULT_CENTER;
const map = new window.ymaps.Map(mapRef.current, {
center,
zoom: 14,
controls: ["zoomControl", "geolocationControl"],
});
ymapRef.current = map;
if (startCoords) {
const pm = new window.ymaps.Placemark(center, {}, { draggable: true });
map.geoObjects.add(pm);
placemarkRef.current = pm;
attachPlacemarkDrag(map, pm);
resolveAddress(startCoords.latitude, startCoords.longitude);
}
map.events.add("click", e => {
const [lat, lng] = e.get("coords");
moveTo(lat, lng);
});
// Если геолокация успела вернуть координаты раньше, чем создалась карта,
// применяем отложенный фокус сразу после инициализации.
if (pendingFocusRef.current) {
const pending = pendingFocusRef.current;
pendingFocusRef.current = null;
moveTo(pending.lat, pending.lng);
}
}, [attachPlacemarkDrag, resolveAddress, moveTo]);
useEffect(() => {
if (!enabled || !mapRef.current) return;
if (initializedRef.current) return; // уже инициализировали
initializedRef.current = true;
const init = () => {
if (ymapRef.current) return;
const startCoords = coordsRef.current;
const center = startCoords
? [startCoords.latitude, startCoords.longitude]
: [55.751574, 37.573856];
const map = new window.ymaps.Map(mapRef.current, {
center,
zoom: 14,
controls: ["zoomControl", "geolocationControl"],
});
ymapRef.current = map;
if (startCoords) {
const pm = new window.ymaps.Placemark(center, {}, { draggable: true });
map.geoObjects.add(pm);
placemarkRef.current = pm;
resolveAddress(startCoords.latitude, startCoords.longitude);
pm.events.add("dragend", () => {
const [lat, lng] = pm.geometry.getCoordinates();
setCoords({ latitude: lat, longitude: lng });
resolveAddress(lat, lng);
});
}
map.events.add("click", e => {
const [lat, lng] = e.get("coords");
setCoords({ latitude: lat, longitude: lng });
resolveAddress(lat, lng);
if (placemarkRef.current) {
placemarkRef.current.geometry.setCoordinates([lat, lng]);
} else {
const pm = new window.ymaps.Placemark([lat, lng], {}, { draggable: true });
map.geoObjects.add(pm);
placemarkRef.current = pm;
pm.events.add("dragend", () => {
const [la, ln] = pm.geometry.getCoordinates();
setCoords({ latitude: la, longitude: ln });
resolveAddress(la, ln);
});
}
});
};
if (window.ymaps?.ready) {
window.ymaps.ready(init);
}
@@ -111,12 +138,13 @@ export function useYandexMap(enabled, initialCoords = null, initialAddress = "")
ymapRef.current = null;
placemarkRef.current = null;
initializedRef.current = false;
pendingFocusRef.current = null;
};
// enabled намеренно в deps — карта инициализируется/разрушается при открытии
// и закрытии модалки. Начальные координаты читаются из coordsRef, чтобы
// при повторном открытии маркер вставал на последний выбранный адрес.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [enabled, resolveAddress]);
}, [enabled, init]);
return { mapRef, coords, address, addressRef, loading };
}
return { mapRef, coords, address, addressRef, loading, moveTo };
}