mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 05:18:11 +00:00
Synchronizacja danych z API przez websockety
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div class="train-filters">
|
||||
<span v-for="category in availableCategories" :key="category">
|
||||
{{ category }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Train from '@/scripts/interfaces/Train';
|
||||
import { computed, defineComponent, inject, TrainFilter } from 'vue';
|
||||
|
||||
const defaultFilters: TrainFilter[] = [
|
||||
{
|
||||
id: "comments",
|
||||
isActive: true
|
||||
},
|
||||
{
|
||||
id: "twr",
|
||||
isActive: true
|
||||
},
|
||||
{
|
||||
id: "skr",
|
||||
isActive: true
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
trainList: {
|
||||
type: Object as () => Train[],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
setup(props) {
|
||||
const filters = inject('filtersActive') as TrainFilter[];
|
||||
const trainList = props.trainList;
|
||||
|
||||
|
||||
// Setup default train filters
|
||||
filters.push(...defaultFilters);
|
||||
|
||||
const availableCategories = computed(() => trainList.reduce((acc, train) => {
|
||||
if(!train.timetableData) return acc;
|
||||
if(acc.includes(train.timetableData.category)) return acc;
|
||||
|
||||
acc.push(train.timetableData.category);
|
||||
return acc;
|
||||
}, [] as string[]));
|
||||
|
||||
|
||||
// Remove unavailable train categories
|
||||
for(let filter of filters) {
|
||||
if(availableCategories.value.includes(filter.id)) continue;
|
||||
|
||||
filters.slice(filters.indexOf(filter), -1);
|
||||
}
|
||||
|
||||
return {
|
||||
availableCategories
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
+3
-4
@@ -1,13 +1,13 @@
|
||||
import { createApp, Directive, ref } from 'vue'
|
||||
import { createApp, Directive } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import { store, key } from './store'
|
||||
|
||||
import enLang from '@/locales/en.json';
|
||||
import plLang from '@/locales/pl.json';
|
||||
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
|
||||
const i18n = createI18n({
|
||||
locale: 'pl',
|
||||
fallbackLocale: 'pl',
|
||||
@@ -18,6 +18,7 @@ const i18n = createI18n({
|
||||
enableLegacy: false,
|
||||
})
|
||||
|
||||
|
||||
const clickOutsideDirective: Directive = {
|
||||
mounted(el, binding) {
|
||||
|
||||
@@ -32,9 +33,7 @@ const clickOutsideDirective: Directive = {
|
||||
}
|
||||
|
||||
createApp(App)
|
||||
.use(store, key)
|
||||
.use(router)
|
||||
.use(i18n)
|
||||
.provide('isFilterCardVisible', ref(false))
|
||||
.directive('click-outside', clickOutsideDirective)
|
||||
.mount('#app')
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import { TrainFilter } from "vue";
|
||||
import Train from "../interfaces/Train";
|
||||
import TrainStop from "../interfaces/TrainStop";
|
||||
|
||||
function confirmedPercentage(stops: TrainStop[] | undefined) {
|
||||
if (!stops) return -1;
|
||||
|
||||
return Number(((stops.filter((stop) => stop.confirmed).length / stops.length) * 100).toFixed(0));
|
||||
};
|
||||
|
||||
function currentDelay(stops: TrainStop[] | undefined) {
|
||||
if (!stops) return -Infinity;
|
||||
|
||||
const delay =
|
||||
stops.find((stop, i) => (i == 0 && !stop.confirmed) || (i > 0 && stops[i - 1].confirmed && !stop.confirmed))
|
||||
?.departureDelay || 0;
|
||||
|
||||
return delay;
|
||||
};
|
||||
|
||||
function filterTrainList(trainList: Train[], searchedTrain: string, searchedDriver: string, filters: TrainFilter[]) {
|
||||
console.log(filters);
|
||||
|
||||
return trainList.filter(
|
||||
(train) =>
|
||||
(searchedTrain.length > 0 ? train.trainNo.toString().startsWith(searchedTrain) : true) &&
|
||||
(searchedDriver.length > 0 ? train.driverName.toLowerCase().startsWith(searchedDriver.toLowerCase()) : true)
|
||||
);
|
||||
}
|
||||
|
||||
function sortTrainList(trainList: Train[], sorterActive: { id: string; dir: number }) {
|
||||
return trainList.sort((a: Train, b: Train) => {
|
||||
switch (sorterActive.id) {
|
||||
case 'mass':
|
||||
if (a.mass > b.mass) return sorterActive.dir;
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'distance':
|
||||
if ((a.timetableData?.routeDistance || -1) > (b.timetableData?.routeDistance || -1)) return sorterActive.dir;
|
||||
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'progress':
|
||||
if (confirmedPercentage(a.timetableData?.followingStops) > confirmedPercentage(b.timetableData?.followingStops))
|
||||
return sorterActive.dir;
|
||||
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'delay':
|
||||
if (currentDelay(a.timetableData?.followingStops) > currentDelay(b.timetableData?.followingStops))
|
||||
return sorterActive.dir;
|
||||
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'speed':
|
||||
if (a.speed > b.speed) return sorterActive.dir;
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'timetable':
|
||||
if (a.trainNo > b.trainNo) return sorterActive.dir;
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'length':
|
||||
if (a.length > b.length) return sorterActive.dir;
|
||||
return -sorterActive.dir;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
export function filteredTrainList(
|
||||
trainList: Train[],
|
||||
searchedTrain: string,
|
||||
searchedDriver: string,
|
||||
sorterActive: { id: string; dir: number },
|
||||
filters: TrainFilter[]
|
||||
) {
|
||||
let finalTrainList: Train[] = [];
|
||||
|
||||
const filtered = filterTrainList(trainList, searchedTrain, searchedDriver, filters);
|
||||
|
||||
switch (sorterActive.id) {
|
||||
case 'comments':
|
||||
const trainsSortedByComments = filtered
|
||||
.sort((a, b) => {
|
||||
const commentsA = a.timetableData?.followingStops.some((s) => s.comments) ? 1 : 0;
|
||||
const commentsB = b.timetableData?.followingStops.some((s) => s.comments) ? 1 : 0;
|
||||
|
||||
return commentsB - commentsA;
|
||||
});
|
||||
|
||||
const trainsWithComments = trainsSortedByComments.filter((train) =>
|
||||
train.timetableData?.followingStops.some((s) => s.comments)
|
||||
);
|
||||
|
||||
const trainsWithoutComments = trainsSortedByComments.slice(trainsWithComments.length);
|
||||
|
||||
finalTrainList.push(...trainsWithComments);
|
||||
finalTrainList.push(...sortTrainList(trainsWithoutComments, sorterActive));
|
||||
break;
|
||||
|
||||
default:
|
||||
finalTrainList.push(...sortTrainList(filtered, sorterActive));
|
||||
break;
|
||||
}
|
||||
|
||||
return finalTrainList;
|
||||
};
|
||||
+10
-4
@@ -20,14 +20,17 @@ import { getLocoURL, getScheduledTrain, getStatusID, getStatusTimestamp, parseSp
|
||||
import { URLs } from '@/scripts/utils/apiURLs';
|
||||
import ScheduledTrain from '@/scripts/interfaces/ScheduledTrain';
|
||||
import StationRoutes from '@/scripts/interfaces/StationRoutes';
|
||||
import StorageManager from '@/scripts/managers/storageManager';
|
||||
import { connect } from 'socket.io-client';
|
||||
|
||||
// Websocket config
|
||||
const socket = connect(process.env.NODE_ENV === 'production' ? URLs.stacjownikAPI : URLs.stacjownikAPIDev)
|
||||
socket.emit('connection');
|
||||
|
||||
export interface State {
|
||||
stationList: Station[],
|
||||
trainList: Train[],
|
||||
|
||||
lastDispatcherStatuses: { hash: string; statusTimestamp: number; statusID: string; }[],
|
||||
// timetableList: Timetable[],
|
||||
|
||||
sceneryData: any[][],
|
||||
|
||||
@@ -119,9 +122,12 @@ export const store = createStore<State>({
|
||||
if (state.listenerLaunched) return;
|
||||
|
||||
await dispatch(ACTIONS.loadStaticStationData);
|
||||
dispatch(ACTIONS.fetchOnlineData);
|
||||
|
||||
setInterval(() => dispatch(ACTIONS.fetchOnlineData), Math.floor(Math.random() * 5000) + 25000);
|
||||
socket.on('DATA_UPDATE', () => {
|
||||
dispatch(ACTIONS.fetchOnlineData);
|
||||
});
|
||||
|
||||
// setInterval(() => dispatch(ACTIONS.fetchOnlineData), Math.floor(Math.random() * 5000) + 25000);
|
||||
},
|
||||
|
||||
async loadStaticStationData({ commit }) {
|
||||
|
||||
+6
-111
@@ -13,9 +13,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, ComputedRef, defineComponent, provide, reactive, ref } from 'vue';
|
||||
import { computed, ComputedRef, defineComponent, provide, reactive, ref, TrainFilter } from 'vue';
|
||||
import { filteredTrainList } from "@/scripts/managers/trainFilterManager";
|
||||
|
||||
import { DataStatus } from '@/scripts/enums/DataStatus';
|
||||
import Train from '@/scripts/interfaces/Train';
|
||||
|
||||
import TrainTable from '@/components/TrainsView/TrainTable.vue';
|
||||
@@ -24,114 +24,7 @@ import TrainOptions from '@/components/TrainsView/TrainOptions.vue';
|
||||
|
||||
import { useStore } from '@/store';
|
||||
import { GETTERS } from '@/constants/storeConstants';
|
||||
import TrainStop from '@/scripts/interfaces/TrainStop';
|
||||
|
||||
const confirmedPercentage = (stops: TrainStop[] | undefined) => {
|
||||
if (!stops) return -1;
|
||||
|
||||
return Number(((stops.filter((stop) => stop.confirmed).length / stops.length) * 100).toFixed(0));
|
||||
};
|
||||
|
||||
const currentDelay = (stops: TrainStop[] | undefined) => {
|
||||
if (!stops) return -Infinity;
|
||||
|
||||
const delay =
|
||||
stops.find((stop, i) => (i == 0 && !stop.confirmed) || (i > 0 && stops[i - 1].confirmed && !stop.confirmed))
|
||||
?.departureDelay || 0;
|
||||
|
||||
return delay;
|
||||
};
|
||||
|
||||
function filterTrainList(trainList: Train[], searchedTrain: string, searchedDriver: string) {
|
||||
return trainList.filter(
|
||||
(train) =>
|
||||
(searchedTrain.length > 0 ? train.trainNo.toString().startsWith(searchedTrain) : true) &&
|
||||
(searchedDriver.length > 0 ? train.driverName.toLowerCase().startsWith(searchedDriver.toLowerCase()) : true)
|
||||
);
|
||||
}
|
||||
|
||||
function sortTrainList(trainList: Train[], sorterActive: { id: string; dir: number }) {
|
||||
return trainList.sort((a: Train, b: Train) => {
|
||||
switch (sorterActive.id) {
|
||||
case 'mass':
|
||||
if (a.mass > b.mass) return sorterActive.dir;
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'distance':
|
||||
if ((a.timetableData?.routeDistance || -1) > (b.timetableData?.routeDistance || -1)) return sorterActive.dir;
|
||||
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'progress':
|
||||
if (confirmedPercentage(a.timetableData?.followingStops) > confirmedPercentage(b.timetableData?.followingStops))
|
||||
return sorterActive.dir;
|
||||
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'delay':
|
||||
if (currentDelay(a.timetableData?.followingStops) > currentDelay(b.timetableData?.followingStops))
|
||||
return sorterActive.dir;
|
||||
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'speed':
|
||||
if (a.speed > b.speed) return sorterActive.dir;
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'timetable':
|
||||
if (a.trainNo > b.trainNo) return sorterActive.dir;
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'length':
|
||||
if (a.length > b.length) return sorterActive.dir;
|
||||
return -sorterActive.dir;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
const filteredTrainList = (
|
||||
trainList: Train[],
|
||||
searchedTrain: string,
|
||||
searchedDriver: string,
|
||||
sorterActive: { id: string; dir: number },
|
||||
priorityProp: string
|
||||
) => {
|
||||
let finalTrainList: Train[] = [];
|
||||
|
||||
const filtered = filterTrainList(trainList, searchedTrain, searchedDriver);
|
||||
|
||||
switch (sorterActive.id) {
|
||||
case 'comments':
|
||||
const trainsSortedByComments = filtered
|
||||
.sort((a, b) => {
|
||||
const commentsA = a.timetableData?.followingStops.some((s) => s.comments) ? 1 : 0;
|
||||
const commentsB = b.timetableData?.followingStops.some((s) => s.comments) ? 1 : 0;
|
||||
|
||||
return commentsB - commentsA;
|
||||
});
|
||||
|
||||
const trainsWithComments = trainsSortedByComments.filter((train) =>
|
||||
train.timetableData?.followingStops.some((s) => s.comments)
|
||||
);
|
||||
|
||||
const trainsWithoutComments = trainsSortedByComments.slice(trainsWithComments.length);
|
||||
|
||||
finalTrainList.push(...trainsWithComments);
|
||||
finalTrainList.push(...sortTrainList(trainsWithoutComments, sorterActive));
|
||||
break;
|
||||
|
||||
default:
|
||||
finalTrainList.push(...sortTrainList(filtered, sorterActive));
|
||||
break;
|
||||
}
|
||||
|
||||
return finalTrainList;
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@@ -155,13 +48,15 @@ export default defineComponent({
|
||||
// const timetableDataStatus: ComputedRef<DataStatus> = computed(() => store.getters[GETTERS.timetableDataStatus]);
|
||||
|
||||
const sorterActive = ref({ id: 'distance', dir: -1 });
|
||||
const filtersActive = reactive([]) as TrainFilter[];
|
||||
|
||||
const searchedDriver = ref('');
|
||||
const searchedTrain = ref('');
|
||||
const priorityProp = ref('');
|
||||
|
||||
provide('searchedTrain', searchedTrain);
|
||||
provide('searchedDriver', searchedDriver);
|
||||
provide('sorterActive', sorterActive);
|
||||
provide('filtersActive', filtersActive);
|
||||
|
||||
const computedTrains: ComputedRef<Train[]> = computed(() => {
|
||||
return filteredTrainList(
|
||||
@@ -169,7 +64,7 @@ export default defineComponent({
|
||||
searchedTrain.value,
|
||||
searchedDriver.value,
|
||||
sorterActive.value,
|
||||
priorityProp.value
|
||||
filtersActive
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
Vendored
+6
@@ -11,4 +11,10 @@ declare module '@vue/runtime-core' {
|
||||
interface ComponentCustomProperties {
|
||||
$store: Store<State>
|
||||
}
|
||||
|
||||
// Train filter for TrainView
|
||||
interface TrainFilter {
|
||||
id: string;
|
||||
isActive: boolean;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user