diff --git a/aleksis/core/assets/components/LegacyBaseTemplate.vue b/aleksis/core/assets/components/LegacyBaseTemplate.vue index ecb534fdbbad3de5883f42c5563a47376f63f22d..3f620e7a0b04e16eae53e15b505f4b847064c6b0 100644 --- a/aleksis/core/assets/components/LegacyBaseTemplate.vue +++ b/aleksis/core/assets/components/LegacyBaseTemplate.vue @@ -73,7 +73,7 @@ export default { }, mounted() { // Subscribe to message channel to receive height from iframe - window.addEventListener("message", this.receiveMessage); + this.safeAddEventListener(window, "message", this.receiveMessage); }, watch: { $route() { @@ -81,10 +81,6 @@ export default { this.$root.contentLoading = true; }, }, - beforeDestroy() { - window.removeEventListener("message", this.receiveMessage); - this.$root.contentLoading = false; - }, name: "LegacyBaseTemplate", }; </script> diff --git a/aleksis/core/assets/mixins/aleksis.js b/aleksis/core/assets/mixins/aleksis.js new file mode 100644 index 0000000000000000000000000000000000000000..7dc82d67f9d687f0f5c218373b846685c6b2d307 --- /dev/null +++ b/aleksis/core/assets/mixins/aleksis.js @@ -0,0 +1,38 @@ +/** + * Mixin with utilities for AlekSIS view components. + */ +const aleksisMixin = { + data: () => { + return { + $_aleksis_safeTrackedEvents: new Array(), + }; + }, + methods: { + safeAddEventListener(target, event, handler) { + console.debug("Safely adding handler for %s on %o", event, target); + target.addEventListener(event, handler); + // Add to tracker so we can unregister the handler later + this.$data.$_aleksis_safeTrackedEvents.push({ + target: target, + event: event, + handler: handler, + }); + }, + }, + beforeDestroy() { + // Unregister all safely added event listeners as to not leak them + for (let trackedEvent in this.$data.$_aleksis_safeTrackedEvents) { + console.debug( + "Removing handler for %s on %o", + trackedEvent.event, + trackedEvent.target + ); + trackedEvent.target.removeEventListener( + trackedEvent.event, + trackedEvent.handler + ); + } + }, +}; + +export default aleksisMixin; diff --git a/aleksis/core/assets/mixins/offline.js b/aleksis/core/assets/mixins/offline.js index dfdc034848483fdd93f4e42f3679821dc4727e12..6b9aaeba0f3bee2c6596a69d1a3011a87aa6b901 100644 --- a/aleksis/core/assets/mixins/offline.js +++ b/aleksis/core/assets/mixins/offline.js @@ -22,15 +22,15 @@ const offlineMixin = { }; }, mounted() { - window.addEventListener("online", () => { + this.safeAddEventListener(window, "online", () => { console.info("Navigator changed status to online."); this.checkOfflineState(); }); - window.addEventListener("offline", () => { + this.safeAddEventListener(window, "offline", () => { console.info("Navigator changed status to offline."); this.checkOfflineState(); }); - document.addEventListener("visibilitychange", () => { + this.safeAddEventListener(document, "visibilitychange", () => { console.info("Visibility changed status to", document.visibilityState); this.checkOfflineState(); }); diff --git a/aleksis/core/assets/plugins/aleksis.js b/aleksis/core/assets/plugins/aleksis.js index 9d511d823595b857ec66e60a9b0ebc1068004b28..d2c87d40d396da87beb308a60f9b6e9786c70dab 100644 --- a/aleksis/core/assets/plugins/aleksis.js +++ b/aleksis/core/assets/plugins/aleksis.js @@ -4,6 +4,7 @@ // aleksisAppImporter is a virtual module defined in Vite config import { appMessages } from "aleksisAppImporter"; +import aleksisMixin from "../mixins/aleksis.js"; console.debug("Defining AleksisVue plugin"); const AleksisVue = {}; @@ -166,6 +167,9 @@ AleksisVue.install = function (Vue) { next(); }); }; + + // Add default behaviour for all components + Vue.mixin(aleksisMixin); }; export default AleksisVue;