Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add toast notifications for favoriting actions #114

Merged
merged 2 commits into from
Mar 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions src/Components/Favorite/Favorite.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,26 @@ class Favorite extends Component {
};
}

componentWillReceiveProps(nextProps) {
this.setState({ loading: nextProps.isLoading });
shouldComponentUpdate(nextProps, nextState) {
let isUpdate = true;

const { compareArray, refKey } = nextProps;
const oldState = this.getSavedState();
const newState = existsInArray(refKey, compareArray);

if (this.state.loading && !nextProps.isLoading) {
// Only update the loading state if current state.loading
// and prop change detected is turning it off
this.setState({
loading: nextProps.isLoading,
});
}

isUpdate = (oldState !== newState) ||
(this.state.loading !== nextState.isLoading) ||
nextProps.hasErrored;

return isUpdate;
}

getSavedState() {
Expand Down Expand Up @@ -177,6 +195,7 @@ Favorite.propTypes = {
hideText: PropTypes.bool,
compareArray: FAVORITE_POSITIONS_ARRAY.isRequired,
isLoading: PropTypes.bool,
hasErrored: PropTypes.bool,
hasBorder: PropTypes.bool,
useLongText: PropTypes.bool,
useButtonClass: PropTypes.bool,
Expand All @@ -190,6 +209,7 @@ Favorite.defaultProps = {
as: 'div',
hideText: false,
isLoading: false,
hasErrored: false,
compareArray: [],
hasBorder: false,
useLongText: false,
Expand Down
13 changes: 13 additions & 0 deletions src/Components/FavoriteMessages/Success.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { POSITION_DETAILS } from '../../Constants/PropTypes';

const Success = ({ pos }) => (
<span>{pos.title} ({pos.position_number}) has been successfully added to Favorites. <Link to="/profile/favorites/">Go to Favorites</Link>.</span>
);

Success.propTypes = {
pos: POSITION_DETAILS.isRequired,
};

export default Success;
11 changes: 11 additions & 0 deletions src/Components/FavoriteMessages/Success.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { shallow } from 'enzyme';
import React from 'react';
import Success from './Success';
import detailsObject from '../../__mocks__/detailsObject';

describe('Success', () => {
it('is defined', () => {
const wrapper = shallow(<Success pos={detailsObject} />);
expect(wrapper).toBeDefined();
});
});
10 changes: 10 additions & 0 deletions src/Constants/SystemMessages.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import FavoriteSuccess from '../Components/FavoriteMessages/Success';

export const DEFAULT_TEXT = 'None listed';

export const NO_ASSIGNMENT_DATE = DEFAULT_TEXT;
Expand Down Expand Up @@ -37,6 +39,14 @@ export const DELETE_BID_ITEM_ERROR = 'Error trying to delete this bid.';
export const ADD_BID_ITEM_SUCCESS = 'Bid successfully added.';
export const ADD_BID_ITEM_ERROR = 'Error trying to add this bid.';

export const ADD_FAVORITE_TITLE = 'Favorite Added';
export const DELETE_FAVORITE_TITLE = 'Favorite Removed';
export const ERROR_FAVORITE_TITLE = 'Favorite Error';
export const DELETE_FAVORITE_SUCCESS = pos => `${pos.title} (${pos.position_number}) has been successfully removed from favorites.`;
export const DELETE_FAVORITE_ERROR = () => "We're experiencing an error attemtping to remove this position to your Favorites. Please try again.";
export const ADD_FAVORITE_SUCCESS = pos => FavoriteSuccess({ pos });
export const ADD_FAVORITE_ERROR = () => "We're experiencing an error attemtping to add this position to your Favorites. Please try again.";

export const ACCEPT_BID_SUCCESS = 'Bid successfully accepted.';
export const ACCEPT_BID_ERROR = 'Error trying to accept this bid.';
export const DECLINE_BID_SUCCESS = 'Bid successfully declined.';
Expand Down
14 changes: 8 additions & 6 deletions src/Containers/Toast/Toast.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ export class Toast extends Component {
}
}

notify({ type = 'success', message = 'Message' }) { // eslint-disable-line
let title;
if (type === 'success') { title = 'Success'; }
if (type === 'error') { title = 'Error'; }
notify({ type = 'success', message = 'Message', title = '' }) { // eslint-disable-line
let title$;
if (type === 'success') { title$ = 'Success'; }
if (type === 'error') { title$ = 'Error'; }
if (title) { title$ = title; }
toast[type](
<Alert type={type} title={title} messages={[{ body: message }]} />,
<Alert type={type} title={title$} messages={[{ body: message }]} />,
);
}

Expand All @@ -36,7 +37,8 @@ export class Toast extends Component {
Toast.propTypes = {
toastData: PropTypes.shape({
type: PropTypes.string,
message: PropTypes.string,
message: PropTypes.node,
title: PropTypes.string,
}),
};

Expand Down
6 changes: 4 additions & 2 deletions src/actions/toast.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
export function toastSuccess(toast) {
export function toastSuccess(toast, title) {
return {
type: 'TOAST_NOTIFICATION_SUCCESS',
toast,
title,
};
}

export function toastError(toast) {
export function toastError(toast, title) {
return {
type: 'TOAST_NOTIFICATION_ERROR',
toast,
title,
};
}
29 changes: 25 additions & 4 deletions src/actions/userProfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { indexOf } from 'lodash';

import api from '../api';
import { favoritePositionsFetchData } from './favoritePositions';
import { toastSuccess, toastError } from './toast';
import * as SystemMessages from '../Constants/SystemMessages';

export function userProfileHasErrored(bool) {
return {
Expand Down Expand Up @@ -110,21 +112,40 @@ export function userProfileToggleFavoritePosition(id, remove, refreshFavorites =
url: `/position/${idString}/favorite/`,
};

/**
* create functions for creating the action and fetching position data to supply to message
*/
// action
const getAction = () => api(config);

// position
const getPosition = () => api.get(`/position/${id}/`);

dispatch(userProfileFavoritePositionIsLoading(true, id));
dispatch(userProfileFavoritePositionHasErrored(false));

api(config)
.then(() => {
axios.all([getAction(), getPosition()])
.then(axios.spread((action, position) => {
const pos = position.data;
const message = remove ?
SystemMessages.DELETE_FAVORITE_SUCCESS(pos) : SystemMessages.ADD_FAVORITE_SUCCESS(pos);
const title = remove ? SystemMessages.DELETE_FAVORITE_TITLE
: SystemMessages.ADD_FAVORITE_TITLE;
const cb = () => userProfileFavoritePositionIsLoading(false, id);
dispatch(userProfileFetchData(true, cb));
dispatch(userProfileFavoritePositionHasErrored(false));
dispatch(toastSuccess(message, title));
if (refreshFavorites) {
dispatch(favoritePositionsFetchData());
}
})
}))
.catch(() => {
dispatch(userProfileFavoritePositionHasErrored(true));
const message = remove ?
SystemMessages.DELETE_FAVORITE_ERROR() : SystemMessages.ADD_FAVORITE_ERROR();
const title = SystemMessages.ERROR_FAVORITE_TITLE;
dispatch(userProfileFavoritePositionIsLoading(false, id));
dispatch(userProfileFavoritePositionHasErrored(true));
dispatch(toastError(message, title));
});
};
}
6 changes: 3 additions & 3 deletions src/reducers/toast/toast.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
export default function toast(state = { type: 'success', message: '' }, action) {
export default function toast(state = { type: 'success', message: '', title: '' }, action) {
switch (action.type) {
case 'TOAST_NOTIFICATION_SUCCESS':
return { type: 'success', message: action.toast };
return { type: 'success', message: action.toast, title: action.title };
case 'TOAST_NOTIFICATION_ERROR':
return { type: 'error', message: action.toast };
return { type: 'error', message: action.toast, title: action.title };
default:
return state;
}
Expand Down