RC-10: add new values, reducers and selectors for history and favorites to user slice
This commit is contained in:
@@ -6,6 +6,63 @@ import { showMessage } from 'app/store/fuse/messageSlice';
|
|||||||
import { logoutUser, setUser } from 'app/store/userSlice';
|
import { logoutUser, setUser } from 'app/store/userSlice';
|
||||||
import { authService, firebase } from '../services';
|
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();
|
const AuthContext = React.createContext();
|
||||||
|
|
||||||
function AuthProvider({ children }) {
|
function AuthProvider({ children }) {
|
||||||
@@ -25,14 +82,19 @@ function AuthProvider({ children }) {
|
|||||||
authService.onAuthStateChanged((authUser) => {
|
authService.onAuthStateChanged((authUser) => {
|
||||||
dispatch(showMessage({ message: 'Signing...' }));
|
dispatch(showMessage({ message: 'Signing...' }));
|
||||||
if (authUser) {
|
if (authUser) {
|
||||||
|
const storageUser = JSON.parse(localStorage.user ?? '{}');
|
||||||
authService
|
authService
|
||||||
.getUserData(authUser.uid)
|
.getUserData(authUser.uid)
|
||||||
.then((user) => {
|
.then((user) => {
|
||||||
if (user) {
|
if (user) {
|
||||||
success(user, 'Signed in');
|
success({ ...user, ...storageUser }, 'Signed in');
|
||||||
} else {
|
} else {
|
||||||
|
// First login
|
||||||
const { displayName, photoURL, email } = authUser;
|
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) => {
|
.catch((error) => {
|
||||||
|
|||||||
@@ -1,35 +1,86 @@
|
|||||||
/* eslint import/no-extraneous-dependencies: off */
|
import browserHistory from '@history';
|
||||||
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
|
|
||||||
import history from '@history';
|
|
||||||
import _ from '@lodash';
|
import _ from '@lodash';
|
||||||
import { setInitialSettings } from 'app/store/fuse/settingsSlice';
|
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
|
||||||
import { showMessage } from 'app/store/fuse/messageSlice';
|
|
||||||
import settingsConfig from 'app/configs/settingsConfig';
|
import settingsConfig from 'app/configs/settingsConfig';
|
||||||
|
import { showMessage } from 'app/store/fuse/messageSlice';
|
||||||
|
import { setInitialSettings } from 'app/store/fuse/settingsSlice';
|
||||||
import { authService } from '../services';
|
import { authService } from '../services';
|
||||||
|
|
||||||
export const setUser = createAsyncThunk('user/setUser', async (user, { dispatch, getState }) => {
|
export const setUser = createAsyncThunk('user/setUser', async (user) => {
|
||||||
/*
|
// You can redirect the logged-in user to a specific route depending on his role
|
||||||
You can redirect the logged-in user to a specific route depending on his role
|
|
||||||
*/
|
|
||||||
if (user.loginRedirectUrl) {
|
if (user.loginRedirectUrl) {
|
||||||
settingsConfig.loginRedirectUrl = user.loginRedirectUrl; // for example 'apps/academy'
|
settingsConfig.loginRedirectUrl = user.loginRedirectUrl; // for example 'apps/academy'
|
||||||
}
|
}
|
||||||
|
|
||||||
return user;
|
return _.merge({}, initialState, user);
|
||||||
});
|
});
|
||||||
|
|
||||||
export const updateUserSettings = createAsyncThunk(
|
export const updateUserSettings = createAsyncThunk(
|
||||||
'user/updateSettings',
|
'user/updateSettings',
|
||||||
async (settings, { dispatch, getState }) => {
|
async (settings, { dispatch, getState }) => {
|
||||||
const { user } = 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;
|
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) => {
|
export const logoutUser = () => async (dispatch, getState) => {
|
||||||
const { user } = getState();
|
const { user } = getState();
|
||||||
|
|
||||||
@@ -38,7 +89,7 @@ export const logoutUser = () => async (dispatch, getState) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
history.push({
|
browserHistory.push({
|
||||||
pathname: '/',
|
pathname: '/',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -53,14 +104,8 @@ export const updateUserData = (user) => async (dispatch, getState) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
authService
|
// eslint-disable-next-line consistent-return
|
||||||
.updateUserData(user)
|
return authService.updateUserData(user);
|
||||||
.then(() => {
|
|
||||||
dispatch(showMessage({ message: 'User data saved' }));
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
dispatch(showMessage({ message: error.message }));
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
@@ -69,6 +114,8 @@ const initialState = {
|
|||||||
displayName: 'John Doe',
|
displayName: 'John Doe',
|
||||||
email: 'johndoe@withinpixels.com',
|
email: 'johndoe@withinpixels.com',
|
||||||
},
|
},
|
||||||
|
history: [],
|
||||||
|
favorites: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const userSlice = createSlice({
|
const userSlice = createSlice({
|
||||||
@@ -76,15 +123,21 @@ const userSlice = createSlice({
|
|||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
userLoggedOut: (state, action) => initialState,
|
userLoggedOut: (state, action) => initialState,
|
||||||
|
userHistoryUpdated: (state, action) => ({ ...state, history: action.payload }),
|
||||||
},
|
},
|
||||||
extraReducers: {
|
extraReducers: {
|
||||||
[updateUserSettings.fulfilled]: (state, action) => action.payload,
|
[updateUserSettings.fulfilled]: (state, action) => action.payload,
|
||||||
|
[updateUserFavorites.fulfilled]: (state, action) => ({ ...state, favorites: action.payload }),
|
||||||
[setUser.fulfilled]: (state, action) => 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 selectUser = ({ user }) => user;
|
||||||
|
|
||||||
|
export const selectUserHistory = ({ user }) => user.history;
|
||||||
|
|
||||||
|
export const selectUserFavorites = ({ user }) => user.favorites;
|
||||||
|
|
||||||
export default userSlice.reducer;
|
export default userSlice.reducer;
|
||||||
|
|||||||
Reference in New Issue
Block a user