fix some memory leaks
This commit is contained in:
parent
c3570cb38e
commit
12fb18d902
@ -20,7 +20,7 @@
|
|||||||
<stop-details :stop="stop"></stop-details>
|
<stop-details :stop="stop"></stop-details>
|
||||||
</fold>
|
</fold>
|
||||||
|
|
||||||
<popper reference="action-map" :visible="map" arrow class="popper--no-padding" style="width: 500px;">
|
<popper reference="action-map" :visible="map" arrow class="popper--no-padding" style="width: 500px;" placement="right-start">
|
||||||
<div style="height: 300px">
|
<div style="height: 300px">
|
||||||
<l-map :center="stop.location" :zoom=17 :options="{ zoomControl: false, dragging: false }">
|
<l-map :center="stop.location" :zoom=17 :options="{ zoomControl: false, dragging: false }">
|
||||||
<l-tile-layer url="//{s}.tile.osm.org/{z}/{x}/{y}.png" attribution='© <a href="//osm.org/copyright">OpenStreetMap</a> contributors'></l-tile-layer>
|
<l-tile-layer url="//{s}.tile.osm.org/{z}/{x}/{y}.png" attribution='© <a href="//osm.org/copyright">OpenStreetMap</a> contributors'></l-tile-layer>
|
||||||
|
@ -11,7 +11,7 @@ window['Popper'] = Popper;
|
|||||||
|
|
||||||
// dependencies
|
// dependencies
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import Vuex, { mapActions, mapState, Store } from 'vuex';
|
import Vuex, { mapActions, mapMutations, mapState, Store } from 'vuex';
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
|
|
||||||
@ -25,12 +25,13 @@ Vue.use(Vuex);
|
|||||||
import('bootstrap'),
|
import('bootstrap'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
store.dispatch('messages/update');
|
|
||||||
|
|
||||||
// here goes "public" API
|
// here goes "public" API
|
||||||
window['czydojade'] = {
|
window['czydojade'] = Object.assign({}, window['czydojade'], {
|
||||||
components
|
components
|
||||||
};
|
});
|
||||||
|
|
||||||
|
store.dispatch('messages/update');
|
||||||
|
store.dispatch('load', window['czydojade'].state);
|
||||||
|
|
||||||
let intervals = { messages: null, departures: null };
|
let intervals = { messages: null, departures: null };
|
||||||
|
|
||||||
@ -38,7 +39,6 @@ Vue.use(Vuex);
|
|||||||
el: '#app',
|
el: '#app',
|
||||||
store: store,
|
store: store,
|
||||||
data: {
|
data: {
|
||||||
stops: [],
|
|
||||||
sections: {
|
sections: {
|
||||||
messages: true
|
messages: true
|
||||||
},
|
},
|
||||||
@ -70,6 +70,14 @@ Vue.use(Vuex);
|
|||||||
return {
|
return {
|
||||||
state: this.$store.state.departures.state
|
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: {
|
watch: {
|
||||||
@ -104,7 +112,13 @@ Vue.use(Vuex);
|
|||||||
...mapActions({
|
...mapActions({
|
||||||
updateMessages: 'messages/update',
|
updateMessages: 'messages/update',
|
||||||
updateDepartures: 'departures/update'
|
updateDepartures: 'departures/update'
|
||||||
})
|
}),
|
||||||
|
...mapMutations({
|
||||||
|
updateStops: 'updateStops'
|
||||||
|
}),
|
||||||
|
save(this: Vue) {
|
||||||
|
this.$store.dispatch('save').then(x => console.log(x));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$el.classList.remove('not-ready');
|
this.$el.classList.remove('not-ready');
|
||||||
|
@ -68,7 +68,7 @@ export class PopperComponent extends Vue {
|
|||||||
window.dispatchEvent(new Event('resize'));
|
window.dispatchEvent(new Event('resize'));
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyed() {
|
beforeDestroy() {
|
||||||
this._popper.destroy();
|
this._popper.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,7 +94,7 @@ export class FoldComponent extends Vue {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyed() {
|
beforeDestroy() {
|
||||||
this.observer.disconnect();
|
this.observer.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,55 +4,74 @@ import { condition } from "./decorators";
|
|||||||
|
|
||||||
Vue.filter('signed', signed);
|
Vue.filter('signed', signed);
|
||||||
|
|
||||||
Vue.directive('hover', (el, binding, node) => {
|
Vue.directive('hover', {
|
||||||
const update = (hovered: boolean, e: Event) => {
|
bind(el, binding, node) {
|
||||||
if (typeof binding.value === 'function') {
|
const update = (hovered: boolean, e: Event) => {
|
||||||
binding.value(hovered, e);
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
el.classList.add(`size-${breakpoint}`);
|
if (typeof binding.value === 'boolean') {
|
||||||
}
|
set(node.context, binding.expression, hovered);
|
||||||
};
|
}
|
||||||
|
|
||||||
resize();
|
if (typeof binding.arg !== 'undefined') {
|
||||||
if (!binding.modifiers['once']) {
|
set(node.context, binding.arg, hovered);
|
||||||
window.addEventListener('resize', resize);
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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']);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2,7 +2,9 @@ import Vuex from 'vuex';
|
|||||||
|
|
||||||
import messages from './messages';
|
import messages from './messages';
|
||||||
import departures from './departures';
|
import departures from './departures';
|
||||||
|
import { state, mutations, actions } from "./root";
|
||||||
|
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
|
state, mutations, actions,
|
||||||
modules: { messages, departures }
|
modules: { messages, departures }
|
||||||
})
|
})
|
@ -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 default <Module<RootState, unknown>>{
|
|
||||||
state
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SavedState {
|
||||||
|
version: 1,
|
||||||
|
stops: string[],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const state: RootState = {
|
||||||
|
stops: []
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mutations: MutationTree<RootState> = {
|
||||||
|
updateStops: (state, stops) => state.stops = stops,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const actions: ActionTree<RootState, undefined> = {
|
||||||
|
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<SavedState> => ({
|
||||||
|
version: 1,
|
||||||
|
stops: state.stops.map(stop => stop.id)
|
||||||
|
})
|
||||||
|
};
|
56
src/Provider/Dummy/DummyDepartureRepository.php
Normal file
56
src/Provider/Dummy/DummyDepartureRepository.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Provider\Dummy;
|
||||||
|
|
||||||
|
use App\Model\Departure;
|
||||||
|
use App\Model\Line;
|
||||||
|
use App\Model\Stop;
|
||||||
|
use App\Model\Vehicle;
|
||||||
|
use App\Provider\DepartureRepository;
|
||||||
|
use App\Service\Proxy\ReferenceFactory;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Tightenco\Collect\Support\Collection;
|
||||||
|
|
||||||
|
class DummyDepartureRepository implements DepartureRepository
|
||||||
|
{
|
||||||
|
private $reference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DummyDepartureProviderRepository constructor.
|
||||||
|
*
|
||||||
|
* @param $reference
|
||||||
|
*/
|
||||||
|
public function __construct(ReferenceFactory $reference)
|
||||||
|
{
|
||||||
|
$this->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]),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -12,9 +12,23 @@ use App\Provider\TrackRepository;
|
|||||||
|
|
||||||
class DummyProvider implements Provider
|
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
|
public function getDepartureRepository(): DepartureRepository
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
return $this->departures;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLineRepository(): LineRepository
|
public function getLineRepository(): LineRepository
|
||||||
@ -24,7 +38,7 @@ class DummyProvider implements Provider
|
|||||||
|
|
||||||
public function getStopRepository(): StopRepository
|
public function getStopRepository(): StopRepository
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
return $this->stops;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMessageRepository(): MessageRepository
|
public function getMessageRepository(): MessageRepository
|
||||||
|
49
src/Provider/Dummy/DummyStopRepository.php
Normal file
49
src/Provider/Dummy/DummyStopRepository.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Provider\Dummy;
|
||||||
|
|
||||||
|
use App\Model\Stop;
|
||||||
|
use App\Provider\StopRepository;
|
||||||
|
use App\Service\Proxy\ReferenceFactory;
|
||||||
|
use Tightenco\Collect\Support\Collection;
|
||||||
|
use Kadet\Functional as f;
|
||||||
|
|
||||||
|
class DummyStopRepository implements StopRepository
|
||||||
|
{
|
||||||
|
private $reference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DummyDepartureProviderRepository constructor.
|
||||||
|
*
|
||||||
|
* @param $reference
|
||||||
|
*/
|
||||||
|
public function __construct(ReferenceFactory $reference)
|
||||||
|
{
|
||||||
|
$this->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();
|
||||||
|
}
|
||||||
|
}
|
@ -12,12 +12,12 @@
|
|||||||
Komunikaty <span class="ml-2 badge badge-pill badge-dark">{{ '{{ messages.count }}' }}</span>
|
Komunikaty <span class="ml-2 badge badge-pill badge-dark">{{ '{{ messages.count }}' }}</span>
|
||||||
</h2>
|
</h2>
|
||||||
<button class="btn btn-action flex-space-left" ref="settings-messages" v-hover="settings.messages">
|
<button class="btn btn-action flex-space-left" ref="settings-messages" v-hover="settings.messages">
|
||||||
<fa :icon="['fal', 'cog']"></fa>
|
<fa :icon="['fal', 'cog']" fixed-width></fa>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-action" @click="updateMessages" ref="btn-messages-refresh">
|
<button class="btn btn-action" @click="updateMessages" ref="btn-messages-refresh">
|
||||||
<fa :icon="['fal', 'sync']" :spin="messages.state === 'fetching'"></fa>
|
<fa :icon="['fal', 'sync']" :spin="messages.state === 'fetching'" fixed-width></fa>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-action" @click="sections.messages = !sections.messages">
|
<button class="btn btn-action" @click="sections.messages = !sections.messages" fixed-width>
|
||||||
<fa :icon="['fal', sections.messages ? 'chevron-up' : 'chevron-down']" fixed-width/>
|
<fa :icon="['fal', sections.messages ? 'chevron-up' : 'chevron-down']" fixed-width/>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@ -47,10 +47,10 @@
|
|||||||
<span class="text">Odjazdy</span>
|
<span class="text">Odjazdy</span>
|
||||||
</h2>
|
</h2>
|
||||||
<button class="btn btn-action flex-space-left" ref="settings-departures" v-hover="settings.departures">
|
<button class="btn btn-action flex-space-left" ref="settings-departures" v-hover="settings.departures">
|
||||||
<fa :icon="['fal', 'cog']"></fa>
|
<fa :icon="['fal', 'cog']" fixed-width></fa>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-action" @click="updateDepartures({ stops })">
|
<button class="btn btn-action" @click="updateDepartures({ stops })">
|
||||||
<fa :icon="['fal', 'sync']" :spin="departures.state === 'fetching'"></fa>
|
<fa :icon="['fal', 'sync']" :spin="departures.state === 'fetching'" fixed-width></fa>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<popper reference="settings-departures" :visible="settings.departures" arrow placement="left-start">
|
<popper reference="settings-departures" :visible="settings.departures" arrow placement="left-start">
|
||||||
|
@ -35,6 +35,14 @@
|
|||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
{% block javascripts %}{% endblock %}
|
{% block javascripts %}{% endblock %}
|
||||||
|
<script>
|
||||||
|
window.czydojade = {
|
||||||
|
state: {{ {
|
||||||
|
version: 1,
|
||||||
|
stops: app.request.query.get('stop', [])
|
||||||
|
}|json_encode|raw }}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
<script src="{{ asset('bundle.js') }}"></script>
|
<script src="{{ asset('bundle.js') }}"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
Reference in New Issue
Block a user