Skip to content
Snippets Groups Projects
Commit d2720ef0 authored by Matthias Feyll's avatar Matthias Feyll
Browse files

(ui): refactor rehydration routine subscriptions

parent 4c7056c8
No related branches found
No related tags found
4 merge requests!1196[renovate] Update module golang.org/x/net to v0.32.0,!1195UI: implement add device functionality,!1167Ui refactor style,!1161Ui refactor style
This commit is part of merge request !1161. Comments created here will be created in the context of that merge request.
...@@ -45,13 +45,27 @@ const deviceSlice = createSlice({ ...@@ -45,13 +45,27 @@ const deviceSlice = createSlice({
setActiveTab: (state, action: PayloadAction<DeviceViewTabValues>) => { setActiveTab: (state, action: PayloadAction<DeviceViewTabValues>) => {
state.activeTab = action.payload state.activeTab = action.payload
}, },
setSelectedDevice: (state, action: PayloadAction<Device | null>) => { setSelectedDevice: {
let selectedObject = null; reducer: (state, action: PayloadAction<Device | null, string, { skipListener?: boolean }>) => {
if (action.payload) { // do thing if desired device is already selected
selectedObject = { device: action.payload, mne: null, json: null } if (state.selected?.device.id === action.payload?.id) {
} action.meta.skipListener = true
return
}
let selectedObject = null;
if (action.payload) {
selectedObject = { device: action.payload, mne: null, json: null }
}
state.selected = selectedObject state.selected = selectedObject
},
prepare: (device: Device | null) => {
return {
payload: device,
meta: { skipListener: false } // set to true when needed
}
}
}, },
setSelectedMne: (state, action: PayloadAction<NetworkelementManagedNetworkElement>) => { setSelectedMne: (state, action: PayloadAction<NetworkelementManagedNetworkElement>) => {
if (!state.selected) { if (!state.selected) {
......
...@@ -7,7 +7,8 @@ import { ...@@ -7,7 +7,8 @@ import {
} from '@component/devices/reducer/device.reducer' } from '@component/devices/reducer/device.reducer'
import { createAsyncThunk } from '@reduxjs/toolkit' import { createAsyncThunk } from '@reduxjs/toolkit'
import { addRoutine } from '@shared/reducer/routine.reducer' import { addRoutine } from '@shared/reducer/routine.reducer'
import { Category } from '@shared/types/category.type' import { Category, CategoryType } from '@shared/types/category.type'
import { RoutineHolderSingleton } from '@utils/routine-holder.singleton'
import { RootState } from 'src/stores' import { RootState } from 'src/stores'
import { startListening } from '../../../stores/middleware/listener.middleware' import { startListening } from '../../../stores/middleware/listener.middleware'
...@@ -20,13 +21,14 @@ export const FETCH_MNE_ACTION = 'subscription/device/fetchSelectedMNE' ...@@ -20,13 +21,14 @@ export const FETCH_MNE_ACTION = 'subscription/device/fetchSelectedMNE'
* Triggered by a selectedDevice * Triggered by a selectedDevice
*/ */
startListening({ startListening({
predicate: (action) => setSelectedDevice.match(action) && !!action.payload, predicate: (action) => setSelectedDevice.match(action) && !!action.payload && !action.meta?.skipListener,
effect: async (action, listenerApi) => { effect: async (action, listenerApi) => {
const factory = RoutineHolderSingleton.getInstance();
listenerApi.dispatch( listenerApi.dispatch(
addRoutine({ addRoutine({
thunk: fetchSelectedMneThunk, thunk: factory.getRoutineByName("fetchSelectedMneThunk"),
category: Category.TAB, category: Category.TAB as CategoryType,
payload: action.payload, payload: action.payload as Object,
}) })
) )
}, },
......
import { fetchSelectedMneThunk } from '@component/devices/routines/mne.routine'
import { UtilsProvider } from '@provider/utils.provider' import { UtilsProvider } from '@provider/utils.provider'
import { RoutineHolderSingleton } from '@utils/routine-holder.singleton'
import i18next from 'i18next' import i18next from 'i18next'
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom/client' import ReactDOM from 'react-dom/client'
...@@ -18,6 +20,12 @@ import { persistor, store } from './stores' ...@@ -18,6 +20,12 @@ import { persistor, store } from './stores'
window.env = window.location.hostname === 'localhost' ? 'development' : 'production'; 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( ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode> <React.StrictMode>
<ErrorBoundary fallback={<div>Something went wrong</div>}> <ErrorBoundary fallback={<div>Something went wrong</div>}>
......
...@@ -16,7 +16,7 @@ export const fetchUser = createAsyncThunk('user/fetchUser', (_, thunkAPI) => { ...@@ -16,7 +16,7 @@ export const fetchUser = createAsyncThunk('user/fetchUser', (_, thunkAPI) => {
const matchedUser = response.data.user.find((_user) => _user.name === user.username) const matchedUser = response.data.user.find((_user) => _user.name === user.username)
if (!matchedUser) { if (!matchedUser) {
// TODO proper error handling // TODO proper error handling => logout
throw new Error('No user found with the provided username') throw new Error('No user found with the provided username')
} }
......
import { PayloadAction, createSlice } from '@reduxjs/toolkit' import { PayloadAction, createSlice } from '@reduxjs/toolkit'
import { CategoryType } from '@shared/types/category.type' import { CategoryType } from '@shared/types/category.type'
import { ThunkEntityDTO } from '@shared/types/thunk.type' import { ThunkDTO, ThunkPersist } from '@shared/types/thunk.type'
import { RoutineHolderSingleton } from '@utils/routine-holder.singleton'
import { RoutineManager } from '@utils/routine.manager' import { RoutineManager } from '@utils/routine.manager'
import { REHYDRATE } from 'redux-persist'
import { RootState } from '../../stores' import { RootState } from '../../stores'
import { startListening } from '../../stores/middleware/listener.middleware' import { startListening } from '../../stores/middleware/listener.middleware'
import { setToken } from './user.reducer' import { setToken } from './user.reducer'
export interface ReducerState { export interface ReducerState {
thunks: Record<CategoryType, ThunkEntityDTO | null> thunks: Record<CategoryType, ThunkPersist | null>
} }
const initialState: ReducerState = { const initialState: ReducerState = {
...@@ -23,9 +25,14 @@ const RoutineSlice = createSlice({ ...@@ -23,9 +25,14 @@ const RoutineSlice = createSlice({
name: 'routine', name: 'routine',
initialState, initialState,
reducers: { reducers: {
addRoutine: (state: any, { payload }: PayloadAction<ThunkEntityDTO>) => { addRoutine: (state: any, { payload }: PayloadAction<ThunkDTO>) => {
const newThunk: ThunkEntityDTO = payload const thunk: ThunkPersist = {
state.thunks[payload.category] = newThunk category: payload.category,
payload: payload.payload,
thunkId: payload.thunk.id
}
state.thunks[payload.category] = thunk
}, },
removeAll: (state) => { removeAll: (state) => {
...@@ -48,19 +55,26 @@ startListening({ ...@@ -48,19 +55,26 @@ startListening({
// on rehydrate add all persistet routines // 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. // 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 // at this point we have to figure out how to get the thunk function out of the "string" name
// startListening({ startListening({
// predicate: ({ type }) => type === REHYDRATE, predicate: ({ type }) => type === REHYDRATE,
// effect: async (_, listenerApi) => { effect: async (_, listenerApi) => {
// const { routine } = listenerApi.getState() as RootState const { routine } = listenerApi.getState() as RootState
// for (const [_, thunk] of Object.entries<ThunkEntity>(routine.thunks)) { const routines = RoutineHolderSingleton.getInstance()
// if (!thunk) {
// continue Object.values(routine.thunks)
// } .filter(thunk => !!thunk)
// const dto: ThunkEntityDTO = thunk .forEach(thunk => {
// listenerApi.dispatch(addRoutine(dto)) const container = routines.getRoutineById(thunk.thunkId)
// } const dto: ThunkDTO = {
// }, category: thunk.category,
// }) payload: thunk.payload,
thunk: container
}
listenerApi.dispatch(addRoutine(dto))
})
},
})
/** /**
* Add new routine * Add new routine
...@@ -72,8 +86,8 @@ startListening({ ...@@ -72,8 +86,8 @@ startListening({
startListening({ startListening({
predicate: (action) => addRoutine.match(action), predicate: (action) => addRoutine.match(action),
effect: async (action, listenerApi) => { effect: async (action, listenerApi) => {
const { thunk } = action.payload as ThunkEntityDTO const { thunk } = action.payload as ThunkDTO
const subscription = await listenerApi.dispatch(thunk(action.payload.payload)) const subscription = await listenerApi.dispatch(thunk.func(action.payload.payload))
RoutineManager.add(subscription.payload, action.payload.category) RoutineManager.add(subscription.payload, action.payload.category)
}, },
}) })
......
import { CategoryType } from "./category.type" import { CategoryType } from "./category.type"
export interface ThunkEntityDTO {
thunk: any /**
* 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,
}
export interface ThunkDTO {
thunk: ThunkContainer
payload: Object payload: Object
/** /**
...@@ -9,4 +23,10 @@ export interface ThunkEntityDTO { ...@@ -9,4 +23,10 @@ export interface ThunkEntityDTO {
* New subscription will unsubscribe and overwrite the old one * New subscription will unsubscribe and overwrite the old one
*/ */
category: CategoryType category: CategoryType
} }
\ No newline at end of file
export interface ThunkPersist {
thunkId: number,
payload: Object
category: CategoryType
}
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
...@@ -38,6 +38,8 @@ export const RoutineManager = (() => { ...@@ -38,6 +38,8 @@ export const RoutineManager = (() => {
...state.routines, ...state.routines,
[category]: entity [category]: entity
} }
infoMessage("Routine subscribed to category " + category)
return true return true
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment