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