diff --git a/client/src/composables.ts b/client/src/composables.ts index e69de29b..3cb1bfad 100644 --- a/client/src/composables.ts +++ b/client/src/composables.ts @@ -0,0 +1,16 @@ +import { useCourseSessionsStore } from "@/stores/courseSessions"; +import type { CourseSession } from "@/types"; +import { computed } from "vue"; + +export function useCurrentCourseSession(): CourseSession { + const store = useCourseSessionsStore(); + const currentCourseSession = computed(() => store.currentCourseSession); + + // This will always return a defined value, but you should still handle the case where currentCourseSession is undefined in the store. + if (!currentCourseSession.value) { + throw new Error("currentCourseSession is not defined in the store"); + } else { + // return a non-reactive copy of the value + return currentCourseSession.value; + } +} diff --git a/client/src/router/index.ts b/client/src/router/index.ts index 81e2ab16..e0389718 100644 --- a/client/src/router/index.ts +++ b/client/src/router/index.ts @@ -1,7 +1,6 @@ import DashboardPage from "@/pages/DashboardPage.vue"; import LoginPage from "@/pages/LoginPage.vue"; import { redirectToLoginIfRequired, updateLoggedIn } from "@/router/guards"; -import { useAppStore } from "@/stores/app"; import { useCourseSessionsStore } from "@/stores/courseSessions"; import { createRouter, createWebHistory } from "vue-router"; @@ -182,18 +181,19 @@ const router = createRouter({ router.beforeEach(updateLoggedIn); router.beforeEach(redirectToLoginIfRequired); -router.beforeEach((to) => { +router.beforeEach(async (to) => { + // register after login hooks const courseSessionsStore = useCourseSessionsStore(); if (to.params.courseSlug) { courseSessionsStore._currentCourseSlug = to.params.courseSlug as string; + if (!courseSessionsStore.loaded) { + await courseSessionsStore.loadCourseSessionsData(); + } } else { courseSessionsStore._currentCourseSlug = ""; + // load async + courseSessionsStore.loadCourseSessionsData(); } }); -router.afterEach(() => { - const appStore = useAppStore(); - appStore.routingFinished = true; -}); - export default router; diff --git a/client/src/stores/app.ts b/client/src/stores/app.ts deleted file mode 100644 index 29024c29..00000000 --- a/client/src/stores/app.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { defineStore } from "pinia"; - -export type AppState = { - userLoaded: boolean; - routingFinished: boolean; -}; - -export const useAppStore = defineStore({ - id: "app", - state: () => - ({ - userLoaded: false, - routingFinished: false, - } as AppState), - getters: {}, - actions: {}, -}); diff --git a/client/src/stores/courseSessions.ts b/client/src/stores/courseSessions.ts index b76ff5c1..492f3af2 100644 --- a/client/src/stores/courseSessions.ts +++ b/client/src/stores/courseSessions.ts @@ -25,12 +25,14 @@ export type LearningSequenceCircleDocument = { const SELECTED_COURSE_SESSIONS_KEY = "selectedCourseSessionMap"; -function loadCourseSessionsData(reload = false) { - log.debug("loadCourseSessionsData called"); - const courseSessions = ref([]); +export const useCourseSessionsStore = defineStore("courseSessions", () => { + const loaded = ref(false); + const allCourseSessions = ref([]); - async function loadAndUpdate() { - courseSessions.value = await itGetCached(`/api/course/sessions/`, { + async function loadCourseSessionsData(reload = false) { + log.debug("loadCourseSessionsData called"); + + allCourseSessions.value = await itGetCached(`/api/course/sessions/`, { reload: reload, }); @@ -38,7 +40,7 @@ function loadCourseSessionsData(reload = false) { if (userStore.loggedIn) { // TODO: refactor after implementing of Klassenkonzept await Promise.all( - courseSessions.value.map(async (cs) => { + allCourseSessions.value.map(async (cs) => { const users = (await itGetCached(`/api/course/sessions/${cs.id}/users/`, { reload: reload, })) as CourseSessionUser[]; @@ -46,26 +48,14 @@ function loadCourseSessionsData(reload = false) { }) ); - if (!courseSessions.value) { + if (!allCourseSessions.value) { throw `No courseSessionData found for user`; } } + + loaded.value = true; } - loadAndUpdate(); // this will be called asynchronously, but does not block - - // returns the empty sessions array at first, then after loading populates the ref - return { allCourseSessions: courseSessions }; -} - -export const useCourseSessionsStore = defineStore("courseSessions", () => { - // using setup function seems cleaner, see https://pinia.vuejs.org/core-concepts/#setup-stores - - // this will become a state variable (like each other `ref()` - - // store should do own setup, we don't want to have each component initialize it - // that's why we call the load function in here - const { allCourseSessions } = loadCourseSessionsData(); const selectedCourseSessionMap = useLocalStorage( SELECTED_COURSE_SESSIONS_KEY, new Map() @@ -259,6 +249,8 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => { findAssignmentDetails, // only used so that `router.afterEach` can switch it + loadCourseSessionsData, + loaded, _currentCourseSlug, // only used for unit testing diff --git a/client/src/stores/user.ts b/client/src/stores/user.ts index bdbb9496..35c71172 100644 --- a/client/src/stores/user.ts +++ b/client/src/stores/user.ts @@ -2,7 +2,6 @@ import log from "loglevel"; import { bustItGetCache, itGetCached, itPost } from "@/fetchHelpers"; import { loadLocaleMessages, setI18nLanguage } from "@/i18n"; -import { useAppStore } from "@/stores/app"; import { defineStore } from "pinia"; const logoutRedirectUrl = import.meta.env.VITE_LOGOUT_REDIRECT || "/"; @@ -85,11 +84,9 @@ export const useUserStore = defineStore({ }); }, async fetchUser() { - const appStore = useAppStore(); const data = await itGetCached("/api/core/me/"); this.$state = data; this.loggedIn = true; - appStore.userLoaded = true; await setLocale(data.language); }, async setUserLanguages(language: AvailableLanguages) {