fix for broken backend
All checks were successful
release-tag / release-image (push) Successful in 45s
All checks were successful
release-tag / release-image (push) Successful in 45s
This commit is contained in:
parent
b9ab746cfd
commit
f9ca0bdbd9
@ -122,6 +122,7 @@ body {
|
||||
background: #806c58;
|
||||
border-radius: 10px 10px 0 0;
|
||||
height: 50px;
|
||||
width: 450px;
|
||||
margin: 0 25px;
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
|
File diff suppressed because one or more lines are too long
@ -36,19 +36,25 @@ export default {
|
||||
fullPolyline: null,
|
||||
passedPolyline: null,
|
||||
tramDirection: "right",
|
||||
routeId: null,
|
||||
segmentLengths: [],
|
||||
cumulativeDistances: [],
|
||||
totalLength: 0,
|
||||
sightPollTimer: null,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
async mounted() {
|
||||
this.initializeMap();
|
||||
this.fetchRoute();
|
||||
this.fetchSights();
|
||||
await this.fetchContext(); // obtain current routeId
|
||||
this.startTracking();
|
||||
},
|
||||
methods: {
|
||||
async fetchSights() {
|
||||
if (!this.routeId) return;
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/route/1/sight`);
|
||||
const response = await fetch(`${API_URL}/route/${this.routeId}/sight`);
|
||||
const data = await response.json();
|
||||
console.log("Данные достопримечательностей:", data);
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
const grouped = {};
|
||||
@ -99,6 +105,30 @@ export default {
|
||||
} catch (error) {
|
||||
console.error("Ошибка при получении достопримечательностей:", error);
|
||||
}
|
||||
// Stop polling once at least one sight marker exists
|
||||
if (this.sightMarkers.length > 0 && this.sightPollTimer) {
|
||||
clearInterval(this.sightPollTimer);
|
||||
this.sightPollTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
// ── Poll fetchSights every 3 s until at least one sight is on the map ──
|
||||
startSightPolling() {
|
||||
if (this.sightPollTimer) return; // already polling
|
||||
this.sightPollTimer = setInterval(() => {
|
||||
this.fetchSights();
|
||||
if (this.sightMarkers.length > 0) {
|
||||
clearInterval(this.sightPollTimer);
|
||||
this.sightPollTimer = null;
|
||||
}
|
||||
}, 3000);
|
||||
},
|
||||
|
||||
stopSightPolling() {
|
||||
if (this.sightPollTimer) {
|
||||
clearInterval(this.sightPollTimer);
|
||||
this.sightPollTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
initializeMap() {
|
||||
@ -120,11 +150,27 @@ export default {
|
||||
},
|
||||
|
||||
async fetchRoute() {
|
||||
fetch(`${API_URL}/route/1`)
|
||||
if (!this.routeId) return;
|
||||
fetch(`${API_URL}/route/${this.routeId}`)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if (data.path && Array.isArray(data.path)) {
|
||||
this.routeLatlngs = data.path.map((coord) => [coord[0], coord[1]]);
|
||||
// ── Pre-compute cumulative distances for distance-based progress ──
|
||||
this.segmentLengths = [];
|
||||
this.cumulativeDistances = [0];
|
||||
for (let i = 1; i < this.routeLatlngs.length; i++) {
|
||||
const prev = L.latLng(this.routeLatlngs[i - 1]);
|
||||
const curr = L.latLng(this.routeLatlngs[i]);
|
||||
const segLen = prev.distanceTo(curr); // metres
|
||||
this.segmentLengths.push(segLen);
|
||||
this.cumulativeDistances.push(
|
||||
this.cumulativeDistances[i - 1] + segLen
|
||||
);
|
||||
}
|
||||
this.totalLength =
|
||||
this.cumulativeDistances[this.cumulativeDistances.length - 1] ||
|
||||
0;
|
||||
if (
|
||||
typeof data.scale_min === "number" &&
|
||||
typeof data.scale_max === "number"
|
||||
@ -190,6 +236,7 @@ export default {
|
||||
},
|
||||
|
||||
async fetchStations() {
|
||||
if (!this.routeId) return;
|
||||
this.stationMarkers.forEach(({ marker }) => {
|
||||
if (this.map.hasLayer(marker)) {
|
||||
this.map.removeLayer(marker);
|
||||
@ -199,8 +246,12 @@ export default {
|
||||
|
||||
try {
|
||||
const [ruStations, enStations] = await Promise.all([
|
||||
fetch(`${API_URL}/route/1/station`).then((r) => r.json()),
|
||||
fetch(`${API_URL}/route/1/station?lang=en`).then((r) => r.json()),
|
||||
fetch(`${API_URL}/route/${this.routeId}/station`).then((r) =>
|
||||
r.json()
|
||||
),
|
||||
fetch(`${API_URL}/route/${this.routeId}/station?lang=en`).then((r) =>
|
||||
r.json()
|
||||
),
|
||||
]);
|
||||
|
||||
const enById = {};
|
||||
@ -368,39 +419,83 @@ export default {
|
||||
setInterval(this.updateTramPosition, 500);
|
||||
},
|
||||
|
||||
async fetchContext() {
|
||||
try {
|
||||
const response = await fetch(`${GEO_URL}/v1/geolocation/context`);
|
||||
const data = await response.json();
|
||||
const ctxRouteId =
|
||||
(data && data.routeId) ||
|
||||
(data.routeProgress && data.routeProgress.routeId) ||
|
||||
null;
|
||||
|
||||
if (ctxRouteId && ctxRouteId !== this.routeId) {
|
||||
this.routeId = ctxRouteId;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Ошибка при получении контекста геолокации:", error);
|
||||
}
|
||||
},
|
||||
|
||||
async updateTramPosition() {
|
||||
try {
|
||||
const response = await fetch(`${GEO_URL}/v1/geolocation/context`);
|
||||
const data = await response.json();
|
||||
if (
|
||||
data.routeId &&
|
||||
data.routeId !== 0 &&
|
||||
data.routeId !== this.routeId
|
||||
) {
|
||||
this.routeId = data.routeId;
|
||||
}
|
||||
// console.log("Текущие координаты трамвая:", data.currentCoordinates);
|
||||
|
||||
const { percentageCompleted } = data.routeProgress;
|
||||
|
||||
if (this.routeLatlngs.length === 0) return;
|
||||
if (this.totalLength === 0) return;
|
||||
|
||||
const progressIndex = Math.min(
|
||||
Math.floor(percentageCompleted * this.routeLatlngs.length),
|
||||
this.routeLatlngs.length - 1
|
||||
);
|
||||
// Дистанция, пройденная трамваем (м)
|
||||
const targetDistance = percentageCompleted * this.totalLength;
|
||||
|
||||
const tramLatLng = this.routeLatlngs[progressIndex];
|
||||
// Определяем сегмент маршрута, в котором находится трамвай
|
||||
let segIdx = 0;
|
||||
while (
|
||||
segIdx < this.cumulativeDistances.length - 1 &&
|
||||
this.cumulativeDistances[segIdx + 1] < targetDistance
|
||||
) {
|
||||
segIdx++;
|
||||
}
|
||||
|
||||
const segStart = this.routeLatlngs[segIdx];
|
||||
const segEnd =
|
||||
this.routeLatlngs[Math.min(segIdx + 1, this.routeLatlngs.length - 1)];
|
||||
const segStartDist = this.cumulativeDistances[segIdx];
|
||||
const segLen = this.cumulativeDistances[segIdx + 1] - segStartDist || 1;
|
||||
|
||||
// Интерполируем точку на сегменте
|
||||
const t = segLen === 0 ? 0 : (targetDistance - segStartDist) / segLen;
|
||||
const tramLatLng = [
|
||||
segStart[0] + (segEnd[0] - segStart[0]) * t,
|
||||
segStart[1] + (segEnd[1] - segStart[1]) * t,
|
||||
];
|
||||
|
||||
// Формируем координаты пройденной и оставшейся части
|
||||
const passedCoords = this.routeLatlngs.slice(0, segIdx + 1);
|
||||
passedCoords.push(tramLatLng);
|
||||
const fullCoords = [tramLatLng, ...this.routeLatlngs.slice(segIdx + 1)];
|
||||
|
||||
// Удаляем старые линии, если есть
|
||||
if (this.passedPolyline) this.map.removeLayer(this.passedPolyline);
|
||||
if (this.fullPolyline) this.map.removeLayer(this.fullPolyline);
|
||||
|
||||
// Красная линия — уже проехали
|
||||
this.passedPolyline = L.polyline(
|
||||
this.routeLatlngs.slice(0, progressIndex + 1),
|
||||
{
|
||||
color: "red",
|
||||
weight: 7,
|
||||
pane: "routePane",
|
||||
}
|
||||
).addTo(this.map);
|
||||
// Пройденная часть (красная)
|
||||
this.passedPolyline = L.polyline(passedCoords, {
|
||||
color: "red",
|
||||
weight: 7,
|
||||
pane: "routePane",
|
||||
}).addTo(this.map);
|
||||
|
||||
// Белая линия — ещё впереди
|
||||
this.fullPolyline = L.polyline(this.routeLatlngs.slice(progressIndex), {
|
||||
// Оставшаяся часть (белая)
|
||||
this.fullPolyline = L.polyline(fullCoords, {
|
||||
color: "white",
|
||||
weight: 7,
|
||||
pane: "routePane",
|
||||
@ -425,8 +520,9 @@ export default {
|
||||
0
|
||||
);
|
||||
|
||||
const isPassed = stationIndex < progressIndex;
|
||||
const color = isPassed ? "red" : "white";
|
||||
const stationPassed =
|
||||
this.cumulativeDistances[stationIndex] <= targetDistance;
|
||||
const color = stationPassed ? "red" : "white";
|
||||
|
||||
marker.setStyle({
|
||||
color: "black",
|
||||
@ -461,5 +557,22 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// When routeId becomes available or changes, (re)load dependent data
|
||||
routeId(newVal, oldVal) {
|
||||
if (newVal && newVal !== oldVal) {
|
||||
this.fetchRoute();
|
||||
this.fetchSights();
|
||||
this.fetchStations();
|
||||
this.startSightPolling();
|
||||
}
|
||||
},
|
||||
},
|
||||
beforeUnmount() {
|
||||
if (this.sightPollTimer) {
|
||||
clearInterval(this.sightPollTimer);
|
||||
this.sightPollTimer = null;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -85,6 +85,8 @@ export default {
|
||||
isStartScrolling: false,
|
||||
isEndScrolling: false,
|
||||
isEnScrolling: false,
|
||||
pollId: null,
|
||||
hasLoadedStops: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
@ -101,41 +103,69 @@ export default {
|
||||
this.$nextTick(this.checkScroll);
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
const contextRes = await fetch(`${GEO_URL}/v1/geolocation/context`);
|
||||
const data = await contextRes.json();
|
||||
this.routeNumber = data.routeNumber;
|
||||
|
||||
const startStopId = data.startStopId;
|
||||
const endStopId = data.endStopId;
|
||||
|
||||
const [startStopRes, endStopRes] = await Promise.all([
|
||||
fetch(`${API_URL}/station/${startStopId}`),
|
||||
fetch(`${API_URL}/station/${endStopId}`),
|
||||
]);
|
||||
|
||||
const startStopData = await startStopRes.json();
|
||||
const endStopData = await endStopRes.json();
|
||||
|
||||
this.startStopName = startStopData.name;
|
||||
this.endStopName = endStopData.name;
|
||||
|
||||
const [startStopEnRes, endStopEnRes] = await Promise.all([
|
||||
fetch(`${API_URL}/station/${startStopId}?lang=en`),
|
||||
fetch(`${API_URL}/station/${endStopId}?lang=en`),
|
||||
]);
|
||||
|
||||
const startStopEnData = await startStopEnRes.json();
|
||||
const endStopEnData = await endStopEnRes.json();
|
||||
|
||||
this.startStopNameEn = startStopEnData.name;
|
||||
this.endStopNameEn = endStopEnData.name;
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.checkScroll();
|
||||
});
|
||||
mounted() {
|
||||
this.startPollingContext();
|
||||
},
|
||||
methods: {
|
||||
startPollingContext() {
|
||||
// first request immediately, then every 3 s
|
||||
this.fetchContext();
|
||||
this.pollId = setInterval(this.fetchContext, 3000);
|
||||
},
|
||||
|
||||
async fetchContext() {
|
||||
try {
|
||||
const res = await fetch(`${GEO_URL}/v1/geolocation/context`);
|
||||
const data = await res.json();
|
||||
|
||||
if (data.routeNumber) {
|
||||
this.routeNumber = data.routeNumber;
|
||||
}
|
||||
|
||||
const { startStopId, endStopId } = data;
|
||||
|
||||
// when both IDs are available for the first time, load names and stop polling
|
||||
if (startStopId && endStopId && !this.hasLoadedStops) {
|
||||
await this.loadStopNames(startStopId, endStopId);
|
||||
this.hasLoadedStops = true;
|
||||
if (this.pollId) {
|
||||
clearInterval(this.pollId);
|
||||
this.pollId = null;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to fetch geolocation context", err);
|
||||
}
|
||||
},
|
||||
|
||||
async loadStopNames(startStopId, endStopId) {
|
||||
try {
|
||||
const [startRuRes, endRuRes] = await Promise.all([
|
||||
fetch(`${API_URL}/station/${startStopId}`),
|
||||
fetch(`${API_URL}/station/${endStopId}`),
|
||||
]);
|
||||
const startRu = await startRuRes.json();
|
||||
const endRu = await endRuRes.json();
|
||||
|
||||
this.startStopName = startRu.name;
|
||||
this.endStopName = endRu.name;
|
||||
|
||||
const [startEnRes, endEnRes] = await Promise.all([
|
||||
fetch(`${API_URL}/station/${startStopId}?lang=en`),
|
||||
fetch(`${API_URL}/station/${endStopId}?lang=en`),
|
||||
]);
|
||||
const startEn = await startEnRes.json();
|
||||
const endEn = await endEnRes.json();
|
||||
|
||||
this.startStopNameEn = startEn.name;
|
||||
this.endStopNameEn = endEn.name;
|
||||
|
||||
this.$nextTick(this.checkScroll);
|
||||
} catch (err) {
|
||||
console.error("Failed to load station names", err);
|
||||
}
|
||||
},
|
||||
|
||||
checkScroll() {
|
||||
const threshold = 280;
|
||||
if (this.$refs.startStopRuText) {
|
||||
|
@ -204,6 +204,7 @@ export default {
|
||||
nextStopTransfers: null,
|
||||
stops: [],
|
||||
routeProgress: null,
|
||||
routeId: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -301,28 +302,44 @@ export default {
|
||||
}
|
||||
},
|
||||
async fetchSights() {
|
||||
const geoRes = await axios.get(`${GEO_URL}/v1/geolocation/context`);
|
||||
const routeId = geoRes.data.routeId;
|
||||
// Prefer cached routeId, otherwise fetch context once
|
||||
let routeId = this.routeId;
|
||||
if (!routeId) {
|
||||
console.warn("Missing routeId in geo context:", geoRes.data);
|
||||
try {
|
||||
const geoRes = await axios.get(`${GEO_URL}/v1/geolocation/context`);
|
||||
routeId = geoRes.data.routeId;
|
||||
if (routeId && routeId !== this.routeId) {
|
||||
this.routeId = routeId;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to get routeId from context:", error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!routeId) {
|
||||
console.warn("Missing routeId — skipping fetchSights");
|
||||
return;
|
||||
}
|
||||
const sightsRes = await axios.get(`${API_URL}/route/${routeId}/sight`);
|
||||
const rawSights = sightsRes.data;
|
||||
const detailedSights = await Promise.all(
|
||||
rawSights.map(async (sight) => {
|
||||
const detailRes = await axios.get(`${API_URL}/sight/${sight.id}`);
|
||||
const thumbnailUrl = detailRes.data.thumbnail
|
||||
? await this.getMediaBlobUrl(detailRes.data.thumbnail)
|
||||
: "";
|
||||
return {
|
||||
id: sight.id,
|
||||
name: detailRes.data.name,
|
||||
thumbnailUrl,
|
||||
};
|
||||
})
|
||||
);
|
||||
this.sights = detailedSights;
|
||||
try {
|
||||
const sightsRes = await axios.get(`${API_URL}/route/${routeId}/sight`);
|
||||
const rawSights = sightsRes.data;
|
||||
const detailedSights = await Promise.all(
|
||||
rawSights.map(async (sight) => {
|
||||
const detailRes = await axios.get(`${API_URL}/sight/${sight.id}`);
|
||||
const thumbnailUrl = detailRes.data.thumbnail
|
||||
? await this.getMediaBlobUrl(detailRes.data.thumbnail)
|
||||
: "";
|
||||
return {
|
||||
id: sight.id,
|
||||
name: detailRes.data.name,
|
||||
thumbnailUrl,
|
||||
};
|
||||
})
|
||||
);
|
||||
this.sights = detailedSights;
|
||||
} catch (error) {
|
||||
console.error("Error fetching sights:", error);
|
||||
}
|
||||
},
|
||||
toggleSightsList() {
|
||||
this.showSightsList = !this.showSightsList;
|
||||
@ -414,10 +431,18 @@ export default {
|
||||
try {
|
||||
const response = await axios.get(`${GEO_URL}/v1/geolocation/context`);
|
||||
this.routeProgress = response.data.routeProgress;
|
||||
const stopsResponse = await axios.get(
|
||||
`${API_URL}/route/${response.data.routeId}/station`
|
||||
);
|
||||
this.stops = stopsResponse.data;
|
||||
const newRouteId = response.data.routeId;
|
||||
if (newRouteId && newRouteId !== this.routeId) {
|
||||
this.routeId = newRouteId;
|
||||
}
|
||||
if (this.routeId) {
|
||||
const stopsResponse = await axios.get(
|
||||
`${API_URL}/route/${this.routeId}/station`
|
||||
);
|
||||
this.stops = stopsResponse.data;
|
||||
} else {
|
||||
this.stops = [];
|
||||
}
|
||||
let newSightId = response.data.nearestSightId;
|
||||
|
||||
if (!newSightId) {
|
||||
@ -490,6 +515,14 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
routeId(newVal, oldVal) {
|
||||
if (newVal && newVal !== oldVal) {
|
||||
// Refresh sights because routeId changed
|
||||
this.fetchSights();
|
||||
}
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
await this.fetchSights();
|
||||
await this.fetchGeolocationContext();
|
||||
|
@ -96,7 +96,7 @@ export default {
|
||||
isModalOpen: false,
|
||||
autoCloseTimer: null,
|
||||
imageUrl: "",
|
||||
sightId: 14,
|
||||
sightId: null,
|
||||
stopName: "",
|
||||
articles: [],
|
||||
selectedArticleId: null,
|
||||
@ -120,10 +120,16 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async fetchSightInfo() {
|
||||
// Do nothing if sightId is null, zero, or empty
|
||||
if (!this.sightId) return;
|
||||
|
||||
const response = await axios.get(`${API_URL}/sight/${this.sightId}`);
|
||||
this.stopName = response.data.name;
|
||||
},
|
||||
async fetchArticles() {
|
||||
// Do nothing if sightId is null, zero, or empty
|
||||
if (!this.sightId) return;
|
||||
|
||||
const response = await axios.get(
|
||||
`${API_URL}/sight/${this.sightId}/article`
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user