import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import type { RootState } from './store'; import { DatabaseStatus } from '../broker/wsState'; import { getParam, setParam, URLParams } from '../api/url'; import { cloneDeep } from 'lodash-es'; import { SaveState, SaveStateAuthorizationHeaders } from 'ts-common'; /** Message format of the error message from the backend */ export type ErrorMessage = { errorStatus: any; queryID: any; }; /** Cache type */ export type SessionCacheI = { currentSaveState?: string; // id of the current save state saveStates: Record<string, SaveState>; saveStatesAuthorization: Record<string, SaveStateAuthorizationHeaders>; fetchingSaveStates: boolean; testedSaveState: Record<string, DatabaseStatus>; }; const newStateAuthorizationHeaders: SaveStateAuthorizationHeaders = { query: { W: true, R: true }, database: { W: true, R: true }, visualization: { W: true, R: true }, schema: { W: true, R: true }, savestate: { W: true, R: true }, }; // Define the initial state using that type export const initialState: SessionCacheI = { currentSaveState: undefined, saveStates: {}, saveStatesAuthorization: {}, fetchingSaveStates: true, // default to true to prevent flashing of the UI testedSaveState: {}, }; export const sessionSlice = createSlice({ name: 'session', initialState: initialState, reducers: { setFetchingSaveStates: (state: SessionCacheI, action: PayloadAction<boolean>) => { state.fetchingSaveStates = action.payload; }, selectSaveState: (state: SessionCacheI, action: PayloadAction<string | undefined>) => { if (action.payload && action.payload in state.saveStates) { state.currentSaveState = action.payload; } else { state.currentSaveState = undefined; } setParam(URLParams.saveState, action.payload); }, updateSelectedSaveState: (state: SessionCacheI, action: PayloadAction<SaveState>) => { if (state.currentSaveState === action.payload.id && state.currentSaveState) state.saveStates[state.currentSaveState] = action.payload; }, updateSaveStateList: (state: SessionCacheI, action: PayloadAction<SaveState[]>) => { // Does NOT clear the states, just adds in new data const newState: Record<string, SaveState> = {}; if (!action.payload) return; action.payload.forEach((ss: SaveState) => { newState[ss.id] = ss; }); state.saveStates = { ...state.saveStates, ...newState }; const paramSaveState = getParam(URLParams.saveState); if (!state.currentSaveState) { if (paramSaveState && paramSaveState in state.saveStates) { state.currentSaveState = paramSaveState; } else if (Object.keys(state.saveStates).length > 0) { state.currentSaveState = Object.keys(state.saveStates)[0]; } else state.currentSaveState = undefined; } }, setSaveStateList: (state: SessionCacheI, action: PayloadAction<SaveState[]>) => { // Clears the states and puts in new data const newState: Record<string, SaveState> = {}; action.payload.forEach(ss => { newState[ss.id] = ss; }); state.saveStates = newState; const paramSaveState = getParam(URLParams.saveState); if (!state.currentSaveState) { if (paramSaveState && paramSaveState in state.saveStates) { state.currentSaveState = paramSaveState; } else if (Object.keys(state.saveStates).length > 0) { state.currentSaveState = Object.keys(state.saveStates)[0]; } else state.currentSaveState = undefined; } }, addSaveState: (state: SessionCacheI, action: PayloadAction<SaveState>) => { if (state.saveStates === undefined) state.saveStates = {}; state.saveStates[action.payload.id] = action.payload; state.currentSaveState = action.payload.id; if (!state.saveStatesAuthorization[action.payload.id]) { state.saveStatesAuthorization[action.payload.id] = cloneDeep(newStateAuthorizationHeaders); } }, deleteSaveState: (state: SessionCacheI, action: PayloadAction<string>) => { delete state.saveStates[action.payload]; delete state.saveStatesAuthorization[action.payload]; if (state.currentSaveState === action.payload) { if (Object.keys(state.saveStates).length > 0) state.currentSaveState = Object.keys(state.saveStates)[0]; else state.currentSaveState = undefined; } }, testedSaveState: (state: SessionCacheI, action: PayloadAction<string>) => { state.testedSaveState = { ...state.testedSaveState, [action.payload]: DatabaseStatus.tested }; }, setStateAuthorization: (state: SessionCacheI, action: PayloadAction<{ id: string; authorization: SaveStateAuthorizationHeaders }>) => { state.saveStatesAuthorization[action.payload.id] = action.payload.authorization; }, deleteAuthorization: (state: SessionCacheI, action: PayloadAction<string>) => { delete state.saveStatesAuthorization[action.payload]; }, }, }); export const { selectSaveState, deleteSaveState, updateSaveStateList, setSaveStateList, addSaveState, testedSaveState, setFetchingSaveStates, updateSelectedSaveState, setStateAuthorization, deleteAuthorization, } = sessionSlice.actions; // Other code such as selectors can use the imported `RootState` type export const sessionCacheState = (state: RootState) => state.sessionCache; export const activeSaveState = (state: RootState): SaveState => state.sessionCache.saveStates?.[state.sessionCache.currentSaveState!]; export const activeSaveStateAuthorization = (state: RootState): SaveStateAuthorizationHeaders => state.sessionCache.saveStatesAuthorization?.[state.sessionCache.currentSaveState!] || newStateAuthorizationHeaders; export default sessionSlice.reducer;