diff --git a/src/app/contexts/AuthContext.js b/src/app/contexts/AuthContext.js index 7cf9d2f..5718efe 100644 --- a/src/app/contexts/AuthContext.js +++ b/src/app/contexts/AuthContext.js @@ -6,6 +6,63 @@ import { showMessage } from 'app/store/fuse/messageSlice'; import { logoutUser, setUser } from 'app/store/userSlice'; import { authService, firebase } from '../services'; +const cards = [ + { + id: '123', + image: + 'https://images.unsplash.com/photo-1600585154340-be6161a56a0c?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80', + title: '6 Via delle Crosarolle, Crosarolle, Veneto, Crosarolle, Veneto', + category: 'buy', + status: 'new', + favorite: false, + update: '12.12.2022', + statistics: [ + { subject: 'Monthly Cash Flow', value: '$ 78,000', mode: 'extra_positive' }, + { subject: 'Cash on Cash Return', value: '78%', mode: 'positive' }, + { subject: 'Selling Prise', value: '$500,000', mode: '' }, + { subject: 'Cash Out of Pocket', value: '$125,000', mode: '' }, + { subject: 'Annual Revenue', value: '$5,000', mode: '' }, + { subject: 'Annual Net Profit', value: '+$50,000', mode: 'extra_positive' }, + ], + }, + { + id: '456', + image: + 'https://images.unsplash.com/photo-1600585154340-be6161a56a0c?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80', + title: '6 Via delle Crosarolle, Crosarolle, Veneto', + category: 'buy', + status: 'new', + favorite: false, + update: '12.12.2022', + statistics: [ + { subject: 'Monthly Cash Flow', value: '$ 78,000', mode: 'extra_negative' }, + { subject: 'Cash on Cash Return', value: '78%', mode: 'negative' }, + { subject: 'Selling Prise', value: '$500,000', mode: '' }, + { subject: 'Cash Out of Pocket', value: '$125,000', mode: '' }, + { subject: 'Annual Revenue', value: '$5,000', mode: '' }, + { subject: 'Annual Net Profit', value: '+$50,000', mode: 'extra_negative' }, + ], + }, + { + id: '789', + image: + 'https://images.unsplash.com/photo-1600585154340-be6161a56a0c?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80', + title: '6 Via delle Crosarolle, Crosarolle, Veneto', + category: 'rent', + status: 'new', + favorite: false, + update: '12.12.2022', + statistics: [ + { subject: 'Monthly Cash Flow', value: '$ 78,000', mode: 'positive' }, + { subject: 'Cash on Cash Return', value: '78%', mode: 'positive' }, + { subject: 'Selling Prise', value: '$500,000', mode: '' }, + { subject: 'Cash Out of Pocket', value: '$125,000', mode: '' }, + { subject: 'Annual Revenue', value: '$5,000', mode: '' }, + { subject: 'Annual Net Profit', value: '+$50,000', mode: 'extra_positive' }, + ], + }, +]; + const AuthContext = React.createContext(); function AuthProvider({ children }) { @@ -25,14 +82,19 @@ function AuthProvider({ children }) { authService.onAuthStateChanged((authUser) => { dispatch(showMessage({ message: 'Signing...' })); if (authUser) { + const storageUser = JSON.parse(localStorage.user ?? '{}'); authService .getUserData(authUser.uid) .then((user) => { if (user) { - success(user, 'Signed in'); + success({ ...user, ...storageUser }, 'Signed in'); } else { + // First login const { displayName, photoURL, email } = authUser; - success({ role: 'user', data: { displayName, photoURL, email } }, 'Signed in'); + success( + { role: 'user', data: { displayName, photoURL, email }, ...storageUser }, + 'Signed in' + ); } }) .catch((error) => { diff --git a/src/app/store/userSlice.js b/src/app/store/userSlice.js index 002b421..a65492e 100644 --- a/src/app/store/userSlice.js +++ b/src/app/store/userSlice.js @@ -1,35 +1,86 @@ -/* eslint import/no-extraneous-dependencies: off */ -import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; -import history from '@history'; +import browserHistory from '@history'; import _ from '@lodash'; -import { setInitialSettings } from 'app/store/fuse/settingsSlice'; -import { showMessage } from 'app/store/fuse/messageSlice'; +import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; import settingsConfig from 'app/configs/settingsConfig'; +import { showMessage } from 'app/store/fuse/messageSlice'; +import { setInitialSettings } from 'app/store/fuse/settingsSlice'; import { authService } from '../services'; -export const setUser = createAsyncThunk('user/setUser', async (user, { dispatch, getState }) => { - /* - You can redirect the logged-in user to a specific route depending on his role - */ +export const setUser = createAsyncThunk('user/setUser', async (user) => { + // You can redirect the logged-in user to a specific route depending on his role if (user.loginRedirectUrl) { settingsConfig.loginRedirectUrl = user.loginRedirectUrl; // for example 'apps/academy' } - return user; + return _.merge({}, initialState, user); }); export const updateUserSettings = createAsyncThunk( 'user/updateSettings', async (settings, { dispatch, getState }) => { const { user } = getState(); - const newUser = _.merge({}, user, { data: { ...settings } }); + const newUser = _.omit(_.merge({}, user, { data: { ...settings } }), 'history'); - dispatch(updateUserData(newUser)); + dispatch(updateUserData(newUser)) + .then(() => { + dispatch(showMessage({ message: 'User data saved' })); + }) + .catch((error) => { + dispatch(showMessage({ message: error.message })); + }); return newUser; } ); +export const updateUserFavorites = createAsyncThunk( + 'user/updateFavorites', + async (item, { dispatch, getState }) => { + const { user } = getState(); + const hasItemInFavorites = user.favorites.find( + (favoriteItem) => favoriteItem.id === item.id && item.favorite + ); + const hasItemInHistory = user.history.find((history) => history.id === item.id); + + const favorites = hasItemInFavorites + ? user.favorites.filter((favorite) => favorite.id !== item.id) + : [...user.favorites, { ...item, favorite: true }]; + + if (hasItemInHistory) { + const history = user.history.map((historyItem) => { + if (historyItem.id === item.id) { + return { ...historyItem, favorite: !hasItemInFavorites }; + } + + return historyItem; + }); + + dispatch(updateUserHistory(history)); + } + + const newUserData = _.omit({ ...user, favorites }, 'history'); + + dispatch(updateUserData(newUserData)) + .then(() => { + if (hasItemInFavorites) { + dispatch(showMessage({ message: 'The property is removed from favorites' })); + } else { + dispatch(showMessage({ message: 'The property is saved to favorites' })); + } + }) + .catch((error) => { + dispatch(showMessage({ message: error.message })); + }); + + return favorites; + } +); + +export const updateUserHistory = (history) => (dispatch) => { + localStorage.setItem('user', JSON.stringify({ history })); + dispatch(userHistoryUpdated(history)); +}; + export const logoutUser = () => async (dispatch, getState) => { const { user } = getState(); @@ -38,7 +89,7 @@ export const logoutUser = () => async (dispatch, getState) => { return null; } - history.push({ + browserHistory.push({ pathname: '/', }); @@ -53,14 +104,8 @@ export const updateUserData = (user) => async (dispatch, getState) => { return; } - authService - .updateUserData(user) - .then(() => { - dispatch(showMessage({ message: 'User data saved' })); - }) - .catch((error) => { - dispatch(showMessage({ message: error.message })); - }); + // eslint-disable-next-line consistent-return + return authService.updateUserData(user); }; const initialState = { @@ -69,6 +114,8 @@ const initialState = { displayName: 'John Doe', email: 'johndoe@withinpixels.com', }, + history: [], + favorites: [], }; const userSlice = createSlice({ @@ -76,15 +123,21 @@ const userSlice = createSlice({ initialState, reducers: { userLoggedOut: (state, action) => initialState, + userHistoryUpdated: (state, action) => ({ ...state, history: action.payload }), }, extraReducers: { [updateUserSettings.fulfilled]: (state, action) => action.payload, + [updateUserFavorites.fulfilled]: (state, action) => ({ ...state, favorites: action.payload }), [setUser.fulfilled]: (state, action) => action.payload, }, }); -export const { userLoggedOut } = userSlice.actions; +export const { userLoggedOut, userHistoryUpdated } = userSlice.actions; export const selectUser = ({ user }) => user; +export const selectUserHistory = ({ user }) => user.history; + +export const selectUserFavorites = ({ user }) => user.favorites; + export default userSlice.reducer;