move departures data to vuex

This commit is contained in:
Kacper Donat 2018-09-30 14:59:10 +02:00
parent f29fb2206e
commit 73134a0e01
11 changed files with 130 additions and 84 deletions

View File

@ -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'
})
}
});

View File

@ -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);

View File

@ -0,0 +1,3 @@
export interface Error {
message: string;
}

View File

@ -1,3 +1,4 @@
export * from './stop'
export * from './departure'
export * from './line'
export * from './error'

View 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 };

View 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;

View File

@ -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 }
})

View File

@ -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;

View File

@ -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));
}

View File

@ -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']));
}

View File

@ -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>