From 4bcc2e2cca5871bd169f426b8a8e9250b8439f0b Mon Sep 17 00:00:00 2001 From: itoshi Date: Mon, 6 Oct 2025 10:03:41 +0300 Subject: [PATCH] fix: Fix webp + delete ctrl z + filter by city --- src/pages/MapPage/index.tsx | 306 +----------------- src/pages/Media/MediaEditPage/index.tsx | 12 +- src/pages/Sight/SightListPage/index.tsx | 20 +- src/pages/Station/StationListPage/index.tsx | 23 +- src/shared/modals/UploadMediaDialog/index.tsx | 6 +- src/widgets/MediaViewer/index.tsx | 5 - tsconfig.tsbuildinfo | 2 +- 7 files changed, 65 insertions(+), 309 deletions(-) diff --git a/src/pages/MapPage/index.tsx b/src/pages/MapPage/index.tsx index 39ea4d6..3577995 100644 --- a/src/pages/MapPage/index.tsx +++ b/src/pages/MapPage/index.tsx @@ -595,10 +595,6 @@ interface MapServiceConfig { zoom: number; } -interface HistoryState { - state: string; -} - type FeatureType = "station" | "route" | "sight"; class MapService { @@ -620,9 +616,6 @@ class MapService { private modifyInteraction: Modify; private selectInteraction: Select; private hoveredFeatureId: string | number | null; - private history: HistoryState[]; - private historyIndex: number; - private beforeActionState: string | null = null; private boundHandlePointerMove: ( event: MapBrowserEvent ) => void; @@ -674,8 +667,6 @@ class MapService { this.currentDrawingFeatureType = null; this.currentInteraction = null; this.hoveredFeatureId = null; - this.history = []; - this.historyIndex = -1; this.clusterStyleCache = {}; this.setLoading = setLoading; @@ -1037,21 +1028,10 @@ class MapService { }, }); - // @ts-ignore - this.modifyInteraction.on("modifystart", () => { - if (!this.beforeActionState) { - this.beforeActionState = this.getCurrentStateAsGeoJSON(); - } - }); - this.modifyInteraction.on("modifyend", (event) => { - if (this.beforeActionState) { - this.addStateToHistory(this.beforeActionState); - } event.features.getArray().forEach((feature) => { this.saveModifiedFeature(feature as Feature); }); - this.beforeActionState = null; }); if (this.map) { @@ -1100,11 +1080,6 @@ class MapService { return; } - const beforeState = this.getCurrentStateAsGeoJSON(); - if (beforeState) { - this.addStateToHistory(beforeState); - } - const newCoordinates = coordinates.filter( (_, index) => index !== closestIndex ); @@ -1330,197 +1305,6 @@ class MapService { this.lineSource.addFeatures(lineFeatures); this.updateFeaturesInReact(); - const initialState = this.getCurrentStateAsGeoJSON(); - if (initialState) { - this.addStateToHistory(initialState); - } - } - - private addStateToHistory(stateToSave: string): void { - this.history = this.history.slice(0, this.historyIndex + 1); - this.history.push({ state: stateToSave }); - this.historyIndex = this.history.length - 1; - } - - private getCurrentStateAsGeoJSON(): string | null { - if (!this.map) return null; - const geoJSONFormat = new GeoJSON(); - const allFeatures = [ - ...this.pointSource.getFeatures(), - ...this.lineSource.getFeatures(), - ]; - return geoJSONFormat.writeFeatures(allFeatures, { - dataProjection: "EPSG:4326", - featureProjection: this.map.getView().getProjection().getCode(), - }); - } - - private applyHistoryState(geoJSONState: string) { - if (!this.map) return; - const projection = this.map.getView().getProjection(); - const geoJSONFormat = new GeoJSON({ - dataProjection: "EPSG:4326", - featureProjection: projection.getCode(), - }); - const features = geoJSONFormat.readFeatures( - geoJSONState - ) as Feature[]; - - this.unselect(); - this.pointSource.clear(); - this.lineSource.clear(); - - const pointFeatures: Feature[] = []; - const lineFeatures: Feature[] = []; - - features.forEach((feature) => { - const featureType = feature.get("featureType"); - const isProxy = feature.get("isProxy"); - if (featureType === "route" && !isProxy) { - lineFeatures.push(feature as Feature); - } else { - pointFeatures.push(feature as Feature); - } - }); - - this.pointSource.addFeatures(pointFeatures); - this.lineSource.addFeatures(lineFeatures); - - this.updateFeaturesInReact(); - - const newStations: ApiStation[] = []; - const newRoutes: ApiRoute[] = []; - const newSights: ApiSight[] = []; - - features.forEach((feature) => { - const id = feature.getId(); - if (!id || feature.get("isProxy")) return; - - const [featureType, numericIdStr] = String(id).split("-"); - const numericId = parseInt(numericIdStr, 10); - if (isNaN(numericId)) return; - const geometry = feature.getGeometry(); - if (!geometry) return; - const properties = feature.getProperties(); - - if (featureType === "station") { - const coords = (geometry as Point).getCoordinates(); - const [lon, lat] = toLonLat(coords, projection); - newStations.push({ - id: numericId, - name: properties.name, - latitude: lat, - longitude: lon, - city_id: properties.city_id || 1, // Default city_id if not available - }); - } else if (featureType === "sight") { - const coords = (geometry as Point).getCoordinates(); - const [lon, lat] = toLonLat(coords, projection); - newSights.push({ - id: numericId, - name: properties.name, - description: properties.description, - latitude: lat, - longitude: lon, - city_id: properties.city_id || 1, // Default city_id if not available - }); - } else if (featureType === "route") { - const coords = (geometry as LineString).getCoordinates(); - const path = coords.map((c) => { - const [lon, lat] = toLonLat(c, projection); - return [lat, lon] as [number, number]; - }); - - const centerCoords = getCenter(geometry.getExtent()); - const [center_longitude, center_latitude] = toLonLat( - centerCoords, - projection - ); - - newRoutes.push({ - id: numericId, - route_number: properties.name, - path: path, - center_latitude, - center_longitude, - }); - } - }); - - mapStore.stations = newStations; - mapStore.routes = newRoutes.sort((a, b) => - a.route_number.localeCompare(b.route_number) - ); - mapStore.sights = newSights; - } - - public undo(): void { - if (this.historyIndex > 0) { - this.historyIndex--; - const stateToRestore = this.history[this.historyIndex].state; - this.applyHistoryState(stateToRestore); - - const features = [ - ...this.pointSource.getFeatures().filter((f) => !f.get("isProxy")), - ...this.lineSource.getFeatures(), - ]; - const updatePromises = features.map((feature) => { - const featureType = feature.get("featureType"); - const geoJSONFormat = new GeoJSON({ - dataProjection: "EPSG:4326", - featureProjection: this.map?.getView().getProjection().getCode(), - }); - const featureGeoJSON = geoJSONFormat.writeFeatureObject(feature); - return mapStore.updateFeature(featureType, featureGeoJSON); - }); - - Promise.all(updatePromises) - .then(() => {}) - .catch((error) => { - console.error("Failed to update backend after undo:", error); - this.historyIndex++; - const previousState = this.history[this.historyIndex].state; - this.applyHistoryState(previousState); - }); - } else { - toast.info("Больше отменять нечего"); - } - } - - public redo(): void { - if (this.historyIndex < this.history.length - 1) { - this.historyIndex++; - const stateToRestore = this.history[this.historyIndex].state; - this.applyHistoryState(stateToRestore); - - const features = [ - ...this.pointSource.getFeatures().filter((f) => !f.get("isProxy")), - ...this.lineSource.getFeatures(), - ]; - const updatePromises = features.map((feature) => { - const featureType = feature.get("featureType"); - const geoJSONFormat = new GeoJSON({ - dataProjection: "EPSG:4326", - featureProjection: this.map?.getView().getProjection().getCode(), - }); - const featureGeoJSON = geoJSONFormat.writeFeatureObject(feature); - return mapStore.updateFeature(featureType, featureGeoJSON); - }); - - Promise.all(updatePromises) - .then(() => { - toast.info("Действие повторено"); - }) - .catch((error) => { - console.error("Failed to update backend after redo:", error); - toast.error("Не удалось обновить данные на сервере"); - this.historyIndex--; - const previousState = this.history[this.historyIndex].state; - this.applyHistoryState(previousState); - }); - } else { - toast.info("Больше повторять нечего"); - } } private updateFeaturesInReact(): void { @@ -1543,16 +1327,6 @@ class MapService { } private handleKeyDown(event: KeyboardEvent): void { - if ((event.ctrlKey || event.metaKey) && event.key === "z") { - event.preventDefault(); - this.undo(); - return; - } - if ((event.ctrlKey || event.metaKey) && event.key === "y") { - event.preventDefault(); - this.redo(); - return; - } if (event.key === "Escape") { this.unselect(); } @@ -1619,36 +1393,20 @@ class MapService { style: styleForDrawing, }); - this.currentInteraction.on("drawstart", () => { - this.beforeActionState = this.getCurrentStateAsGeoJSON(); - }); - this.currentInteraction.on("drawend", async (event: DrawEvent) => { - if (this.beforeActionState) { - this.addStateToHistory(this.beforeActionState); - } - this.beforeActionState = null; - const feature = event.feature as Feature; const fType = this.currentDrawingFeatureType; if (!fType) return; feature.set("featureType", fType); let resourceName: string; - const allFeatures = [ - ...this.pointSource.getFeatures(), - ...this.lineSource.getFeatures(), - ]; switch (fType) { case "station": - const existingStations = allFeatures.filter( - (f) => f.get("featureType") === "station" - ); - const stationNumbers = existingStations - .map((f) => { - const name = f.get("name") as string; - const match = name?.match(/^Остановка (\d+)$/); + // Используем полный список из mapStore, а не отфильтрованный + const stationNumbers = mapStore.stations + .map((station) => { + const match = station.name?.match(/^Остановка (\d+)$/); return match ? parseInt(match[1], 10) : 0; }) .filter((num) => num > 0); @@ -1657,13 +1415,10 @@ class MapService { resourceName = `Остановка ${nextStationNumber}`; break; case "sight": - const existingSights = allFeatures.filter( - (f) => f.get("featureType") === "sight" - ); - const sightNumbers = existingSights - .map((f) => { - const name = f.get("name") as string; - const match = name?.match(/^Достопримечательность (\d+)$/); + // Используем полный список из mapStore, а не отфильтрованный + const sightNumbers = mapStore.sights + .map((sight) => { + const match = sight.name?.match(/^Достопримечательность (\d+)$/); return match ? parseInt(match[1], 10) : 0; }) .filter((num) => num > 0); @@ -1672,13 +1427,10 @@ class MapService { resourceName = `Достопримечательность ${nextSightNumber}`; break; case "route": - const existingRoutes = allFeatures.filter( - (f) => f.get("featureType") === "route" && !f.get("isProxy") - ); - const routeNumbers = existingRoutes - .map((f) => { - const name = f.get("name") as string; - const match = name?.match(/^Маршрут (\d+)$/); + // Используем полный список из mapStore, а не отфильтрованный + const routeNumbers = mapStore.routes + .map((route) => { + const match = route.route_number?.match(/^Маршрут (\d+)$/); return match ? parseInt(match[1], 10) : 0; }) .filter((num) => num > 0); @@ -1841,18 +1593,12 @@ class MapService { ): void { if (featureId === undefined) return; - this.beforeActionState = this.getCurrentStateAsGeoJSON(); - const numericId = parseInt(String(featureId).split("-")[1], 10); if (!recourse || isNaN(numericId)) return; mapStore .deleteFeature(recourse, numericId) .then(() => { - if (this.beforeActionState) - this.addStateToHistory(this.beforeActionState); - this.beforeActionState = null; - if (recourse === "route") { const lineFeature = this.lineSource.getFeatureById(featureId); if (lineFeature) @@ -1877,8 +1623,6 @@ class MapService { public deleteMultipleFeatures(featureIds: (string | number)[]): void { if (!featureIds || featureIds.length === 0) return; - this.beforeActionState = this.getCurrentStateAsGeoJSON(); - const deletePromises = Array.from(featureIds).map((id) => { const recourse = String(id).split("-")[0]; const numericId = parseInt(String(id).split("-")[1], 10); @@ -1895,10 +1639,6 @@ class MapService { | number )[]; if (successfulDeletes.length > 0) { - if (this.beforeActionState) - this.addStateToHistory(this.beforeActionState); - this.beforeActionState = null; - successfulDeletes.forEach((id) => { const recourse = String(id).split("-")[0]; if (recourse === "route") { @@ -2029,9 +1769,6 @@ class MapService { // Метод для сброса кешей карты public clearCaches() { this.clusterStyleCache = {}; - this.history = []; - this.historyIndex = -1; - this.beforeActionState = null; this.hoveredFeatureId = null; this.selectedIds.clear(); @@ -2086,9 +1823,6 @@ class MapService { } catch (error) { console.error("Failed to update feature:", error); toast.error(`Не удалось обновить: ${error}`); - if (this.beforeActionState) { - this.applyHistoryState(this.beforeActionState); - } } } @@ -2156,10 +1890,6 @@ class MapService { if (this.pointSource.hasFeature(feature as Feature)) this.pointSource.removeFeature(feature as Feature); } - if (this.beforeActionState) { - this.applyHistoryState(this.beforeActionState); - } - this.beforeActionState = null; } } } @@ -2994,18 +2724,6 @@ export const MapPage: React.FC = observer(() => { Esc{" "} - Отменить выделение -
  • - - Ctrl+Z - {" "} - - Отменить действие -
  • -
  • - - Ctrl+Y - {" "} - - Повторить действие -