| @@ -33,6 +33,8 @@ | |||
| "react-scripts": "4.0.3", | |||
| "react-select": "^4.3.1", | |||
| "redux": "^4.1.0", | |||
| "redux-persist": "^6.0.0", | |||
| "redux-persist-transform-filter": "0.0.20", | |||
| "redux-saga": "^1.1.3", | |||
| "sass": "^1.34.1", | |||
| "web-vitals": "^1.1.2", | |||
| @@ -1,24 +1,28 @@ | |||
| import React from 'react'; | |||
| import ReactDOM from 'react-dom'; | |||
| import { Provider } from 'react-redux'; | |||
| import { HelmetProvider } from 'react-helmet-async'; | |||
| import React from "react"; | |||
| import ReactDOM from "react-dom"; | |||
| import { Provider } from "react-redux"; | |||
| import { HelmetProvider } from "react-helmet-async"; | |||
| import './main.scss'; | |||
| import App from './App'; | |||
| import store from './store'; | |||
| import "./main.scss"; | |||
| import App from "./App"; | |||
| import { store, persistor } from "./store"; | |||
| import './i18n'; | |||
| import ColorModeProvider from './context/ColorModeContext'; | |||
| import "./i18n"; | |||
| import ColorModeProvider from "./context/ColorModeContext"; | |||
| import { PersistGate } from "redux-persist/integration/react"; | |||
| ReactDOM.render( | |||
| <HelmetProvider> | |||
| <React.StrictMode> | |||
| <Provider store={store}> | |||
| <ColorModeProvider> | |||
| <PersistGate loading={null} persistor={persistor}> | |||
| <button onClick={() => console.log(store.getState())}>Dugme</button> | |||
| <App /> | |||
| </PersistGate> | |||
| </ColorModeProvider> | |||
| </Provider> | |||
| </React.StrictMode> | |||
| </HelmetProvider>, | |||
| document.getElementById('root'), | |||
| document.getElementById("root") | |||
| ); | |||
| @@ -1,3 +1,4 @@ | |||
| import { createLoadingType } from '../actionHelpers'; | |||
| export const APP_LOADING = createLoadingType('APP_LOADING'); | |||
| export const UPDATE_LOADER = createLoadingType("UPDATE_LOADER") | |||
| @@ -5,10 +5,11 @@ import rootSaga from './saga'; | |||
| import loadingMiddleware from './middleware/loadingMiddleware'; | |||
| import requestStatusMiddleware from './middleware/requestStatusMiddleware'; | |||
| import internalServerErrorMiddleware from './middleware/internalServerErrorMiddleware'; | |||
| import persistStore from 'redux-persist/es/persistStore'; | |||
| const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; | |||
| const sagaMiddleware = createSagaMiddleware(); | |||
| export default createStore( | |||
| export const store = createStore( | |||
| rootReducer, | |||
| composeEnhancers( | |||
| applyMiddleware( | |||
| @@ -19,5 +20,6 @@ export default createStore( | |||
| ), | |||
| ), | |||
| ); | |||
| export const persistor = persistStore(store); | |||
| sagaMiddleware.run(rootSaga); | |||
| @@ -1,12 +1,43 @@ | |||
| import { combineReducers } from 'redux'; | |||
| import loginReducer from './login/loginReducer'; | |||
| import loadingReducer from './loading/loadingReducer'; | |||
| import userReducer from './user/userReducer'; | |||
| import randomDataReducer from './randomData/randomDataReducer'; | |||
| import { combineReducers } from "redux"; | |||
| import loginReducer from "./login/loginReducer"; | |||
| import loadingReducer from "./loading/loadingReducer"; | |||
| import userReducer from "./user/userReducer"; | |||
| import randomDataReducer from "./randomData/randomDataReducer"; | |||
| import storage from "redux-persist/lib/storage"; | |||
| import createFilter from "redux-persist-transform-filter"; | |||
| import persistReducer from "redux-persist/es/persistReducer"; | |||
| const loginPersistConfig = { | |||
| key: "login", | |||
| storage: storage, | |||
| transform: [createFilter("login", ["email", "token", "errorMessage"])], | |||
| }; | |||
| const userPersistConfig = { | |||
| key: "user", | |||
| storage: storage, | |||
| transform: [createFilter("user", ["user"])], | |||
| }; | |||
| const randomDataPersistConfig = { | |||
| key: "randomData", | |||
| storage: storage, | |||
| transform: [ | |||
| createFilter("randomData", [ | |||
| "items", | |||
| "filteredItems", | |||
| "count", | |||
| "page", | |||
| "itemsPerPage", | |||
| "filter", | |||
| "sort", | |||
| ]), | |||
| ], | |||
| }; | |||
| export default combineReducers({ | |||
| login: loginReducer, | |||
| user: userReducer, | |||
| loading:loadingReducer, | |||
| randomData: randomDataReducer | |||
| login: persistReducer(loginPersistConfig, loginReducer), | |||
| user: persistReducer(userPersistConfig, userReducer), | |||
| loading: loadingReducer, | |||
| randomData: persistReducer(randomDataPersistConfig, randomDataReducer), | |||
| }); | |||
| @@ -1,17 +1,20 @@ | |||
| import createReducer from '../../utils/createReducer'; | |||
| import { APP_LOADING } from '../../actions/app/appActionConstants'; | |||
| import { RESET_LOGIN_STATE } from '../../actions/login/loginActionConstants'; | |||
| import createReducer from "../../utils/createReducer"; | |||
| import { | |||
| APP_LOADING, | |||
| UPDATE_LOADER, | |||
| } from "../../actions/app/appActionConstants"; | |||
| import { RESET_LOGIN_STATE } from "../../actions/login/loginActionConstants"; | |||
| const initialState = { | |||
| [APP_LOADING]: true, | |||
| }; | |||
| export default createReducer( | |||
| { | |||
| UPDATE_LOADER: updateLoader, | |||
| [UPDATE_LOADER]: updateLoader, | |||
| [APP_LOADING]: updateAppReadyLoader, | |||
| [RESET_LOGIN_STATE]: setAppNotReady, | |||
| }, | |||
| initialState, | |||
| initialState | |||
| ); | |||
| function updateLoader(state, action) { | |||
| @@ -0,0 +1,28 @@ | |||
| import storage from "redux-persist/lib/storage"; | |||
| const createStorageKey = (key) => `persist:${key}`; | |||
| export const LOGIN_KEY = createStorageKey(`login`); | |||
| export const USER_KEY = createStorageKey(`user`); | |||
| export const RANDOM_DATA_KEY = createStorageKey(`randomData`); | |||
| export const getFromStorage = async (key) => { | |||
| const item = await storage.getItem(key); | |||
| if (!item) { | |||
| return Promise.reject("Item is not found!"); | |||
| } | |||
| //Deserialization of whole item | |||
| const itemJson = JSON.parse(item); | |||
| //Deserialization of each property (it must be done when dealing with persistor) | |||
| const itemProperties = Object.getOwnPropertyNames(itemJson); | |||
| itemProperties.forEach((property) => { | |||
| itemJson[property] = JSON.parse(itemJson[property]); | |||
| }); | |||
| return Promise.resolve(itemJson); | |||
| }; | |||