feature/webgl-preview-update (#21)

Co-authored-by: itoshi <kkzemeow@gmail.com>
Reviewed-on: #21
This commit was merged in pull request #21.
This commit is contained in:
2026-10-13 16:49:18 +00:00
parent 50ad374cf5
commit 71834904a1
327 changed files with 39034 additions and 8829 deletions

View File

@@ -1,13 +1,21 @@
import { makeAutoObservable, runInAction } from "mobx";
import { authInstance } from "@shared";
import {
authInstance,
languageInstance,
languageStore,
isMediaIdEmpty,
} from "@shared";
export type Route = {
route_name: string;
carrier: string;
carrier_id: number;
center_latitude: number;
center_longitude: number;
governor_appeal: number;
button_text?: string;
id: number;
icon: string;
path: number[][];
rotate: number;
route_direction: boolean;
@@ -16,6 +24,7 @@ export type Route = {
scale_max: number;
scale_min: number;
video_preview: string;
video_timer: number;
};
class RouteStore {
@@ -45,7 +54,7 @@ class RouteStore {
});
};
createRoute = async (route: any) => {
createRoute = async (route: any): Promise<number> => {
const response = await authInstance.post("/route", route);
const id = response.data.id;
@@ -53,6 +62,8 @@ class RouteStore {
this.route[id] = { ...route, id };
this.routes.data = [...this.routes.data, { ...route, id }];
});
return id;
};
deleteRoute = async (id: number) => {
@@ -88,21 +99,54 @@ class RouteStore {
};
saveRouteStations = async (routeId: number, stationId: number) => {
await authInstance.patch(`/route/${routeId}/station`, {
...this.routeStations[routeId]?.find(
(station) => station.id === stationId
),
const { language } = languageStore;
const stationResponse = await languageInstance(language).get(
`/station/${stationId}`
);
const fullStationData = stationResponse.data;
// Получаем отредактированные данные из локального кеша
const editedStationData = this.routeStations[routeId]?.find(
(station) => station.id === stationId
);
const dataToSend: any = {
station_id: stationId,
offset_x: editedStationData?.offset_x ?? fullStationData.offset_x ?? 0,
offset_y: editedStationData?.offset_y ?? fullStationData.offset_y ?? 0,
align: editedStationData?.align ?? fullStationData.align ?? 0,
transfers: fullStationData.transfers || {},
};
await authInstance.patch(`/route/${routeId}/station`, dataToSend);
// Обновляем локальный кеш после успешного сохранения
runInAction(() => {
if (this.routeStations[routeId]) {
this.routeStations[routeId] = this.routeStations[routeId].map(
(station) =>
station.id === stationId
? {
...station,
...dataToSend,
}
: station
);
}
});
};
editRouteData = {
route_name: "",
carrier: "",
carrier_id: 0,
center_latitude: "",
center_longitude: "",
governor_appeal: 0,
button_text: "" as string | undefined,
id: 0,
icon: "",
path: [] as number[][],
rotate: 0,
route_direction: false,
@@ -110,7 +154,8 @@ class RouteStore {
route_sys_number: "",
scale_max: 0,
scale_min: 0,
video_preview: "",
video_preview: "" as string | undefined,
video_timer: 420,
};
setEditRouteData = (data: any) => {
@@ -118,11 +163,30 @@ class RouteStore {
};
editRoute = async (id: number) => {
const response = await authInstance.patch(`/route/${id}`, {
if (
!this.editRouteData.video_preview ||
isMediaIdEmpty(this.editRouteData.video_preview)
) {
delete this.editRouteData.video_preview;
}
if (!this.editRouteData.icon || isMediaIdEmpty(this.editRouteData.icon)) {
delete (this.editRouteData as any).icon;
}
const dataToSend: any = {
...this.editRouteData,
route_name: (this.editRouteData.route_name || "").trim(),
route_number: (this.editRouteData.route_number || "").trim(),
route_sys_number: (this.editRouteData.route_sys_number || "").trim(),
center_latitude: parseFloat(this.editRouteData.center_latitude),
center_longitude: parseFloat(this.editRouteData.center_longitude),
});
};
if (
this.editRouteData.governor_appeal === 0 ||
!this.editRouteData.governor_appeal
) {
dataToSend.governor_appeal = null;
}
const response = await authInstance.patch(`/route/${id}`, dataToSend);
runInAction(() => {
this.route[id] = response.data;
@@ -140,6 +204,49 @@ class RouteStore {
});
};
sightCounts: Map<number, number> = new Map();
stationCounts: Map<number, number> = new Map();
countsLoading = false;
loadCounts = async (routeIds: number[]) => {
if (routeIds.length === 0) return;
runInAction(() => {
this.countsLoading = true;
});
const batchSize = 20;
for (let i = 0; i < routeIds.length; i += batchSize) {
const batch = routeIds.slice(i, i + batchSize);
const results = await Promise.allSettled(
batch.flatMap((id) => [
authInstance.get(`/route/${id}/sight/count`).then((res) => ({ id, type: "sight", data: res.data })),
authInstance.get(`/route/${id}/station/count`).then((res) => ({ id, type: "station", data: res.data })),
])
);
runInAction(() => {
for (const result of results) {
if (result.status === "fulfilled") {
const { id, type, data } = result.value;
let count = 0;
if (typeof data === "number") {
count = data;
} else if (data && typeof data === "object") {
count = Object.values(data).reduce((sum: number, v: any) => sum + (Number(v) || 0), 0);
}
if (type === "sight") this.sightCounts.set(id, count);
else this.stationCounts.set(id, count);
}
}
});
}
runInAction(() => {
this.countsLoading = false;
});
};
selectedStationId = 0;
setSelectedStationId = (id: number) => {