diff --git a/react-ui/src/components/devices/reducer/device.reducer.ts b/react-ui/src/components/devices/reducer/device.reducer.ts index 4afcba7886f6f251b029d4eade2cddaf4599545c..cea12fbc9c972d2e94469357c566c0ba1f321b51 100755 --- a/react-ui/src/components/devices/reducer/device.reducer.ts +++ b/react-ui/src/components/devices/reducer/device.reducer.ts @@ -5,6 +5,7 @@ import { } from '@api/api' import { DeviceViewTabValues } from '@component/devices/view/device.view.tabs' import { createSlice, PayloadAction } from '@reduxjs/toolkit' +import { REHYDRATE } from 'redux-persist' import { RootState } from 'src/stores' import '../routines/index' import { startListening } from '/src/stores/middleware/listener.middleware' @@ -32,6 +33,13 @@ const initialState: DeviceSliceState = { selected: null, } +interface SetSelectedDeviceType { + device: Device | null, + options?: { + bypassCheck: boolean + } +} + const deviceSlice = createSlice({ name: 'device', initialState, @@ -46,24 +54,33 @@ const deviceSlice = createSlice({ state.activeTab = action.payload }, setSelectedDevice: { - reducer: (state, action: PayloadAction<Device | null, string, { skipListener?: boolean }>) => { - // do thing if desired device is already selected - if (state.selected?.device.id === action.payload?.id) { - action.meta.skipListener = true + reducer: (state, { payload, meta }: PayloadAction<SetSelectedDeviceType, string, { skipListener?: boolean }>) => { + /** + * Do nothing if desired device is already selected + * Bypass the check if the flag is set to true. We + * use this bypass to trigger the listener functions + * accordingly + */ + if (!payload?.options?.bypassCheck && state.selected?.device.id === payload.device?.id) { + meta.skipListener = true return } - let selectedObject = null; - if (action.payload) { - selectedObject = { device: action.payload, mne: null, json: null } + if (!payload.device) { + throw Error('Passed null device as parameter while bypassing the safety check') + } + + let selectedObject: SelectedObject | null = null; + if (payload) { + selectedObject = { device: payload.device, mne: null, json: null } } state.selected = selectedObject }, - prepare: (device: Device | null) => { + prepare: (payload) => { return { - payload: device, - meta: { skipListener: false } // set to true when needed + payload, + meta: { skipListener: false } } } }, @@ -107,7 +124,24 @@ startListening({ } // if there are no devices available do set null - const newDevices = action.payload?.[0] || null - listenerApi.dispatch(setSelectedDevice(newDevices)) + const device = action.payload?.[0] || null + listenerApi.dispatch(setSelectedDevice({ device } as SetSelectedDeviceType)) }, }) + + +/** + * On startup reset the selected device + */ +startListening({ + predicate: ({ type }: any) => type === REHYDRATE, + effect: async (_, listenerApi) => { + const { device: state } = listenerApi.getState() as RootState + const device = state.selected?.device + if (!device) { + return + } + + listenerApi.dispatch(setSelectedDevice({ device, options: { bypassCheck: true } } as SetSelectedDeviceType)) + }, +}) \ No newline at end of file diff --git a/react-ui/src/components/devices/routines/device.routine.ts b/react-ui/src/components/devices/routines/device.routine.ts index f9b3e1ee243279a111b67e0b133aeb261cfe0a03..ef92b1c8e9141fa922040487149282ab0a861a3a 100755 --- a/react-ui/src/components/devices/routines/device.routine.ts +++ b/react-ui/src/components/devices/routines/device.routine.ts @@ -20,7 +20,7 @@ export const fetchDevicesThunk = createAsyncThunk(FETCH_DEVICE_ACTION, (_, thunk const { user } = thunkApi.getState() as RootState if (!user.user?.roles) { - throw new Error('Background MNE fetching failed! User data is missing. Reload page or logout and login again') + throw new Error('Background device fetching failed! User data is missing. Reload page or logout and login again') // TODO } diff --git a/react-ui/src/components/devices/routines/mne.routine.ts b/react-ui/src/components/devices/routines/mne.routine.ts index 876317dd2e56f0da069ee454fc138376682a168c..2928693075a75bf7af1044d44a91488b56e10b23 100755 --- a/react-ui/src/components/devices/routines/mne.routine.ts +++ b/react-ui/src/components/devices/routines/mne.routine.ts @@ -8,12 +8,12 @@ import { import { createAsyncThunk } from '@reduxjs/toolkit' import { addRoutine } from '@shared/reducer/routine.reducer' import { Category, CategoryType } from '@shared/types/category.type' -import { RoutineHolderSingleton } from '@utils/routine-holder.singleton' import { RootState } from 'src/stores' import { startListening } from '../../../stores/middleware/listener.middleware' export const FETCH_MNE_ACTION = 'subscription/device/fetchSelectedMNE' + /** * #0 * Trigger fetch MNE (#1) @@ -21,14 +21,16 @@ export const FETCH_MNE_ACTION = 'subscription/device/fetchSelectedMNE' * Triggered by a selectedDevice */ startListening({ - predicate: (action) => setSelectedDevice.match(action) && !!action.payload && !action.meta?.skipListener, + predicate: (action) => setSelectedDevice.match(action) && !!action.payload.device && !action.meta?.skipListener, effect: async (action, listenerApi) => { - const factory = RoutineHolderSingleton.getInstance(); + + const device = action.payload.device + listenerApi.dispatch( addRoutine({ - thunk: factory.getRoutineByName("fetchSelectedMneThunk"), + thunk: fetchSelectedMneThunk, category: Category.TAB as CategoryType, - payload: action.payload as Object, + payload: device, }) ) }, diff --git a/react-ui/src/components/devices/view_model/device.table.viewmodel.ts b/react-ui/src/components/devices/view_model/device.table.viewmodel.ts index df75953288d659c17a53a744c7c4ba015f0dcab9..5769780ffd9299d1ff0bc2ec0bf4edbae421c7cf 100755 --- a/react-ui/src/components/devices/view_model/device.table.viewmodel.ts +++ b/react-ui/src/components/devices/view_model/device.table.viewmodel.ts @@ -26,7 +26,7 @@ export const useDeviceTableViewModel = (searchRef) => { }, []); const trClickHandler = (device: Device) => { - dispatch(setSelectedDevice(device)); + dispatch(setSelectedDevice({ device })); } diff --git a/react-ui/src/index.tsx b/react-ui/src/index.tsx index 559e1bc54369e597163c25c111fea8cd4d5bd8a7..3697efd07379da0126d042b824b9c3d857bb6fed 100755 --- a/react-ui/src/index.tsx +++ b/react-ui/src/index.tsx @@ -1,6 +1,4 @@ -import { fetchSelectedMneThunk } from '@component/devices/routines/mne.routine' import { UtilsProvider } from '@provider/utils.provider' -import { RoutineHolderSingleton } from '@utils/routine-holder.singleton' import i18next from 'i18next' import React from 'react' import ReactDOM from 'react-dom/client' @@ -20,12 +18,6 @@ import { persistor, store } from './stores' window.env = window.location.hostname === 'localhost' ? 'development' : 'production'; -const factory = RoutineHolderSingleton.getInstance(); -factory.registerRoutine("fetchSelectedMneThunk", { - func: fetchSelectedMneThunk, - id: 0 -}); - ReactDOM.createRoot(document.getElementById("root")).render( <React.StrictMode> <ErrorBoundary fallback={<div>Something went wrong</div>}> diff --git a/react-ui/src/shared/reducer/routine.reducer.ts b/react-ui/src/shared/reducer/routine.reducer.ts index f8c8c31ebca79c89056a787c428998cb4e21bcd1..5e9c3401a441b2e7718564a9dfc00282ee95783c 100755 --- a/react-ui/src/shared/reducer/routine.reducer.ts +++ b/react-ui/src/shared/reducer/routine.reducer.ts @@ -1,9 +1,7 @@ import { PayloadAction, createSlice } from '@reduxjs/toolkit' import { CategoryType } from '@shared/types/category.type' import { ThunkDTO, ThunkPersist } from '@shared/types/thunk.type' -import { RoutineHolderSingleton } from '@utils/routine-holder.singleton' import { RoutineManager } from '@utils/routine.manager' -import { REHYDRATE } from 'redux-persist' import { RootState } from '../../stores' import { startListening } from '../../stores/middleware/listener.middleware' import { setToken } from './user.reducer' @@ -52,30 +50,6 @@ startListening({ }, }) -// on rehydrate add all persistet routines -// TODO -> thunk does not have the thunk function object due to its coming from the store that ignores the value. -// at this point we have to figure out how to get the thunk function out of the "string" name -startListening({ - predicate: ({ type }) => type === REHYDRATE, - effect: async (_, listenerApi) => { - const { routine } = listenerApi.getState() as RootState - const routines = RoutineHolderSingleton.getInstance() - - Object.values(routine.thunks) - .filter(thunk => !!thunk) - .forEach(thunk => { - const container = routines.getRoutineById(thunk.thunkId) - const dto: ThunkDTO = { - category: thunk.category, - payload: thunk.payload, - thunk: container - } - - listenerApi.dispatch(addRoutine(dto)) - }) - }, -}) - /** * Add new routine * @@ -87,12 +61,17 @@ startListening({ predicate: (action) => addRoutine.match(action), effect: async (action, listenerApi) => { const { thunk } = action.payload as ThunkDTO - const subscription = await listenerApi.dispatch(thunk.func(action.payload.payload)) + const subscription = await listenerApi.dispatch(thunk(action.payload.payload)) + + if (subscription.error) { + throw new Error('Error during routine execution: ' + subscription.error.message) + } + RoutineManager.add(subscription.payload, action.payload.category) }, }) -// unsubscribe old routine +// unsubscribe old routine that is in the same category startListening({ predicate: (action) => addRoutine.match(action), effect: async (action, listenerApi) => { diff --git a/react-ui/src/shared/types/thunk.type.ts b/react-ui/src/shared/types/thunk.type.ts index 90b8460393f308fe99177a97b6333f9f0d3e6cf6..9143871f0ac8bce1ef58a92ecba24d993a9fbe12 100644 --- a/react-ui/src/shared/types/thunk.type.ts +++ b/react-ui/src/shared/types/thunk.type.ts @@ -1,21 +1,12 @@ import { CategoryType } from "./category.type" -/** - * Contains the thunk function combined with a unique id - * Giving a explicit id (and not the index of the object) - * prevents missmatching the function if a update changes - * the RoutineList object length - */ -export interface ThunkContainer { - id: number - func: any, -} +// The actual Thunk type is hard to determine because is very generic +export type ThunkFunc = any export interface ThunkDTO { - thunk: ThunkContainer - + thunk: ThunkFunc payload: Object /** diff --git a/react-ui/src/shared/utils/routine-holder.singleton.ts b/react-ui/src/shared/utils/routine-holder.singleton.ts deleted file mode 100644 index 47332ab0bce45ea7660298095f541c4f40c34043..0000000000000000000000000000000000000000 --- a/react-ui/src/shared/utils/routine-holder.singleton.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { ThunkContainer } from "@shared/types/thunk.type"; - -interface LocalThunkContainer { - container: ThunkContainer, - name: string -} - -export class RoutineHolderSingleton { - private static instance: RoutineHolderSingleton; - private routineList: Array<LocalThunkContainer> = [] - - private constructor() { } - - static getInstance(): RoutineHolderSingleton { - if (!RoutineHolderSingleton.instance) { - RoutineHolderSingleton.instance = new RoutineHolderSingleton(); - } - return RoutineHolderSingleton.instance; - } - - registerRoutine(name: string, thunk: ThunkContainer) { - this.routineList = [...this.routineList, { container: thunk, name }]; - } - - getRoutineById(id: number): ThunkContainer { - const routine = this.routineList.find((thunk) => thunk.container.id === id) - - if (!routine) { - throw new Error('') - // TODO - } - - return routine.container; - } - - - getRoutineByName(name: string): ThunkContainer { - const routine = this.routineList.find((thunk) => thunk.name === name) - - if (!routine) { - throw new Error('') - // TODO - } - - return routine.container; - } -} \ No newline at end of file