From 12fb18d902122e03bf4c94e4afa4ed9ac1b98cf3 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Wed, 3 Oct 2018 21:10:11 +0200 Subject: [PATCH] fix some memory leaks --- resources/components/stop.html | 2 +- resources/ts/app.ts | 28 +++-- resources/ts/components/utils.ts | 4 +- resources/ts/filters.ts | 113 ++++++++++-------- resources/ts/model/stop.ts | 2 +- resources/ts/store/index.ts | 2 + resources/ts/store/root.ts | 39 +++++- .../Dummy/DummyDepartureRepository.php | 56 +++++++++ src/Provider/Dummy/DummyProvider.php | 18 ++- src/Provider/Dummy/DummyStopRepository.php | 49 ++++++++ templates/app.html.twig | 10 +- templates/base.html.twig | 8 ++ 12 files changed, 260 insertions(+), 71 deletions(-) create mode 100644 src/Provider/Dummy/DummyDepartureRepository.php create mode 100644 src/Provider/Dummy/DummyStopRepository.php diff --git a/resources/components/stop.html b/resources/components/stop.html index e021e16..47ea0b0 100644 --- a/resources/components/stop.html +++ b/resources/components/stop.html @@ -20,7 +20,7 @@ - +
diff --git a/resources/ts/app.ts b/resources/ts/app.ts index f398b8f..0bdb0d6 100644 --- a/resources/ts/app.ts +++ b/resources/ts/app.ts @@ -11,7 +11,7 @@ window['Popper'] = Popper; // dependencies import Vue from "vue"; -import Vuex, { mapActions, mapState, Store } from 'vuex'; +import Vuex, { mapActions, mapMutations, mapState, Store } from 'vuex'; Vue.use(Vuex); @@ -25,12 +25,13 @@ Vue.use(Vuex); import('bootstrap'), ]); - store.dispatch('messages/update'); - // here goes "public" API - window['czydojade'] = { + window['czydojade'] = Object.assign({}, window['czydojade'], { components - }; + }); + + store.dispatch('messages/update'); + store.dispatch('load', window['czydojade'].state); let intervals = { messages: null, departures: null }; @@ -38,7 +39,6 @@ Vue.use(Vuex); el: '#app', store: store, data: { - stops: [], sections: { messages: true }, @@ -70,6 +70,14 @@ Vue.use(Vuex); return { state: this.$store.state.departures.state }; + }, + stops: { + get(this: Vue) { + return this.$store.state.stops; + }, + set(this: Vue, value) { + this.$store.commit('updateStops', value); + } } }, watch: { @@ -104,7 +112,13 @@ Vue.use(Vuex); ...mapActions({ updateMessages: 'messages/update', updateDepartures: 'departures/update' - }) + }), + ...mapMutations({ + updateStops: 'updateStops' + }), + save(this: Vue) { + this.$store.dispatch('save').then(x => console.log(x)); + } }, mounted() { this.$el.classList.remove('not-ready'); diff --git a/resources/ts/components/utils.ts b/resources/ts/components/utils.ts index c4b4923..3fed9f1 100644 --- a/resources/ts/components/utils.ts +++ b/resources/ts/components/utils.ts @@ -68,7 +68,7 @@ export class PopperComponent extends Vue { window.dispatchEvent(new Event('resize')); } - destroyed() { + beforeDestroy() { this._popper.destroy(); } } @@ -94,7 +94,7 @@ export class FoldComponent extends Vue { }); } - destroyed() { + beforeDestroy() { this.observer.disconnect(); } diff --git a/resources/ts/filters.ts b/resources/ts/filters.ts index d828ae4..edf0d3f 100644 --- a/resources/ts/filters.ts +++ b/resources/ts/filters.ts @@ -4,55 +4,74 @@ import { condition } from "./decorators"; Vue.filter('signed', signed); -Vue.directive('hover', (el, binding, node) => { - const update = (hovered: boolean, e: Event) => { - if (typeof binding.value === 'function') { - binding.value(hovered, e); - } - - if (typeof binding.value === 'boolean') { - set(node.context, binding.expression, hovered); - } - - if (typeof binding.arg !== 'undefined') { - set(node.context, binding.arg, hovered); - } - }; - - const activate = event => update(true, event); - const deactivate = event => update(false, event); - - el.addEventListener('mouseenter', activate); - el.addEventListener('click', activate); - el.addEventListener('keydown', condition.decorate(deactivate, e => e.keyCode == 27)); - el.addEventListener('mouseleave', deactivate); - el.addEventListener('focusout', deactivate); -}); - -Vue.directive('responsive', (el, binding) => { - const breakpoints = typeof binding.value === 'object' ? binding.value : { - 'xs': 0, - 'sm': 576, - 'md': 768, - 'lg': 1024, - 'xl': 1200, - }; - - const resize = () => { - const width = el.scrollWidth; - el.classList.remove(...Object.keys(breakpoints).map(breakpoint => `size-${breakpoint}`)); - - for (let [ breakpoint, size ] of Object.entries(breakpoints)) { - if (width < size) { - break; +Vue.directive('hover', { + bind(el, binding, node) { + const update = (hovered: boolean, e: Event) => { + if (typeof binding.value === 'function') { + binding.value(hovered, e); } - el.classList.add(`size-${breakpoint}`); - } - }; + if (typeof binding.value === 'boolean') { + set(node.context, binding.expression, hovered); + } - resize(); - if (!binding.modifiers['once']) { - window.addEventListener('resize', resize); + if (typeof binding.arg !== 'undefined') { + set(node.context, binding.arg, hovered); + } + }; + + const activate = event => update(true, event); + const deactivate = event => update(false, event); + const keyboard = condition.decorate(deactivate, e => e.keyCode == 27); + + binding['events'] = { activate, deactivate, keyboard }; + + el.addEventListener('mouseenter', activate); + el.addEventListener('click', activate); + el.addEventListener('keydown', keyboard); + el.addEventListener('mouseleave', deactivate); + el.addEventListener('focusout', deactivate); + }, + unbind(el, binding) { + const { activate, deactivate, keyboard } = binding['events']; + + el.removeEventListener('mouseenter', activate); + el.removeEventListener('click', activate); + el.removeEventListener('keydown', keyboard); + el.removeEventListener('mouseleave', deactivate); + el.removeEventListener('focusout', deactivate); + } +}); + +Vue.directive('responsive', { + inserted(el, binding) { + const breakpoints = typeof binding.value === 'object' ? binding.value : { + 'xs': 0, + 'sm': 576, + 'md': 768, + 'lg': 1024, + 'xl': 1200, + }; + + const resize = binding['resize'] = () => { + const width = el.scrollWidth; + el.classList.remove(...Object.keys(breakpoints).map(breakpoint => `size-${breakpoint}`)); + + for (let [ breakpoint, size ] of Object.entries(breakpoints)) { + if (width < size) { + break; + } + + el.classList.add(`size-${breakpoint}`); + } + }; + resize(); + + if (!binding.modifiers['once']) { + window.addEventListener('resize', resize); + } + }, + unbind(el, binding) { + window.removeEventListener('resize', binding['resize']); } }); diff --git a/resources/ts/model/stop.ts b/resources/ts/model/stop.ts index e50a744..415acd3 100644 --- a/resources/ts/model/stop.ts +++ b/resources/ts/model/stop.ts @@ -11,4 +11,4 @@ export type StopGroup = Stop[]; export type StopGroups = { [name: string]: StopGroup; -} \ No newline at end of file +} diff --git a/resources/ts/store/index.ts b/resources/ts/store/index.ts index 149e700..f629d16 100644 --- a/resources/ts/store/index.ts +++ b/resources/ts/store/index.ts @@ -2,7 +2,9 @@ import Vuex from 'vuex'; import messages from './messages'; import departures from './departures'; +import { state, mutations, actions } from "./root"; export default new Vuex.Store({ + state, mutations, actions, modules: { messages, departures } }) \ No newline at end of file diff --git a/resources/ts/store/root.ts b/resources/ts/store/root.ts index 566fbcb..57b1120 100644 --- a/resources/ts/store/root.ts +++ b/resources/ts/store/root.ts @@ -1,9 +1,36 @@ -import { Module } from "vuex"; +import { Stop } from "../model"; +import { ActionTree, MutationTree } from "vuex"; +import urls from "../urls"; -const state = { }; +export interface RootState { + stops: Stop[], +} -export type RootState = typeof state; +export interface SavedState { + version: 1, + stops: string[], +} -export default >{ - state -} \ No newline at end of file +export const state: RootState = { + stops: [] +}; + +export const mutations: MutationTree = { + updateStops: (state, stops) => state.stops = stops, +}; + +export const actions: ActionTree = { + async load({ commit }, { stops }: SavedState) { + if (stops.length > 0) { + const response = await fetch(urls.prepare(urls.stops.all, { id: stops })); + + if (response.ok) { + commit('updateStops', await response.json()); + } + } + }, + save: async ({ state }): Promise => ({ + version: 1, + stops: state.stops.map(stop => stop.id) + }) +}; \ No newline at end of file diff --git a/src/Provider/Dummy/DummyDepartureRepository.php b/src/Provider/Dummy/DummyDepartureRepository.php new file mode 100644 index 0000000..5210943 --- /dev/null +++ b/src/Provider/Dummy/DummyDepartureRepository.php @@ -0,0 +1,56 @@ +reference = $reference; + } + + public function getForStop(Stop $stop): Collection + { + return collect([ + [ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ], + [ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ], + [ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ], + [ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ], + [ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ], + [ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ], + [ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ], + [ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ], + [ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ], + [ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ], + ])->map(function ($departure) use ($stop) { + list($symbol, $type, $display, $vehicle) = $departure; + $scheduled = new Carbon(); + $estimated = (clone $scheduled)->addSeconds(40); + + return Departure::createFromArray([ + 'scheduled' => $scheduled, + 'estimated' => $estimated, + 'stop' => $stop, + 'display' => $display, + 'vehicle' => $this->reference->get(Vehicle::class, $vehicle), + 'line' => Line::createFromArray(['symbol' => $symbol, 'type' => $type]), + ]); + }); + } +} \ No newline at end of file diff --git a/src/Provider/Dummy/DummyProvider.php b/src/Provider/Dummy/DummyProvider.php index 7e33693..eb88b36 100644 --- a/src/Provider/Dummy/DummyProvider.php +++ b/src/Provider/Dummy/DummyProvider.php @@ -12,9 +12,23 @@ use App\Provider\TrackRepository; class DummyProvider implements Provider { + private $departures; + private $stops; + + /** + * DummyProvider constructor. + * + * @param $departures + */ + public function __construct(DummyDepartureRepository $departures, DummyStopRepository $stops) + { + $this->departures = $departures; + $this->stops = $stops; + } + public function getDepartureRepository(): DepartureRepository { - throw new NotSupportedException(); + return $this->departures; } public function getLineRepository(): LineRepository @@ -24,7 +38,7 @@ class DummyProvider implements Provider public function getStopRepository(): StopRepository { - throw new NotSupportedException(); + return $this->stops; } public function getMessageRepository(): MessageRepository diff --git a/src/Provider/Dummy/DummyStopRepository.php b/src/Provider/Dummy/DummyStopRepository.php new file mode 100644 index 0000000..cdb8ecb --- /dev/null +++ b/src/Provider/Dummy/DummyStopRepository.php @@ -0,0 +1,49 @@ +reference = $reference; + } + + public function getAll(): Collection + { + return collect(); + } + + public function getAllGroups(): Collection + { + return collect(); + } + + public function getById($id): ?Stop + { + return Stop::createFromArray(['id' => $id, 'name' => 'lorem']); + } + + public function getManyById($ids): Collection + { + return collect($ids)->map(f\ref([ $this, 'getById' ])); + } + + public function findGroupsByName(string $name): Collection + { + return collect(); + } +} \ No newline at end of file diff --git a/templates/app.html.twig b/templates/app.html.twig index a35c0d9..221839e 100644 --- a/templates/app.html.twig +++ b/templates/app.html.twig @@ -12,12 +12,12 @@ Komunikaty {{ '{{ messages.count }}' }} - @@ -47,10 +47,10 @@ Odjazdy diff --git a/templates/base.html.twig b/templates/base.html.twig index 9bf075c..2247e5a 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -35,6 +35,14 @@ {% block javascripts %}{% endblock %} +