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
+5 -3
View File
@@ -82,28 +82,30 @@ export default defineComponent({
required: true
}
},
setup() {
return {
store: useStore()
};
},
methods: {
changeRegion(region: { id: string; value: string }) {
this.store.changeRegion(region);
},
changeLang(lang: string) {
this.$emit('changeLang', lang);
}
},
computed: {
onlineTrainsCount() {
return this.store.trainList.filter((train) => train.online).length;
},
onlineDispatchersCount() {
return this.store.stationList.filter(
(station) => station.onlineInfo && station.onlineInfo.region == this.store.region.id
).length;
return this.store.onlineSceneryList.length;
},
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: {
selectOption(item: Item) {
this.selectedItem = item;
@@ -76,6 +76,7 @@ import { URLs } from '../../scripts/utils/apiURLs';
import Loading from '../Global/Loading.vue';
import styleMixin from '../../mixins/styleMixin';
import listObserverMixin from '../../mixins/listObserverMixin';
import { OnlineScenery } from '../../scripts/interfaces/store/storeTypes';
export default defineComponent({
name: 'SceneryDispatchersHistory',
@@ -85,6 +86,10 @@ export default defineComponent({
station: {
type: Object as PropType<Station>,
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>
</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>
</template>
<script lang="ts">
import { PropType, defineComponent } from 'vue';
import Station from '../../scripts/interfaces/Station';
import { OnlineScenery } from '../../scripts/interfaces/store/storeTypes';
export default defineComponent({
props: {
station: {
type: Object as PropType<Station>,
required: true
},
onlineScenery: {
type: Object as PropType<OnlineScenery>,
required: false
}
}
});
+9 -9
View File
@@ -1,6 +1,6 @@
<template>
<div class="scenery-info">
<section v-if="!timetableOnly">
<section>
<div class="scenery-info-general" v-if="station.generalInfo">
<SceneryInfoIcons :station="station" />
@@ -68,14 +68,14 @@
<div style="margin: 2em 0; height: 2px; background-color: white"></div>
<!-- info dispatcher -->
<SceneryInfoDispatcher :station="station" :onlineFrom="onlineFrom" />
<SceneryInfoDispatcher :onlineScenery="onlineScenery" />
<div class="info-lists">
<!-- user list -->
<SceneryInfoUserList :station="station" />
<SceneryInfoUserList :onlineScenery="onlineScenery" />
<!-- spawn list -->
<SceneryInfoSpawnList :station="station" />
<SceneryInfoSpawnList :onlineScenery="onlineScenery" />
</div>
</section>
</div>
@@ -90,6 +90,7 @@ import SceneryInfoUserList from './SceneryInfo/SceneryInfoUserList.vue';
import SceneryInfoSpawnList from './SceneryInfo/SceneryInfoSpawnList.vue';
import SceneryInfoRoutes from './SceneryInfo/SceneryInfoRoutes.vue';
import Station from '../../scripts/interfaces/Station';
import { OnlineScenery } from '../../scripts/interfaces/store/storeTypes';
export default defineComponent({
components: {
@@ -105,12 +106,11 @@ export default defineComponent({
required: true
},
timetableOnly: Boolean
onlineScenery: {
type: Object as PropType<OnlineScenery>,
required: false
}
},
data: () => ({
onlineFrom: -1
})
});
</script>
@@ -1,35 +1,30 @@
<template>
<section class="info-dispatcher">
<div class="dispatcher" v-if="station.onlineInfo">
<div class="dispatcher" v-if="onlineScenery">
<span
class="dispatcher_level"
:style="
calculateExpStyle(
station.onlineInfo.dispatcherExp,
station.onlineInfo.dispatcherIsSupporter
)
"
:style="calculateExpStyle(onlineScenery.dispatcherExp, onlineScenery.dispatcherIsSupporter)"
>
{{ station.onlineInfo.dispatcherExp > 1 ? station.onlineInfo.dispatcherExp : 'L' }}
{{ onlineScenery.dispatcherExp > 1 ? onlineScenery.dispatcherExp : 'L' }}
</span>
<router-link
class="dispatcher_name"
:to="`/journal/dispatchers?dispatcherName=${station.onlineInfo.dispatcherName}`"
:to="`/journal/dispatchers?dispatcherName=${onlineScenery.dispatcherName}`"
>
{{ station.onlineInfo.dispatcherName }}
{{ onlineScenery.dispatcherName }}
</router-link>
<span class="dispatcher_likes text--primary">
<img src="/images/icon-like.svg" alt="Likes count icon" />
<span>{{ station.onlineInfo?.dispatcherRate || '0' }}</span>
<span>{{ onlineScenery?.dispatcherRate || '0' }}</span>
</span>
</div>
<StationStatusBadge
:statusID="station.onlineInfo?.statusID"
:isOnline="station.onlineInfo ? true : false"
:statusTimestamp="station.onlineInfo?.statusTimestamp"
:statusID="onlineScenery?.statusID"
:isOnline="onlineScenery ? true : false"
:statusTimestamp="onlineScenery?.statusTimestamp"
/>
</section>
</template>
@@ -39,19 +34,15 @@ import { PropType, defineComponent } from 'vue';
import dateMixin from '../../../mixins/dateMixin';
import routerMixin from '../../../mixins/routerMixin';
import styleMixin from '../../../mixins/styleMixin';
import Station from '../../../scripts/interfaces/Station';
import StationStatusBadge from '../../Global/StationStatusBadge.vue';
import { OnlineScenery } from '../../../scripts/interfaces/store/storeTypes';
export default defineComponent({
mixins: [styleMixin, dateMixin, routerMixin],
props: {
station: {
type: Object as PropType<Station>,
required: true
},
onlineFrom: {
type: Number,
default: -1
onlineScenery: {
type: Object as PropType<OnlineScenery>,
required: false
}
},
components: { StationStatusBadge }
@@ -3,14 +3,14 @@
<h3 class="spawn-header section-header">
<img src="/images/icon-spawn.svg" alt="Open spawns icon" />
&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>
<span v-if="station.onlineInfo">
<span v-if="onlineScenery">
<span
class="badge spawn"
v-for="(spawn, i) in sortedSpawns"
:key="spawn.spawnName + station.onlineInfo?.dispatcherName + i"
:key="spawn.spawnName + onlineScenery?.dispatcherName + i"
:data-electrified="spawn.isElectrified"
>
<span class="spawn_name">{{ spawn.spawnName }}</span>
@@ -18,9 +18,7 @@
</span>
</span>
<span
class="badge spawn badge-none"
v-if="!station.onlineInfo || station.onlineInfo.spawns.length == 0"
<span class="badge spawn badge-none" v-if="!onlineScenery || onlineScenery.spawns.length == 0"
>{{ $t('scenery.no-spawns') }}
</span>
</section>
@@ -28,21 +26,21 @@
<script lang="ts">
import { PropType, defineComponent } from 'vue';
import Station from '../../../scripts/interfaces/Station';
import { OnlineScenery } from '../../../scripts/interfaces/store/storeTypes';
export default defineComponent({
props: {
station: {
type: Object as PropType<Station>,
required: true
onlineScenery: {
type: Object as PropType<OnlineScenery>,
required: false
}
},
computed: {
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
);
}
@@ -3,12 +3,12 @@
<h3 class="user-header section-header">
<img src="/images/icon-user.svg" alt="Users icon" />
&nbsp;{{ $t('scenery.users') }} &nbsp;
<span class="text--primary">{{ station.onlineInfo?.currentUsers || '0' }}</span
>&nbsp;/&nbsp;<span class="text--primary">{{ station.onlineInfo?.maxUsers || '0' }}</span>
<span class="text--primary">{{ onlineScenery?.currentUsers || 0 }}</span
>&nbsp;/&nbsp;<span class="text--primary">{{ onlineScenery?.maxUsers || 0 }}</span>
</h3>
<div
v-for="train in computedStationTrains"
v-for="train in onlineScenery?.stationTrains"
class="badge user"
:class="train.stopStatus"
:key="train.trainId"
@@ -22,7 +22,7 @@
<div
class="badge user badge-none"
v-if="!computedStationTrains || computedStationTrains.length == 0"
v-if="!onlineScenery?.scheduledTrains || onlineScenery.scheduledTrains.length == 0"
>
{{ $t('scenery.no-users') }}
</div>
@@ -30,45 +30,19 @@
</template>
<script lang="ts">
import { PropType, computed, defineComponent } from 'vue';
import { PropType, defineComponent } from 'vue';
import modalTrainMixin from '../../../mixins/modalTrainMixin';
import routerMixin from '../../../mixins/routerMixin';
import Station from '../../../scripts/interfaces/Station';
import { useStore } from '../../../store/store';
import { OnlineScenery } from '../../../scripts/interfaces/store/storeTypes';
export default defineComponent({
mixins: [routerMixin, modalTrainMixin],
props: {
station: {
type: Object as PropType<Station>,
required: true
onlineScenery: {
type: Object as PropType<OnlineScenery>,
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>
+30 -48
View File
@@ -6,14 +6,12 @@
<span>{{ $t('scenery.timetables') }}</span>
<span>
<span class="text--primary">{{
station.onlineInfo?.scheduledTrains?.length || '0'
}}</span>
<span class="text--primary">{{ onlineScenery?.scheduledTrains?.length || '0' }}</span>
<span> / </span>
<span class="text--grayed">
{{
station.onlineInfo?.scheduledTrains?.filter((train) => train.stopInfo.confirmed)
.length || '0'
onlineScenery?.scheduledTrains?.filter((train) => train.stopInfo.confirmed).length ||
'0'
}}
</span>
</span>
@@ -59,7 +57,7 @@
<span
class="timetable-item empty"
v-else-if="computedScheduledTrains.length == 0 && !station.onlineInfo"
v-else-if="computedScheduledTrains.length == 0 && !onlineScenery"
>
{{ $t('scenery.offline') }}
</span>
@@ -186,6 +184,7 @@ import Station from '../../scripts/interfaces/Station';
import { useStore } from '../../store/store';
import modalTrainMixin from '../../mixins/modalTrainMixin';
import ScheduledTrainStatus from './ScheduledTrainStatus.vue';
import { OnlineScenery } from '../../scripts/interfaces/store/storeTypes';
export default defineComponent({
name: 'SceneryTimetable',
@@ -199,9 +198,9 @@ export default defineComponent({
type: Object as PropType<Station>,
required: true
},
timetableOnly: {
type: Boolean
onlineScenery: {
type: Object as PropType<OnlineScenery>,
required: false
}
},
@@ -229,36 +228,9 @@ export default defineComponent({
: 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 {
currentURL,
chosenCheckpoint,
computedScheduledTrains,
store
};
},
@@ -269,27 +241,37 @@ export default defineComponent({
if (this.chosenCheckpoint) url += `&checkpoint=${this.chosenCheckpoint}`;
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: {
loadSelectedOption() {
if (!this.station) return;
if (!this.station.generalInfo) return;
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;
this.chosenCheckpoint =
this.station.generalInfo?.checkpoints[0]?.checkpointName || this.station.name;
},
setCheckpoint(cp: { checkpointName: string }) {
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 Loading from '../Global/Loading.vue';
import listObserverMixin from '../../mixins/listObserverMixin';
import { OnlineScenery } from '../../scripts/interfaces/store/storeTypes';
export default defineComponent({
name: 'SceneryTimetablesHistory',
@@ -73,6 +74,10 @@ export default defineComponent({
station: {
type: Object as PropType<Station>,
required: true
},
onlineScenery: {
type: Object as PropType<OnlineScenery>,
required: false
}
},
+2 -2
View File
@@ -1,8 +1,8 @@
<template>
<label @dblclick="handleDbClick">
<input
:value="optionValue"
@input="$emit('update:optionValue', ($event.target as HTMLInputElement).value)"
:checked="optionValue"
@input="$emit('update:optionValue', ($event.target as HTMLInputElement).checked)"
type="checkbox"
:class="option.section"
:name="option.id"
+23 -8
View File
@@ -79,11 +79,19 @@
</span>
<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 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 v-else>
@@ -234,7 +242,7 @@
</td>
<td
class="station_schedules"
class="station_schedules all"
style="width: 30px"
:class="{ inactive: !station.onlineInfo }"
>
@@ -244,20 +252,23 @@
</td>
<td
class="station_schedules"
class="station_schedules unconfirmed"
style="width: 30px"
:class="{ inactive: !station.onlineInfo }"
>
<span style="color: #ccc">
{{
station.onlineInfo?.scheduledTrains?.filter((train) => !train.stopInfo.confirmed)
.length || 0
new Set([
...(station.onlineInfo?.scheduledTrains
?.filter((train) => !train.stopInfo.confirmed)
.map((train) => train.checkpointName) || [])
]).size || 0
}}
</span>
</td>
<td
class="station_schedules"
class="station_schedules confirmed"
style="width: 30px"
:class="{ inactive: !station.onlineInfo }"
>
@@ -336,9 +347,13 @@ export default defineComponent({
if (!station) return;
this.lastSelectedStationName = station.name;
this.$router.push({
name: 'SceneryView',
query: { station: station.name.replaceAll(' ', '_') }
query: {
station: station.name.replaceAll(' ', '_'),
region: this.$route.query.region || undefined
}
});
},