diff --git a/frontend/src/App.vue b/frontend/src/App.vue index eada9cdb1735089098109dd633a3fd30f5a0fdea..5fba23531559515d75d87cf120d1c69287782823 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -9,6 +9,7 @@ </template> <script> +import {UI} from '@/store/modules/ui' import AutoLogout from '@/components/AutoLogout' export default { @@ -16,7 +17,7 @@ export default { name: 'app', computed: { darkMode () { - return this.$store.state.ui.darkMode + return UI.state.darkMode } } } diff --git a/frontend/src/components/BaseLayout.vue b/frontend/src/components/BaseLayout.vue index 87a1485174e18ffbc677a134398a15598d774210..b02083b3b0c8423a6270fc8309d920fff25d4cc6 100644 --- a/frontend/src/components/BaseLayout.vue +++ b/frontend/src/components/BaseLayout.vue @@ -85,7 +85,7 @@ <script> import { mapGetters, mapState } from 'vuex' -import {uiMut} from '@/store/modules/ui' +import {UI} from '@/store/modules/ui' import { mapStateToComputedGetterSetter } from '@/util/helpers' import UserOptions from '@/components/UserOptions' @@ -101,20 +101,20 @@ export default { userRole: state => state.authentication.user.role }), ...mapStateToComputedGetterSetter({ - pathPrefix: 'ui', + pathPrefix: 'UI', items: [ { name: 'darkMode', - mutation: uiMut.SET_DARK_MODE + mutation: UI.SET_DARK_MODE }, { name: 'darkModeUnlocked', - mutation: uiMut.SET_DARK_MODE_UNLOCKED + mutation: UI.SET_DARK_MODE_UNLOCKED }, { name: 'mini', path: 'sideBarCollapsed', - mutation: uiMut.SET_SIDEBAR_COLLAPSED + mutation: UI.SET_SIDEBAR_COLLAPSED } ] }), diff --git a/frontend/src/components/SubmissionType.vue b/frontend/src/components/SubmissionType.vue index 253ebad3794f388ef37b34eba0d99673d90f2713..65f694bd08a8db6a027e6443427555abca458925 100644 --- a/frontend/src/components/SubmissionType.vue +++ b/frontend/src/components/SubmissionType.vue @@ -32,8 +32,9 @@ </template> <script> -import {mapState} from 'vuex' import {highlight} from 'highlight.js' +import {UI} from '@/store/modules/ui' + export default { name: 'submission-type', props: { @@ -72,9 +73,6 @@ export default { } }, computed: { - ...mapState({ - darkMode: state => state.ui.darkMode - }), typeItems () { let items = [ { @@ -96,7 +94,7 @@ export default { return highlight(this.programmingLanguage, this.solution, true).value }, backgroundColor () { - return this.darkMode ? 'grey' : '#F3F3F3' + return UI.state.darkMode ? 'grey' : '#F3F3F3' } } } diff --git a/frontend/src/components/submission_notes/base/FeedbackComment.vue b/frontend/src/components/submission_notes/base/FeedbackComment.vue index 989689e6f2fe3cc4e7a9e7d13c27355ccdbe2b4d..29dd835708ecd5e1d01e5bb39b822e4eb327d0e6 100644 --- a/frontend/src/components/submission_notes/base/FeedbackComment.vue +++ b/frontend/src/components/submission_notes/base/FeedbackComment.vue @@ -37,6 +37,7 @@ <script> import {mapState} from 'vuex' +import {UI} from '@/store/modules/ui' import {subNotesMut, subNotesNamespace} from '@/store/modules/submission-notes' export default { @@ -77,8 +78,7 @@ export default { }, computed: { ...mapState({ - markedForDeletion: state => state.submissionNotes.commentsMarkedForDeletion, - darkMode: state => state.ui.darkMode + markedForDeletion: state => state.submissionNotes.commentsMarkedForDeletion }), parsedCreated () { if (this.created) { @@ -94,7 +94,7 @@ export default { return 'orange' }, backgroundColor () { - return this.darkMode ? 'grey' : '#F3F3F3' + return UI.state.darkMode ? 'grey' : '#F3F3F3' } }, methods: { diff --git a/frontend/src/components/subscriptions/SubscriptionList.vue b/frontend/src/components/subscriptions/SubscriptionList.vue index 2e8b7f72b1a704b90fea1c8bd582e3bd2aa0b371..d62b05a93266d1e036fc6646cfda881966ff1aa1 100644 --- a/frontend/src/components/subscriptions/SubscriptionList.vue +++ b/frontend/src/components/subscriptions/SubscriptionList.vue @@ -29,6 +29,7 @@ <script> import {mapGetters, mapActions, mapState} from 'vuex' +import {UI} from '@/store/modules/ui' import SubscriptionCreation from '@/components/subscriptions/SubscriptionCreation' import SubscriptionForList from '@/components/subscriptions/SubscriptionForList' import SubscriptionsForStage from '@/components/subscriptions/SubscriptionsForStage' @@ -52,16 +53,13 @@ export default { } }, computed: { - ...mapState({ - sideBarCollapsed: state => state.ui.sideBarCollapsed - }), ...mapGetters({ subscriptions: 'getSubscriptionsGroupedByType', stages: 'availableStages', stagesReadable: 'availableStagesReadable' }), showDetail () { - return !this.sidebar || (this.sidebar && !this.sideBarCollapsed) + return !this.sidebar || (this.sidebar && !UI.state.sideBarCollapsed) } }, methods: { diff --git a/frontend/src/pages/student/StudentLayout.vue b/frontend/src/pages/student/StudentLayout.vue index f8bc7d5b0187d9f2f44191ceebed6642ecca9b34..2ab8b56116a9efaac2c552a829353a135919e368 100644 --- a/frontend/src/pages/student/StudentLayout.vue +++ b/frontend/src/pages/student/StudentLayout.vue @@ -42,6 +42,7 @@ <script> import { mapState } from 'vuex' +import {UI} from '@/store/modules/ui' import BaseLayout from '@/components/BaseLayout' import ExamInformation from '@/components/student/ExamInformation' export default { @@ -63,9 +64,9 @@ export default { moduleReference: state => state.studentPage.exam.moduleReference, submissions: state => state.studentPage.submissionsForList, exam: state => state.studentPage.exam, - visited: state => state.studentPage.visited, - mini: state => state.ui.sideBarCollapsed + visited: state => state.studentPage.visited }), + mini: function () { return UI.state.mini }, submissionNavItems: function () { return this.submissions.map((sub, index) => { return { diff --git a/frontend/src/store/getters.ts b/frontend/src/store/getters.ts index c36c0d45d106f7a8292c35429312ebc9789aa546..6d4aafded1ef2b08b0cb013ea40397db819499bd 100644 --- a/frontend/src/store/getters.ts +++ b/frontend/src/store/getters.ts @@ -10,12 +10,12 @@ const correctedGetter = mb.read(function corrected (state) { }) const submissionGetter = mb.read(function submission (state) { return (pk: string) => { - return state.submissions[pk] + return state.submissions[pk] } }) const submissionTypeGetter = mb.read(function submissionType (state) { return (pk: string) => { - return state.submissionTypes[pk] + return state.submissionTypes[pk] } }) diff --git a/frontend/src/store/modules/ui.ts b/frontend/src/store/modules/ui.ts index b1760cea54c4250af73939c126485a1ff7e768e3..776384463438ccb8bfb091c4118d568a3e350abd 100644 --- a/frontend/src/store/modules/ui.ts +++ b/frontend/src/store/modules/ui.ts @@ -1,14 +1,14 @@ -import {Module} from 'vuex' +import {getStoreBuilder} from 'vuex-typex' import {RootState} from '@/store/store' -interface UiState { +export interface UIState { sideBarCollapsed: boolean darkMode: boolean darkModeUnlocked: boolean showJumbotron: boolean } -function initialState (): UiState { +function initialState (): UIState { return { sideBarCollapsed: false, darkMode: false, @@ -17,29 +17,30 @@ function initialState (): UiState { } } -export const uiMut = Object.freeze({ - SET_SIDEBAR_COLLAPSED: 'SET_SIDEBAR_COLLAPSED', - SET_DARK_MODE: 'SET_DARK_MODE', - SET_DARK_MODE_UNLOCKED: 'SET_DARK_MODE_UNLOCKED', - SET_SHOW_JUMBOTRON: 'SET_SHOW_JUMBOTRON' -}) - -const ui: Module<UiState, RootState> = { - state: initialState(), - mutations: { - [uiMut.SET_SIDEBAR_COLLAPSED] (state, collapsed: boolean) { - state.sideBarCollapsed = collapsed - }, - [uiMut.SET_DARK_MODE] (state, val: boolean) { - state.darkMode = val - }, - [uiMut.SET_DARK_MODE_UNLOCKED] (state, val: boolean) { - state.darkModeUnlocked = val - }, - [uiMut.SET_SHOW_JUMBOTRON] (state, val: boolean) { - state.showJumbotron = val - } - } +const mb = getStoreBuilder<RootState>().module('UI', initialState()) + + +const stateGetter = mb.state() + + +function SET_SIDEBAR_COLLAPSED (state: UIState, collapsed: boolean) { + state.sideBarCollapsed = collapsed +} +function SET_DARK_MODE (state: UIState, val: boolean) { + state.darkMode = val +} +function SET_DARK_MODE_UNLOCKED (state: UIState, val: boolean) { + state.darkModeUnlocked = val +} +function SET_SHOW_JUMBOTRON (state: UIState, val: boolean) { + state.showJumbotron = val } -export default ui +export const UI = { + get state() { return stateGetter() }, + + SET_SIDEBAR_COLLAPSED: mb.commit(SET_SIDEBAR_COLLAPSED), + SET_DARK_MODE: mb.commit(SET_DARK_MODE), + SET_DARK_MODE_UNLOCKED: mb.commit(SET_DARK_MODE_UNLOCKED), + SET_SHOW_JUMBOTRON: mb.commit(SET_SHOW_JUMBOTRON) +} diff --git a/frontend/src/store/store.ts b/frontend/src/store/store.ts index c7379d0ccccc7f37af47ccf7a6588ef9a4855ed3..e6ca81843fe422c872d6b243693148cac3df715c 100644 --- a/frontend/src/store/store.ts +++ b/frontend/src/store/store.ts @@ -6,7 +6,6 @@ import {getStoreBuilder} from 'vuex-typex' import studentPage from './modules/student-page' import submissionNotes from './modules/submission-notes' import authentication from './modules/authentication' -import ui from './modules/ui' import subscriptions from './modules/subscriptions' import feedbackTable from './modules/feedback_list/feedback-table' import feedbackSearchOptions from './modules/feedback_list/feedback-search-options' @@ -14,6 +13,10 @@ import feedbackSearchOptions from './modules/feedback_list/feedback-search-optio import './mutations' import './actions' import './getters' + +import './modules/ui' +import {UIState} from './modules/ui' + import {lastInteraction} from '@/store/plugins/lastInteractionPlugin' import { Exam, Feedback, @@ -25,7 +28,7 @@ import { Vue.use(Vuex) -export interface RootState { +export interface RootInitialState { lastAppInteraction: number examTypes: {[pk: string]: Exam} feedback: {[pk: number]: Feedback} @@ -37,7 +40,11 @@ export interface RootState { tutors: Array<Tutor> } -export function initialState (): RootState { +export interface RootState extends RootInitialState{ + UI: UIState +} + +export function initialState (): RootInitialState { return { lastAppInteraction: Date.now(), examTypes: {}, @@ -64,7 +71,6 @@ const store = getStoreBuilder<RootState>().vuexStore({ authentication, studentPage, submissionNotes, - ui, subscriptions, feedbackTable, feedbackSearchOptions @@ -76,12 +82,13 @@ const store = getStoreBuilder<RootState>().vuexStore({ // authentication.token is manually saved since using it with this plugin caused issues // when manually reloading the page paths: Object.keys(initialState()).concat( - ['ui', 'studentPage', 'submissionNotes', 'feedbackSearchOptions', 'subscriptions', + ['UI', 'studentPage', 'submissionNotes', 'feedbackSearchOptions', 'subscriptions', 'authentication.user', 'authentication.jwtTimeDelta', 'authentication.tokenCreationTime']) }), lastInteraction], - state: initialState() + // TODO is there a better way than casting the initialState ? + state: <RootState> initialState() }) export default store diff --git a/frontend/src/util/helpers.ts b/frontend/src/util/helpers.ts index 11f1d8fe8112c194580df5c05456b0aaf6392822..8d8fe65cd457ba1dfd64a123c41fce51c59d14a8 100644 --- a/frontend/src/util/helpers.ts +++ b/frontend/src/util/helpers.ts @@ -1,5 +1,6 @@ import {AxiosError} from "axios"; import {Dispatch} from "vuex"; +import {MutationHandler} from "vuex-typex"; export function nameSpacer (namespace: string) { return function (commitType: string) { @@ -24,9 +25,9 @@ export function getObjectValueByPath (obj: any, path: string): any { return obj } -interface GetSetPair { +interface GetSetPair<P> { get: () => any, - set: (val: object) => void + set: (val: P) => void } /** @@ -38,15 +39,19 @@ interface GetSetPair { * @param namespace to prepend the mutation type with * @returns {*} */ -export function createComputedGetterSetter ( +export function createComputedGetterSetter<P> ( {path, mutation, namespace}: - {path: string, mutation: string, namespace:string}): GetSetPair { + {path: string, mutation: string | ((payload?: P) => void), namespace:string}): GetSetPair<P> { return { get (): any { return getObjectValueByPath((this as any).$store.state, path) }, - set (val: object): void { - (this as any).$store.commit(`${namespace ? namespace + '/' : ''}${mutation}`, val) + set (val: P): void { + if (typeof mutation === "string") { + (this as any).$store.commit(`${namespace ? namespace + '/' : ''}${mutation}`, val) + } else { + mutation(val) + } } } } @@ -58,7 +63,7 @@ interface StateMapperItem { } interface MappedState { - [key: string]: GetSetPair + [key: string]: GetSetPair<any> } /** diff --git a/frontend/yarn.lock b/frontend/yarn.lock index b91413a1e4b1c625208da60fd2b699dfa1edaa67..924b2c18bbde969f55b052c846e5041e60918174 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -6931,8 +6931,9 @@ vuex-persistedstate@^2.5.4: "vuex-typex@https://github.com/robinhundt/vuex-typex.git": version "3.0.1" - resolved "https://github.com/robinhundt/vuex-typex.git#72c5eb30ac7fe7c4bf657ebe4e40e115f309fa39" + resolved "https://github.com/robinhundt/vuex-typex.git#3e0da4d1e01eef6e0eed609ef97cb036ca8a692f" dependencies: + deepmerge "^2.1.1" vuex "^3.0.0" vuex@^3.0.0, vuex@^3.0.1: