Skip to content
Snippets Groups Projects
Verified Commit 15429d18 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Improve lifecycle of SPA

parent 84edf371
No related branches found
No related tags found
3 merge requests!1237Release 3.0,!1211Improve lifecycle of SPA,!1183Release 3.0
Pipeline #121276 failed
...@@ -15,11 +15,19 @@ Added ...@@ -15,11 +15,19 @@ Added
* GraphQL schema for Rooms * GraphQL schema for Rooms
* [Dev] UpdateIndicator Vue Component to display the status of interactive pages * [Dev] UpdateIndicator Vue Component to display the status of interactive pages
Changed
~~~~~~~
* Show message on successful logout to inform users properly.
Fixed Fixed
~~~~~ ~~~~~
* GraphQL endpoints for groups, persons, and notifications didn't expose all necessary fields. * GraphQL endpoints for groups, persons, and notifications didn't expose all necessary fields.
* Loading indicator in toolbar was not shown at the complete loading progress.
* 404 page was sometimes shown while the page was still loading.
* Setting of page height in the iframe was not working correctly.
* App switched to offline state when the user was logged out/in.
`3.0b3`_ - 2023-03-19 `3.0b3`_ - 2023-03-19
--------------------- ---------------------
......
...@@ -3,6 +3,7 @@ from typing import Any, Optional ...@@ -3,6 +3,7 @@ from typing import Any, Optional
import django.apps import django.apps
from django.apps import apps from django.apps import apps
from django.conf import settings from django.conf import settings
from django.contrib import messages
from django.http import HttpRequest from django.http import HttpRequest
from django.utils.module_loading import autodiscover_modules from django.utils.module_loading import autodiscover_modules
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
...@@ -144,6 +145,11 @@ class CoreConfig(AppConfig): ...@@ -144,6 +145,11 @@ class CoreConfig(AppConfig):
# Save the associated person to pick up defaults # Save the associated person to pick up defaults
user.person.save() user.person.save()
def user_logged_out(
self, sender: type, request: Optional[HttpRequest], user: "User", **kwargs
) -> None:
messages.success(request, _("You have been logged out successfully."))
@classmethod @classmethod
def get_all_scopes(cls) -> dict[str, str]: def get_all_scopes(cls) -> dict[str, str]:
scopes = { scopes = {
......
...@@ -70,7 +70,7 @@ const apolloOpts = { ...@@ -70,7 +70,7 @@ const apolloOpts = {
} }
// Add a snackbar on all errors returned by the GraphQL endpoint // Add a snackbar on all errors returned by the GraphQL endpoint
// If App is offline, don't add snackbar since only the ping query is active // If App is offline, don't add snackbar since only the ping query is active
if (!vm.$root.offline) { if (!vm.$root.offline && !vm.$root.invalidation) {
vm.$root.snackbarItems.push({ vm.$root.snackbarItems.push({
id: crypto.randomUUID(), id: crypto.randomUUID(),
timeout: 5000, timeout: 5000,
...@@ -79,7 +79,7 @@ const apolloOpts = { ...@@ -79,7 +79,7 @@ const apolloOpts = {
}); });
} }
} }
if (networkError) { if (networkError && !vm.$root.invalidation) {
// Set app offline globally on network errors // Set app offline globally on network errors
// This will cause the offline logic to kick in, starting a ping check or // This will cause the offline logic to kick in, starting a ping check or
// similar recovery strategies depending on the app/navigator state // similar recovery strategies depending on the app/navigator state
......
...@@ -78,12 +78,23 @@ export default { ...@@ -78,12 +78,23 @@ export default {
this.$root.$setPageTitle(title); this.$root.$setPageTitle(title);
// Adapt height of IFrame according to the height of its contents once and observe height changes // Adapt height of IFrame according to the height of its contents once and observe height changes
this.iFrameHeight = if (
this.$refs.contentIFrame.contentDocument.body.scrollHeight; this.$refs.contentIFrame.contentDocument &&
new ResizeObserver(() => { this.$refs.contentIFrame.contentDocument.body
) {
this.iFrameHeight = this.iFrameHeight =
this.$refs.contentIFrame.contentDocument.body.scrollHeight; this.$refs.contentIFrame.contentDocument.body.scrollHeight;
}).observe(this.$refs.contentIFrame.contentDocument.body); new ResizeObserver(() => {
if (
this.$refs.contentIFrame &&
this.$refs.contentIFrame.contentDocument &&
this.$refs.contentIFrame.contentDocument.body
) {
this.iFrameHeight =
this.$refs.contentIFrame.contentDocument.body.scrollHeight;
}
}).observe(this.$refs.contentIFrame.contentDocument.body);
}
this.$root.contentLoading = false; this.$root.contentLoading = false;
}, },
......
...@@ -45,7 +45,10 @@ ...@@ -45,7 +45,10 @@
> >
<v-icon>mdi-update</v-icon> <v-icon>mdi-update</v-icon>
</v-btn> </v-btn>
<div v-if="whoAmI && whoAmI.isAuthenticated" class="d-flex"> <div
v-if="whoAmI && whoAmI.isAuthenticated && whoAmI.person"
class="d-flex"
>
<notification-list v-if="!whoAmI.person.isDummy" /> <notification-list v-if="!whoAmI.person.isDummy" />
<account-menu <account-menu
:account-menu="accountMenu" :account-menu="accountMenu"
...@@ -83,7 +86,7 @@ ...@@ -83,7 +86,7 @@
</div> </div>
<error-page <error-page
v-if="error404" v-if="error404 && !$root.contentLoading"
short-error-message-key="network_errors.error_404" short-error-message-key="network_errors.error_404"
long-error-message-key="network_errors.page_not_found" long-error-message-key="network_errors.page_not_found"
redirect-button-text-key="network_errors.back_to_start" redirect-button-text-key="network_errors.back_to_start"
...@@ -97,6 +100,7 @@ ...@@ -97,6 +100,7 @@
checkPermission($route.meta.permission) || checkPermission($route.meta.permission) ||
$route.name === 'dashboard' $route.name === 'dashboard'
" "
@mounted="routeComponentMounted"
/> />
<error-page <error-page
v-else-if=" v-else-if="
...@@ -253,6 +257,13 @@ export default { ...@@ -253,6 +257,13 @@ export default {
pollInterval: 1000, pollInterval: 1000,
}, },
}, },
methods: {
routeComponentMounted() {
if (!this.$root.isLegacyBaseTemplate) {
this.$root.contentLoading = false;
}
},
},
watch: { watch: {
systemProperties: function (newProperties) { systemProperties: function (newProperties) {
this.$vuetify.theme.themes.light.primary = this.$vuetify.theme.themes.light.primary =
...@@ -272,7 +283,7 @@ export default { ...@@ -272,7 +283,7 @@ export default {
}, },
$route: { $route: {
handler(newRoute) { handler(newRoute) {
if (newRoute.matched.length == 0) { if (newRoute.matched.length === 0) {
this.error404 = true; this.error404 = true;
} else { } else {
this.error404 = false; this.error404 = false;
......
...@@ -65,11 +65,25 @@ const app = new Vue({ ...@@ -65,11 +65,25 @@ const app = new Vue({
render: (h) => h(App), render: (h) => h(App),
data: () => ({ data: () => ({
showCacheAlert: false, showCacheAlert: false,
contentLoading: false, contentLoading: true,
offline: false, offline: false,
backgroundActive: true, backgroundActive: true,
invalidation: false,
snackbarItems: [], snackbarItems: [],
}), }),
computed: {
matchedComponents() {
if (this.$route.matched.length > 0) {
return this.$route.matched.map(
(route) => route.components.default.name
);
}
return [];
},
isLegacyBaseTemplate() {
return this.matchedComponents.includes("LegacyBaseTemplate");
},
},
router, router,
i18n, i18n,
}); });
......
...@@ -20,7 +20,7 @@ const aleksisMixin = { ...@@ -20,7 +20,7 @@ const aleksisMixin = {
}, },
}, },
mounted() { mounted() {
this.$root.contentLoading = false; this.$emit("mounted");
}, },
beforeDestroy() { beforeDestroy() {
// Unregister all safely added event listeners as to not leak them // Unregister all safely added event listeners as to not leak them
......
...@@ -123,15 +123,19 @@ AleksisVue.install = function (Vue) { ...@@ -123,15 +123,19 @@ AleksisVue.install = function (Vue) {
Vue.prototype.$invalidateState = function () { Vue.prototype.$invalidateState = function () {
console.info("Invalidating application state"); console.info("Invalidating application state");
this.invalidation = true;
this.$apollo this.$apollo
.getClient() .getClient()
.resetStore() .resetStore()
.then( .then(
function () { () => {
console.info("GraphQL cache cleared"); console.info("GraphQL cache cleared");
this.invalidation = false;
}, },
function (error) { (error) => {
console.error("Could not clear GraphQL cache:", error); console.error("Could not clear GraphQL cache:", error);
this.invalidation = false;
} }
); );
}; };
...@@ -156,6 +160,12 @@ AleksisVue.install = function (Vue) { ...@@ -156,6 +160,12 @@ AleksisVue.install = function (Vue) {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
this.$router.afterEach((to, from) => { this.$router.afterEach((to, from) => {
if (vm.isLegacyBaseTemplate) {
// Skip resetting loading state for legacy pages
// as they are probably not finished with loading yet
// LegacyBaseTemplate will reset the loading state later
return;
}
vm.contentLoading = false; vm.contentLoading = false;
}); });
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment