move departures data to vuex
This commit is contained in:
parent
f29fb2206e
commit
73134a0e01
@ -39,9 +39,6 @@ Vue.use(Vuex);
|
|||||||
stops: [],
|
stops: [],
|
||||||
sections: {
|
sections: {
|
||||||
messages: true
|
messages: true
|
||||||
},
|
|
||||||
departures: {
|
|
||||||
state: ''
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -51,11 +48,22 @@ Vue.use(Vuex);
|
|||||||
counts: this.$store.getters['messages/counts'],
|
counts: this.$store.getters['messages/counts'],
|
||||||
state: this.$store.state.messages.state
|
state: this.$store.state.messages.state
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
departures(this: any) {
|
||||||
|
return {
|
||||||
|
state: this.$store.state.departures.state
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
stops(this: any, stops) {
|
||||||
|
this.updateDepartures({ stops });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
updateMessages: 'messages/update'
|
updateMessages: 'messages/update',
|
||||||
|
updateDepartures: 'departures/update'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,65 +1,18 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import { Departure, Stop } from "../model";
|
import { Departure, Stop } from "../model";
|
||||||
import { Component, Prop, Watch } from "vue-property-decorator";
|
import { Component, Prop, Watch } from "vue-property-decorator";
|
||||||
import urls from '../urls';
|
import { namespace } from 'vuex-class';
|
||||||
import * as moment from "moment";
|
import store from '../store'
|
||||||
import { FetchingState, Jsonified } from "../utils";
|
|
||||||
import { debounce, notify } from "../decorators";
|
|
||||||
|
|
||||||
@Component({ template: require("../../components/departures.html") })
|
const { State } = namespace('departures');
|
||||||
|
|
||||||
|
@Component({ template: require("../../components/departures.html"), store })
|
||||||
export class Departures extends Vue {
|
export class Departures extends Vue {
|
||||||
private _intervalId: number;
|
@State
|
||||||
|
departures: Departure[];
|
||||||
departures: Departure[] = [];
|
|
||||||
|
|
||||||
@Prop(Array)
|
@Prop(Array)
|
||||||
stops: Stop[];
|
stops: Stop[];
|
||||||
|
|
||||||
@Prop({ default: false, type: Boolean })
|
|
||||||
autoRefresh: boolean;
|
|
||||||
|
|
||||||
@Prop({ default: 20, type: Number })
|
|
||||||
interval: number;
|
|
||||||
|
|
||||||
@notify()
|
|
||||||
state: FetchingState;
|
|
||||||
|
|
||||||
@Watch('stops')
|
|
||||||
@debounce(300)
|
|
||||||
async update() {
|
|
||||||
this.state = 'fetching';
|
|
||||||
const response = await fetch(urls.prepare(urls.departures, {
|
|
||||||
stop: this.stops.map(stop => stop.id),
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
const departures = await response.json() as Jsonified<Departure>[];
|
|
||||||
|
|
||||||
this.departures = departures.map(departure => {
|
|
||||||
departure.scheduled = moment.parseZone(departure.scheduled);
|
|
||||||
departure.estimated = moment.parseZone(departure.estimated);
|
|
||||||
|
|
||||||
return departure as Departure;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.state = 'ready';
|
|
||||||
} else {
|
|
||||||
this.state = 'error';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Watch('interval')
|
|
||||||
@Watch('autoRefresh')
|
|
||||||
private setupAutoRefresh() {
|
|
||||||
if (this._intervalId) {
|
|
||||||
window.clearInterval(this._intervalId);
|
|
||||||
this._intervalId = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.autoRefresh) {
|
|
||||||
this._intervalId = window.setInterval(() => this.update(), this.interval * 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.component('Departures', Departures);
|
Vue.component('Departures', Departures);
|
3
resources/ts/model/error.ts
Normal file
3
resources/ts/model/error.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export interface Error {
|
||||||
|
message: string;
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
export * from './stop'
|
export * from './stop'
|
||||||
export * from './departure'
|
export * from './departure'
|
||||||
export * from './line'
|
export * from './line'
|
||||||
|
export * from './error'
|
27
resources/ts/store/common.ts
Normal file
27
resources/ts/store/common.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { FetchingState } from "../utils";
|
||||||
|
import { Moment } from "moment";
|
||||||
|
import { Module, MutationTree } from "vuex";
|
||||||
|
import { RootState } from "./root";
|
||||||
|
import * as moment from "moment";
|
||||||
|
|
||||||
|
export interface CommonState {
|
||||||
|
state: FetchingState,
|
||||||
|
lastUpdate: Moment,
|
||||||
|
error: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const state: CommonState = {
|
||||||
|
state: "not-initialized",
|
||||||
|
error: "",
|
||||||
|
lastUpdate: moment()
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mutations: MutationTree<CommonState> = {
|
||||||
|
fetching: (state) => state.state = 'fetching',
|
||||||
|
error: (state, error) => {
|
||||||
|
state.state = 'error';
|
||||||
|
state.error = error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default { state, mutations };
|
56
resources/ts/store/departures.ts
Normal file
56
resources/ts/store/departures.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { Module } from "vuex";
|
||||||
|
import { RootState } from "./root";
|
||||||
|
import { Departure, Stop } from "../model";
|
||||||
|
import * as moment from 'moment'
|
||||||
|
import common, { CommonState } from './common'
|
||||||
|
import urls from "../urls";
|
||||||
|
import { Jsonified } from "../utils";
|
||||||
|
|
||||||
|
export interface DeparturesState extends CommonState {
|
||||||
|
departures: Departure[],
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ObtainPayload {
|
||||||
|
stops: Stop[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const departures: Module<DeparturesState, RootState> = {
|
||||||
|
namespaced: true,
|
||||||
|
state: {
|
||||||
|
departures: [ ],
|
||||||
|
...common.state
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
update: (state, departures) => {
|
||||||
|
state.departures = departures;
|
||||||
|
state.lastUpdate = moment();
|
||||||
|
state.state = 'ready';
|
||||||
|
},
|
||||||
|
...common.mutations
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
async update({ commit }, { stops }: ObtainPayload) {
|
||||||
|
commit('fetching');
|
||||||
|
const response = await fetch(urls.prepare(urls.departures, {
|
||||||
|
stop: stops.map(stop => stop.id),
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const error = await response.json() as Error;
|
||||||
|
commit('error', error.message);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const departures = await response.json() as Jsonified<Departure>[];
|
||||||
|
commit('update', departures.map(departure => {
|
||||||
|
departure.scheduled = moment.parseZone(departure.scheduled);
|
||||||
|
departure.estimated = moment.parseZone(departure.estimated);
|
||||||
|
|
||||||
|
return departure as Departure;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default departures;
|
@ -1,7 +1,8 @@
|
|||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
|
|
||||||
import { messages } from './messages';
|
import messages from './messages';
|
||||||
|
import departures from './departures';
|
||||||
|
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
modules: { messages }
|
modules: { messages, departures }
|
||||||
})
|
})
|
@ -1,24 +1,20 @@
|
|||||||
import { ActionContext, Module, Store } from "vuex";
|
import { ActionContext, Module } from "vuex";
|
||||||
import { RootState } from "./root";
|
import { RootState } from "./root";
|
||||||
import { Message, MessageType } from "../model/message";
|
import { Message, MessageType } from "../model/message";
|
||||||
|
import common, { CommonState } from "./common";
|
||||||
import urls from "../urls";
|
import urls from "../urls";
|
||||||
import { FetchingState, Jsonified } from "../utils";
|
import { Jsonified } from "../utils";
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import { Moment } from "moment";
|
|
||||||
|
|
||||||
export interface MessagesState {
|
export interface MessagesState extends CommonState {
|
||||||
messages: Message[],
|
messages: Message[]
|
||||||
state: FetchingState,
|
|
||||||
lastUpdate: Moment
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const messages: Module<MessagesState, RootState> = {
|
export const messages: Module<MessagesState, RootState> = {
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
state: {
|
state: {
|
||||||
messages: [],
|
messages: [],
|
||||||
state: "not-initialized",
|
...common.state,
|
||||||
lastUpdate: moment()
|
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
count: state => state.messages.length,
|
count: state => state.messages.length,
|
||||||
@ -34,8 +30,7 @@ export const messages: Module<MessagesState, RootState> = {
|
|||||||
state.lastUpdate = moment();
|
state.lastUpdate = moment();
|
||||||
state.state = 'ready';
|
state.state = 'ready';
|
||||||
},
|
},
|
||||||
fetching: (state: MessagesState) => state.state = 'fetching',
|
...common.mutations
|
||||||
error: (state: MessagesState, error) => state.state = 'error',
|
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async update({ commit }: ActionContext<MessagesState, RootState>) {
|
async update({ commit }: ActionContext<MessagesState, RootState>) {
|
||||||
@ -44,7 +39,8 @@ export const messages: Module<MessagesState, RootState> = {
|
|||||||
const response = await fetch(urls.prepare(urls.messages));
|
const response = await fetch(urls.prepare(urls.messages));
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
commit('error', await response.json());
|
const error = await response.json() as Error;
|
||||||
|
commit('error', error.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,3 +56,5 @@ export const messages: Module<MessagesState, RootState> = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default messages;
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Controller\Api\v1;
|
namespace App\Controller\Api\v1;
|
||||||
|
|
||||||
use App\Controller\Controller;
|
use App\Controller\Controller;
|
||||||
|
use App\Model\Stop;
|
||||||
use App\Provider\TrackRepository;
|
use App\Provider\TrackRepository;
|
||||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
@ -40,14 +41,16 @@ class TracksController extends Controller
|
|||||||
|
|
||||||
private function byStop(Request $request, TrackRepository $repository)
|
private function byStop(Request $request, TrackRepository $repository)
|
||||||
{
|
{
|
||||||
$stop = encapsulate($request->query->get('stop'));
|
$stop = $request->query->get('stop');
|
||||||
|
$stop = array_map([Stop::class, 'reference'], encapsulate($stop));
|
||||||
|
|
||||||
return $this->json($repository->getByStop($stop));
|
return $this->json($repository->getByStop($stop));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function byLine(Request $request, TrackRepository $repository)
|
private function byLine(Request $request, TrackRepository $repository)
|
||||||
{
|
{
|
||||||
$line = encapsulate($request->query->get('line'));
|
$line = $request->query->get('line');
|
||||||
|
$line = array_map([Stop::class, 'reference'], encapsulate($line));
|
||||||
|
|
||||||
return $this->json($repository->getByLine($line));
|
return $this->json($repository->getByLine($line));
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,6 @@ class GenericTrackRepository extends DatabaseRepository implements TrackReposito
|
|||||||
public function getByStop($stop): Collection
|
public function getByStop($stop): Collection
|
||||||
{
|
{
|
||||||
$reference = f\apply(f\ref([$this, 'reference']), StopEntity::class);
|
$reference = f\apply(f\ref([$this, 'reference']), StopEntity::class);
|
||||||
$stop = array_map([Stop::class, 'reference'], encapsulate($stop));
|
|
||||||
|
|
||||||
$tracks = $this->em->createQueryBuilder()
|
$tracks = $this->em->createQueryBuilder()
|
||||||
->from(StopInTrack::class, 'st')
|
->from(StopInTrack::class, 'st')
|
||||||
@ -43,7 +42,7 @@ class GenericTrackRepository extends DatabaseRepository implements TrackReposito
|
|||||||
->where('st.stop in (:stop)')
|
->where('st.stop in (:stop)')
|
||||||
->select(['st', 't'])
|
->select(['st', 't'])
|
||||||
->getQuery()
|
->getQuery()
|
||||||
->execute(['stop' => array_map($reference, $stop)]);
|
->execute(['stop' => array_map($reference, encapsulate($stop))]);
|
||||||
|
|
||||||
return collect($tracks)->map(function (StopInTrack $entity) {
|
return collect($tracks)->map(function (StopInTrack $entity) {
|
||||||
return [ $this->convert($entity->getTrack()), $entity->getOrder() ];
|
return [ $this->convert($entity->getTrack()), $entity->getOrder() ];
|
||||||
@ -53,7 +52,6 @@ class GenericTrackRepository extends DatabaseRepository implements TrackReposito
|
|||||||
public function getByLine($line): Collection
|
public function getByLine($line): Collection
|
||||||
{
|
{
|
||||||
$reference = f\apply(f\ref([$this, 'reference']), LineEntity::class);
|
$reference = f\apply(f\ref([$this, 'reference']), LineEntity::class);
|
||||||
$line = array_map([Stop::class, 'reference'], encapsulate($line));
|
|
||||||
|
|
||||||
$tracks = $this->em->createQueryBuilder()
|
$tracks = $this->em->createQueryBuilder()
|
||||||
->from(StopInTrack::class, 'st')
|
->from(StopInTrack::class, 'st')
|
||||||
@ -62,7 +60,7 @@ class GenericTrackRepository extends DatabaseRepository implements TrackReposito
|
|||||||
->where('st.line in (:line)')
|
->where('st.line in (:line)')
|
||||||
->select(['st', 't', 's'])
|
->select(['st', 't', 's'])
|
||||||
->getQuery()
|
->getQuery()
|
||||||
->execute(['stop' => array_map($reference, $line)]);
|
->execute(['stop' => array_map($reference, encapsulate($line))]);
|
||||||
|
|
||||||
return collect($tracks)->map(f\ref([$this, 'convert']));
|
return collect($tracks)->map(f\ref([$this, 'convert']));
|
||||||
}
|
}
|
||||||
|
@ -25,13 +25,11 @@
|
|||||||
<h2 class="section__title flex">
|
<h2 class="section__title flex">
|
||||||
<fa :icon="['fal', 'clock']" fixed-width class="mr-1"></fa>
|
<fa :icon="['fal', 'clock']" fixed-width class="mr-1"></fa>
|
||||||
Odjazdy
|
Odjazdy
|
||||||
<button class="btn btn-action flex-space-left" @click="$refs.departures.update()">
|
<button class="btn btn-action flex-space-left" @click="updateDepartures({ stops })">
|
||||||
<fa-layers>
|
|
||||||
<fa :icon="['fal', 'sync']" :spin="departures.state === 'fetching'"></fa>
|
<fa :icon="['fal', 'sync']" :spin="departures.state === 'fetching'"></fa>
|
||||||
</fa-layers>
|
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<departures :stops="stops" ref="departures" @update:state="departures.state = $event"></departures>
|
<departures :stops="stops"></departures>
|
||||||
{% if provider.attribution %}
|
{% if provider.attribution %}
|
||||||
<div class="attribution">
|
<div class="attribution">
|
||||||
<fa :icon="['fal', 'info-circle']"></fa>
|
<fa :icon="['fal', 'info-circle']"></fa>
|
||||||
|
Loading…
Reference in New Issue
Block a user