diff --git a/src/App.vue b/src/App.vue
index 3ce9988..0cb897a 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -37,7 +37,7 @@
{{ onlineDispatchers.length }}
/
- {{ store.trainList.length }}
+ {{ trainList.length }}
@@ -125,6 +125,12 @@ export default defineComponent({
};
},
+ computed: {
+ trainList() {
+ return this.store.trainList.filter(train => train.online);
+ }
+ },
+
data: () => ({
VERSION: packageInfo.version,
updateModalVisible: false,
diff --git a/src/components/SceneryView/SceneryInfo/SceneryInfoUserList.vue b/src/components/SceneryView/SceneryInfo/SceneryInfoUserList.vue
index 949d4fa..49725d9 100644
--- a/src/components/SceneryView/SceneryInfo/SceneryInfoUserList.vue
+++ b/src/components/SceneryView/SceneryInfo/SceneryInfoUserList.vue
@@ -3,7 +3,8 @@
{
+ const computedStationTrains = computed(() => {
if (!props.station) return [];
const station = props.station as Station;
@@ -49,9 +50,7 @@ export default defineComponent({
if (!station.onlineInfo.stationTrains) return [];
return station.onlineInfo.stationTrains.map((train) => {
- const scheduledTrainStatus = station.onlineInfo?.scheduledTrains?.find(
- (st) => st.trainNo === train.trainNo
- );
+ const scheduledTrainStatus = station.onlineInfo?.scheduledTrains?.find((st) => st.trainNo === train.trainNo);
return {
...train,
diff --git a/src/components/TrainsView/TrainInfo.vue b/src/components/TrainsView/TrainInfo.vue
index ecf2e2f..7903e2a 100644
--- a/src/components/TrainsView/TrainInfo.vue
+++ b/src/components/TrainsView/TrainInfo.vue
@@ -61,6 +61,10 @@
+
+ Offline - {{ lastSeenMessage(train.lastSeen) }}
+
+
{{ $t('trains.current-scenery') }} {{ train['currentStationName'] }}
diff --git a/src/locales/en.json b/src/locales/en.json
index 3e66830..e86bf69 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -181,7 +181,11 @@
"loco-diesel": "Diesel locomotive",
"timetable-comments": "Exploitation comments available for this train",
"comment": "Exploitation comments for: ",
- "table-limit": "For performance reasons there's a limit of 10 trains shown at the same time."
+ "table-limit": "For performance reasons there's a limit of 10 trains shown at the same time.",
+
+ "last-seen-now": "last seen: just now",
+ "last-seen-min": "last seen: one minute ago",
+ "last-seen-ago": "last seen: {minutes} mins ago"
},
"journal": {
"title": "DISPATCHER HISTORY",
diff --git a/src/locales/pl.json b/src/locales/pl.json
index 503b2b6..2b76150 100644
--- a/src/locales/pl.json
+++ b/src/locales/pl.json
@@ -161,7 +161,6 @@
"filter-noTimetable": "bez RJ",
"filter-reset": "X RESETUJ",
-
"sorter-prefix": "Sortuj: ",
"search-train": "Numer pociągu",
"search-driver": "Nick maszynisty",
@@ -183,7 +182,11 @@
"loco-diesel": "Spalinowóz",
"timetable-comments": "Pociąg z uwagami eksploatacyjnymi",
"comment": "Uwagi eksploatacyjne dla: ",
- "table-limit": "Dla płynności działania strony pokazanych jest tylko 10 pociągów zgodnie z wybranymi filtrami."
+ "table-limit": "Dla płynności działania strony pokazanych jest tylko 10 pociągów zgodnie z wybranymi filtrami.",
+
+ "last-seen-now": "ostatnio widziany: przed chwilą",
+ "last-seen-min": "ostatnio widziany: minutę temu",
+ "last-seen-ago": "ostatnio widziany: {minutes} min. temu"
},
"journal": {
"title": "HISTORIA DYŻURÓW",
diff --git a/src/mixins/trainInfoMixin.ts b/src/mixins/trainInfoMixin.ts
index cd1f452..3ae046e 100644
--- a/src/mixins/trainInfoMixin.ts
+++ b/src/mixins/trainInfoMixin.ts
@@ -1,117 +1,128 @@
-import Train from "@/scripts/interfaces/Train";
-import TrainStop from "@/scripts/interfaces/TrainStop";
-import { defineComponent } from "vue";
+import Train from '@/scripts/interfaces/Train';
+import TrainStop from '@/scripts/interfaces/TrainStop';
+import { defineComponent } from 'vue';
export default defineComponent({
- data: () => ({
- STATS: {
- main: [
- {
- name: 'speed',
- unit: 'km/h',
- },
- {
- name: 'length',
- unit: 'm',
- },
- {
- name: 'mass',
- unit: 't',
- multiplier: 0.001,
- },
- ],
-
- position: [
- {
- name: 'scenery',
- prop: 'currentStationName',
- },
- {
- name: 'route',
- prop: 'connectedTrack',
- },
- {
- name: 'signal',
- prop: 'signal',
- },
- {
- name: 'distance',
- prop: 'distance',
- unit: 'm',
- },
- ],
+ data: () => ({
+ STATS: {
+ main: [
+ {
+ name: 'speed',
+ unit: 'km/h',
},
- }),
-
- methods: {
- displayStopList(stops: TrainStop[]): string | undefined {
- if (!stops) return '';
-
- return stops
- .reduce((acc: string[], stop: TrainStop, i: number) => {
- if (stop.stopType.includes('ph') && !stop.stopNameRAW.includes('po.'))
- acc.push(`${stop.stopName}`);
- else if (
- i > 0 &&
- i < stops.length - 1 &&
- !stop.stopNameRAW.includes('po.') &&
- !stop.stopNameRAW.includes('SBL')
- )
- acc.push(`${stop.stopName}`);
- return acc;
- }, [])
- .join(' > ');
+ {
+ name: 'length',
+ unit: 'm',
},
-
- currentDistance(stops: TrainStop[]) {
- return stops.filter(stop => stop.confirmed).slice(-1)[0]?.stopDistance || 0;
+ {
+ name: 'mass',
+ unit: 't',
+ multiplier: 0.001,
},
+ ],
- confirmedPercentage(stops: TrainStop[]) {
- return Number(((stops.filter((stop) => stop.confirmed).length / stops.length) * 100).toFixed(0));
+ position: [
+ {
+ name: 'scenery',
+ prop: 'currentStationName',
},
-
- currentDelay(stops: TrainStop[]) {
- const delay =
- stops.find((stop, i) => (i == 0 && !stop.confirmed) || (i > 0 && stops[i - 1].confirmed && !stop.confirmed))
- ?.departureDelay || 0;
-
- if (delay > 0) return `${this.$t('trains.delayed')} ${delay} min`;
- else if (delay < 0) return `${this.$t('trains.preponed')} ${delay} min`;
- else return this.$t('trains.on-time');
+ {
+ name: 'route',
+ prop: 'connectedTrack',
},
-
- displayLocoInfo(locoType: string) {
- if (locoType.includes('EN')) return `${this.$t('trains.EZT')}`;
- if (locoType.includes('SN')) return `${this.$t('trains.SZT')}`;
- if (locoType.startsWith('E')) return `${this.$t('trains.loco-electric')}`;
- if (locoType.startsWith('S')) return `${this.$t('trains.loco-diesel')}`;
-
- return '';
+ {
+ name: 'signal',
+ prop: 'signal',
},
-
- getSceneriesWithComments(timetableData: Train['timetableData']) {
- const commentList = timetableData?.followingStops.reduce((acc, stop, i) => {
- if (stop.comments) acc.push(stop.stopNameRAW);
-
- return acc;
- }, [] as string[]) || []
-
- const moreCount = commentList.length - 10;
-
-
- return commentList.slice(0, 10).join(", ") + (moreCount > 0 ? `... (+${moreCount})` : '');
+ {
+ name: 'distance',
+ prop: 'distance',
+ unit: 'm',
},
+ ],
+ },
+ }),
- displayDistance(distance: number) {
- if (distance < 1000) return `${distance}m`;
+ methods: {
+ lastSeenMessage(timestamp: number) {
+ const diff = Date.now() - timestamp;
+ const diffMins = Math.floor(diff / 60000);
- return `${(distance / 1000).toPrecision(2)}km`;
- },
+ return diffMins < 1
+ ? this.$t('trains.last-seen-now')
+ : diffMins < 2
+ ? this.$t('trains.last-seen-min')
+ : this.$t('trains.last-seen-ago', { minutes: diffMins });
+ },
- onImageError(e: Event) {
- const imageEl = e.target as HTMLImageElement;
- imageEl.src = require('@/assets/unknown.png');
- }
- }
-})
\ No newline at end of file
+ displayStopList(stops: TrainStop[]): string | undefined {
+ if (!stops) return '';
+
+ return stops
+ .reduce((acc: string[], stop: TrainStop, i: number) => {
+ if (stop.stopType.includes('ph') && !stop.stopNameRAW.includes('po.'))
+ acc.push(`${stop.stopName}`);
+ else if (
+ i > 0 &&
+ i < stops.length - 1 &&
+ !stop.stopNameRAW.includes('po.') &&
+ !stop.stopNameRAW.includes('SBL')
+ )
+ acc.push(`${stop.stopName}`);
+ return acc;
+ }, [])
+ .join(' > ');
+ },
+
+ currentDistance(stops: TrainStop[]) {
+ return stops.filter((stop) => stop.confirmed).slice(-1)[0]?.stopDistance || 0;
+ },
+
+ confirmedPercentage(stops: TrainStop[]) {
+ return Number(((stops.filter((stop) => stop.confirmed).length / stops.length) * 100).toFixed(0));
+ },
+
+ currentDelay(stops: TrainStop[]) {
+ const delay =
+ stops.find((stop, i) => (i == 0 && !stop.confirmed) || (i > 0 && stops[i - 1].confirmed && !stop.confirmed))
+ ?.departureDelay || 0;
+
+ if (delay > 0) return `${this.$t('trains.delayed')} ${delay} min`;
+ else if (delay < 0) return `${this.$t('trains.preponed')} ${delay} min`;
+ else return this.$t('trains.on-time');
+ },
+
+ displayLocoInfo(locoType: string) {
+ if (locoType.includes('EN')) return `${this.$t('trains.EZT')}`;
+ if (locoType.includes('SN')) return `${this.$t('trains.SZT')}`;
+ if (locoType.startsWith('E')) return `${this.$t('trains.loco-electric')}`;
+ if (locoType.startsWith('S')) return `${this.$t('trains.loco-diesel')}`;
+
+ return '';
+ },
+
+ getSceneriesWithComments(timetableData: Train['timetableData']) {
+ const commentList =
+ timetableData?.followingStops.reduce((acc, stop, i) => {
+ if (stop.comments) acc.push(stop.stopNameRAW);
+
+ return acc;
+ }, [] as string[]) || [];
+
+ const moreCount = commentList.length - 10;
+
+ return commentList.slice(0, 10).join(', ') + (moreCount > 0 ? `... (+${moreCount})` : '');
+ },
+
+ displayDistance(distance: number) {
+ if (distance < 1000) return `${distance}m`;
+
+ return `${(distance / 1000).toPrecision(2)}km`;
+ },
+
+ onImageError(e: Event) {
+ const imageEl = e.target as HTMLImageElement;
+ imageEl.src = require('@/assets/unknown.png');
+ },
+ },
+});
diff --git a/src/scripts/interfaces/Train.ts b/src/scripts/interfaces/Train.ts
index 4d11c31..a01b470 100644
--- a/src/scripts/interfaces/Train.ts
+++ b/src/scripts/interfaces/Train.ts
@@ -15,6 +15,7 @@ export default interface Train {
locoURL: string;
locoType: string;
online: boolean;
+ lastSeen: number;
region: string;
cars: string[];
diff --git a/src/store/store.ts b/src/store/store.ts
index 80bb84a..9e33a5c 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -1,11 +1,8 @@
import { DataStatus } from '@/scripts/enums/DataStatus';
-import { DispatcherStatsAPIData } from '@/scripts/interfaces/api/DispatcherStatsAPIData';
import StationAPIData from '@/scripts/interfaces/api/StationAPIData';
-import TrainAPIData from '@/scripts/interfaces/api/TrainAPIData';
import ScheduledTrain from '@/scripts/interfaces/ScheduledTrain';
import Station from '@/scripts/interfaces/Station';
import StationRoutes from '@/scripts/interfaces/StationRoutes';
-import { StoreData } from '@/scripts/interfaces/StoreData';
import Train from '@/scripts/interfaces/Train';
import { URLs } from '@/scripts/utils/apiURLs';
import {
@@ -62,7 +59,10 @@ export const useStore = defineStore('store', {
if (!trains) return [];
this.trainList = trains
- .filter((train) => train.region === this.region.id)
+ .filter(
+ (train) =>
+ train.region === this.region.id && (train.online || train.timetable || train.lastSeen > Date.now() - 180000)
+ )
.map((train) => {
const stock = train.stockString.split(';');
const locoType = stock ? stock[0] : train.stockString;
@@ -88,6 +88,8 @@ export const useStore = defineStore('store', {
locoURL: getLocoURL(locoType),
cars: stock.slice(1),
+ lastSeen: train.lastSeen,
+
timetableData: timetable
? {
timetableId: timetable.timetableId,
@@ -206,7 +208,6 @@ export const useStore = defineStore('store', {
const prevDispatcherStatuses: StoreState['lastDispatcherStatuses'] = [];
this.apiData.stations?.forEach((stationAPIData) => {
-
if (stationAPIData.region !== this.region.id || !stationAPIData.isOnline) return;
const station = this.stationList.find((s) => s.name === stationAPIData.stationName);
@@ -345,7 +346,6 @@ export const useStore = defineStore('store', {
this.setOnlineData();
console.log(data);
-
});
socket.emit('FETCH_DATA', {}, (data: APIData) => {