From 37652f1d05b2b6beac2edc4edd2422954c29317c Mon Sep 17 00:00:00 2001 From: sashtje Date: Sun, 8 Oct 2023 19:21:16 +0300 Subject: [PATCH] fix: fix saving user and theme --- src/app/App.tsx | 14 ++++++--- .../ThemeProvider/ui/ThemeProvider.tsx | 14 +++++++-- src/entities/User/api/userApi.ts | 7 +++++ src/entities/User/index.ts | 1 + .../User/model/services/initAuthData.ts | 29 +++++++++++++++++++ src/entities/User/model/slice/userSlice.ts | 21 +++++++------- .../loginByUsername/loginByUsername.ts | 3 -- .../authByUsername/ui/LoginForm/LoginForm.tsx | 7 ++++- src/shared/lib/hooks/useTheme/useTheme.ts | 3 +- 9 files changed, 77 insertions(+), 22 deletions(-) create mode 100644 src/entities/User/model/services/initAuthData.ts diff --git a/src/app/App.tsx b/src/app/App.tsx index d0d28330..11e5df46 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -1,20 +1,26 @@ import { Suspense, useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useSelector } from 'react-redux'; import { Navbar, Sidebar } from '@/widgets'; import { classNames } from '@/shared/lib/classNames'; -import { getUserInited, userActions } from '@/entities/User'; +import { getUserInited, initAuthData } from '@/entities/User'; +import { useAppDispatch } from '@/shared/lib/hooks/useAppDispatch/useAppDispatch'; +import { PageLoader } from '@/widgets/PageLoader'; import { AppRouter } from './providers/router'; export function App() { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const inited = useSelector(getUserInited); useEffect(() => { - dispatch(userActions.initAuthData()); + dispatch(initAuthData()); }, [dispatch]); + if (!inited) { + return ; + } + return (
diff --git a/src/app/providers/ThemeProvider/ui/ThemeProvider.tsx b/src/app/providers/ThemeProvider/ui/ThemeProvider.tsx index f231b36f..53771353 100644 --- a/src/app/providers/ThemeProvider/ui/ThemeProvider.tsx +++ b/src/app/providers/ThemeProvider/ui/ThemeProvider.tsx @@ -1,4 +1,4 @@ -import { ReactNode, useMemo, useState } from 'react'; +import { ReactNode, useEffect, useMemo, useState } from 'react'; import { ThemeContext } from '@/shared/lib/context/ThemeContext'; import { Theme } from '@/shared/const/theme'; @@ -11,8 +11,16 @@ interface ThemeProviderProps { export const ThemeProvider = (props: ThemeProviderProps) => { const { children, initialTheme } = props; - const { theme: defaultTheme = Theme.LIGHT } = useJsonSettings(); - const [theme, setTheme] = useState(initialTheme || defaultTheme); + const { theme: defaultTheme } = useJsonSettings(); + const [isThemeInited, setThemeInited] = useState(false); + const [theme, setTheme] = useState(initialTheme || defaultTheme || Theme.LIGHT); + + useEffect(() => { + if (!isThemeInited && !initialTheme && defaultTheme) { + setTheme(defaultTheme); + setThemeInited(true); + } + }, [defaultTheme, initialTheme, isThemeInited]); const defaultProps = useMemo( () => ({ diff --git a/src/entities/User/api/userApi.ts b/src/entities/User/api/userApi.ts index d95755be..bad524c3 100644 --- a/src/entities/User/api/userApi.ts +++ b/src/entities/User/api/userApi.ts @@ -19,7 +19,14 @@ const userApi = rtkApi.injectEndpoints({ }, }), }), + getUserDataById: build.query({ + query: (userId) => ({ + url: `/users/${userId}`, + method: 'GET', + }), + }), }), }); export const setJsonSettingsMutation = userApi.endpoints.setJsonSettings.initiate; +export const getUserDataByIdQuery = userApi.endpoints.getUserDataById.initiate; diff --git a/src/entities/User/index.ts b/src/entities/User/index.ts index 401c3ca6..cc42a9ee 100644 --- a/src/entities/User/index.ts +++ b/src/entities/User/index.ts @@ -10,3 +10,4 @@ export { export { UserRole } from './model/consts/consts'; export { useJsonSettingByKey, useJsonSettings } from './model/selectors/jsonSettings/jsonSettings'; export { saveJsonSettings } from './model/services/saveJsonSettings'; +export { initAuthData } from './model/services/initAuthData'; diff --git a/src/entities/User/model/services/initAuthData.ts b/src/entities/User/model/services/initAuthData.ts new file mode 100644 index 00000000..3cce70b5 --- /dev/null +++ b/src/entities/User/model/services/initAuthData.ts @@ -0,0 +1,29 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; + +import { ThunkConfig } from '@/app/providers/StoreProvider'; +import { USER_LOCALSTORAGE_KEY } from '@/shared/const/localStorage'; + +import { getUserDataByIdQuery } from '../../api/userApi'; +import { User } from '../types/user'; + +export const initAuthData = createAsyncThunk>( + 'user/initAuthData', + async (_, thunkAPI) => { + const { rejectWithValue, dispatch } = thunkAPI; + + const userId = localStorage.getItem(USER_LOCALSTORAGE_KEY); + + if (!userId) { + return rejectWithValue(''); + } + + try { + const response = await dispatch(getUserDataByIdQuery(userId)).unwrap(); + + return response; + } catch (e) { + console.log(e); + return rejectWithValue(''); + } + }, +); diff --git a/src/entities/User/model/slice/userSlice.ts b/src/entities/User/model/slice/userSlice.ts index 8f7e3067..76434042 100644 --- a/src/entities/User/model/slice/userSlice.ts +++ b/src/entities/User/model/slice/userSlice.ts @@ -3,6 +3,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { USER_LOCALSTORAGE_KEY } from '@/shared/const/localStorage'; import { setFeatureFlags } from '@/shared/lib/features'; +import { initAuthData } from '../services/initAuthData'; import { saveJsonSettings } from '../services/saveJsonSettings'; import { User, UserSchema } from '../types/user'; import { JsonSettings } from '../types/jsonSettings'; @@ -18,16 +19,7 @@ export const userSlice = createSlice({ setAuthData: (state, action: PayloadAction) => { state.authData = action.payload; setFeatureFlags(action.payload.features); - }, - initAuthData: (state) => { - const userData = localStorage.getItem(USER_LOCALSTORAGE_KEY); - - if (userData) { - const user = JSON.parse(userData) as User; - state.authData = user; - setFeatureFlags(user.features); - } - state._inited = true; + localStorage.setItem(USER_LOCALSTORAGE_KEY, action.payload.id); }, logout: (state) => { state.authData = undefined; @@ -44,6 +36,15 @@ export const userSlice = createSlice({ } }, ); + builder + .addCase(initAuthData.fulfilled, (state, { payload }: PayloadAction) => { + state.authData = payload; + setFeatureFlags(payload.features); + state._inited = true; + }) + .addCase(initAuthData.rejected, (state) => { + state._inited = true; + }); }, }); diff --git a/src/features/authByUsername/model/services/loginByUsername/loginByUsername.ts b/src/features/authByUsername/model/services/loginByUsername/loginByUsername.ts index 8f10b187..b36b84ea 100644 --- a/src/features/authByUsername/model/services/loginByUsername/loginByUsername.ts +++ b/src/features/authByUsername/model/services/loginByUsername/loginByUsername.ts @@ -1,7 +1,6 @@ import { createAsyncThunk } from '@reduxjs/toolkit'; import { User, userActions } from '@/entities/User'; -import { USER_LOCALSTORAGE_KEY } from '@/shared/const/localStorage'; import { ThunkConfig } from '@/app/providers/StoreProvider'; interface LoginByUsernameProps { @@ -21,8 +20,6 @@ export const loginByUsername = createAsyncThunk { const password = useSelector(getLoginPassword); const isLoading = useSelector(getLoginIsLoading); const error = useSelector(getLoginError); + const { setTheme } = useTheme(); const onChangeUsername = useCallback( (value: string) => { @@ -59,9 +61,12 @@ export const LoginForm = memo((props: LoginFormProps) => { const result = await dispatch(loginByUsername({ username, password })); if (result.meta.requestStatus === 'fulfilled') { + if (typeof result?.payload === 'object' && result?.payload?.jsonSettings?.theme) { + setTheme?.(result.payload.jsonSettings.theme); + } onSuccess(); } - }, [onSuccess, dispatch, password, username]); + }, [onSuccess, dispatch, password, username, setTheme]); return ( diff --git a/src/shared/lib/hooks/useTheme/useTheme.ts b/src/shared/lib/hooks/useTheme/useTheme.ts index 93dd9ae1..356309d4 100644 --- a/src/shared/lib/hooks/useTheme/useTheme.ts +++ b/src/shared/lib/hooks/useTheme/useTheme.ts @@ -7,6 +7,7 @@ import { ThemeContext } from '../../context/ThemeContext'; interface UseThemeResult { toggleTheme: (saveAction: (theme: Theme) => void) => void; theme: Theme; + setTheme?: (theme: Theme) => void; } export function useTheme(): UseThemeResult { @@ -38,5 +39,5 @@ export function useTheme(): UseThemeResult { saveAction?.(newTheme); }; - return { theme: theme || Theme.LIGHT, toggleTheme }; + return { theme: theme || Theme.LIGHT, toggleTheme, setTheme }; }