From b5d9046291defa8a45616e5eea1ce38daaad6ac8 Mon Sep 17 00:00:00 2001 From: Linda Paiste Date: Sat, 6 May 2023 21:20:30 -0500 Subject: [PATCH] Refactor toast reducer and actions. --- client/components/Nav.jsx | 10 ++----- client/components/Nav.unit.test.jsx | 1 - client/modules/IDE/actions/collections.js | 13 +++------ client/modules/IDE/actions/project.js | 24 ++++++--------- client/modules/IDE/actions/toast.js | 29 +++++-------------- .../IDE/components/AddToCollectionList.jsx | 2 -- .../components/AddToCollectionSketchList.jsx | 9 +----- .../CollectionList/CollectionList.jsx | 2 -- .../CollectionList/CollectionListRow.jsx | 9 +----- client/modules/IDE/components/Editor.jsx | 3 -- client/modules/IDE/components/SketchList.jsx | 9 +----- client/modules/IDE/reducers/toast.js | 29 ++++++++++--------- client/modules/User/actions.js | 5 ++-- client/modules/User/components/Collection.jsx | 9 +----- .../modules/User/components/Notification.jsx | 5 ++-- 15 files changed, 48 insertions(+), 111 deletions(-) diff --git a/client/components/Nav.jsx b/client/components/Nav.jsx index 725117acb1..60c902bbd7 100644 --- a/client/components/Nav.jsx +++ b/client/components/Nav.jsx @@ -102,12 +102,10 @@ class Nav extends React.PureComponent { handleNew() { const { unsavedChanges, warnIfUnsavedChanges } = this.props; if (!unsavedChanges) { - this.props.showToast(1500); - this.props.setToastText('Toast.OpenedNewSketch'); + this.props.showToast('Toast.OpenedNewSketch'); this.props.newProject(); } else if (warnIfUnsavedChanges && warnIfUnsavedChanges()) { - this.props.showToast(1500); - this.props.setToastText('Toast.OpenedNewSketch'); + this.props.showToast('Toast.OpenedNewSketch'); this.props.newProject(); } this.setDropdown('none'); @@ -169,8 +167,7 @@ class Nav extends React.PureComponent { handleLangSelection(event) { this.props.setLanguage(event.target.value); - this.props.showToast(1500); - this.props.setToastText('Toast.LangChange'); + this.props.showToast('Toast.LangChange'); this.setDropdown('none'); } @@ -948,7 +945,6 @@ class Nav extends React.PureComponent { Nav.propTypes = { newProject: PropTypes.func.isRequired, showToast: PropTypes.func.isRequired, - setToastText: PropTypes.func.isRequired, saveProject: PropTypes.func.isRequired, autosaveProject: PropTypes.func.isRequired, cloneProject: PropTypes.func.isRequired, diff --git a/client/components/Nav.unit.test.jsx b/client/components/Nav.unit.test.jsx index 0051cb6409..6349e2e2ed 100644 --- a/client/components/Nav.unit.test.jsx +++ b/client/components/Nav.unit.test.jsx @@ -42,7 +42,6 @@ describe('Nav', () => { stopSketch: jest.fn(), setAllAccessibleOutput: jest.fn(), showToast: jest.fn(), - setToastText: jest.fn(), rootFile: { id: 'root-file' }, diff --git a/client/modules/IDE/actions/collections.js b/client/modules/IDE/actions/collections.js index 69da0f23ba..faffd187bf 100644 --- a/client/modules/IDE/actions/collections.js +++ b/client/modules/IDE/actions/collections.js @@ -2,9 +2,7 @@ import { browserHistory } from 'react-router'; import apiClient from '../../../utils/apiClient'; import * as ActionTypes from '../../../constants'; import { startLoader, stopLoader } from './loader'; -import { setToastText, showToast } from './toast'; - -const TOAST_DISPLAY_TIME_MS = 1500; +import { showToast } from './toast'; // eslint-disable-next-line export function getCollections(username) { @@ -50,8 +48,7 @@ export function createCollection(collection) { dispatch(stopLoader()); const newCollection = response.data; - dispatch(setToastText(`Created "${newCollection.name}"`)); - dispatch(showToast(TOAST_DISPLAY_TIME_MS)); + dispatch(showToast(`Created "${newCollection.name}"`)); const pathname = `/${newCollection.owner.username}/collections/${newCollection.id}`; const location = { pathname, state: { skipSavingPath: true } }; @@ -85,8 +82,7 @@ export function addToCollection(collectionId, projectId) { const collectionName = response.data.name; - dispatch(setToastText(`Added to "${collectionName}`)); - dispatch(showToast(TOAST_DISPLAY_TIME_MS)); + dispatch(showToast(`Added to "${collectionName}`)); return response.data; }) @@ -118,8 +114,7 @@ export function removeFromCollection(collectionId, projectId) { const collectionName = response.data.name; - dispatch(setToastText(`Removed from "${collectionName}`)); - dispatch(showToast(TOAST_DISPLAY_TIME_MS)); + dispatch(showToast(`Removed from "${collectionName}`)); return response.data; }) diff --git a/client/modules/IDE/actions/project.js b/client/modules/IDE/actions/project.js index 9a528a34f6..8f8a5c7ff8 100644 --- a/client/modules/IDE/actions/project.js +++ b/client/modules/IDE/actions/project.js @@ -5,7 +5,7 @@ import isEqual from 'lodash/isEqual'; import apiClient from '../../../utils/apiClient'; import getConfig from '../../../utils/getConfig'; import * as ActionTypes from '../../../constants'; -import { showToast, setToastText } from './toast'; +import { showToast } from './toast'; import { setUnsavedChanges, justOpenedProject, @@ -175,24 +175,21 @@ export function saveProject( dispatch(projectSaveSuccess()); if (!autosave) { if (state.ide.justOpenedProject && state.preferences.autosave) { - dispatch(showToast(5500)); - dispatch(setToastText('Toast.SketchSaved')); + dispatch(showToast('Toast.SketchSaved', 5500)); setTimeout( - () => dispatch(setToastText('Toast.AutosaveEnabled')), + () => dispatch(showToast('Toast.AutosaveEnabled', 5500)), 1500 ); dispatch(resetJustOpenedProject()); } else { - dispatch(showToast(1500)); - dispatch(setToastText('Toast.SketchSaved')); + dispatch(showToast('Toast.SketchSaved')); } } }) .catch((error) => { const { response } = error; dispatch(endSavingProject()); - dispatch(setToastText('Toast.SketchFailedSave')); - dispatch(showToast(1500)); + dispatch(showToast('Toast.SketchFailedSave')); if (response.status === 403) { dispatch(showErrorModal('staleSession')); } else if (response.status === 409) { @@ -225,24 +222,21 @@ export function saveProject( dispatch(projectSaveSuccess()); if (!autosave) { if (state.preferences.autosave) { - dispatch(showToast(5500)); - dispatch(setToastText('Toast.SketchSaved')); + dispatch(showToast('Toast.SketchSaved', 5500)); setTimeout( - () => dispatch(setToastText('Toast.AutosaveEnabled')), + () => dispatch(showToast('Toast.AutosaveEnabled')), 1500 ); dispatch(resetJustOpenedProject()); } else { - dispatch(showToast(1500)); - dispatch(setToastText('Toast.SketchSaved')); + dispatch(showToast('Toast.SketchSaved')); } } }) .catch((error) => { const { response } = error; dispatch(endSavingProject()); - dispatch(setToastText('Toast.SketchFailedSave')); - dispatch(showToast(1500)); + dispatch(showToast('Toast.SketchFailedSave')); if (response.status === 403) { dispatch(showErrorModal('staleSession')); } else { diff --git a/client/modules/IDE/actions/toast.js b/client/modules/IDE/actions/toast.js index 25f472e14b..92ae94a69c 100644 --- a/client/modules/IDE/actions/toast.js +++ b/client/modules/IDE/actions/toast.js @@ -1,23 +1,10 @@ -import * as ActionTypes from '../../../constants'; +import { setToast, hideToast } from '../reducers/toast'; -export function hideToast() { - return { - type: ActionTypes.HIDE_TOAST - }; -} +export const TOAST_DISPLAY_TIME_MS = 1500; -export function showToast(time) { - return (dispatch) => { - dispatch({ - type: ActionTypes.SHOW_TOAST - }); - setTimeout(() => dispatch(hideToast()), time); - }; -} - -export function setToastText(text) { - return { - type: ActionTypes.SET_TOAST_TEXT, - text - }; -} +export const showToast = (text, timeout = TOAST_DISPLAY_TIME_MS) => ( + dispatch +) => { + dispatch(setToast(text)); + setTimeout(() => dispatch(hideToast()), timeout); +}; diff --git a/client/modules/IDE/components/AddToCollectionList.jsx b/client/modules/IDE/components/AddToCollectionList.jsx index 26addfaa34..12bd2fbb80 100644 --- a/client/modules/IDE/components/AddToCollectionList.jsx +++ b/client/modules/IDE/components/AddToCollectionList.jsx @@ -8,7 +8,6 @@ import { withTranslation } from 'react-i18next'; import * as ProjectActions from '../actions/project'; import * as ProjectsActions from '../actions/projects'; import * as CollectionsActions from '../actions/collections'; -import * as ToastActions from '../actions/toast'; import * as SortingActions from '../actions/sorting'; import getSortedCollections from '../selectors/collections'; import Loader from '../../App/components/loader'; @@ -171,7 +170,6 @@ function mapDispatchToProps(dispatch) { CollectionsActions, ProjectsActions, ProjectActions, - ToastActions, SortingActions ), dispatch diff --git a/client/modules/IDE/components/AddToCollectionSketchList.jsx b/client/modules/IDE/components/AddToCollectionSketchList.jsx index e42aff8d60..c3d116e14f 100644 --- a/client/modules/IDE/components/AddToCollectionSketchList.jsx +++ b/client/modules/IDE/components/AddToCollectionSketchList.jsx @@ -7,7 +7,6 @@ import { withTranslation } from 'react-i18next'; // import find from 'lodash/find'; import * as ProjectsActions from '../actions/projects'; import * as CollectionsActions from '../actions/collections'; -import * as ToastActions from '../actions/toast'; import * as SortingActions from '../actions/sorting'; import getSortedSketches from '../selectors/projects'; import Loader from '../../App/components/loader'; @@ -145,13 +144,7 @@ function mapStateToProps(state) { function mapDispatchToProps(dispatch) { return bindActionCreators( - Object.assign( - {}, - ProjectsActions, - CollectionsActions, - ToastActions, - SortingActions - ), + Object.assign({}, ProjectsActions, CollectionsActions, SortingActions), dispatch ); } diff --git a/client/modules/IDE/components/CollectionList/CollectionList.jsx b/client/modules/IDE/components/CollectionList/CollectionList.jsx index 646b9824b5..ef72e6a37f 100644 --- a/client/modules/IDE/components/CollectionList/CollectionList.jsx +++ b/client/modules/IDE/components/CollectionList/CollectionList.jsx @@ -9,7 +9,6 @@ import find from 'lodash/find'; import * as ProjectActions from '../../actions/project'; import * as ProjectsActions from '../../actions/projects'; import * as CollectionsActions from '../../actions/collections'; -import * as ToastActions from '../../actions/toast'; import * as SortingActions from '../../actions/sorting'; import getSortedCollections from '../../selectors/collections'; import Loader from '../../../App/components/loader'; @@ -302,7 +301,6 @@ function mapDispatchToProps(dispatch) { CollectionsActions, ProjectsActions, ProjectActions, - ToastActions, SortingActions ), dispatch diff --git a/client/modules/IDE/components/CollectionList/CollectionListRow.jsx b/client/modules/IDE/components/CollectionList/CollectionListRow.jsx index bb5282027c..f47e1b6235 100644 --- a/client/modules/IDE/components/CollectionList/CollectionListRow.jsx +++ b/client/modules/IDE/components/CollectionList/CollectionListRow.jsx @@ -7,7 +7,6 @@ import { withTranslation } from 'react-i18next'; import * as ProjectActions from '../../actions/project'; import * as CollectionsActions from '../../actions/collections'; import * as IdeActions from '../../actions/ide'; -import * as ToastActions from '../../actions/toast'; import dates from '../../../../utils/formatDate'; import DownFilledTriangleIcon from '../../../../images/down-filled-triangle.svg'; @@ -281,13 +280,7 @@ CollectionListRowBase.defaultProps = { function mapDispatchToPropsSketchListRow(dispatch) { return bindActionCreators( - Object.assign( - {}, - CollectionsActions, - ProjectActions, - IdeActions, - ToastActions - ), + Object.assign({}, CollectionsActions, ProjectActions, IdeActions), dispatch ); } diff --git a/client/modules/IDE/components/Editor.jsx b/client/modules/IDE/components/Editor.jsx index e1899e0bfe..9054dd1081 100644 --- a/client/modules/IDE/components/Editor.jsx +++ b/client/modules/IDE/components/Editor.jsx @@ -58,7 +58,6 @@ import * as ProjectActions from '../actions/project'; import * as EditorAccessibilityActions from '../actions/editorAccessibility'; import * as PreferencesActions from '../actions/preferences'; import * as UserActions from '../../User/actions'; -import * as ToastActions from '../actions/toast'; import * as ConsoleActions from '../actions/console'; emmet(CodeMirror); @@ -504,7 +503,6 @@ function mapStateToProps(state) { editorAccessibility: state.editorAccessibility, user: state.user, project: state.project, - toast: state.toast, consoleEvents: state.console, ...state.preferences, @@ -525,7 +523,6 @@ function mapDispatchToProps(dispatch) { IDEActions, PreferencesActions, UserActions, - ToastActions, ConsoleActions ), dispatch diff --git a/client/modules/IDE/components/SketchList.jsx b/client/modules/IDE/components/SketchList.jsx index 757bacd904..04a5cf9aae 100644 --- a/client/modules/IDE/components/SketchList.jsx +++ b/client/modules/IDE/components/SketchList.jsx @@ -11,7 +11,6 @@ import dates from '../../../utils/formatDate'; import * as ProjectActions from '../actions/project'; import * as ProjectsActions from '../actions/projects'; import * as CollectionsActions from '../actions/collections'; -import * as ToastActions from '../actions/toast'; import * as SortingActions from '../actions/sorting'; import * as IdeActions from '../actions/ide'; import getSortedSketches from '../selectors/projects'; @@ -591,13 +590,7 @@ function mapStateToProps(state) { function mapDispatchToProps(dispatch) { return bindActionCreators( - Object.assign( - {}, - ProjectsActions, - CollectionsActions, - ToastActions, - SortingActions - ), + Object.assign({}, ProjectsActions, CollectionsActions, SortingActions), dispatch ); } diff --git a/client/modules/IDE/reducers/toast.js b/client/modules/IDE/reducers/toast.js index b680c7cfef..9c092862a6 100644 --- a/client/modules/IDE/reducers/toast.js +++ b/client/modules/IDE/reducers/toast.js @@ -1,21 +1,24 @@ -import * as ActionTypes from '../../../constants'; +import { createSlice } from '@reduxjs/toolkit'; const initialState = { isVisible: false, text: '' }; -const toast = (state = initialState, action) => { - switch (action.type) { - case ActionTypes.SHOW_TOAST: - return Object.assign({}, state, { isVisible: true }); - case ActionTypes.HIDE_TOAST: - return Object.assign({}, state, { isVisible: false }); - case ActionTypes.SET_TOAST_TEXT: - return Object.assign({}, state, { text: action.text }); - default: - return state; +const toastSlice = createSlice({ + name: 'toast', + initialState, + reducers: { + setToast: (state, action) => { + state.isVisible = true; + state.text = action.payload; + }, + hideToast: (state) => { + state.isVisible = false; + } } -}; +}); + +export const { setToast, hideToast } = toastSlice.actions; -export default toast; +export default toastSlice.reducer; diff --git a/client/modules/User/actions.js b/client/modules/User/actions.js index 826ade8676..a7986696e2 100644 --- a/client/modules/User/actions.js +++ b/client/modules/User/actions.js @@ -4,7 +4,7 @@ import * as ActionTypes from '../../constants'; import apiClient from '../../utils/apiClient'; import { showErrorModal, justOpenedProject } from '../IDE/actions/ide'; import { setLanguage } from '../IDE/actions/preferences'; -import { showToast, setToastText } from '../IDE/actions/toast'; +import { showToast } from '../IDE/actions/toast'; export function authError(error) { return { @@ -279,8 +279,7 @@ export function updateSettings(formValues) { submitSettings(formValues) .then((response) => { dispatch(updateSettingsSuccess(response.data)); - dispatch(showToast(5500)); - dispatch(setToastText('Toast.SettingsSaved')); + dispatch(showToast('Toast.SettingsSaved', 5500)); resolve(); }) .catch((error) => resolve({ error })) diff --git a/client/modules/User/components/Collection.jsx b/client/modules/User/components/Collection.jsx index 563fd7d6ff..8d17040587 100644 --- a/client/modules/User/components/Collection.jsx +++ b/client/modules/User/components/Collection.jsx @@ -12,7 +12,6 @@ import { DropdownArrowIcon } from '../../../common/icons'; import * as ProjectActions from '../../IDE/actions/project'; import * as ProjectsActions from '../../IDE/actions/projects'; import * as CollectionsActions from '../../IDE/actions/collections'; -import * as ToastActions from '../../IDE/actions/toast'; import * as SortingActions from '../../IDE/actions/sorting'; import * as IdeActions from '../../IDE/actions/ide'; import { getCollection } from '../../IDE/selectors/collections'; @@ -537,13 +536,7 @@ function mapStateToProps(state, ownProps) { function mapDispatchToProps(dispatch) { return bindActionCreators( - Object.assign( - {}, - CollectionsActions, - ProjectsActions, - ToastActions, - SortingActions - ), + Object.assign({}, CollectionsActions, ProjectsActions, SortingActions), dispatch ); } diff --git a/client/modules/User/components/Notification.jsx b/client/modules/User/components/Notification.jsx index c7189c3af1..9f257f68ad 100644 --- a/client/modules/User/components/Notification.jsx +++ b/client/modules/User/components/Notification.jsx @@ -1,7 +1,7 @@ import { useEffect } from 'react'; import Cookies from 'js-cookie'; import { useDispatch } from 'react-redux'; -import { showToast, setToastText } from '../../IDE/actions/toast'; +import { showToast } from '../../IDE/actions/toast'; function Notification() { const dispatch = useDispatch(); @@ -9,10 +9,9 @@ function Notification() { const notification = Cookies.get('p5-notification'); if (!notification) { // show the toast - dispatch(showToast(30000)); const text = `There is a scheduled outage on Sunday, April 9 3AM - 5AM UTC. The entire site will be down, so please plan accordingly.`; - dispatch(setToastText(text)); + dispatch(showToast(text, 30000)); Cookies.set('p5-notification', true, { expires: 365 }); } });