diff --git a/resources/ts/app.ts b/resources/ts/app.ts index 3336e21..6539bb2 100644 --- a/resources/ts/app.ts +++ b/resources/ts/app.ts @@ -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' }) } }); diff --git a/resources/ts/components/departures.ts b/resources/ts/components/departures.ts index 367c374..7771db9 100644 --- a/resources/ts/components/departures.ts +++ b/resources/ts/components/departures.ts @@ -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[]; - - 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); \ No newline at end of file diff --git a/resources/ts/model/error.ts b/resources/ts/model/error.ts new file mode 100644 index 0000000..080115f --- /dev/null +++ b/resources/ts/model/error.ts @@ -0,0 +1,3 @@ +export interface Error { + message: string; +} \ No newline at end of file diff --git a/resources/ts/model/index.ts b/resources/ts/model/index.ts index b18fa3a..4a7a5f3 100644 --- a/resources/ts/model/index.ts +++ b/resources/ts/model/index.ts @@ -1,3 +1,4 @@ export * from './stop' export * from './departure' export * from './line' +export * from './error' \ No newline at end of file diff --git a/resources/ts/store/common.ts b/resources/ts/store/common.ts new file mode 100644 index 0000000..49e12c0 --- /dev/null +++ b/resources/ts/store/common.ts @@ -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 = { + fetching: (state) => state.state = 'fetching', + error: (state, error) => { + state.state = 'error'; + state.error = error; + } +}; + +export default { state, mutations }; \ No newline at end of file diff --git a/resources/ts/store/departures.ts b/resources/ts/store/departures.ts new file mode 100644 index 0000000..012c69f --- /dev/null +++ b/resources/ts/store/departures.ts @@ -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 = { + 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[]; + commit('update', departures.map(departure => { + departure.scheduled = moment.parseZone(departure.scheduled); + departure.estimated = moment.parseZone(departure.estimated); + + return departure as Departure; + })); + } + } +}; + +export default departures; \ No newline at end of file diff --git a/resources/ts/store/index.ts b/resources/ts/store/index.ts index 36998bd..149e700 100644 --- a/resources/ts/store/index.ts +++ b/resources/ts/store/index.ts @@ -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 } }) \ No newline at end of file diff --git a/resources/ts/store/messages.ts b/resources/ts/store/messages.ts index 29ecb1e..0812424 100644 --- a/resources/ts/store/messages.ts +++ b/resources/ts/store/messages.ts @@ -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 = { 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 = { 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) { @@ -44,7 +39,8 @@ export const messages: Module = { 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 = { } } }; + +export default messages; \ No newline at end of file diff --git a/src/Controller/Api/v1/TracksController.php b/src/Controller/Api/v1/TracksController.php index e054679..d6d5126 100644 --- a/src/Controller/Api/v1/TracksController.php +++ b/src/Controller/Api/v1/TracksController.php @@ -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)); } diff --git a/src/Provider/Database/GenericTrackRepository.php b/src/Provider/Database/GenericTrackRepository.php index d13bd19..9a25d44 100644 --- a/src/Provider/Database/GenericTrackRepository.php +++ b/src/Provider/Database/GenericTrackRepository.php @@ -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'])); } diff --git a/templates/app.html.twig b/templates/app.html.twig index f2e257b..2f82145 100644 --- a/templates/app.html.twig +++ b/templates/app.html.twig @@ -25,13 +25,11 @@

Odjazdy -

- + {% if provider.attribution %}