fix: fix build errors

This commit is contained in:
2026-04-24 16:44:18 +03:00
parent d67df0c2e1
commit 7f8b90c15e
20 changed files with 78 additions and 262 deletions

4
src/client/src/App.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
import React from "react";
declare const App: React.FC;
export default App;

View File

@@ -21,6 +21,7 @@ import {
GetCityResponse,
GetSightArticleResponse,
} from "./types";
// @ts-ignore
import { orderStationsByRoute } from "../../utils/routeStationsUtils";
class ApiStore {

View File

@@ -5,6 +5,7 @@ export type GetContextResponse = {
};
endStopId: string;
nearestSightId: string;
nearestStationId?: string | null;
rawCoordinates: {
latitude: number;
longitude: number;
@@ -105,6 +106,7 @@ export type GetRouteSightsResponse = {
icon?: string;
alt_icon?: string;
is_default_icon?: boolean;
short_name?: string;
}[];
export type GetRouteStationsResponse = {

9
src/client/src/api/apiConfig.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
import { AxiosInstance } from "axios";
export declare const apiBaseURL: string;
export declare const geoBaseURL: string;
export declare const weatherBaseURL: string;
export declare const getMediaUrl: (id: string) => string;
export declare const apiInstance: AxiosInstance;
export declare const geoInstance: AxiosInstance;
export declare const weatherInstance: AxiosInstance;

8
src/client/src/assets/Constants.d.ts vendored Normal file
View File

@@ -0,0 +1,8 @@
export declare const UP_SCALE: number;
export declare const PATH_WIDTH: number;
export declare const STATION_RADIUS: number;
export declare const STATION_OUTLINE_WIDTH: number;
export declare const SIGHT_SIZE: number;
export declare const SCALE_FACTOR: number;
export declare const BACKGROUND_COLOR: number;
export declare const PATH_COLOR: number;

View File

@@ -0,0 +1,13 @@
import React from "react";
export declare const OverlayScrollbarsWrapper: React.ForwardRefExoticComponent<
React.PropsWithChildren<{
className?: string;
onScroll?: (event: Event) => void;
overflowX?: string;
overflowY?: string;
scrollbarVisibility?: string;
[key: string]: any;
}> &
React.RefAttributes<HTMLElement>
>;

View File

@@ -1,4 +1,4 @@
import React from "react";
import "react";
import ReactMarkdown from "react-markdown";
import rehypeRaw from "rehype-raw";
import "./ReactMarkdown.css";

View File

@@ -1,12 +1,8 @@
import { FederatedPointerEvent, FederatedWheelEvent } from "pixi.js";
import { ReactNode, useEffect, useState, useRef, useCallback } from "react";
import { ReactNode, useEffect, useState, useRef } from "react";
import { useTransform } from "./transformContext";
import { BACKGROUND_COLOR, SCALE_FACTOR } from "../../assets/Constants";
import { useApplication } from "@pixi/react";
import { useGeolocation } from "../../context/GeolocationContext";
import ContentAPI from "../../api/content/content.api";
import React from "react";
import { useGeolocationStore } from "../../stores/hooks/useGeolocationStore";
import { useCameraAnimationStore } from "../../stores";
import { observer } from "mobx-react-lite";
import debounce from "lodash/debounce";
@@ -25,7 +21,6 @@ export const InfiniteCanvas = observer(
setIsAutoMode,
userActivityTimestamp,
updateUserActivity,
autoModeStartTimestamp,
setAutoModeStartTimestamp,
} = useTransform();
const [loaded, setLoaded] = useState(false);
@@ -49,17 +44,14 @@ export const InfiniteCanvas = observer(
position: { x: number; y: number };
} | null>(null);
// Keep these for backward compatibility, but we'll use pinchStartData for calculations
const [initialPinchDistance, setInitialPinchDistance] = useState<
number | null
>(null);
const [initialPinchMidpoint, setInitialPinchMidpoint] = useState<{
const [, setInitialPinchDistance] = useState<number | null>(null);
const [, setInitialPinchMidpoint] = useState<{
x: number;
y: number;
} | null>(null);
const [scaleMin, setScaleMin] = useState(0.1); // Default min scale
const [scaleMax, setScaleMax] = useState(3); // Default max scale
const store = useGeolocationStore();
const cameraAnimationStore = useCameraAnimationStore();
// Add debounced version of syncState to reduce jittering
@@ -269,13 +261,14 @@ export const InfiniteCanvas = observer(
setInitialPinchMidpoint(null);
pinchStartData.current = null;
if (isDragging) {
const newPosition = {
x: startPosition.x - startMousePosition.x + e.globalX,
y: startPosition.y - startMousePosition.y + e.globalY,
};
setPosition(newPosition);
syncStateDebounced(newPosition, scale);
if (isDragging) {
const newPosition = {
x: startPosition.x - startMousePosition.x + e.globalX,
y: startPosition.y - startMousePosition.y + e.globalY,
};
setPosition(newPosition);
syncStateDebounced(newPosition, scale);
}
}
e.stopPropagation();
};

View File

@@ -1,31 +1,20 @@
import React, {
import {
useCallback,
useEffect,
useRef,
useState,
useMemo,
} from "react";
import { observer } from "mobx-react-lite";
import { Application, extend } from "@pixi/react";
import { extend } from "@pixi/react";
import { Container, Graphics, Sprite, Text } from "pixi.js";
import { MapDataProvider, useMapData } from "./MapDataContext";
import { TransformProvider, useTransform } from "./transformContext";
import { InfiniteCanvas } from "./InfiniteCanvas";
import { TravelPath } from "./TravelPath";
import { Station } from "./Station";
import { SightsLayer } from "./Sight";
// @ts-ignore
import Loader from "../Loader";
import {
BACKGROUND_COLOR,
BUS_COLOR,
STATION_OUTLINE_WIDTH,
STATION_RADIUS,
UP_SCALE,
} from "./Constants";
import { UP_SCALE } from "./Constants";
import "../../styles/MapLayer.css";
import { useGeolocationStore, useCameraAnimationStore } from "../../stores";
import { useCameraAnimationStore } from "../../stores";
import { coordinatesToLocal } from "./utils";
import { TramIcon } from "./TramIcon";
import { SCALE_FACTOR } from "../../assets/Constants";
import { apiStore } from "../../api/ApiStore/store";
import WebGLMap from "./WebGLMap";
@@ -42,7 +31,7 @@ export function Map() {
);
}
function rotatePoint(x, y, originX, originY, angle) {
function rotatePoint(x: number, y: number, originX: number, originY: number, angle: number) {
const cos = Math.cos(angle);
const sin = Math.sin(angle);
const dx = x - originX;
@@ -53,8 +42,6 @@ function rotatePoint(x, y, originX, originY, angle) {
}
const RouteMap = observer(() => {
const store = useGeolocationStore();
const { contextData } = store;
const {
routeData,
stationData,
@@ -77,7 +64,6 @@ const RouteMap = observer(() => {
scale,
} = useTransform();
const cameraAnimationStore = useCameraAnimationStore();
const parentRef = useRef(null);
const [rotationAngle, setRotationAngle] = useState(0);
@@ -143,7 +129,7 @@ const RouteMap = observer(() => {
const rotationOriginY = 0;
const transformGeoToMapLocal = useCallback(
(latitude, longitude) => {
(latitude: number, longitude: number) => {
if (centerLat === undefined || centerLon === undefined) {
return { x: 0, y: 0 };
}
@@ -239,99 +225,6 @@ const RouteMap = observer(() => {
transformedStations,
]);
const drawActualBusPos = useCallback(
(g: Graphics) => {
g.clear();
if (transformedCurrentCoordinates) {
g.circle(
transformedCurrentCoordinates.x,
transformedCurrentCoordinates.y,
STATION_RADIUS / scale < 10
? 10
: STATION_RADIUS / scale > 20
? 20
: STATION_RADIUS / scale
);
g.fill({ color: BUS_COLOR });
g.stroke({ color: BACKGROUND_COLOR, width: STATION_OUTLINE_WIDTH });
}
},
[transformedCurrentCoordinates, scale]
);
const scaledPoints = useMemo(() => {
if (!routeData?.path) return [];
return routeData.path.map(([latitude, longitude]) => {
const { x, y } = transformGeoToMapLocal(latitude, longitude);
return rotatePoint(x, y, rotationOriginX, rotationOriginY, rotationAngle);
});
}, [
routeData?.path,
transformGeoToMapLocal,
rotationOriginX,
rotationOriginY,
rotationAngle,
]);
const transformedStationsEn = useMemo(() => {
if (!stationDataEn) return [];
return stationDataEn.map((station) => {
const { x, y } = transformGeoToMapLocal(
station.latitude,
station.longitude
);
const rotatedCoords = rotatePoint(
x,
y,
rotationOriginX,
rotationOriginY,
rotationAngle
);
return {
...station,
longitude: rotatedCoords.x,
latitude: rotatedCoords.y,
};
});
}, [
stationDataEn,
transformGeoToMapLocal,
rotationOriginX,
rotationOriginY,
rotationAngle,
]);
const transformedStationsZh = useMemo(() => {
if (!stationDataZh) return [];
return stationDataZh.map((station) => {
const { x, y } = transformGeoToMapLocal(
station.latitude,
station.longitude
);
const rotatedCoords = rotatePoint(
x,
y,
rotationOriginX,
rotationOriginY,
rotationAngle
);
return {
...station,
longitude: rotatedCoords.x,
latitude: rotatedCoords.y,
};
});
}, [
stationDataZh,
transformGeoToMapLocal,
rotationOriginX,
rotationOriginY,
rotationAngle,
]);
if (
!routeData ||
!stationData ||

View File

@@ -1,5 +1,4 @@
// SightsLayer.tsx
import React from "react";
import { Graphics, Assets, Texture, TextStyle } from "pixi.js";
import { useCallback, useEffect, useState, useMemo } from "react";
import { useTransform } from "./transformContext";
@@ -9,7 +8,6 @@ import { useGeolocationStore } from "../../stores"; // Импортируем us
const BASE_ICON_SIZE = 30;
const CLUSTER_RADIUS_BASE = 10;
const CLUSTER_COLOR = 0x1a73e8;
type Cluster = {
id: string;
@@ -150,7 +148,7 @@ function SingleSight({
readonly sight: SightData;
onSightClick: (sightId: string) => void;
}) {
const { scale } = useTransform();
useTransform();
const [texture, setTexture] = useState<Texture>(Texture.EMPTY);
const store = useGeolocationStore();
const { setIsGovernorWidgetOpen } = store;
@@ -197,7 +195,7 @@ function SightCluster({
}) {
const store = useGeolocationStore();
const { setIsGovernorWidgetOpen } = store;
const { scale } = useTransform();
useTransform();
const radius = CLUSTER_RADIUS_BASE;
const [texture, setTexture] = useState<Texture>(Texture.EMPTY);
const fontSize = 14;
@@ -334,7 +332,7 @@ export function SightsLayer({
sights,
pathPoints,
}: Readonly<SightsLayerProps>) {
const { scale } = useTransform();
useTransform();
const distanceThreshold = BASE_ICON_SIZE * 3;
const store = useGeolocationStore(); // Получаем доступ к MobX хранилищу

View File

@@ -1,5 +1,4 @@
import React from "react";
import { Texture, Assets, Graphics } from "pixi.js";
import { Texture, Assets } from "pixi.js";
import { useEffect, useState, useMemo, useRef } from "react";
import { useTransform } from "./transformContext";
import { lerp, lerpAngle } from "../../utils/animationUtils";
@@ -11,8 +10,6 @@ const basePath = new URL(
const tramPath = new URL("../../assets/tramPosition/Tram.svg", import.meta.url)
.href;
// Константы анимации (как в HTML файле)
const ANIMATION_DURATION = 1200; // 1.2 секунды
const LERP_SPEED = 0.1; // Скорость интерполяции (10% каждый кадр)
// Функция для проверки расстояния до ближайшей точки маршрута
@@ -101,7 +98,7 @@ const getDistanceToStations = (
offset_x?: number;
offset_y?: number;
}[],
debug: boolean = false
_debug: boolean = false
) => {
if (!stations || stations.length === 0) {
return Infinity;

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useState, useRef } from "react";
import { lerp, lerpAngle } from "../../utils/animationUtils";
import { lerpAngle } from "../../utils/animationUtils";
import tramBase from "../../assets/tramPosition/Tram Base.svg";
import tramSvg from "../../assets/tramPosition/Tram_Second.svg";
import { getMediaUrl } from "../../api/apiConfig";

View File

@@ -18,8 +18,6 @@ import {
BUS_COLOR,
BASE_ICON_SIZE,
CLUSTER_RADIUS_BASE,
CLUSTER_COLOR,
ACTIVE_STATION_COLOR,
} from "./Constants";
import { SCALE_FACTOR } from "../../assets/Constants";
import { apiStore } from "../../api/ApiStore/store";
@@ -40,7 +38,7 @@ const YELLOW_ICON_FILTER =
const clamp = (value: number, min: number, max: number) =>
Math.min(max, Math.max(min, value));
const debugWebglLog = (...args: unknown[]) => {
const debugWebglLog = (..._args: unknown[]) => {
if (!DEBUG_WEBGL_ROUTE_MAP) return;
};
@@ -424,7 +422,7 @@ export const WebGLMap = observer(() => {
}, []);
const clampPosition = useCallback(
(pos: { x: number; y: number }, currentScale: number) => {
(pos: { x: number; y: number }, _currentScale: number) => {
return pos;
},
[],
@@ -1165,7 +1163,6 @@ export const WebGLMap = observer(() => {
gl.enableVertexAttribArray(attribs.a_pos);
gl.vertexAttribPointer(attribs.a_pos, 2, gl.FLOAT, false, 0, 0);
const vcount = routePath.length / 2;
let tramSegIndex = getCurrentSegIndex();
const dpr = Math.max(1, window.devicePixelRatio || 1);
@@ -1452,53 +1449,6 @@ export const WebGLMap = observer(() => {
const toPointsArray = (arr: number[]) => new Float32Array(arr);
const pathPts: { x: number; y: number }[] = [];
for (let i = 0; i < routePath.length; i += 2)
pathPts.push({ x: routePath[i], y: routePath[i + 1] });
const getSeg = (px: number, py: number) => {
if (pathPts.length < 2) return -1;
let best = -1,
bestD = Infinity;
for (let i = 0; i < pathPts.length - 1; i++) {
const p1 = pathPts[i],
p2 = pathPts[i + 1];
const dx = p2.x - p1.x,
dy = p2.y - p1.y;
const len2 = dx * dx + dy * dy;
if (!len2) continue;
const t = ((px - p1.x) * dx + (py - p1.y) * dy) / len2;
const tt = Math.max(0, Math.min(1, t));
const cx = p1.x + tt * dx,
cy = p1.y + tt * dy;
const d = Math.hypot(px - cx, py - cy);
if (d < bestD) {
bestD = d;
best = i;
}
}
return best;
};
let tramSegForStations = -1;
{
const cLat = routeData?.center_latitude,
cLon = routeData?.center_longitude;
const tram = apiStore?.context?.currentCoordinates as any;
if (tram && cLat !== undefined && cLon !== undefined) {
const loc = coordinatesToLocal(
tram.latitude - cLat,
tram.longitude - cLon,
);
const wx = loc.x * UP_SCALE,
wy = loc.y * UP_SCALE;
const cosR = Math.cos(rotationAngle),
sinR = Math.sin(rotationAngle);
const tx = wx * cosR - wy * sinR,
ty = wx * sinR + wy * cosR;
tramSegForStations = getSeg(tx, ty);
}
}
let activeStationIndex = -1;
const tramCoords = apiStore?.context?.currentCoordinates;
if (
@@ -1719,10 +1669,10 @@ export const WebGLMap = observer(() => {
const sin = Math.sin(rotationAngle);
const startStationData = stationData.find(
(station) => station.id.toString() === apiStore.context?.startStopId,
(station: any) => station.id.toString() === apiStore.context?.startStopId,
);
const endStationData = stationData.find(
(station) => station.id.toString() === apiStore.context?.endStopId,
(station: any) => station.id.toString() === apiStore.context?.endStopId,
);
const terminalStations: number[] = [];
@@ -2331,11 +2281,10 @@ export const WebGLMap = observer(() => {
? { right: 0, transform: "none" }
: { left: "50%", transform: "translateX(-50%)" };
const apiBaseUrl = apiBaseURL;
const isMediaIdEmptyResult = isMediaIdEmpty(station?.icon);
const iconSrc = isMediaIdEmptyResult
? null
: `${apiBaseUrl}/media/${station?.icon}/download`;
: buildMediaDownloadUrl(mediaBaseUrl, station!.icon!, mediaToken);
const iconSizePx = Math.round(primaryFontSize * 1.2);
return (

View File

@@ -4,9 +4,7 @@ import React, {
useContext,
useState,
useCallback,
useRef,
} from "react";
import { UP_SCALE } from "./Constants";
const TransformContext = createContext<{
position: { x: number; y: number };

View File

@@ -231,7 +231,6 @@ export const ThreeView: React.FC<ThreeViewProps> = ({
height = "100%",
onLoad,
onError,
onAspectRatioCalculated,
controlRef,
}) => {
return (
@@ -244,7 +243,7 @@ export const ThreeView: React.FC<ThreeViewProps> = ({
}}
camera={{ position: [0, 0, 5], fov: 40 }}
style={{ width: "100%", height: "100%" }}
onError={(e) => onError?.(e.message)}
onError={(e: any) => onError?.(e.message)}
>
<AutoResize />
<TouchController />
@@ -269,7 +268,7 @@ export const ThreeView: React.FC<ThreeViewProps> = ({
<Stage
environment={null}
intensity={1}
contactShadow={false}
castShadow={false}
shadows={false}
adjustCamera={true}
center={{ precise: true }}

View File

@@ -52,33 +52,6 @@ class CameraAnimationStore {
return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
}
private calculateDistance(p1: CameraPosition, p2: CameraPosition): number {
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
}
private isNearStation(
tramPos: CameraPosition,
stations: Station[]
): { isNear: boolean; distance: number } {
if (!stations || stations.length === 0)
return { isNear: false, distance: Infinity };
const threshold = 300; // Порог в координатах карты
let minDistance = Infinity;
for (const station of stations) {
const distance = this.calculateDistance(tramPos, {
x: station.longitude,
y: station.latitude,
});
minDistance = Math.min(minDistance, distance);
}
return {
isNear: minDistance < threshold,
distance: minDistance,
};
}
public setUpdateCallback(
callback: ((pos: CameraPosition, zoom: number) => void) | null
) {
@@ -140,7 +113,7 @@ class CameraAnimationStore {
public followTram(
tramMapPos: CameraPosition,
screenCenter: CameraPosition,
stations: Station[] = []
_stations: Station[] = []
) {
// Анимация начинается с текущего зума и плавно переходит к максимальному зуму
// для плавного приближения к желтой точке при слежении

View File

@@ -215,6 +215,8 @@
transition:
transform 0.3s ease-out,
opacity 0.3s ease-out;
display: flex;
flex-direction: column;
}
.side-menu-sights.slide-in {
@@ -227,7 +229,8 @@
}
.side-menu-sights-block {
height: calc(100% - 20px);
flex: 1;
min-height: 0;
margin-left: 20px;
margin-top: 8px;
touch-action: none;
@@ -236,6 +239,7 @@
max-width: calc(100% - 20px);
box-sizing: border-box;
overflow-x: hidden;
overflow-y: auto;
}
.side-menu-sight {

View File

@@ -1,4 +1,4 @@
import React, { useEffect } from "react";
import { useEffect } from "react";
import { useParams } from "react-router-dom";
import { apiStore } from "../../../client/src/api/ApiStore/store";
import App from "../../../client/src/App";

View File

@@ -9,7 +9,6 @@ import {
SelectArticleModal,
UploadMediaDialog,
Language,
articlesStore,
} from "@shared";
import {
LanguageSwitcher,
@@ -85,31 +84,7 @@ export const LeftWidgetTab = observer(
) => {
setIsSelectArticleDialogOpen(false);
const ruArticle = await articlesStore.getArticle(articleId, "ru");
const enArticle = await articlesStore.getArticle(articleId, "en");
const zhArticle = await articlesStore.getArticle(articleId, "zh");
updateSightInfo("ru", {
left: {
heading: ruArticle.data.heading,
body: ruArticle.data.body,
media: ruArticle.data.media || [],
},
});
updateSightInfo("en", {
left: {
heading: enArticle.data.heading,
body: enArticle.data.body,
media: enArticle.data.media || [],
},
});
updateSightInfo("zh", {
left: {
heading: zhArticle.data.heading,
body: zhArticle.data.body,
media: zhArticle.data.media || [],
},
});
await editSightStore.getLeftArticle(articleId);
updateSightInfo(
languageStore.language,

File diff suppressed because one or more lines are too long