Skip to content

Commit

Permalink
feat(redux): use redux-persist
Browse files Browse the repository at this point in the history
Signed-off-by: Thuan Vo <thvo@redhat.com>
  • Loading branch information
tthvo committed Dec 13, 2022
1 parent e7574d4 commit 2f05779
Show file tree
Hide file tree
Showing 13 changed files with 147 additions and 57 deletions.
4 changes: 2 additions & 2 deletions src/app/Dashboard/AddCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ import {
Text,
TextArea,
TextInput,
Title
Title,
} from '@patternfly/react-core';
import {
CustomWizardNavFunction,
Wizard,
WizardControlStep,
WizardNav,
WizardNavItem,
WizardStep
WizardStep,
} from '@patternfly/react-core/dist/js/next';
import { PlusCircleIcon } from '@patternfly/react-icons';
import * as React from 'react';
Expand Down
10 changes: 5 additions & 5 deletions src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,21 @@ import {
automatedAnalysisDeleteCategoryFiltersIntent,
automatedAnalysisDeleteFilterIntent,
RootState,
StateDispatch
StateDispatch,
} from '@app/Shared/Redux/ReduxStore';
import {
ArchivedRecording,
automatedAnalysisRecordingName,
isGraphQLAuthError,
Recording
Recording,
} from '@app/Shared/Services/Api.service';
import {
CategorizedRuleEvaluations,
FAILED_REPORT_MESSAGE,
NO_RECORDINGS_MESSAGE,
RECORDING_FAILURE_MESSAGE,
RuleEvaluation,
TEMPLATE_UNSUPPORTED_MESSAGE
TEMPLATE_UNSUPPORTED_MESSAGE,
} from '@app/Shared/Services/Report.service';
import { ServiceContext } from '@app/Shared/Services/Services';
import { automatedAnalysisConfigToRecordingAttributes } from '@app/Shared/Services/Settings.service';
Expand Down Expand Up @@ -91,7 +91,7 @@ import {
ToolbarContent,
ToolbarGroup,
ToolbarItem,
Tooltip
Tooltip,
} from '@patternfly/react-core';
import { InfoCircleIcon, OutlinedQuestionCircleIcon, Spinner2Icon, TrashIcon } from '@patternfly/react-icons';
import * as React from 'react';
Expand All @@ -105,7 +105,7 @@ import {
AutomatedAnalysisFiltersCategories,
AutomatedAnalysisGlobalFiltersCategories,
emptyAutomatedAnalysisFilters,
filterAutomatedAnalysis
filterAutomatedAnalysis,
} from './AutomatedAnalysisFilters';
import { clickableAutomatedAnalysisKey, ClickableAutomatedAnalysisLabel } from './ClickableAutomatedAnalysisLabel';
import { AutomatedAnalysisScoreFilter } from './Filters/AutomatedAnalysisScoreFilter';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import {
SliderStepObject,
Text,
TextVariants,
Tooltip
Tooltip,
} from '@patternfly/react-core';
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
Expand Down
26 changes: 19 additions & 7 deletions src/app/Shared/Redux/Configurations/DashboardConfigSlicer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@
* SOFTWARE.
*/

import { getFromLocalStorage } from '@app/utils/LocalStorage';
import { createAction, createReducer } from '@reduxjs/toolkit';
import { persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage'; // localStorage

// Common action string format: "resource(s)/action"
export enum DashboardConfigAction {
Expand Down Expand Up @@ -67,19 +68,30 @@ export const deleteCardIntent = createAction(DashboardConfigAction.CARD_REMOVE,
} as DashboardDeleteConfigActionPayload,
}));

// Initial states are loaded from local storage if there are any
const initialState = {
list: getFromLocalStorage('DASHBOARD_CFG', []) as string[],
export interface CardConfig {
name: string;
props: any;
}

const INITIAL_STATE = {
list: [] as CardConfig[],
};

export const dashboardConfigReducer = createReducer(initialState, (builder) => {
export const dashboardConfigReducer = createReducer(INITIAL_STATE, (builder) => {
builder
.addCase(addCardIntent, (state, { payload }) => {
state.list.splice(payload.idx || 0, 0, payload.name);
state.list.push(payload);
})
.addCase(deleteCardIntent, (state, { payload }) => {
state.list.splice(payload.idx || 0, 1);
});
});

export default dashboardConfigReducer;
export type DashboardConfigState = ReturnType<typeof dashboardConfigReducer>;

const persistConfig = {
key: 'DASHBOARD_CFG',
storage: storage,
};

export default persistReducer<DashboardConfigState>(persistConfig, dashboardConfigReducer);
21 changes: 14 additions & 7 deletions src/app/Shared/Redux/Filters/AutomatedAnalysisFilterSlice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ import {
AutomatedAnalysisGlobalFiltersCategories,
emptyAutomatedAnalysisFilters,
} from '@app/Dashboard/AutomatedAnalysis/AutomatedAnalysisFilters';
import { getFromLocalStorage } from '@app/utils/LocalStorage';
import { createAction, createReducer } from '@reduxjs/toolkit';
import { WritableDraft } from 'immer/dist/internal';
import { persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage'; // localStorage
import { UpdateFilterOptions } from './Common';

// Common action string format: "resource(s)/action"
Expand Down Expand Up @@ -220,15 +221,14 @@ export const deleteAllAutomatedAnalysisFilters = (automatedAnalysisFilter: Targe
};
};

// Initial states are loaded from local storage if there are any
const initialState = {
state: getFromLocalStorage('AUTOMATED_ANALYSIS_FILTERS', {
const INITIAL_STATE = {
state: {
targetFilters: [],
globalFilters: { filters: { Score: 0 } },
}) as AutomatedAnalysisFilterState,
} as AutomatedAnalysisFilterState,
};

export const automatedAnalysisFilterReducer = createReducer(initialState, (builder) => {
export const automatedAnalysisFilterReducer = createReducer(INITIAL_STATE, (builder) => {
builder
.addCase(automatedAnalysisAddGlobalFilterIntent, (state, { payload }) => {
const oldAutomatedAnalysisGlobalFilter = getAutomatedAnalysisGlobalFilter(state.state);
Expand Down Expand Up @@ -322,4 +322,11 @@ export const automatedAnalysisFilterReducer = createReducer(initialState, (build
});
});

export default automatedAnalysisFilterReducer;
export type AutomaticAnalysisFilterState = ReturnType<typeof automatedAnalysisFilterReducer>;

const persistConfig = {
key: 'AUTOMATED_ANALYSIS_FILTERS',
storage: storage,
};

export default persistReducer<AutomaticAnalysisFilterState>(persistConfig, automatedAnalysisFilterReducer);
19 changes: 13 additions & 6 deletions src/app/Shared/Redux/Filters/RecordingFilterSlice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ import {
emptyArchivedRecordingFilters,
RecordingFiltersCategories,
} from '@app/Recordings/RecordingFilters';
import { getFromLocalStorage } from '@app/utils/LocalStorage';
import { createAction, createReducer } from '@reduxjs/toolkit';
import { WritableDraft } from 'immer/dist/internal';
import { persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage'; // localStorage
import { UpdateFilterOptions } from './Common';

// Common action string format: "resource(s)/action"
Expand Down Expand Up @@ -211,12 +212,11 @@ export const deleteAllTargetRecordingFilters = (targetRecordingFilter: TargetRec
};
};

// Initial states are loaded from local storage if there are any
const initialState = {
list: getFromLocalStorage('TARGET_RECORDING_FILTERS', []) as TargetRecordingFilters[],
const INITIAL_STATE = {
list: [] as TargetRecordingFilters[],
};

export const recordingFilterReducer = createReducer(initialState, (builder) => {
export const recordingFilterReducer = createReducer(INITIAL_STATE, (builder) => {
builder
.addCase(addFilterIntent, (state, { payload }) => {
const oldTargetRecordingFilter = getTargetRecordingFilter(state, payload.target);
Expand Down Expand Up @@ -342,4 +342,11 @@ export const recordingFilterReducer = createReducer(initialState, (builder) => {
});
});

export default recordingFilterReducer;
export type RecordingFilterState = ReturnType<typeof recordingFilterReducer>;

const persistConfig = {
key: 'TARGET_RECORDING_FILTERS',
storage: storage,
};

export default persistReducer<RecordingFilterState>(persistConfig, recordingFilterReducer);
20 changes: 10 additions & 10 deletions src/app/Shared/Redux/ReduxStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
* SOFTWARE.
*/

import { saveToLocalStorage } from '@app/utils/LocalStorage';
import { combineReducers, configureStore, PreloadedState } from '@reduxjs/toolkit';
import { FLUSH, PAUSE, PERSIST, persistStore, PURGE, REGISTER, REHYDRATE } from 'redux-persist';
import dashboardConfigReducer, * as dashboardConfigSlice from './Configurations/DashboardConfigSlicer';
import automatedAnalysisFilterReducer, * as automatedAnalysisFilterSlice from './Filters//AutomatedAnalysisFilterSlice';
import recordingFilterReducer, * as recordingFilterSlice from './Filters/RecordingFilterSlice';
Expand Down Expand Up @@ -74,20 +74,20 @@ export const setupStore = (preloadedState?: PreloadedState<RootState>) =>
configureStore({
reducer: rootReducer,
preloadedState,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
// Ignore redux-persist actions: https://redux-toolkit.js.org/usage/usage-guide#use-with-redux-persist
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}),
});

export const store = setupStore();

export const persistor = persistStore(store);

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof rootReducer>;
export type StateDispatch = typeof store.dispatch;
export type Store = ReturnType<typeof setupStore>;

// Add a subscription to save filter states to local storage
// if states change.
// TODO: Optimize with redux-persist
store.subscribe(() => {
saveToLocalStorage('DASHBOARD_CFG', store.getState().dashboardConfigs.list);
saveToLocalStorage('TARGET_RECORDING_FILTERS', store.getState().recordingFilters.list);
saveToLocalStorage('AUTOMATED_ANALYSIS_FILTERS', store.getState().automatedAnalysisFilters.state);
});
42 changes: 23 additions & 19 deletions src/app/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ import { LastLocationProvider, useLastLocation } from 'react-router-last-locatio
import { SessionState } from './Shared/Services/Login.service';
import { useSubscriptions } from './utils/useSubscriptions';
import LoadingView from './LoadingView/LoadingView';
import { PersistGate } from 'redux-persist/integration/react';
import { persistor } from './Shared/Redux/ReduxStore';

let routeFocusTimer: number;
const OVERVIEW = 'Overview';
Expand Down Expand Up @@ -226,25 +228,27 @@ const AppRoutes: React.FunctionComponent<AppRoutesProps> = (props) => {

return (
<LastLocationProvider>
<Suspense fallback={<LoadingView />}>
<Switch>
{showDashboard ? (
flatten(routes).map(({ path, exact, component, title, isAsync }, idx) => (
<RouteWithTitleUpdates
path={path}
exact={exact}
component={component}
key={idx}
title={title}
isAsync={isAsync}
/>
))
) : (
<Login />
)}
<PageNotFound title="404 Page Not Found" />
</Switch>
</Suspense>
<PersistGate persistor={persistor} loading={<LoadingView />}>
<Suspense fallback={<LoadingView />}>
<Switch>
{showDashboard ? (
flatten(routes).map(({ path, exact, component, title, isAsync }, idx) => (
<RouteWithTitleUpdates
path={path}
exact={exact}
component={component}
key={idx}
title={title}
isAsync={isAsync}
/>
))
) : (
<Login />
)}
<PageNotFound title="404 Page Not Found" />
</Switch>
</Suspense>
</PersistGate>
</LastLocationProvider>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,17 @@ describe('<AutomatedAnalysisCard />', () => {
beforeEach(() => {
preloadedState = {
dashboardConfigs: {
_persist: {
version: 1,
rehydrated: false,
},
list: [],
},
recordingFilters: {
_persist: {
version: 1,
rehydrated: false,
},
list: [
{
target: mockTarget.connectUrl,
Expand All @@ -192,6 +200,10 @@ describe('<AutomatedAnalysisCard />', () => {
],
},
automatedAnalysisFilters: {
_persist: {
version: 1,
rehydrated: false,
},
state: {
targetFilters: [],
globalFilters: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,17 @@ describe('<AutomatedAnalysisScoreFilter />', () => {
beforeEach(() => {
preloadedState = {
dashboardConfigs: {
_persist: {
version: 1,
rehydrated: false,
},
list: [],
},
recordingFilters: {
_persist: {
version: 1,
rehydrated: false,
},
list: [
{
target: mockTargetConnectUrl,
Expand All @@ -72,6 +80,10 @@ describe('<AutomatedAnalysisScoreFilter />', () => {
],
},
automatedAnalysisFilters: {
_persist: {
version: 1,
rehydrated: false,
},
state: {
targetFilters: [],
globalFilters: {
Expand Down
12 changes: 12 additions & 0 deletions src/test/Recordings/ActiveRecordingsTable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,17 @@ describe('<ActiveRecordingsTable />', () => {
history.go(-history.length);
preloadedState = {
dashboardConfigs: {
_persist: {
version: 1,
rehydrated: false,
},
list: [],
},
recordingFilters: {
_persist: {
version: 1,
rehydrated: false,
},
list: [
{
target: mockTarget.connectUrl,
Expand All @@ -197,6 +205,10 @@ describe('<ActiveRecordingsTable />', () => {
],
},
automatedAnalysisFilters: {
_persist: {
version: 1,
rehydrated: false,
},
state: {
targetFilters: [],
globalFilters: {
Expand Down
Loading

0 comments on commit 2f05779

Please sign in to comment.