rework reaktywności danych z API i WS

This commit is contained in:
2023-10-30 23:19:17 +01:00
parent 12ece46089
commit 8de03b9210
24 changed files with 501 additions and 446 deletions
+6 -32
View File
@@ -32,20 +32,19 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, provide, ref, watch } from 'vue'; import { defineComponent, watch } from 'vue';
import Clock from './components/App/Clock.vue'; import Clock from './components/App/Clock.vue';
import packageInfo from '.././package.json'; import packageInfo from '.././package.json';
import { useStore } from './store/store';
import StatusIndicator from './components/App/StatusIndicator.vue'; import StatusIndicator from './components/App/StatusIndicator.vue';
import SelectBox from './components/Global/SelectBox.vue'; import SelectBox from './components/Global/SelectBox.vue';
import { useStore } from './store/store';
import TrainModal from './components/Global/TrainModal.vue'; import TrainModal from './components/Global/TrainModal.vue';
import StorageManager from './scripts/managers/storageManager'; import StorageManager from './scripts/managers/storageManager';
import AppHeader from './components/App/AppHeader.vue'; import AppHeader from './components/App/AppHeader.vue';
import axios from 'axios'; import axios from 'axios';
import useCustomSW from './mixins/useCustomSW';
export default defineComponent({ export default defineComponent({
components: { components: {
@@ -56,31 +55,9 @@ export default defineComponent({
AppHeader AppHeader
}, },
setup() {
const store = useStore();
store.connectToAPI();
useCustomSW();
const isFilterCardVisible = ref(false);
provide('isFilterCardVisible', isFilterCardVisible);
return {
store,
isFilterCardVisible,
onlineDispatchers: computed(() =>
store.stationList.filter(
(station) => station.onlineInfo && station.onlineInfo.region == store.region.id
)
),
dispatcherDataStatus: store.dataStatuses.dispatchers
};
},
data: () => ({ data: () => ({
VERSION: packageInfo.version, VERSION: packageInfo.version,
store: useStore(),
currentLang: 'pl', currentLang: 'pl',
releaseURL: '', releaseURL: '',
@@ -89,6 +66,7 @@ export default defineComponent({
created() { created() {
this.loadLang(); this.loadLang();
this.store.connectToAPI();
this.store.isOffline = !window.navigator.onLine; this.store.isOffline = !window.navigator.onLine;
@@ -116,12 +94,8 @@ export default defineComponent({
watch( watch(
() => this.store.blockScroll, () => this.store.blockScroll,
(value) => { (value) => {
if (value) { if (value) document.body.classList.add('no-scroll');
document.body.classList.add('no-scroll'); else document.body.classList.remove('no-scroll');
return;
}
document.body.classList.remove('no-scroll');
} }
); );
}, },
+5 -3
View File
@@ -82,28 +82,30 @@ export default defineComponent({
required: true required: true
} }
}, },
setup() { setup() {
return { return {
store: useStore() store: useStore()
}; };
}, },
methods: { methods: {
changeRegion(region: { id: string; value: string }) { changeRegion(region: { id: string; value: string }) {
this.store.changeRegion(region); this.store.changeRegion(region);
}, },
changeLang(lang: string) { changeLang(lang: string) {
this.$emit('changeLang', lang); this.$emit('changeLang', lang);
} }
}, },
computed: { computed: {
onlineTrainsCount() { onlineTrainsCount() {
return this.store.trainList.filter((train) => train.online).length; return this.store.trainList.filter((train) => train.online).length;
}, },
onlineDispatchersCount() { onlineDispatchersCount() {
return this.store.stationList.filter( return this.store.onlineSceneryList.length;
(station) => station.onlineInfo && station.onlineInfo.region == this.store.region.id
).length;
}, },
factorU() { factorU() {
+13
View File
@@ -87,6 +87,19 @@ export default defineComponent({
}; };
}, },
watch: {
'$route.query': {
immediate: true,
handler(newVal) {
if (newVal.region) {
const item = this.itemList.find((it) => it.id == newVal.region);
if (item) this.selectedItem = item;
}
}
}
},
methods: { methods: {
selectOption(item: Item) { selectOption(item: Item) {
this.selectedItem = item; this.selectedItem = item;
@@ -76,6 +76,7 @@ import { URLs } from '../../scripts/utils/apiURLs';
import Loading from '../Global/Loading.vue'; import Loading from '../Global/Loading.vue';
import styleMixin from '../../mixins/styleMixin'; import styleMixin from '../../mixins/styleMixin';
import listObserverMixin from '../../mixins/listObserverMixin'; import listObserverMixin from '../../mixins/listObserverMixin';
import { OnlineScenery } from '../../scripts/interfaces/store/storeTypes';
export default defineComponent({ export default defineComponent({
name: 'SceneryDispatchersHistory', name: 'SceneryDispatchersHistory',
@@ -85,6 +86,10 @@ export default defineComponent({
station: { station: {
type: Object as PropType<Station>, type: Object as PropType<Station>,
required: true required: true
},
onlineScenery: {
type: Object as PropType<OnlineScenery>,
required: false
} }
}, },
+7 -1
View File
@@ -8,19 +8,25 @@
{{ $t('scenery.abbrev') }} <b>{{ station.generalInfo?.abbr }}</b> {{ $t('scenery.abbrev') }} <b>{{ station.generalInfo?.abbr }}</b>
</div> </div>
<div class="scenery-hash" v-if="station.onlineInfo?.hash">#{{ station.onlineInfo.hash }}</div> <div class="scenery-hash" v-if="onlineScenery?.hash">#{{ onlineScenery.hash }}</div>
</section> </section>
</template> </template>
<script lang="ts"> <script lang="ts">
import { PropType, defineComponent } from 'vue'; import { PropType, defineComponent } from 'vue';
import Station from '../../scripts/interfaces/Station'; import Station from '../../scripts/interfaces/Station';
import { OnlineScenery } from '../../scripts/interfaces/store/storeTypes';
export default defineComponent({ export default defineComponent({
props: { props: {
station: { station: {
type: Object as PropType<Station>, type: Object as PropType<Station>,
required: true required: true
},
onlineScenery: {
type: Object as PropType<OnlineScenery>,
required: false
} }
} }
}); });
+9 -9
View File
@@ -1,6 +1,6 @@
<template> <template>
<div class="scenery-info"> <div class="scenery-info">
<section v-if="!timetableOnly"> <section>
<div class="scenery-info-general" v-if="station.generalInfo"> <div class="scenery-info-general" v-if="station.generalInfo">
<SceneryInfoIcons :station="station" /> <SceneryInfoIcons :station="station" />
@@ -68,14 +68,14 @@
<div style="margin: 2em 0; height: 2px; background-color: white"></div> <div style="margin: 2em 0; height: 2px; background-color: white"></div>
<!-- info dispatcher --> <!-- info dispatcher -->
<SceneryInfoDispatcher :station="station" :onlineFrom="onlineFrom" /> <SceneryInfoDispatcher :onlineScenery="onlineScenery" />
<div class="info-lists"> <div class="info-lists">
<!-- user list --> <!-- user list -->
<SceneryInfoUserList :station="station" /> <SceneryInfoUserList :onlineScenery="onlineScenery" />
<!-- spawn list --> <!-- spawn list -->
<SceneryInfoSpawnList :station="station" /> <SceneryInfoSpawnList :onlineScenery="onlineScenery" />
</div> </div>
</section> </section>
</div> </div>
@@ -90,6 +90,7 @@ import SceneryInfoUserList from './SceneryInfo/SceneryInfoUserList.vue';
import SceneryInfoSpawnList from './SceneryInfo/SceneryInfoSpawnList.vue'; import SceneryInfoSpawnList from './SceneryInfo/SceneryInfoSpawnList.vue';
import SceneryInfoRoutes from './SceneryInfo/SceneryInfoRoutes.vue'; import SceneryInfoRoutes from './SceneryInfo/SceneryInfoRoutes.vue';
import Station from '../../scripts/interfaces/Station'; import Station from '../../scripts/interfaces/Station';
import { OnlineScenery } from '../../scripts/interfaces/store/storeTypes';
export default defineComponent({ export default defineComponent({
components: { components: {
@@ -105,12 +106,11 @@ export default defineComponent({
required: true required: true
}, },
timetableOnly: Boolean onlineScenery: {
type: Object as PropType<OnlineScenery>,
required: false
}
}, },
data: () => ({
onlineFrom: -1
})
}); });
</script> </script>
@@ -1,35 +1,30 @@
<template> <template>
<section class="info-dispatcher"> <section class="info-dispatcher">
<div class="dispatcher" v-if="station.onlineInfo"> <div class="dispatcher" v-if="onlineScenery">
<span <span
class="dispatcher_level" class="dispatcher_level"
:style=" :style="calculateExpStyle(onlineScenery.dispatcherExp, onlineScenery.dispatcherIsSupporter)"
calculateExpStyle(
station.onlineInfo.dispatcherExp,
station.onlineInfo.dispatcherIsSupporter
)
"
> >
{{ station.onlineInfo.dispatcherExp > 1 ? station.onlineInfo.dispatcherExp : 'L' }} {{ onlineScenery.dispatcherExp > 1 ? onlineScenery.dispatcherExp : 'L' }}
</span> </span>
<router-link <router-link
class="dispatcher_name" class="dispatcher_name"
:to="`/journal/dispatchers?dispatcherName=${station.onlineInfo.dispatcherName}`" :to="`/journal/dispatchers?dispatcherName=${onlineScenery.dispatcherName}`"
> >
{{ station.onlineInfo.dispatcherName }} {{ onlineScenery.dispatcherName }}
</router-link> </router-link>
<span class="dispatcher_likes text--primary"> <span class="dispatcher_likes text--primary">
<img src="/images/icon-like.svg" alt="Likes count icon" /> <img src="/images/icon-like.svg" alt="Likes count icon" />
<span>{{ station.onlineInfo?.dispatcherRate || '0' }}</span> <span>{{ onlineScenery?.dispatcherRate || '0' }}</span>
</span> </span>
</div> </div>
<StationStatusBadge <StationStatusBadge
:statusID="station.onlineInfo?.statusID" :statusID="onlineScenery?.statusID"
:isOnline="station.onlineInfo ? true : false" :isOnline="onlineScenery ? true : false"
:statusTimestamp="station.onlineInfo?.statusTimestamp" :statusTimestamp="onlineScenery?.statusTimestamp"
/> />
</section> </section>
</template> </template>
@@ -39,19 +34,15 @@ import { PropType, defineComponent } from 'vue';
import dateMixin from '../../../mixins/dateMixin'; import dateMixin from '../../../mixins/dateMixin';
import routerMixin from '../../../mixins/routerMixin'; import routerMixin from '../../../mixins/routerMixin';
import styleMixin from '../../../mixins/styleMixin'; import styleMixin from '../../../mixins/styleMixin';
import Station from '../../../scripts/interfaces/Station';
import StationStatusBadge from '../../Global/StationStatusBadge.vue'; import StationStatusBadge from '../../Global/StationStatusBadge.vue';
import { OnlineScenery } from '../../../scripts/interfaces/store/storeTypes';
export default defineComponent({ export default defineComponent({
mixins: [styleMixin, dateMixin, routerMixin], mixins: [styleMixin, dateMixin, routerMixin],
props: { props: {
station: { onlineScenery: {
type: Object as PropType<Station>, type: Object as PropType<OnlineScenery>,
required: true required: false
},
onlineFrom: {
type: Number,
default: -1
} }
}, },
components: { StationStatusBadge } components: { StationStatusBadge }
@@ -3,14 +3,14 @@
<h3 class="spawn-header section-header"> <h3 class="spawn-header section-header">
<img src="/images/icon-spawn.svg" alt="Open spawns icon" /> <img src="/images/icon-spawn.svg" alt="Open spawns icon" />
&nbsp;{{ $t('scenery.spawns') }} &nbsp; &nbsp;{{ $t('scenery.spawns') }} &nbsp;
<span class="text--primary">{{ station.onlineInfo?.spawns.length || '0' }}</span> <span class="text--primary">{{ onlineScenery?.spawns.length || '0' }}</span>
</h3> </h3>
<span v-if="station.onlineInfo"> <span v-if="onlineScenery">
<span <span
class="badge spawn" class="badge spawn"
v-for="(spawn, i) in sortedSpawns" v-for="(spawn, i) in sortedSpawns"
:key="spawn.spawnName + station.onlineInfo?.dispatcherName + i" :key="spawn.spawnName + onlineScenery?.dispatcherName + i"
:data-electrified="spawn.isElectrified" :data-electrified="spawn.isElectrified"
> >
<span class="spawn_name">{{ spawn.spawnName }}</span> <span class="spawn_name">{{ spawn.spawnName }}</span>
@@ -18,9 +18,7 @@
</span> </span>
</span> </span>
<span <span class="badge spawn badge-none" v-if="!onlineScenery || onlineScenery.spawns.length == 0"
class="badge spawn badge-none"
v-if="!station.onlineInfo || station.onlineInfo.spawns.length == 0"
>{{ $t('scenery.no-spawns') }} >{{ $t('scenery.no-spawns') }}
</span> </span>
</section> </section>
@@ -28,21 +26,21 @@
<script lang="ts"> <script lang="ts">
import { PropType, defineComponent } from 'vue'; import { PropType, defineComponent } from 'vue';
import Station from '../../../scripts/interfaces/Station'; import { OnlineScenery } from '../../../scripts/interfaces/store/storeTypes';
export default defineComponent({ export default defineComponent({
props: { props: {
station: { onlineScenery: {
type: Object as PropType<Station>, type: Object as PropType<OnlineScenery>,
required: true required: false
} }
}, },
computed: { computed: {
sortedSpawns() { sortedSpawns() {
if (!this.station.onlineInfo) return []; if (!this.onlineScenery) return [];
return [...this.station.onlineInfo.spawns].sort((s1, s2) => return [...this.onlineScenery.spawns].sort((s1, s2) =>
s1.spawnLength < s2.spawnLength ? 1 : -1 s1.spawnLength < s2.spawnLength ? 1 : -1
); );
} }
@@ -3,12 +3,12 @@
<h3 class="user-header section-header"> <h3 class="user-header section-header">
<img src="/images/icon-user.svg" alt="Users icon" /> <img src="/images/icon-user.svg" alt="Users icon" />
&nbsp;{{ $t('scenery.users') }} &nbsp; &nbsp;{{ $t('scenery.users') }} &nbsp;
<span class="text--primary">{{ station.onlineInfo?.currentUsers || '0' }}</span <span class="text--primary">{{ onlineScenery?.currentUsers || 0 }}</span
>&nbsp;/&nbsp;<span class="text--primary">{{ station.onlineInfo?.maxUsers || '0' }}</span> >&nbsp;/&nbsp;<span class="text--primary">{{ onlineScenery?.maxUsers || 0 }}</span>
</h3> </h3>
<div <div
v-for="train in computedStationTrains" v-for="train in onlineScenery?.stationTrains"
class="badge user" class="badge user"
:class="train.stopStatus" :class="train.stopStatus"
:key="train.trainId" :key="train.trainId"
@@ -22,7 +22,7 @@
<div <div
class="badge user badge-none" class="badge user badge-none"
v-if="!computedStationTrains || computedStationTrains.length == 0" v-if="!onlineScenery?.scheduledTrains || onlineScenery.scheduledTrains.length == 0"
> >
{{ $t('scenery.no-users') }} {{ $t('scenery.no-users') }}
</div> </div>
@@ -30,45 +30,19 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { PropType, computed, defineComponent } from 'vue'; import { PropType, defineComponent } from 'vue';
import modalTrainMixin from '../../../mixins/modalTrainMixin'; import modalTrainMixin from '../../../mixins/modalTrainMixin';
import routerMixin from '../../../mixins/routerMixin'; import routerMixin from '../../../mixins/routerMixin';
import Station from '../../../scripts/interfaces/Station'; import { OnlineScenery } from '../../../scripts/interfaces/store/storeTypes';
import { useStore } from '../../../store/store';
export default defineComponent({ export default defineComponent({
mixins: [routerMixin, modalTrainMixin], mixins: [routerMixin, modalTrainMixin],
props: { props: {
station: { onlineScenery: {
type: Object as PropType<Station>, type: Object as PropType<OnlineScenery>,
required: true required: false
} }
},
setup(props) {
const store = useStore();
const computedStationTrains = computed(() => {
if (!props.station) return [];
const station = props.station as Station;
if (!station.onlineInfo) return [];
if (!station.onlineInfo.stationTrains) return [];
return station.onlineInfo.stationTrains.map((train) => {
const scheduledTrainStatus = station.onlineInfo?.scheduledTrains?.find(
(st) => st.trainNo === train.trainNo
);
return {
...train,
stopStatus: scheduledTrainStatus?.stopStatus || 'no-timetable'
};
});
});
return { computedStationTrains, store };
} }
}); });
</script> </script>
+30 -48
View File
@@ -6,14 +6,12 @@
<span>{{ $t('scenery.timetables') }}</span> <span>{{ $t('scenery.timetables') }}</span>
<span> <span>
<span class="text--primary">{{ <span class="text--primary">{{ onlineScenery?.scheduledTrains?.length || '0' }}</span>
station.onlineInfo?.scheduledTrains?.length || '0'
}}</span>
<span> / </span> <span> / </span>
<span class="text--grayed"> <span class="text--grayed">
{{ {{
station.onlineInfo?.scheduledTrains?.filter((train) => train.stopInfo.confirmed) onlineScenery?.scheduledTrains?.filter((train) => train.stopInfo.confirmed).length ||
.length || '0' '0'
}} }}
</span> </span>
</span> </span>
@@ -59,7 +57,7 @@
<span <span
class="timetable-item empty" class="timetable-item empty"
v-else-if="computedScheduledTrains.length == 0 && !station.onlineInfo" v-else-if="computedScheduledTrains.length == 0 && !onlineScenery"
> >
{{ $t('scenery.offline') }} {{ $t('scenery.offline') }}
</span> </span>
@@ -186,6 +184,7 @@ import Station from '../../scripts/interfaces/Station';
import { useStore } from '../../store/store'; import { useStore } from '../../store/store';
import modalTrainMixin from '../../mixins/modalTrainMixin'; import modalTrainMixin from '../../mixins/modalTrainMixin';
import ScheduledTrainStatus from './ScheduledTrainStatus.vue'; import ScheduledTrainStatus from './ScheduledTrainStatus.vue';
import { OnlineScenery } from '../../scripts/interfaces/store/storeTypes';
export default defineComponent({ export default defineComponent({
name: 'SceneryTimetable', name: 'SceneryTimetable',
@@ -199,9 +198,9 @@ export default defineComponent({
type: Object as PropType<Station>, type: Object as PropType<Station>,
required: true required: true
}, },
onlineScenery: {
timetableOnly: { type: Object as PropType<OnlineScenery>,
type: Boolean required: false
} }
}, },
@@ -229,36 +228,9 @@ export default defineComponent({
: props.station?.generalInfo?.checkpoints[0].checkpointName || null : props.station?.generalInfo?.checkpoints[0].checkpointName || null
); );
const computedScheduledTrains = computed(() => {
if (!props.station) return [];
const station = props.station as Station;
let scheduledTrains =
station.generalInfo?.checkpoints.find((cp) => cp.checkpointName === chosenCheckpoint.value)
?.scheduledTrains ||
station.onlineInfo?.scheduledTrains ||
[];
if (!scheduledTrains) return [];
return (
scheduledTrains.sort((a, b) => {
if (a.stopStatusID > b.stopStatusID) return 1;
if (a.stopStatusID < b.stopStatusID) return -1;
if (a.stopInfo.arrivalTimestamp > b.stopInfo.arrivalTimestamp) return 1;
if (a.stopInfo.arrivalTimestamp < b.stopInfo.arrivalTimestamp) return -1;
return a.stopInfo.departureTimestamp > b.stopInfo.departureTimestamp ? 1 : -1;
}) || []
);
});
return { return {
currentURL, currentURL,
chosenCheckpoint, chosenCheckpoint,
computedScheduledTrains,
store store
}; };
}, },
@@ -269,27 +241,37 @@ export default defineComponent({
if (this.chosenCheckpoint) url += `&checkpoint=${this.chosenCheckpoint}`; if (this.chosenCheckpoint) url += `&checkpoint=${this.chosenCheckpoint}`;
return url; return url;
},
computedScheduledTrains() {
return (
this.onlineScenery?.scheduledTrains
?.filter(
(train) =>
train.checkpointName.toLocaleLowerCase() ==
(this.chosenCheckpoint || this.station.name).toLocaleLowerCase()
)
.sort((a, b) => {
if (a.stopStatusID > b.stopStatusID) return 1;
if (a.stopStatusID < b.stopStatusID) return -1;
if (a.stopInfo.arrivalTimestamp > b.stopInfo.arrivalTimestamp) return 1;
if (a.stopInfo.arrivalTimestamp < b.stopInfo.arrivalTimestamp) return -1;
return a.stopInfo.departureTimestamp > b.stopInfo.departureTimestamp ? 1 : -1;
}) || []
);
} }
}, },
methods: { methods: {
loadSelectedOption() { loadSelectedOption() {
if (!this.station) return; this.chosenCheckpoint =
if (!this.station.generalInfo) return; this.station.generalInfo?.checkpoints[0]?.checkpointName || this.station.name;
if (!this.station.generalInfo.checkpoints) return;
if (this.station.generalInfo.checkpoints.length == 0) return;
if (this.chosenCheckpoint != '') return;
this.chosenCheckpoint = this.station.generalInfo.checkpoints[0].checkpointName;
}, },
setCheckpoint(cp: { checkpointName: string }) { setCheckpoint(cp: { checkpointName: string }) {
this.chosenCheckpoint = cp.checkpointName; this.chosenCheckpoint = cp.checkpointName;
},
showTimetableOnlyView() {
this.$router.push(`${this.$route.fullPath}&timetableOnly=1`);
} }
} }
}); });
@@ -65,6 +65,7 @@ import Station from '../../scripts/interfaces/Station';
import { URLs } from '../../scripts/utils/apiURLs'; import { URLs } from '../../scripts/utils/apiURLs';
import Loading from '../Global/Loading.vue'; import Loading from '../Global/Loading.vue';
import listObserverMixin from '../../mixins/listObserverMixin'; import listObserverMixin from '../../mixins/listObserverMixin';
import { OnlineScenery } from '../../scripts/interfaces/store/storeTypes';
export default defineComponent({ export default defineComponent({
name: 'SceneryTimetablesHistory', name: 'SceneryTimetablesHistory',
@@ -73,6 +74,10 @@ export default defineComponent({
station: { station: {
type: Object as PropType<Station>, type: Object as PropType<Station>,
required: true required: true
},
onlineScenery: {
type: Object as PropType<OnlineScenery>,
required: false
} }
}, },
+2 -2
View File
@@ -1,8 +1,8 @@
<template> <template>
<label @dblclick="handleDbClick"> <label @dblclick="handleDbClick">
<input <input
:value="optionValue" :checked="optionValue"
@input="$emit('update:optionValue', ($event.target as HTMLInputElement).value)" @input="$emit('update:optionValue', ($event.target as HTMLInputElement).checked)"
type="checkbox" type="checkbox"
:class="option.section" :class="option.section"
:name="option.id" :name="option.id"
+23 -8
View File
@@ -79,11 +79,19 @@
</span> </span>
<span v-else-if="station.generalInfo.availability == 'abandoned'"> <span v-else-if="station.generalInfo.availability == 'abandoned'">
<img src="/images/icon-abandoned.svg" alt="non-public" :title="$t('desc.abandoned')" /> <img
src="/images/icon-abandoned.svg"
alt="non-public"
:title="$t('desc.abandoned')"
/>
</span> </span>
<span v-else-if="station.generalInfo.availability == 'nonPublic'"> <span v-else-if="station.generalInfo.availability == 'nonPublic'">
<img src="/images/icon-lock.svg" alt="non-public" :title="$t('desc.non-public')" /> <img
src="/images/icon-lock.svg"
alt="non-public"
:title="$t('desc.non-public')"
/>
</span> </span>
<span v-else> <span v-else>
@@ -234,7 +242,7 @@
</td> </td>
<td <td
class="station_schedules" class="station_schedules all"
style="width: 30px" style="width: 30px"
:class="{ inactive: !station.onlineInfo }" :class="{ inactive: !station.onlineInfo }"
> >
@@ -244,20 +252,23 @@
</td> </td>
<td <td
class="station_schedules" class="station_schedules unconfirmed"
style="width: 30px" style="width: 30px"
:class="{ inactive: !station.onlineInfo }" :class="{ inactive: !station.onlineInfo }"
> >
<span style="color: #ccc"> <span style="color: #ccc">
{{ {{
station.onlineInfo?.scheduledTrains?.filter((train) => !train.stopInfo.confirmed) new Set([
.length || 0 ...(station.onlineInfo?.scheduledTrains
?.filter((train) => !train.stopInfo.confirmed)
.map((train) => train.checkpointName) || [])
]).size || 0
}} }}
</span> </span>
</td> </td>
<td <td
class="station_schedules" class="station_schedules confirmed"
style="width: 30px" style="width: 30px"
:class="{ inactive: !station.onlineInfo }" :class="{ inactive: !station.onlineInfo }"
> >
@@ -336,9 +347,13 @@ export default defineComponent({
if (!station) return; if (!station) return;
this.lastSelectedStationName = station.name; this.lastSelectedStationName = station.name;
this.$router.push({ this.$router.push({
name: 'SceneryView', name: 'SceneryView',
query: { station: station.name.replaceAll(' ', '_') } query: {
station: station.name.replaceAll(' ', '_'),
region: this.$route.query.region || undefined
}
}); });
}, },
+4
View File
@@ -7,6 +7,7 @@ import plLang from './locales/pl.json';
import { createI18n } from 'vue-i18n'; import { createI18n } from 'vue-i18n';
import { createPinia } from 'pinia'; import { createPinia } from 'pinia';
import useCustomSW from './mixins/useCustomSW';
const i18n = createI18n({ const i18n = createI18n({
locale: 'pl', locale: 'pl',
@@ -20,6 +21,9 @@ const i18n = createI18n({
enableLegacy: false enableLegacy: false
}); });
// SW
useCustomSW();
const clickOutsideDirective: Directive = { const clickOutsideDirective: Directive = {
mounted(el, binding) { mounted(el, binding) {
el.clickOutsideEvent = (event: Event) => { el.clickOutsideEvent = (event: Event) => {
+12 -3
View File
@@ -6,7 +6,10 @@ const routes: Array<RouteRecordRaw> = [
{ {
path: '/', path: '/',
name: 'StationsView', name: 'StationsView',
component: () => import('../views/StationsView.vue') component: () => import('../views/StationsView.vue'),
props: (route) => ({
region: route.query.region
})
}, },
{ {
path: '/trains', path: '/trains',
@@ -21,7 +24,11 @@ const routes: Array<RouteRecordRaw> = [
{ {
path: '/scenery', path: '/scenery',
name: 'SceneryView', name: 'SceneryView',
component: () => import('../views/SceneryView.vue') component: () => import('../views/SceneryView.vue'),
props: (route) => ({
region: route.query.region,
station: route.query.station
})
}, },
{ {
path: '/journal', path: '/journal',
@@ -53,9 +60,11 @@ const routes: Array<RouteRecordRaw> = [
]; ];
const router = createRouter({ const router = createRouter({
scrollBehavior(to, from) { scrollBehavior(to, from, savedPosition) {
if (to.name == 'SceneryView' && from.name) return { el: `.app_main` }; if (to.name == 'SceneryView' && from.name) return { el: `.app_main` };
if (savedPosition) return savedPosition;
// if (from.name == 'SceneryView' && to.name == 'StationsView') return { el: `.last-selected`, top: 20 }; // if (from.name == 'SceneryView' && to.name == 'StationsView') return { el: `.last-selected`, top: 20 };
}, },
history: createWebHistory(), history: createWebHistory(),
+2
View File
@@ -10,6 +10,8 @@ export enum StopStatus {
} }
export interface ScheduledTrain { export interface ScheduledTrain {
checkpointName: string;
trainId: string; trainId: string;
trainNo: number; trainNo: number;
+30 -3
View File
@@ -2,17 +2,16 @@ import { Socket } from 'socket.io-client';
import { DataStatus } from '../../enums/DataStatus'; import { DataStatus } from '../../enums/DataStatus';
import StationAPIData from '../api/StationAPIData'; import StationAPIData from '../api/StationAPIData';
import { TrainAPIData } from '../api/TrainAPIData'; import { TrainAPIData } from '../api/TrainAPIData';
import Station from '../Station';
import Train from '../Train';
import { DispatcherStatsAPIData } from '../api/DispatcherStatsAPIData'; import { DispatcherStatsAPIData } from '../api/DispatcherStatsAPIData';
import { DriverStatsAPIData } from '../api/DriverStatsAPIData'; import { DriverStatsAPIData } from '../api/DriverStatsAPIData';
import { RollingStockGithubData } from '../github_api/StockInfoGithubData'; import { RollingStockGithubData } from '../github_api/StockInfoGithubData';
import Station from '../Station';
import { ScheduledTrain } from '../ScheduledTrain';
export type Availability = 'default' | 'unavailable' | 'nonPublic' | 'abandoned' | 'nonDefault'; export type Availability = 'default' | 'unavailable' | 'nonPublic' | 'abandoned' | 'nonDefault';
export interface StoreState { export interface StoreState {
stationList: Station[]; stationList: Station[];
trainList: Train[];
apiData: APIData; apiData: APIData;
rollingStockData?: RollingStockGithubData; rollingStockData?: RollingStockGithubData;
@@ -91,3 +90,31 @@ export interface StationJSONData {
availability: Availability; availability: Availability;
} }
export interface StationTrain {
driverName: string;
driverId: number;
trainNo: number;
trainId: string;
stopStatus: string;
}
export interface OnlineScenery {
name: string;
hash: string;
region: string;
maxUsers: number;
currentUsers: number;
spawns: { spawnName: string; spawnLength: number; isElectrified: boolean }[];
dispatcherName: string;
dispatcherRate: number;
dispatcherId: number;
dispatcherExp: number;
dispatcherIsSupporter: boolean;
statusTimestamp: number;
statusID: string;
stationTrains?: StationTrain[];
scheduledTrains?: ScheduledTrain[];
}
+130 -9
View File
@@ -1,6 +1,9 @@
import { ScheduledTrain, StopStatus } from '../interfaces/ScheduledTrain'; import { ScheduledTrain, StopStatus } from '../interfaces/ScheduledTrain';
import Station from '../interfaces/Station';
import Train from '../interfaces/Train'; import Train from '../interfaces/Train';
import TrainStop from '../interfaces/TrainStop'; import TrainStop from '../interfaces/TrainStop';
import StationAPIData from '../interfaces/api/StationAPIData';
import { StationTrain, StoreState } from '../interfaces/store/storeTypes';
export const getLocoURL = (locoType: string): string => export const getLocoURL = (locoType: string): string =>
`https://rj.td2.info.pl/dist/img/thumbnails/${ `https://rj.td2.info.pl/dist/img/thumbnails/${
@@ -79,7 +82,7 @@ export const getTimestamp = (date: string | null): number => (date ? new Date(da
export const getTrainStopStatus = ( export const getTrainStopStatus = (
stopInfo: TrainStop, stopInfo: TrainStop,
currentStationName: string, currentStationName: string,
stationName: string sceneryName: string
) => { ) => {
let stopStatus = StopStatus['arriving'], let stopStatus = StopStatus['arriving'],
stopLabel = '', stopLabel = '',
@@ -89,23 +92,23 @@ export const getTrainStopStatus = (
stopStatus = StopStatus['terminated']; stopStatus = StopStatus['terminated'];
stopLabel = 'Skończył bieg'; stopLabel = 'Skończył bieg';
stopStatusID = 5; stopStatusID = 5;
} else if (!stopInfo.terminatesHere && stopInfo.confirmed && currentStationName == stationName) { } else if (!stopInfo.terminatesHere && stopInfo.confirmed && currentStationName == sceneryName) {
stopStatus = StopStatus['departed']; stopStatus = StopStatus['departed'];
stopLabel = 'Odprawiony'; stopLabel = 'Odprawiony';
stopStatusID = 2; stopStatusID = 2;
} else if (!stopInfo.terminatesHere && stopInfo.confirmed && currentStationName != stationName) { } else if (!stopInfo.terminatesHere && stopInfo.confirmed && currentStationName != sceneryName) {
stopStatus = StopStatus['departed-away']; stopStatus = StopStatus['departed-away'];
stopLabel = 'Odjechał'; stopLabel = 'Odjechał';
stopStatusID = 4; stopStatusID = 4;
} else if (currentStationName == stationName && !stopInfo.stopped) { } else if (currentStationName == sceneryName && !stopInfo.stopped) {
stopStatus = StopStatus['online']; stopStatus = StopStatus['online'];
stopLabel = 'Na stacji'; stopLabel = 'Na stacji';
stopStatusID = 0; stopStatusID = 0;
} else if (currentStationName == stationName && stopInfo.stopped) { } else if (currentStationName == sceneryName && stopInfo.stopped) {
stopStatus = StopStatus['stopped']; stopStatus = StopStatus['stopped'];
stopLabel = 'Postój'; stopLabel = 'Postój';
stopStatusID = 1; stopStatusID = 1;
} else if (currentStationName != stationName) { } else if (currentStationName != sceneryName) {
stopStatus = StopStatus['arriving']; stopStatus = StopStatus['arriving'];
stopLabel = 'W drodze'; stopLabel = 'W drodze';
stopStatusID = 3; stopStatusID = 3;
@@ -114,16 +117,16 @@ export const getTrainStopStatus = (
return { stopStatus, stopLabel, stopStatusID }; return { stopStatus, stopLabel, stopStatusID };
}; };
export function getScheduledTrain( export function getCheckpointTrain(
train: Train, train: Train,
trainStopIndex: number, trainStopIndex: number,
stationName: string sceneryName: string
): ScheduledTrain { ): ScheduledTrain {
const timetable = train.timetableData!; const timetable = train.timetableData!;
const followingStops = timetable.followingStops; const followingStops = timetable.followingStops;
const trainStop = followingStops[trainStopIndex]; const trainStop = followingStops[trainStopIndex];
const trainStopStatus = getTrainStopStatus(trainStop, train.currentStationName, stationName); const trainStopStatus = getTrainStopStatus(trainStop, train.currentStationName, sceneryName);
let prevStationName = '', let prevStationName = '',
nextStationName = ''; nextStationName = '';
@@ -177,6 +180,8 @@ export function getScheduledTrain(
} }
return { return {
checkpointName: trainStop.stopNameRAW,
trainNo: train.trainNo, trainNo: train.trainNo,
trainId: train.trainId, trainId: train.trainId,
@@ -206,3 +211,119 @@ export function getScheduledTrain(
prevDepartureLine prevDepartureLine
}; };
} }
export function getDispatcherStatus(state: StoreState, onlineStationData: StationAPIData) {
const { dispatchers } = state.apiData;
const prevDispatcherStatus = state.lastDispatcherStatuses.find(
(dispatcher) => dispatcher.hash === onlineStationData.stationHash
);
const stationStatus = !dispatchers
? undefined
: dispatchers.find(
(status: string[]) =>
status[0] == onlineStationData.stationHash && status[1] == state.region.id
) || -1;
const statusTimestamp =
prevDispatcherStatus && !dispatchers
? prevDispatcherStatus.statusTimestamp
: getStatusTimestamp(stationStatus);
const statusID =
prevDispatcherStatus && !dispatchers
? prevDispatcherStatus.statusID
: getStatusID(stationStatus);
return {
hash: onlineStationData.stationHash,
statusID,
statusTimestamp
};
}
export function getScheduledTrains(
trainList: Train[],
stationAPIData: StationAPIData,
stationGeneralInfo: Station['generalInfo']
): ScheduledTrain[] {
const stationName = stationAPIData.stationName.toLocaleLowerCase();
stationGeneralInfo?.checkpoints.forEach((cp) => (cp.scheduledTrains.length = 0));
return trainList.reduce((acc: ScheduledTrain[], train) => {
if (!train.timetableData) return acc;
const timetable = train.timetableData;
if (!timetable.sceneries.includes(stationAPIData.stationHash)) return acc;
const stopInfoIndex = timetable.followingStops.findIndex((stop) => {
const stopName = stop.stopNameRAW.toLowerCase();
return (
stationName == stopName ||
(!/(po\.|podg\.)/.test(stationName) && stopName.includes(stationName)) ||
(!/(po\.|podg\.)/.test(stopName) && stationName.includes(stopName)) ||
(stopName.split(', podg.')[0] !== undefined &&
stationName.startsWith(stopName.split(', podg.')[0]))
);
});
const checkpointScheduledTrains: ScheduledTrain[] = [];
if (stopInfoIndex != -1) {
checkpointScheduledTrains.push(
getCheckpointTrain(train, stopInfoIndex, stationAPIData.stationName)
);
}
stationGeneralInfo?.checkpoints?.forEach((checkpoint) => {
if (checkpoint.checkpointName.toLocaleLowerCase() == stationName) return;
if (
checkpointScheduledTrains.findIndex(
(cpTrain) =>
cpTrain.checkpointName.toLocaleLowerCase() ==
checkpoint.checkpointName.toLocaleLowerCase()
) != -1
)
return;
const index = timetable.followingStops.findIndex(
(stop) => stop.stopNameRAW.toLowerCase() == checkpoint.checkpointName.toLowerCase()
);
if (index > -1)
checkpointScheduledTrains.push(
getCheckpointTrain(train, index, stationAPIData.stationName)
);
});
acc.push(...checkpointScheduledTrains);
return acc;
}, []) as ScheduledTrain[];
}
export function getStationTrains(
trainList: Train[],
scheduledTrainList: ScheduledTrain[],
region: string,
apiStation: StationAPIData
): StationTrain[] {
return trainList
.filter(
(train) =>
train?.region === region &&
train.online &&
train.currentStationName === apiStation.stationName
)
.map((train) => ({
driverName: train.driverName,
driverId: train.driverId,
trainNo: train.trainNo,
trainId: train.trainId,
stopStatus:
scheduledTrainList.find((st) => st.trainNo === train.trainNo)?.stopStatus || 'no-timetable'
}));
}
+12 -16
View File
@@ -1,6 +1,5 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import inputData from '../data/options.json'; import inputData from '../data/options.json';
import Station from '../scripts/interfaces/Station';
import StorageManager from '../scripts/managers/storageManager'; import StorageManager from '../scripts/managers/storageManager';
import { useStore } from './store'; import { useStore } from './store';
import { filterInitStates } from '../scripts/constants/stores/initFilterStates'; import { filterInitStates } from '../scripts/constants/stores/initFilterStates';
@@ -13,31 +12,28 @@ export const useStationFiltersStore = defineStore('stationFiltersStore', {
inputs: inputData, inputs: inputData,
filters: { ...filterInitStates }, filters: { ...filterInitStates },
sorterActive: { headerName: 'station' as HeadIdsTypes, dir: 1 }, sorterActive: { headerName: 'station' as HeadIdsTypes, dir: 1 },
store: useStore(),
lastClickedFilterId: '' lastClickedFilterId: ''
}; };
}, },
getters: { getters: {
areFiltersAtDefault(state) { areFiltersAtDefault: (state) => {
return Object.keys(state.filters).every((f) => state.filters[f] === filterInitStates[f]); return Object.keys(state.filters).every((f) => state.filters[f] === filterInitStates[f]);
},
filteredStationList: (state) => {
const store = useStore();
return store.stationList
.map((station) => ({
...station,
onlineInfo: store.onlineSceneryList.find((os) => os.name == station.name)
}))
.filter((station) => filterStations(station, state.filters))
.sort((a, b) => sortStations(a, b, state.sorterActive));
} }
}, },
actions: { actions: {
getFilteredStationList(stationList: Station[], region: string): Station[] {
return stationList
.map((station) => {
if (station.onlineInfo && station.onlineInfo.region != region) {
delete station.onlineInfo;
}
return station;
})
.filter((station) => filterStations(station, this.filters))
.sort((a, b) => sortStations(a, b, this.sorterActive));
},
setupFilters() { setupFilters() {
if (!StorageManager.isRegistered('options_saved')) return; if (!StorageManager.isRegistered('options_saved')) return;
+108 -189
View File
@@ -9,12 +9,18 @@ import StationRoutes from '../scripts/interfaces/StationRoutes';
import Train from '../scripts/interfaces/Train'; import Train from '../scripts/interfaces/Train';
import { URLs } from '../scripts/utils/apiURLs'; import { URLs } from '../scripts/utils/apiURLs';
import { import {
getStatusTimestamp, getDispatcherStatus,
getStatusID, getCheckpointTrain,
getScheduledTrain, parseSpawns,
parseSpawns getScheduledTrains,
getStationTrains
} from '../scripts/utils/storeUtils'; } from '../scripts/utils/storeUtils';
import { APIData, StationJSONData, StoreState } from '../scripts/interfaces/store/storeTypes'; import {
APIData,
OnlineScenery,
StationJSONData,
StoreState
} from '../scripts/interfaces/store/storeTypes';
import packageInfo from '../../package.json'; import packageInfo from '../../package.json';
import { RollingStockGithubData } from '../scripts/interfaces/github_api/StockInfoGithubData'; import { RollingStockGithubData } from '../scripts/interfaces/github_api/StockInfoGithubData';
@@ -25,7 +31,7 @@ export const useStore = defineStore('store', {
rollingStockData: undefined, rollingStockData: undefined,
stationList: [], stationList: [],
trainList: [],
routesList: [], routesList: [],
sceneryData: [], sceneryData: [],
@@ -63,13 +69,9 @@ export const useStore = defineStore('store', {
modalLastClickedTarget: null modalLastClickedTarget: null
}) as StoreState, }) as StoreState,
actions: { getters: {
setTrainsOnlineData() { trainList(): Train[] {
const { trains } = this.apiData; return (this.apiData?.trains ?? [])
if (!trains) return [];
this.trainList = trains
.filter( .filter(
(train) => (train) =>
train.region === this.region.id && train.region === this.region.id &&
@@ -123,193 +125,113 @@ export const useStore = defineStore('store', {
}); });
}, },
getDispatcherStatus(onlineStationData: StationAPIData) { onlineSceneryList(state): OnlineScenery[] {
const { dispatchers } = this.apiData; if (state.isOffline) return [];
if (!state.apiData?.stations) return [];
const prevDispatcherStatus = this.lastDispatcherStatuses.find( return state.apiData?.stations
(dispatcher) => dispatcher.hash === onlineStationData.stationHash ?.filter((apiStation) => apiStation.region == state.region.id && apiStation.isOnline)
); .map((apiStation) => {
const dispatcherStatus = getDispatcherStatus(state as StoreState, apiStation);
const station = this.stationList.find((s) => s.name === apiStation.stationName);
const stationStatus = !dispatchers const scheduledTrains = getScheduledTrains(
? undefined this.trainList,
: dispatchers.find( apiStation,
(status: string[]) => station?.generalInfo
status[0] == onlineStationData.stationHash && status[1] == this.region.id );
) || -1;
const statusTimestamp = const stationTrains = getStationTrains(
prevDispatcherStatus && !dispatchers this.trainList,
? prevDispatcherStatus.statusTimestamp scheduledTrains,
: getStatusTimestamp(stationStatus); this.region.id,
const statusID = apiStation
prevDispatcherStatus && !dispatchers );
? prevDispatcherStatus.statusID
: getStatusID(stationStatus);
return { return {
hash: onlineStationData.stationHash, name: apiStation.stationName,
statusID, hash: apiStation.stationHash,
statusTimestamp region: apiStation.region,
}; maxUsers: apiStation.maxUsers,
}, currentUsers: apiStation.currentUsers,
spawns: parseSpawns(apiStation.spawnString),
getScheduledTrains(stationGeneralInfo: Station['generalInfo'], stationAPIData: StationAPIData) { dispatcherName: apiStation.dispatcherName,
const stationName = stationAPIData.stationName.toLowerCase(); dispatcherRate: apiStation.dispatcherRate,
dispatcherId: apiStation.dispatcherId,
stationGeneralInfo?.checkpoints.forEach((cp) => (cp.scheduledTrains.length = 0)); dispatcherExp: apiStation.dispatcherExp,
dispatcherIsSupporter: apiStation.dispatcherIsSupporter,
return this.trainList.reduce((acc: ScheduledTrain[], train) => { scheduledTrains: scheduledTrains,
if (!train.timetableData) return acc; stationTrains: stationTrains,
statusTimestamp: dispatcherStatus.statusTimestamp,
const timetable = train.timetableData; statusID: dispatcherStatus.statusID
if (!timetable.sceneries.includes(stationAPIData.stationHash)) return acc; };
const stopInfoIndex = timetable.followingStops.findIndex((stop) => {
const stopName = stop.stopNameRAW.toLowerCase();
if (stationName === stopName) return true;
if (
stopName.includes(stationName) &&
!stop.stopName.includes('po.') &&
!stop.stopName.includes('podg.')
)
return true;
if (
stationName.includes(stopName) &&
!stop.stopName.includes('po.') &&
!stop.stopName.includes('podg.')
)
return true;
if (
stopName.includes('podg.') &&
stopName.split(', podg.')[0] &&
stationName.includes(stopName.split(', podg.')[0])
)
return true;
if (
stationGeneralInfo &&
stationGeneralInfo.checkpoints &&
stationGeneralInfo.checkpoints.length > 0 &&
stationGeneralInfo.checkpoints.some((cp) =>
cp.checkpointName.toLowerCase().includes(stop.stopNameRAW.toLowerCase())
)
)
return true;
return false;
}); });
}
},
actions: {
// setStationsOnlineInfo() {
// const onlineStationNames: string[] = [];
// const prevDispatcherStatuses: StoreState['lastDispatcherStatuses'] = [];
if (stopInfoIndex == -1) return acc; // if (this.isOffline) {
// this.stationList.forEach((station) => {
// station.onlineInfo = undefined;
// });
const scheduledStopTrain = getScheduledTrain( // return;
train, // }
stopInfoIndex,
stationAPIData.stationName
);
if (stationGeneralInfo?.checkpoints) { // this.apiData.stations?.forEach((stationAPIData) => {
for (const checkpoint of stationGeneralInfo.checkpoints) { // if (stationAPIData.region !== this.region.id || !stationAPIData.isOnline) return;
const index = timetable.followingStops.findIndex(
(stop) => stop.stopNameRAW.toLowerCase() == checkpoint.checkpointName.toLowerCase()
);
if (index == -1) continue; // const station = this.stationList.find((s) => s.name === stationAPIData.stationName);
const scheduledCheckpointTrain = getScheduledTrain( // onlineStationNames.push(stationAPIData.stationName);
train,
index,
stationAPIData.stationName
);
checkpoint.scheduledTrains.push(scheduledCheckpointTrain);
}
}
acc.push(scheduledStopTrain); // const dispatcherStatus = this.getDispatcherStatus(stationAPIData);
return acc; // prevDispatcherStatuses.push(dispatcherStatus);
}, []) as ScheduledTrain[];
},
getStationTrains(stationAPIData: StationAPIData) { // const stationTrains = this.getStationTrains(stationAPIData);
return this.trainList // const scheduledTrains = this.getScheduledTrains(station?.generalInfo, stationAPIData);
.filter(
(train) =>
train?.region === this.region.id &&
train.online &&
train.currentStationName === stationAPIData.stationName
)
.map((train) => ({
driverName: train.driverName,
driverId: train.driverId,
trainNo: train.trainNo,
trainId: train.trainId
}));
},
setStationsOnlineInfo() { // const onlineInfo = {
const onlineStationNames: string[] = []; // name: stationAPIData.stationName,
const prevDispatcherStatuses: StoreState['lastDispatcherStatuses'] = []; // hash: stationAPIData.stationHash,
// region: stationAPIData.region,
// maxUsers: stationAPIData.maxUsers,
// currentUsers: stationAPIData.currentUsers,
// spawns: parseSpawns(stationAPIData.spawnString),
// dispatcherName: stationAPIData.dispatcherName,
// dispatcherRate: stationAPIData.dispatcherRate,
// dispatcherId: stationAPIData.dispatcherId,
// dispatcherExp: stationAPIData.dispatcherExp,
// dispatcherIsSupporter: stationAPIData.dispatcherIsSupporter,
// stationTrains,
// statusTimestamp: dispatcherStatus.statusTimestamp,
// statusID: dispatcherStatus.statusID,
// scheduledTrains
// };
if (this.isOffline) { // if (!station) {
this.stationList.forEach((station) => { // this.stationList.push({
station.onlineInfo = undefined; // name: stationAPIData.stationName,
}); // onlineInfo
// });
return; // return;
} // }
this.apiData.stations?.forEach((stationAPIData) => { // station.onlineInfo = { ...onlineInfo };
if (stationAPIData.region !== this.region.id || !stationAPIData.isOnline) return; // });
const station = this.stationList.find((s) => s.name === stationAPIData.stationName);
onlineStationNames.push(stationAPIData.stationName); // this.stationList
// .filter((station) => !onlineStationNames.includes(station.name) && station.onlineInfo)
// .forEach((offlineStation) => {
// offlineStation.onlineInfo = undefined;
// });
const dispatcherStatus = this.getDispatcherStatus(stationAPIData); // if (this.apiData.dispatchers != null) this.lastDispatcherStatuses = prevDispatcherStatuses;
prevDispatcherStatuses.push(dispatcherStatus); // },
const stationTrains = this.getStationTrains(stationAPIData);
const scheduledTrains = this.getScheduledTrains(station?.generalInfo, stationAPIData);
const onlineInfo = {
name: stationAPIData.stationName,
hash: stationAPIData.stationHash,
region: stationAPIData.region,
maxUsers: stationAPIData.maxUsers,
currentUsers: stationAPIData.currentUsers,
spawns: parseSpawns(stationAPIData.spawnString),
dispatcherName: stationAPIData.dispatcherName,
dispatcherRate: stationAPIData.dispatcherRate,
dispatcherId: stationAPIData.dispatcherId,
dispatcherExp: stationAPIData.dispatcherExp,
dispatcherIsSupporter: stationAPIData.dispatcherIsSupporter,
stationTrains,
statusTimestamp: dispatcherStatus.statusTimestamp,
statusID: dispatcherStatus.statusID,
scheduledTrains
};
if (!station) {
this.stationList.push({
name: stationAPIData.stationName,
onlineInfo
});
return;
}
station.onlineInfo = { ...onlineInfo };
this.stationList
.filter((station) => !onlineStationNames.includes(station.name) && station.onlineInfo)
.forEach((offlineStation) => {
offlineStation.onlineInfo = undefined;
});
});
if (this.apiData.dispatchers != null) this.lastDispatcherStatuses = prevDispatcherStatuses;
},
async fetchStationsGeneralInfo() { async fetchStationsGeneralInfo() {
const sceneryData: StationJSONData[] = await ( const sceneryData: StationJSONData[] = await (
@@ -385,7 +307,7 @@ export const useStore = defineStore('store', {
} }
const socket = io(URLs.stacjownikAPI, { const socket = io(URLs.stacjownikAPI, {
// transports: ['websocket', 'polling'], transports: ['websocket', 'polling'],
rememberUpgrade: true, rememberUpgrade: true,
reconnection: true, reconnection: true,
extraHeaders: { extraHeaders: {
@@ -405,7 +327,6 @@ export const useStore = defineStore('store', {
socket.emit('FETCH_DATA', { version: packageInfo.version }, (data: APIData) => { socket.emit('FETCH_DATA', { version: packageInfo.version }, (data: APIData) => {
this.dataStatuses.connection = DataStatus.Loaded; this.dataStatuses.connection = DataStatus.Loaded;
this.apiData = data; this.apiData = data;
this.setOnlineData(); this.setOnlineData();
}); });
@@ -414,10 +335,9 @@ export const useStore = defineStore('store', {
}, },
async connectToAPI() { async connectToAPI() {
await this.fetchStationsGeneralInfo();
await this.fetchStockInfoData();
this.connectToWebsocket(); this.connectToWebsocket();
this.fetchStockInfoData();
this.fetchStationsGeneralInfo();
}, },
async changeRegion(region: StoreState['region']) { async changeRegion(region: StoreState['region']) {
@@ -453,8 +373,7 @@ export const useStore = defineStore('store', {
? DataStatus.Warning ? DataStatus.Warning
: DataStatus.Loaded; : DataStatus.Loaded;
this.setTrainsOnlineData(); // this.setStationsOnlineInfo();
this.setStationsOnlineInfo();
} }
} }
}); });
-4
View File
@@ -35,10 +35,6 @@
} }
} }
html {
scroll-behavior: smooth;
}
body { body {
background: var(--clr-bg); background: var(--clr-bg);
+35 -25
View File
@@ -1,9 +1,6 @@
<template> <template>
<div class="scenery-view"> <div class="scenery-view">
<div <div class="scenery-offline" v-if="!stationInfo && store.dataStatuses.sceneries == 2">
class="scenery-offline"
v-if="!stationInfo && isComponentVisible && store.dataStatuses.sceneries == 2"
>
<div>{{ $t('scenery.no-scenery') }}</div> <div>{{ $t('scenery.no-scenery') }}</div>
<action-button> <action-button>
@@ -11,21 +8,16 @@
</action-button> </action-button>
</div> </div>
<div <div class="scenery-wrapper" v-if="stationInfo" ref="card-wrapper">
class="scenery-wrapper" <div class="scenery-left">
v-if="stationInfo"
ref="card-wrapper"
:data-timetable-only="timetableOnly"
>
<div class="scenery-left" v-if="!timetableOnly">
<div class="scenery-actions"> <div class="scenery-actions">
<button class="back-btn btn" :title="$t('scenery.return-btn')" @click="navigateTo('/')"> <button class="back-btn btn" :title="$t('scenery.return-btn')" @click="navigateTo('/')">
<img src="/images/icon-back.svg" alt="Back to scenery" /> <img src="/images/icon-back.svg" alt="Back to scenery" />
</button> </button>
</div> </div>
<SceneryHeader :station="stationInfo" /> <SceneryHeader :station="stationInfo" :onlineScenery="onlineSceneryInfo" />
<SceneryInfo :station="stationInfo" /> <SceneryInfo :station="stationInfo" :onlineScenery="onlineSceneryInfo" />
</div> </div>
<div class="scenery-right"> <div class="scenery-right">
@@ -44,6 +36,7 @@
<keep-alive> <keep-alive>
<component <component
:is="currentViewCompontent" :is="currentViewCompontent"
:onlineScenery="onlineSceneryInfo"
:station="stationInfo" :station="stationInfo"
:key="currentViewCompontent" :key="currentViewCompontent"
></component> ></component>
@@ -82,9 +75,22 @@ export default defineComponent({
SceneryDispatchersHistory SceneryDispatchersHistory
}, },
props: {
region: {
type: String,
required: false
},
station: {
type: String,
required: true
}
},
mixins: [routerMixin], mixins: [routerMixin],
data: () => ({ data: () => ({
store: useStore(),
viewModes: [ viewModes: [
{ {
id: 'scenery.option-active-timetables', id: 'scenery.option-active-timetables',
@@ -111,24 +117,28 @@ export default defineComponent({
setup() { setup() {
const route = useRoute(); const route = useRoute();
const store = useStore();
const timetableOnly = computed(() => (route.query['timetableOnly'] == '1' ? true : false));
const isComponentVisible = computed(() => route.path === '/scenery'); const isComponentVisible = computed(() => route.path === '/scenery');
const stationInfo = computed(() => {
return store.stationList.find(
(station) => station.name === route.query.station?.toString().replace(/_/g, ' ')
);
});
return { return {
timetableOnly, isComponentVisible
isComponentVisible,
stationInfo,
store
}; };
}, },
computed: {
stationInfo() {
return this.store.stationList.find(
(station) => station.name === this.station?.toString().replace(/_/g, ' ')
);
},
onlineSceneryInfo() {
return this.store.onlineSceneryList.find(
(scenery) => scenery.name === this.station?.toString().replace(/_/g, ' ')
);
}
},
methods: { methods: {
setViewMode(componentName: string) { setViewMode(componentName: string) {
this.currentViewCompontent = componentName; this.currentViewCompontent = componentName;
+4 -8
View File
@@ -33,19 +33,15 @@ export default defineComponent({
filterCardOpen: false, filterCardOpen: false,
modalHidden: true, modalHidden: true,
STORAGE_KEY: 'options_saved', STORAGE_KEY: 'options_saved',
focusedStationName: '' focusedStationName: '',
filterStore: useStationFiltersStore(),
store: useStore()
}), }),
setup() {
return {
filterStore: useStationFiltersStore(),
store: useStore()
};
},
computed: { computed: {
computedStationList() { computedStationList() {
return this.filterStore.getFilteredStationList(this.store.stationList, this.store.region.id); return this.filterStore.filteredStationList;
} }
}, },
+27 -17
View File
@@ -4,7 +4,7 @@ import { VitePWA } from 'vite-plugin-pwa';
export default defineConfig({ export default defineConfig({
server: { server: {
port: 5001, port: 5001
}, },
plugins: [ plugins: [
vue(), vue(),
@@ -20,13 +20,25 @@ export default defineConfig({
options: { options: {
cacheName: 'sceneries-cache', cacheName: 'sceneries-cache',
expiration: { expiration: {
maxEntries: 1, maxAgeSeconds: 60 * 60 * 24 * 7 // <== 7 days
maxAgeSeconds: 60 * 60 * 24 * 7, // <== 7 days
}, },
cacheableResponse: { cacheableResponse: {
statuses: [0, 200], statuses: [0, 200]
}
}
},
{
urlPattern: new RegExp('^https://raw.githubusercontent.com/Spythere/api/*', 'i'),
handler: 'NetworkFirst',
options: {
cacheName: 'github-api-cache',
expiration: {
maxAgeSeconds: 60 * 60 * 24 * 7 // <== 7 days
}, },
}, cacheableResponse: {
statuses: [0, 200]
}
}
}, },
{ {
urlPattern: /^https:\/\/rj.td2.info.pl\/dist\/img\/thumbnails\/.*/i, urlPattern: /^https:\/\/rj.td2.info.pl\/dist\/img\/thumbnails\/.*/i,
@@ -35,21 +47,19 @@ export default defineConfig({
cacheName: 'images-cache', cacheName: 'images-cache',
expiration: { expiration: {
maxEntries: 100, maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24 * 60, maxAgeSeconds: 60 * 60 * 24 * 7 // <== 7 days
}, },
cacheableResponse: { cacheableResponse: {
statuses: [0, 200, 404], statuses: [0, 200, 404]
}, }
}, }
}, }
], ]
}, },
devOptions: { devOptions: {
enabled: true, enabled: true,
}, suppressWarnings: true
}), }
], })
]
}); });