Extract departures settings to its own component and store module

This commit is contained in:
Kacper Donat 2020-03-18 22:00:17 +01:00
parent 87e4121444
commit 5c8a0238f1
15 changed files with 140 additions and 74 deletions

View File

@ -26,4 +26,4 @@ server {
internal; internal;
} }
} }
} }

View File

@ -2,8 +2,4 @@
<ul class="departures__list list-underlined"> <ul class="departures__list list-underlined">
<departure :departure="departure" :key="departure.key" v-for="departure in departures"/> <departure :departure="departure" :key="departure.key" v-for="departure in departures"/>
</ul> </ul>
<div class="alert alert-info" v-if="stops.length === 0">
<ui-icon icon="info"/>
Wybierz przystanki korzystając z wyszukiwarki poniżej, aby zobaczyć listę odjazdów.
</div>
</div> </div>

View File

@ -0,0 +1,31 @@
<fragment>
<div class="form-group">
<h3 class="popper__heading flex">
<label class="text" for="departures-auto-refresh-interval">
<ui-icon icon="refresh" fixed-width/>
autoodświeżanie
</label>
<ui-switch id="departures-auto-refresh" :value="autorefresh" @input="update({ autorefresh: $event })" class="flex-space-left"/>
</h3>
<div class="flex " v-if="autorefresh">
<label for="departures-auto-refresh-interval" class="text">
<span class="sr-only">częstotliwość odświeżania</span>
co
</label>
<div class="input-group input-group-sm">
<input type="text" class="form-control form-control-sm form-control-simple" id="departures-auto-refresh-interval"
:value="autorefreshInterval" @input="update({ autorefreshInterval: Number.parseInt($event.target.value) })" />
<div class="input-group-append">
<span class="input-group-text" aria-label="sekund">s</span>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="text" for="departures-count">
<ui-icon icon="line-bus" fixed-width/>
liczba wpisów
</label>
<ui-numeric-input id="departures-count" :value="displayedEntriesCount" @input="update({ displayedEntriesCount: $event })" :min="1" :max="20"/>
</div>
</fragment>

View File

@ -38,6 +38,11 @@ label {
margin-left: 0; margin-left: 0;
} }
.btn-addon:disabled {
opacity: 1;
color: rgba($dark, .5)
}
.input-group-prepend { .input-group-prepend {
margin-right: 0; margin-right: 0;
} }

View File

@ -2,9 +2,8 @@ import Vue from 'vue'
import store from '../store' import store from '../store'
import { Component, Watch } from "vue-property-decorator"; import { Component, Watch } from "vue-property-decorator";
import { Mutation, Action } from 'vuex-class' import { Mutation, Action } from 'vuex-class'
import { ObtainPayload } from "../store/departures";
import { Stop } from "../model"; import { Stop } from "../model";
import { PopperComponent } from "./utils"; import { DeparturesSettingsState } from "../store/settings/departures";
@Component({ store }) @Component({ store })
export class Application extends Vue { export class Application extends Vue {
@ -23,15 +22,9 @@ export class Application extends Vue {
messages: { messages: {
active: true, active: true,
interval: 60 interval: 60
},
departures: {
active: true,
interval: 10
} }
}; };
private count = 8;
private intervals = { messages: null, departures: null }; private intervals = { messages: null, departures: null };
get messages() { get messages() {
@ -60,16 +53,39 @@ export class Application extends Vue {
this.$el.classList.remove('not-ready'); this.$el.classList.remove('not-ready');
} }
created() {
this.initDeparturesRefreshInterval();
}
private initDeparturesRefreshInterval() {
const departuresAutorefreshCallback = () => {
const {autorefresh, autorefreshInterval} = this.$store.state['departures-settings'] as DeparturesSettingsState;
if (this.intervals.departures) {
clearInterval(this.intervals.departures);
}
if (autorefresh) {
this.intervals.departures = setInterval(() => this.updateDepartures(), Math.max(5, autorefreshInterval) * 1000)
}
};
this.$store.watch(({"departures-settings": state}) => state.autorefresh, departuresAutorefreshCallback);
this.$store.watch(({"departures-settings": state}) => state.autorefreshInterval, departuresAutorefreshCallback);
departuresAutorefreshCallback();
}
@Action('messages/update') updateMessages: () => void; @Action('messages/update') updateMessages: () => void;
@Action('departures/update') updateDepartures: (payload: ObtainPayload) => void; @Action('departures/update') updateDepartures: () => void;
@Mutation add: (stops: Stop[]) => void; @Mutation add: (stops: Stop[]) => void;
@Mutation remove: (stop: Stop) => void; @Mutation remove: (stop: Stop) => void;
@Mutation clear: () => void; @Mutation clear: () => void;
@Watch('stops') @Watch('stops')
onStopUpdate(this: any, stops) { onStopUpdate() {
this.updateDepartures({ stops }); this.updateDepartures();
} }
@Watch('autorefresh', { immediate: true, deep: true }) @Watch('autorefresh', { immediate: true, deep: true })
@ -79,17 +95,8 @@ export class Application extends Vue {
this.intervals.messages = null; this.intervals.messages = null;
} }
if (this.intervals.departures) {
clearInterval(this.intervals.departures);
this.intervals.messages = null;
}
if (settings.messages.active) { if (settings.messages.active) {
this.intervals.messages = setInterval(() => this.updateMessages(), Math.max(5, settings.messages.interval) * 1000); this.intervals.messages = setInterval(() => this.updateMessages(), Math.max(5, settings.messages.interval) * 1000);
} }
if (settings.departures.active) {
this.intervals.departures = setInterval(() => this.updateDepartures({ stops: this.stops }), Math.max(5, settings.departures.interval) * 1000);
}
} }
} }

View File

@ -1,21 +1,15 @@
import Vue from 'vue' import Vue from 'vue'
import { Departure, Stop } from "../model"; import { Departure } from "../model";
import { Component, Prop, Watch } from "vue-property-decorator"; import { Component, Prop, Watch } from "vue-property-decorator";
import { namespace } from 'vuex-class'; import store, { Departures } from '../store'
import store from '../store'
import { Trip } from "../model/trip"; import { Trip } from "../model/trip";
import urls from "../urls"; import urls from "../urls";
import { Jsonified } from "../utils"; import { Jsonified } from "../utils";
import * as moment from "moment"; import * as moment from "moment";
const { State } = namespace('departures');
@Component({ template: require("../../components/departures.html"), store }) @Component({ template: require("../../components/departures.html"), store })
export class DeparturesComponent extends Vue { export class DeparturesComponent extends Vue {
@State departures: Departure[]; @Departures.State departures: Departure[];
@Prop(Array)
stops: Stop[];
} }
@Component({ template: require("../../components/departures/departure.html") }) @Component({ template: require("../../components/departures/departure.html") })

View File

@ -10,3 +10,5 @@ export * from './app'
export * from './favourites' export * from './favourites'
export * from './trip' export * from './trip'
export * from './ui' export * from './ui'
export * from './settings'
export { Departures } from "../store";

View File

@ -0,0 +1,21 @@
import { Component, Prop } from "vue-property-decorator";
import store, { DeparturesSettings } from "../../store";
import Vue from "vue";
import { DeparturesSettingsState } from "../../store/settings/departures";
@Component({ template: require("../../../components/settings/departures.html"), store })
export class SettingsDepartures extends Vue {
@DeparturesSettings.State
public autorefresh: boolean;
@DeparturesSettings.State
public autorefreshInterval: number;
@DeparturesSettings.State
public displayedEntriesCount: number;
@DeparturesSettings.Mutation
public update: (state: Partial<DeparturesSettingsState>) => void;
}
Vue.component('SettingsDepartures', SettingsDepartures);

View File

@ -0,0 +1 @@
export * from "./departures"

View File

@ -1,7 +1,6 @@
import { FetchingState } from "../utils"; import { FetchingState } from "../utils";
import { Moment } from "moment"; import { Moment } from "moment";
import { Module, MutationTree } from "vuex"; import { Module, MutationTree } from "vuex";
import { RootState } from "./root";
import * as moment from "moment"; import * as moment from "moment";
export interface CommonState { export interface CommonState {
@ -24,4 +23,4 @@ export const mutations: MutationTree<CommonState> = {
} }
}; };
export default { state, mutations }; export default { state, mutations };

View File

@ -10,10 +10,6 @@ export interface DeparturesState extends CommonState {
departures: Departure[], departures: Departure[],
} }
export interface ObtainPayload {
stops: Stop[]
}
export const departures: Module<DeparturesState, RootState> = { export const departures: Module<DeparturesState, RootState> = {
namespaced: true, namespaced: true,
state: { state: {
@ -29,10 +25,15 @@ export const departures: Module<DeparturesState, RootState> = {
...common.mutations ...common.mutations
}, },
actions: { actions: {
async update({ commit }, { stops }: ObtainPayload) { async update({ commit }) {
const count = this.state['departures-settings'].displayedEntriesCount;
const stops = this.state.stops;
commit('fetching'); commit('fetching');
const response = await fetch(urls.prepare(urls.departures, { const response = await fetch(urls.prepare(urls.departures, {
stop: stops.map(stop => stop.id), stop: stops.map(stop => stop.id),
limit: count || 8,
})); }));
if (!response.ok) { if (!response.ok) {

View File

@ -4,9 +4,10 @@ import messages, { MessagesState } from './messages';
import departures, { DeparturesState } from './departures' import departures, { DeparturesState } from './departures'
import favourites, { FavouritesState, localStorageSaver } from './favourites' import favourites, { FavouritesState, localStorageSaver } from './favourites'
import { state, mutations, actions, RootState } from "./root"; import { actions, mutations, RootState, state } from "./root";
import VuexPersistence from "vuex-persist"; import VuexPersistence from "vuex-persist";
import { namespace } from "vuex-class"; import { namespace } from "vuex-class";
import departureSettings from "./settings/departures";
export type State = { export type State = {
messages: MessagesState; messages: MessagesState;
@ -15,7 +16,7 @@ export type State = {
} & RootState; } & RootState;
const localStoragePersist = new VuexPersistence<State>({ const localStoragePersist = new VuexPersistence<State>({
reducer: state => ({ favourites: state.favourites }) modules: ['favourites', 'departures-settings'],
}); });
const sessionStoragePersist = new VuexPersistence<State>({ const sessionStoragePersist = new VuexPersistence<State>({
@ -25,7 +26,12 @@ const sessionStoragePersist = new VuexPersistence<State>({
const store = new Vuex.Store({ const store = new Vuex.Store({
state, mutations, actions, state, mutations, actions,
modules: { messages, departures, favourites }, modules: {
messages,
departures,
favourites,
'departures-settings': departureSettings
},
plugins: [ plugins: [
// todo: remove after some time // todo: remove after some time
localStorageSaver('favourites.favourites', 'favourites'), localStorageSaver('favourites.favourites', 'favourites'),
@ -37,3 +43,5 @@ const store = new Vuex.Store({
export default store; export default store;
export const Favourites = namespace('favourites'); export const Favourites = namespace('favourites');
export const DeparturesSettings = namespace('departures-settings');
export const Departures = namespace('departures');

View File

@ -0,0 +1,24 @@
import { ActionContext, Module } from "vuex";
import { RootState } from "../root";
export type DeparturesSettingsState = {
autorefresh: boolean;
autorefreshInterval?: number;
displayedEntriesCount?: number;
}
const departureSettings: Module<DeparturesSettingsState, RootState> = {
namespaced: true,
state: {
autorefresh: true,
autorefreshInterval: 10,
displayedEntriesCount: 10
},
mutations: {
update(state: DeparturesSettingsState, patch: Partial<DeparturesSettingsState>) {
Object.assign(state, patch);
}
}
};
export default departureSettings;

View File

@ -68,7 +68,7 @@ class DeparturesController extends Controller
*/ */
public function stops(DepartureRepository $departures, StopRepository $stops, Request $request) public function stops(DepartureRepository $departures, StopRepository $stops, Request $request)
{ {
$stops = $stops->all(new IdFilter($request->query->get('stop'))); $stops = $stops->all(new IdFilter($request->query->get('stop', [])));
$result = $departures->current($stops, ...$this->getModifiersFromRequest($request)); $result = $departures->current($stops, ...$this->getModifiersFromRequest($request));
return $this->json( return $this->json(

View File

@ -73,38 +73,15 @@
</button> </button>
<portal to="popups"> <portal to="popups">
<popper reference="settings-departures" v-if="visibility.departures" arrow placement="left-start" @leave="visibility.departures = false"> <popper reference="settings-departures" v-if="visibility.departures" arrow placement="left-start" @leave="visibility.departures = false">
<div class="form-group"> <settings-departures></settings-departures>
<h3 class="popper__heading flex">
<label class="text" for="departures-auto-refresh-interval">
<ui-icon icon="refresh" fixed-width></ui-icon>
autoodświeżanie
</label>
<ui-switch id="departures-auto-refresh" v-model="autorefresh.departures.active" class="flex-space-left"></ui-switch>
</h3>
<div class="flex " v-if="autorefresh.departures.active">
<label for="departures-auto-refresh-interval" class="text">
<span class="sr-only">częstotliwość odświeżania</span>
co
</label>
<div class="input-group input-group-sm">
<input type="text" class="form-control form-control-sm form-control-simple" id="departures-auto-refresh-interval" v-model="autorefresh.departures.interval"/>
<div class="input-group-append">
<span class="input-group-text" aria-label="sekund">s</span>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="text" for="departures-count">
<ui-icon icon="line-bus" fixed-width></ui-icon>
Liczba wpisów
</label>
<ui-numeric-input id="departures-count" v-model="count" :min="1" :max="16"></ui-numeric-input>
</div>
</popper> </popper>
</portal> </portal>
</header> </header>
<departures :stops="stops"></departures> <departures :stops="stops" v-if="stops.length > 0"></departures>
<div class="alert alert-info" v-else>
<ui-icon icon="info"></ui-icon>
Wybierz przystanki korzystając z wyszukiwarki poniżej, aby zobaczyć listę odjazdów.
</div>
{% if provider.attribution %} {% if provider.attribution %}
<div class="attribution"> <div class="attribution">
<ui-icon icon="info"></ui-icon> <ui-icon icon="info"></ui-icon>