feat: Move route-preview
This commit is contained in:
307
src/pages/Route/route-preview/MapDataContext.tsx
Normal file
307
src/pages/Route/route-preview/MapDataContext.tsx
Normal file
@@ -0,0 +1,307 @@
|
||||
import { useParams } from "react-router";
|
||||
import { authInstance, languageInstance } from "@shared";
|
||||
import {
|
||||
createContext,
|
||||
ReactNode,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import {
|
||||
RouteData,
|
||||
SightData,
|
||||
SightPatchData,
|
||||
StationData,
|
||||
StationPatchData,
|
||||
} from "./types";
|
||||
|
||||
import { observer } from "mobx-react-lite";
|
||||
|
||||
const MapDataContext = createContext<{
|
||||
originalRouteData?: RouteData;
|
||||
originalStationData?: StationData[];
|
||||
originalSightData?: SightData[];
|
||||
routeData?: RouteData;
|
||||
stationData?: StationDataWithLanguage;
|
||||
sightData?: SightData[];
|
||||
|
||||
isRouteLoading: boolean;
|
||||
isStationLoading: boolean;
|
||||
isSightLoading: boolean;
|
||||
setScaleRange: (min: number, max: number) => void;
|
||||
setMapRotation: (rotation: number) => void;
|
||||
setMapCenter: (x: number, y: number) => void;
|
||||
setStationOffset: (stationId: number, x: number, y: number) => void;
|
||||
setSightCoordinates: (
|
||||
sightId: number,
|
||||
latitude: number,
|
||||
longitude: number
|
||||
) => void;
|
||||
saveChanges: () => void;
|
||||
}>({
|
||||
originalRouteData: undefined,
|
||||
originalStationData: undefined,
|
||||
originalSightData: undefined,
|
||||
routeData: undefined,
|
||||
stationData: undefined,
|
||||
sightData: undefined,
|
||||
|
||||
isRouteLoading: true,
|
||||
isStationLoading: true,
|
||||
isSightLoading: true,
|
||||
setScaleRange: () => {},
|
||||
setMapRotation: () => {},
|
||||
setMapCenter: () => {},
|
||||
setStationOffset: () => {},
|
||||
setSightCoordinates: () => {},
|
||||
saveChanges: () => {},
|
||||
});
|
||||
|
||||
type StationDataWithLanguage = {
|
||||
[key: string]: StationData[];
|
||||
};
|
||||
export const MapDataProvider = observer(
|
||||
({ children }: Readonly<{ children: ReactNode }>) => {
|
||||
const { id: routeId } = useParams<{ id: string }>();
|
||||
|
||||
const [originalRouteData, setOriginalRouteData] = useState<RouteData>();
|
||||
const [originalStationData, setOriginalStationData] =
|
||||
useState<StationData[]>();
|
||||
const [originalSightData, setOriginalSightData] = useState<SightData[]>();
|
||||
|
||||
const [routeData, setRouteData] = useState<RouteData>();
|
||||
const [stationData, setStationData] = useState<StationDataWithLanguage>({
|
||||
RU: [],
|
||||
EN: [],
|
||||
ZH: [],
|
||||
});
|
||||
const [sightData, setSightData] = useState<SightData[]>();
|
||||
|
||||
const [routeChanges, setRouteChanges] = useState<Partial<RouteData>>({});
|
||||
const [stationChanges, setStationChanges] = useState<StationPatchData[]>(
|
||||
[]
|
||||
);
|
||||
const [sightChanges, setSightChanges] = useState<SightPatchData[]>([]);
|
||||
|
||||
const [isRouteLoading, setIsRouteLoading] = useState(true);
|
||||
const [isStationLoading, setIsStationLoading] = useState(true);
|
||||
const [isSightLoading, setIsSightLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
setIsRouteLoading(true);
|
||||
setIsStationLoading(true);
|
||||
setIsSightLoading(true);
|
||||
|
||||
const [
|
||||
routeResponse,
|
||||
ruStationResponse,
|
||||
enStationResponse,
|
||||
zhStationResponse,
|
||||
sightResponse,
|
||||
] = await Promise.all([
|
||||
authInstance.get(`/route/${routeId}`),
|
||||
languageInstance("ru").get(`/route/${routeId}/station`),
|
||||
languageInstance("en").get(`/route/${routeId}/station`),
|
||||
languageInstance("zh").get(`/route/${routeId}/station`),
|
||||
authInstance.get(`/route/${routeId}/sight`),
|
||||
]);
|
||||
|
||||
setOriginalRouteData(routeResponse.data as RouteData);
|
||||
setOriginalStationData(ruStationResponse.data as StationData[]);
|
||||
setStationData({
|
||||
ru: ruStationResponse.data as StationData[],
|
||||
en: enStationResponse.data as StationData[],
|
||||
zh: zhStationResponse.data as StationData[],
|
||||
});
|
||||
setOriginalSightData(sightResponse as unknown as SightData[]);
|
||||
|
||||
setIsRouteLoading(false);
|
||||
setIsStationLoading(false);
|
||||
setIsSightLoading(false);
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
setIsRouteLoading(false);
|
||||
setIsStationLoading(false);
|
||||
setIsSightLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [routeId]);
|
||||
|
||||
useEffect(() => {
|
||||
// combine changes with original data
|
||||
if (originalRouteData)
|
||||
setRouteData({ ...originalRouteData, ...routeChanges });
|
||||
if (originalSightData) setSightData(originalSightData);
|
||||
}, [
|
||||
originalRouteData,
|
||||
originalSightData,
|
||||
routeChanges,
|
||||
stationChanges,
|
||||
sightChanges,
|
||||
]);
|
||||
|
||||
function setScaleRange(min: number, max: number) {
|
||||
setRouteChanges((prev) => {
|
||||
return { ...prev, scale_min: min, scale_max: max };
|
||||
});
|
||||
}
|
||||
|
||||
function setMapRotation(rotation: number) {
|
||||
setRouteChanges((prev) => {
|
||||
return { ...prev, rotate: rotation };
|
||||
});
|
||||
}
|
||||
|
||||
function setMapCenter(x: number, y: number) {
|
||||
setRouteChanges((prev) => {
|
||||
return { ...prev, center_latitude: x, center_longitude: y };
|
||||
});
|
||||
}
|
||||
|
||||
async function saveChanges() {
|
||||
await authInstance.patch(`/route/${routeId}`, routeData);
|
||||
await saveStationChanges();
|
||||
await saveSightChanges();
|
||||
}
|
||||
|
||||
async function saveStationChanges() {
|
||||
for (const station of stationChanges) {
|
||||
const response = await authInstance.patch(
|
||||
`/route/${routeId}/station`,
|
||||
station
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function saveSightChanges() {
|
||||
console.log("sightChanges", sightChanges);
|
||||
for (const sight of sightChanges) {
|
||||
const response = await authInstance.patch(
|
||||
`/route/${routeId}/sight`,
|
||||
sight
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function setStationOffset(stationId: number, x: number, y: number) {
|
||||
setStationChanges((prev) => {
|
||||
let found = prev.find((station) => station.station_id === stationId);
|
||||
if (found) {
|
||||
found.offset_x = x;
|
||||
found.offset_y = y;
|
||||
|
||||
return prev.map((station) => {
|
||||
if (station.station_id === stationId) {
|
||||
return found;
|
||||
}
|
||||
return station;
|
||||
});
|
||||
} else {
|
||||
const foundStation = stationData.ru?.find(
|
||||
(station) => station.id === stationId
|
||||
);
|
||||
if (foundStation) {
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
station_id: stationId,
|
||||
offset_x: x,
|
||||
offset_y: y,
|
||||
transfers: foundStation.transfers,
|
||||
},
|
||||
];
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setSightCoordinates(
|
||||
sightId: number,
|
||||
latitude: number,
|
||||
longitude: number
|
||||
) {
|
||||
setSightChanges((prev) => {
|
||||
let found = prev.find((sight) => sight.sight_id === sightId);
|
||||
if (found) {
|
||||
found.latitude = latitude;
|
||||
found.longitude = longitude;
|
||||
|
||||
return prev.map((sight) => {
|
||||
if (sight.sight_id === sightId) {
|
||||
return found;
|
||||
}
|
||||
return sight;
|
||||
});
|
||||
} else {
|
||||
const foundSight = sightData?.find((sight) => sight.id === sightId);
|
||||
if (foundSight) {
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
sight_id: sightId,
|
||||
latitude,
|
||||
longitude,
|
||||
},
|
||||
];
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log("sightChanges", sightChanges);
|
||||
}, [sightChanges]);
|
||||
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
originalRouteData,
|
||||
originalStationData,
|
||||
originalSightData,
|
||||
routeData,
|
||||
stationData,
|
||||
sightData,
|
||||
isRouteLoading,
|
||||
isStationLoading,
|
||||
isSightLoading,
|
||||
setScaleRange,
|
||||
setMapRotation,
|
||||
setMapCenter,
|
||||
saveChanges,
|
||||
setStationOffset,
|
||||
setSightCoordinates,
|
||||
}),
|
||||
[
|
||||
originalRouteData,
|
||||
originalStationData,
|
||||
originalSightData,
|
||||
routeData,
|
||||
stationData,
|
||||
sightData,
|
||||
isRouteLoading,
|
||||
isStationLoading,
|
||||
isSightLoading,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<MapDataContext.Provider value={value}>
|
||||
{children}
|
||||
</MapDataContext.Provider>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const useMapData = () => {
|
||||
const context = useContext(MapDataContext);
|
||||
if (!context) {
|
||||
throw new Error("useMapData must be used within a MapDataProvider");
|
||||
}
|
||||
return context;
|
||||
};
|
||||
Reference in New Issue
Block a user