From b808785e9813c811e9b0b5f3b1c5d3bcb7b61296 Mon Sep 17 00:00:00 2001 From: Michael Kohler Date: Sun, 15 Mar 2020 00:59:35 +0100 Subject: [PATCH] chore: split up actions/reducers into domain specific files --- web/src/actions/index.js | 253 -------------------------- web/src/actions/languages.js | 79 ++++++++ web/src/actions/login.js | 105 +++++++++++ web/src/actions/parsing.js | 4 +- web/src/actions/sentences.js | 43 +++++ web/src/actions/settings.js | 33 ++++ web/src/components/app.jsx | 2 +- web/src/components/header.jsx | 2 +- web/src/components/page.jsx | 4 +- web/src/components/pages/add.jsx | 6 +- web/src/components/pages/home.jsx | 8 +- web/src/components/pages/login.jsx | 10 +- web/src/components/pages/profile.jsx | 22 +-- web/src/components/pages/rejected.jsx | 6 +- web/src/components/pages/review.jsx | 8 +- web/src/components/profile-widget.jsx | 6 +- web/src/components/store.jsx | 4 +- web/src/components/submit-form.jsx | 4 +- web/src/reducers/index.js | 176 +----------------- web/src/reducers/languages.js | 52 ++++++ web/src/reducers/login.js | 69 +++++++ web/src/reducers/sentences.js | 48 +++++ web/src/reducers/settings.js | 25 +++ web/src/tests/login.test.js | 2 +- 24 files changed, 504 insertions(+), 467 deletions(-) delete mode 100644 web/src/actions/index.js create mode 100644 web/src/actions/languages.js create mode 100644 web/src/actions/login.js create mode 100644 web/src/actions/sentences.js create mode 100644 web/src/actions/settings.js create mode 100644 web/src/reducers/languages.js create mode 100644 web/src/reducers/login.js create mode 100644 web/src/reducers/sentences.js create mode 100644 web/src/reducers/settings.js diff --git a/web/src/actions/index.js b/web/src/actions/index.js deleted file mode 100644 index 38934311..00000000 --- a/web/src/actions/index.js +++ /dev/null @@ -1,253 +0,0 @@ -import WebDB from '../web-db'; - -export const ACTION_LOGOUT = 'LOGOUT'; -export const ACTION_LOGIN_REQUEST = 'LOGIN_REQUEST'; -export const ACTION_LOGIN_SUCCESS = 'LOGIN_SUCCESS'; -export const ACTION_LOGIN_FAILURE = 'LOGIN_FAILURE'; -export const ACTION_LOGIN_ENABLE = 'ACTION_LOGIN_ENABLE'; -export const ACTION_LOGIN_DISABLE = 'ACTION_LOGIN_DISABLE'; -export const ACTION_LOGIN_CHECK_USERNAME_FAILED = 'ACTION_LOGIN_CHECK_USERNAME_FAILED'; -export const ACTION_LOGIN_CHECK_USERNAME_SUCCESS = 'ACTION_LOGIN_CHECK_USERNAME_SUCCESS'; - -export const ACTION_ADD_LANGUAGE_REQUEST = 'ADD_LANGUAGE_REQUEST'; -export const ACTION_ADD_LANGUAGE_SUCCESS = 'ADD_LANGUAGE_SUCCESS'; -export const ACTION_ADD_LANGUAGE_FAILURE = 'ADD_LANGUAGE_FAILURE'; - -export const ACTION_REMOVE_LANGUAGE_REQUEST = 'REMOVE_LANGUAGE_REQUEST'; -export const ACTION_REMOVE_LANGUAGE_SUCCESS = 'REMOVE_LANGUAGE_SUCCESS'; -export const ACTION_REMOVE_LANGUAGE_FAILURE = 'REMOVE_LANGUAGE_FAILURE'; - -export const ACTION_SUBMIT_SENTENCES_REQUEST = 'SUBMIT_SENTENCES_REQUEST'; -export const ACTION_SUBMIT_SENTENCES_SUCCESS = 'SUBMIT_SENTENCES_SUCCESS'; -export const ACTION_SUBMIT_SENTENCES_FAILURE_SINGLE = 'SUBMIT_SENTENCES_FAILURE_SINGLE'; - -export const ACTION_SETTINGS_CHANGED = 'ACTION_SETTINGS_CHANGED'; -export const ACTION_SETTINGS_CHANGED_FAILURE = 'ACTION_SETTINGS_CHANGED_FAILURE'; - -const VALID_USERNAME_CHARACTERS = /^[a-zA-Z0-9]+$/; - -export function login(username, password) { - return async function(dispatch) { - try { - dispatch(sendLoginRequest()); - - const db = new WebDB(username, password); - const user = await db.auth(); - dispatch(loginSuccess(username, password, user.languages, user.settings)); - } catch (err) { - dispatch(loginFailure()); - } - }; -} - -export function checkLoginInput(username, password) { - return async function(dispatch) { - const validUsername = checkUsername(username); - const validPassword = checkPassword(password); - - const usernameAction = validUsername ? usernameSuccess() : usernameFailure(); - dispatch(usernameAction); - - if (!validUsername || !validPassword) { - return dispatch(disableLogin()); - } - - return dispatch(enableLogin()); - }; -} - -function checkUsername(username) { - return VALID_USERNAME_CHARACTERS.test(username); -} - -function checkPassword(password) { - return password !== ''; -} - -export function setSetting(key, value) { - return async function(dispatch, getState) { - try { - const state = getState(); - const db = new WebDB(state.app.username, state.app.password); - await db.setSetting(key, value); - dispatch(settingsChanged({ - [key]: value, - })); - } catch (err) { - dispatch(settingsChangedFailure()); - throw err; - } - }; -} - -export function addLanguage(language) { - return async function(dispatch, getState) { - try { - dispatch(sendAddLanguage()); - - const state = getState(); - const db = new WebDB(state.app.username, state.app.password); - const updatedLanguages = await db.addLanguage(language); - dispatch(addLanguageSuccess(updatedLanguages)); - } catch (err) { - dispatch(addLanguageFailure()); - throw err; - } - }; -} - -export function removeLanguage(language) { - return async function(dispatch, getState) { - try { - dispatch(sendRemoveLanguage()); - - const state = getState(); - const db = new WebDB(state.app.username, state.app.password); - const updatedLanguages = await db.removeLanguage(language); - dispatch(removeLanguageSuccess(updatedLanguages)); - } catch (err) { - dispatch(removeLanguageFailure()); - throw err; - } - }; -} - -export function submitSentences(language, sentences, source) { - return async function(dispatch, getState) { - dispatch(sendSubmitSentences()); - - const state = getState(); - const db = new WebDB(state.app.username, state.app.password); - const results = await db.submitSentences(language, sentences, source); - dispatch(submitSentencesSuccess(results.sentences.slice(0))); - const errorsWithSentenceInfo = results.errors.filter((error) => error.sentence); - dispatch(submitSentencesFailureSingle(errorsWithSentenceInfo)); - if(!state.app.languages.includes(language)) { - dispatch(addLanguage(language)); - } - return results; - }; -} - -export function usernameSuccess() { - return { - type: ACTION_LOGIN_CHECK_USERNAME_SUCCESS, - }; -} - -export function usernameFailure() { - return { - type: ACTION_LOGIN_CHECK_USERNAME_FAILED, - }; -} - -export function disableLogin() { - return { - type: ACTION_LOGIN_DISABLE, - }; -} - -export function enableLogin() { - return { - type: ACTION_LOGIN_ENABLE, - }; -} - -export function logout() { - return { - type: ACTION_LOGOUT, - }; -} - -export function sendLoginRequest() { - return { - type: ACTION_LOGIN_REQUEST, - }; -} - -export function loginSuccess(username, password, languages, settings) { - return { - type: ACTION_LOGIN_SUCCESS, - username, - password, - languages, - settings, - }; -} - -export function loginFailure() { - return { - type: ACTION_LOGIN_FAILURE, - }; -} - -export function settingsChanged(newSettings) { - return { - type: ACTION_SETTINGS_CHANGED, - newSettings, - }; -} - -export function settingsChangedFailure() { - return { - type: ACTION_SETTINGS_CHANGED_FAILURE, - }; -} - -export function sendAddLanguage() { - return { - type: ACTION_ADD_LANGUAGE_REQUEST, - }; -} - -export function addLanguageSuccess(languages) { - return { - type: ACTION_ADD_LANGUAGE_SUCCESS, - languages, - }; -} - -export function addLanguageFailure() { - return { - type: ACTION_ADD_LANGUAGE_FAILURE, - }; -} - -export function sendRemoveLanguage() { - return { - type: ACTION_REMOVE_LANGUAGE_REQUEST, - }; -} - -export function removeLanguageSuccess(languages) { - return { - type: ACTION_REMOVE_LANGUAGE_SUCCESS, - languages, - }; -} - -export function removeLanguageFailure() { - return { - type: ACTION_REMOVE_LANGUAGE_FAILURE, - }; -} - -export function sendSubmitSentences() { - return { - type: ACTION_SUBMIT_SENTENCES_REQUEST, - }; -} - -export function submitSentencesSuccess(sentences) { - return { - type: ACTION_SUBMIT_SENTENCES_SUCCESS, - sentences, - }; -} - -export function submitSentencesFailureSingle(errors) { - return { - type: ACTION_SUBMIT_SENTENCES_FAILURE_SINGLE, - errors, - }; -} diff --git a/web/src/actions/languages.js b/web/src/actions/languages.js new file mode 100644 index 00000000..3c3a098d --- /dev/null +++ b/web/src/actions/languages.js @@ -0,0 +1,79 @@ +import WebDB from '../web-db'; + +export const ACTION_ADD_LANGUAGE_REQUEST = 'ADD_LANGUAGE_REQUEST'; +export const ACTION_ADD_LANGUAGE_SUCCESS = 'ADD_LANGUAGE_SUCCESS'; +export const ACTION_ADD_LANGUAGE_FAILURE = 'ADD_LANGUAGE_FAILURE'; + +export const ACTION_REMOVE_LANGUAGE_REQUEST = 'REMOVE_LANGUAGE_REQUEST'; +export const ACTION_REMOVE_LANGUAGE_SUCCESS = 'REMOVE_LANGUAGE_SUCCESS'; +export const ACTION_REMOVE_LANGUAGE_FAILURE = 'REMOVE_LANGUAGE_FAILURE'; + +export function addLanguage(language) { + return async function(dispatch, getState) { + try { + dispatch(sendAddLanguage()); + + const state = getState(); + const db = new WebDB(state.login.username, state.login.password); + const updatedLanguages = await db.addLanguage(language); + dispatch(addLanguageSuccess(updatedLanguages)); + } catch (err) { + dispatch(addLanguageFailure()); + throw err; + } + }; +} + +export function removeLanguage(language) { + return async function(dispatch, getState) { + try { + dispatch(sendRemoveLanguage()); + + const state = getState(); + const db = new WebDB(state.login.username, state.login.password); + const updatedLanguages = await db.removeLanguage(language); + dispatch(removeLanguageSuccess(updatedLanguages)); + } catch (err) { + dispatch(removeLanguageFailure()); + throw err; + } + }; +} + +export function sendAddLanguage() { + return { + type: ACTION_ADD_LANGUAGE_REQUEST, + }; +} + +export function addLanguageSuccess(languages) { + return { + type: ACTION_ADD_LANGUAGE_SUCCESS, + languages, + }; +} + +export function addLanguageFailure() { + return { + type: ACTION_ADD_LANGUAGE_FAILURE, + }; +} + +export function sendRemoveLanguage() { + return { + type: ACTION_REMOVE_LANGUAGE_REQUEST, + }; +} + +export function removeLanguageSuccess(languages) { + return { + type: ACTION_REMOVE_LANGUAGE_SUCCESS, + languages, + }; +} + +export function removeLanguageFailure() { + return { + type: ACTION_REMOVE_LANGUAGE_FAILURE, + }; +} diff --git a/web/src/actions/login.js b/web/src/actions/login.js new file mode 100644 index 00000000..1dd0825c --- /dev/null +++ b/web/src/actions/login.js @@ -0,0 +1,105 @@ +import WebDB from '../web-db'; +import { addLanguageSuccess } from './languages'; +import { settingsChanged } from './settings'; + +export const ACTION_LOGOUT = 'LOGOUT'; +export const ACTION_LOGIN_REQUEST = 'LOGIN_REQUEST'; +export const ACTION_LOGIN_SUCCESS = 'LOGIN_SUCCESS'; +export const ACTION_LOGIN_FAILURE = 'LOGIN_FAILURE'; +export const ACTION_LOGIN_ENABLE = 'ACTION_LOGIN_ENABLE'; +export const ACTION_LOGIN_DISABLE = 'ACTION_LOGIN_DISABLE'; +export const ACTION_LOGIN_CHECK_USERNAME_FAILED = 'ACTION_LOGIN_CHECK_USERNAME_FAILED'; +export const ACTION_LOGIN_CHECK_USERNAME_SUCCESS = 'ACTION_LOGIN_CHECK_USERNAME_SUCCESS'; + +const VALID_USERNAME_CHARACTERS = /^[a-zA-Z0-9]+$/; + +export function login(username, password) { + return async function(dispatch) { + try { + dispatch(sendLoginRequest()); + + const db = new WebDB(username, password); + const user = await db.auth(); + dispatch(loginSuccess(username, password)); + dispatch(addLanguageSuccess(user.languages)); + dispatch(settingsChanged(user.settings)); + } catch (err) { + console.log(err); + dispatch(loginFailure()); + } + }; +} + +export function checkLoginInput(username, password) { + return async function(dispatch) { + const validUsername = checkUsername(username); + const validPassword = checkPassword(password); + + const usernameAction = validUsername ? usernameSuccess() : usernameFailure(); + dispatch(usernameAction); + + if (!validUsername || !validPassword) { + return dispatch(disableLogin()); + } + + return dispatch(enableLogin()); + }; +} + +function checkUsername(username) { + return VALID_USERNAME_CHARACTERS.test(username); +} + +function checkPassword(password) { + return password !== ''; +} + +export function usernameSuccess() { + return { + type: ACTION_LOGIN_CHECK_USERNAME_SUCCESS, + }; +} + +export function usernameFailure() { + return { + type: ACTION_LOGIN_CHECK_USERNAME_FAILED, + }; +} + +export function disableLogin() { + return { + type: ACTION_LOGIN_DISABLE, + }; +} + +export function enableLogin() { + return { + type: ACTION_LOGIN_ENABLE, + }; +} + +export function logout() { + return { + type: ACTION_LOGOUT, + }; +} + +export function sendLoginRequest() { + return { + type: ACTION_LOGIN_REQUEST, + }; +} + +export function loginSuccess(username, password) { + return { + type: ACTION_LOGIN_SUCCESS, + username, + password, + }; +} + +export function loginFailure() { + return { + type: ACTION_LOGIN_FAILURE, + }; +} diff --git a/web/src/actions/parsing.js b/web/src/actions/parsing.js index 810e071d..b72bcf8a 100644 --- a/web/src/actions/parsing.js +++ b/web/src/actions/parsing.js @@ -10,8 +10,8 @@ export function parseSentences(language, text) { try { const state = getState(); const credentials = { - username: state.app.username, - password: state.app.password, + username: state.login.username, + password: state.login.password, }; const sentences = text.split(SPLIT_ON).map(s => s.trim()).filter(Boolean); diff --git a/web/src/actions/sentences.js b/web/src/actions/sentences.js new file mode 100644 index 00000000..f86a9eea --- /dev/null +++ b/web/src/actions/sentences.js @@ -0,0 +1,43 @@ +import WebDB from '../web-db'; +import languages from './languages'; + +export const ACTION_SUBMIT_SENTENCES_REQUEST = 'SUBMIT_SENTENCES_REQUEST'; +export const ACTION_SUBMIT_SENTENCES_SUCCESS = 'SUBMIT_SENTENCES_SUCCESS'; +export const ACTION_SUBMIT_SENTENCES_FAILURE_SINGLE = 'SUBMIT_SENTENCES_FAILURE_SINGLE'; + +export function submitSentences(language, sentences, source) { + return async function(dispatch, getState) { + dispatch(sendSubmitSentences()); + + const state = getState(); + const db = new WebDB(state.login.username, state.login.password); + const results = await db.submitSentences(language, sentences, source); + dispatch(submitSentencesSuccess(results.sentences.slice(0))); + const errorsWithSentenceInfo = results.errors.filter((error) => error.sentence); + dispatch(submitSentencesFailureSingle(errorsWithSentenceInfo)); + if(!state.languages.languages.includes(language)) { + dispatch(languages.addLanguage(language)); + } + return results; + }; +} + +export function sendSubmitSentences() { + return { + type: ACTION_SUBMIT_SENTENCES_REQUEST, + }; +} + +export function submitSentencesSuccess(sentences) { + return { + type: ACTION_SUBMIT_SENTENCES_SUCCESS, + sentences, + }; +} + +export function submitSentencesFailureSingle(errors) { + return { + type: ACTION_SUBMIT_SENTENCES_FAILURE_SINGLE, + errors, + }; +} diff --git a/web/src/actions/settings.js b/web/src/actions/settings.js new file mode 100644 index 00000000..36cdb5bb --- /dev/null +++ b/web/src/actions/settings.js @@ -0,0 +1,33 @@ +import WebDB from '../web-db'; + +export const ACTION_SETTINGS_CHANGED = 'ACTION_SETTINGS_CHANGED'; +export const ACTION_SETTINGS_CHANGED_FAILURE = 'ACTION_SETTINGS_CHANGED_FAILURE'; + +export function setSetting(key, value) { + return async function(dispatch, getState) { + try { + const state = getState(); + const db = new WebDB(state.login.username, state.login.password); + await db.setSetting(key, value); + dispatch(settingsChanged({ + [key]: value, + })); + } catch (err) { + dispatch(settingsChangedFailure()); + throw err; + } + }; +} + +export function settingsChanged(newSettings) { + return { + type: ACTION_SETTINGS_CHANGED, + newSettings, + }; +} + +export function settingsChangedFailure() { + return { + type: ACTION_SETTINGS_CHANGED_FAILURE, + }; +} diff --git a/web/src/components/app.jsx b/web/src/components/app.jsx index 50d945a5..8f541490 100644 --- a/web/src/components/app.jsx +++ b/web/src/components/app.jsx @@ -75,7 +75,7 @@ const PrivateRoute = (props) => { function mapStateToProps(state) { return { - authed: state.app.authed, + authed: state.login.authed, }; } diff --git a/web/src/components/header.jsx b/web/src/components/header.jsx index 7444bdd6..a1f1f2c3 100644 --- a/web/src/components/header.jsx +++ b/web/src/components/header.jsx @@ -38,7 +38,7 @@ function mapStateToProps(state) { return { // force a re-render of header active links on location change. location: state.router.location, - authed: state.app.authed, + authed: state.login.authed, }; } diff --git a/web/src/components/page.jsx b/web/src/components/page.jsx index a9a8fe54..8c1d005b 100644 --- a/web/src/components/page.jsx +++ b/web/src/components/page.jsx @@ -5,7 +5,7 @@ import HeaderBtn from './header-btn'; const DEFAULT_STATE = { headerIsOpen: true, -} +}; export default class Page extends React.Component { constructor(props) { @@ -25,7 +25,7 @@ export default class Page extends React.Component { return [
,
- +
{this.props.children}
diff --git a/web/src/components/pages/add.jsx b/web/src/components/pages/add.jsx index 12c1d4a7..3640fe4e 100644 --- a/web/src/components/pages/add.jsx +++ b/web/src/components/pages/add.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; -import { submitSentences } from '../../actions'; +import { submitSentences } from '../../actions/sentences'; import { parseSentences } from '../../actions/parsing'; import SubmitForm from '../submit-form'; @@ -239,9 +239,7 @@ class Add extends React.Component { function mapStateToProps(state) { return { - languages: state.app.languages, - username: state.app.username, - password: state.app.password, + languages: state.languages.languages, }; } diff --git a/web/src/components/pages/home.jsx b/web/src/components/pages/home.jsx index 62b64231..4985c85d 100644 --- a/web/src/components/pages/home.jsx +++ b/web/src/components/pages/home.jsx @@ -47,10 +47,10 @@ const LanguageStats = (props) => { function mapStateToProps(state) { return { - languages: state.app.languages, - authed: state.app.authed, - username: state.app.username, - password: state.app.password, + languages: state.languages.languages, + authed: state.login.authed, + username: state.login.username, + password: state.login.password, }; } diff --git a/web/src/components/pages/login.jsx b/web/src/components/pages/login.jsx index 8254cfc8..a4d27a23 100644 --- a/web/src/components/pages/login.jsx +++ b/web/src/components/pages/login.jsx @@ -6,7 +6,7 @@ import { login, logout, checkLoginInput, -} from '../../actions'; +} from '../../actions/login'; class Login extends React.Component { constructor(props) { @@ -60,10 +60,10 @@ class Login extends React.Component { function mapStateToProps(state) { return { - authed: state.app.authed, - username: state.app.username, - errorMessage: state.app.errorMessage, - loginDisabled: state.app.loginDisabled, + authed: state.login.authed, + username: state.login.username, + errorMessage: state.login.errorMessage, + loginDisabled: state.login.loginDisabled, }; } diff --git a/web/src/components/pages/profile.jsx b/web/src/components/pages/profile.jsx index aed8b5a2..f50b3b28 100644 --- a/web/src/components/pages/profile.jsx +++ b/web/src/components/pages/profile.jsx @@ -5,7 +5,8 @@ import { Link } from 'react-router-dom'; import { getLanguageName } from '../../../../shared/languages'; import { arrayCompare } from '../../../../shared/util'; import WebDB from '../../web-db'; -import { addLanguage, removeLanguage, setSetting } from '../../actions'; +import { addLanguage, removeLanguage } from '../../actions/languages'; +import { setSetting } from '../../actions/settings'; import LanguageSelector from '../language-selector'; import '../../../css/profile.css'; @@ -141,10 +142,8 @@ class Profile extends React.Component { username, languages, pending, - settings = {}, - settingsChangedFailureMessage, + settings, } = this.props; - console.log('settings changed?', settingsChangedFailureMessage); const { useSwipeReview } = settings; return ( @@ -196,8 +195,8 @@ class Profile extends React.Component {

Settings

- {settingsChangedFailureMessage && ( -

{settingsChangedFailureMessage}

+ {settings.errorMessage && ( +

{settings.errorMessage}

)}

Experimental: There are two different tools with which you can review sentences. The normal tool lists 5 sentences per page @@ -239,12 +238,11 @@ const LanguageInfo = (props) => ( function mapStateToProps(state) { return { - username: state.app.username, - password: state.app.password, - languages: state.app.languages, - pending: state.app.pendingLanguages, - settings: state.app.settings, - settingsChangedFailureMessage: state.app.settingsChangedFailureMessage, + username: state.login.username, + password: state.login.password, + languages: state.languages.languages, + pending: state.languages.pendingLanguages, + settings: state.settings, }; } diff --git a/web/src/components/pages/rejected.jsx b/web/src/components/pages/rejected.jsx index 8c30bf57..a580d40b 100644 --- a/web/src/components/pages/rejected.jsx +++ b/web/src/components/pages/rejected.jsx @@ -62,9 +62,9 @@ class Rejected extends React.Component { function mapStateToProps(state) { return { - username: state.app.username, - password: state.app.password, - languages: state.app.languages, + username: state.login.username, + password: state.login.password, + languages: state.languages.languages, }; } diff --git a/web/src/components/pages/review.jsx b/web/src/components/pages/review.jsx index 41156c47..96fe79ce 100644 --- a/web/src/components/pages/review.jsx +++ b/web/src/components/pages/review.jsx @@ -193,10 +193,10 @@ class Review extends React.Component { function mapStateToProps(state) { return { - languages: state.app.languages, - username: state.app.username, - password: state.app.password, - useSwipeReview: state.app.settings && state.app.settings.useSwipeReview, + languages: state.languages.languages, + password: state.login.password, + username: state.login.username, + useSwipeReview: state.settings.useSwipeReview, }; } diff --git a/web/src/components/profile-widget.jsx b/web/src/components/profile-widget.jsx index 87418de9..9cba78d4 100644 --- a/web/src/components/profile-widget.jsx +++ b/web/src/components/profile-widget.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { connect } from 'react-redux'; import { Link } from 'react-router-dom'; -import { logout } from '../actions'; +import { logout } from '../actions/login'; class ProfileWidget extends React.Component { constructor(props) { @@ -30,8 +30,8 @@ class ProfileWidget extends React.Component { function mapStateToProps(state) { return { - authed: state.app.authed, - username: state.app.username, + authed: state.login.authed, + username: state.login.username, }; } diff --git a/web/src/components/store.jsx b/web/src/components/store.jsx index 036da13e..35b03c2b 100644 --- a/web/src/components/store.jsx +++ b/web/src/components/store.jsx @@ -7,7 +7,7 @@ import storage from 'redux-persist/lib/storage'; import thunk from 'redux-thunk'; import { routerMiddleware } from 'connected-react-router'; -import createRootReducer, { INITIAL_STATE } from '../reducers'; +import createRootReducer from '../reducers'; const ROOT_KEY = 'redux'; const persistConfig = { @@ -21,7 +21,7 @@ function getStore(history) { return createStore( persistedRecuder, - { app: INITIAL_STATE }, + {}, compose( applyMiddleware(thunk), applyMiddleware(routerMiddleware(history)) diff --git a/web/src/components/submit-form.jsx b/web/src/components/submit-form.jsx index d3c0459c..d0586de0 100644 --- a/web/src/components/submit-form.jsx +++ b/web/src/components/submit-form.jsx @@ -7,8 +7,8 @@ import SpinnerButton from './spinner-button'; function mapStateToProps(state) { return { - errorMessage: state.app.errorMessage, - sentenceSubmissionFailures: state.app.sentenceSubmissionFailures, + errorMessage: state.sentences.errorMessage, + sentenceSubmissionFailures: state.sentences.sentenceSubmissionFailures, }; } diff --git a/web/src/reducers/index.js b/web/src/reducers/index.js index 1aa10af8..eedc1f71 100644 --- a/web/src/reducers/index.js +++ b/web/src/reducers/index.js @@ -1,177 +1,17 @@ import { combineReducers } from 'redux'; import { connectRouter } from 'connected-react-router'; -import { - ACTION_LOGOUT, - ACTION_LOGIN_REQUEST, - ACTION_LOGIN_SUCCESS, - ACTION_LOGIN_FAILURE, - ACTION_LOGIN_CHECK_USERNAME_FAILED, - ACTION_LOGIN_ENABLE, - ACTION_LOGIN_DISABLE, - ACTION_LOGIN_CHECK_USERNAME_SUCCESS, - ACTION_ADD_LANGUAGE_REQUEST, - ACTION_ADD_LANGUAGE_SUCCESS, - ACTION_ADD_LANGUAGE_FAILURE, - ACTION_REMOVE_LANGUAGE_REQUEST, - ACTION_REMOVE_LANGUAGE_SUCCESS, - ACTION_REMOVE_LANGUAGE_FAILURE, - ACTION_SUBMIT_SENTENCES_REQUEST, - ACTION_SUBMIT_SENTENCES_SUCCESS, - ACTION_SUBMIT_SENTENCES_FAILURE_SINGLE, - ACTION_SETTINGS_CHANGED, - ACTION_SETTINGS_CHANGED_FAILURE, -} from '../actions'; - -import { - ACTION_PARSE_SENTENCES_FAILURE, -} from '../actions/parsing'; - -export const INITIAL_STATE = { - authed: false, - username: null, - password: null, - loginDisabled: true, - languages: [], - pendingLanguages: false, - sentences: [], - errorMessage: null, - sentenceSubmissionFailures: [], - settingsChangedFailureMessage: '', -}; - -function copyInto(oldObj, newObj) { - return Object.assign({}, oldObj, newObj); -} - -function mergeArray(arr1, arr2) { - arr1 && arr1.forEach(item => (arr2.indexOf(item) === -1 && arr2.push(item))); - return arr2; -} +import languages from './languages'; +import login from './login'; +import sentences from './sentences'; +import settings from './settings'; export default function(history) { return combineReducers({ router: connectRouter(history), - app: reducer, + languages, + login, + sentences, + settings, }); } - -function reducer(state = INITIAL_STATE, action) { - switch(action.type) { - case ACTION_LOGOUT: - return copyInto(state, INITIAL_STATE); - - case ACTION_LOGIN_CHECK_USERNAME_SUCCESS: - return copyInto(state, { - errorMessage: null, - }); - - case ACTION_LOGIN_CHECK_USERNAME_FAILED: - return Object.assign({}, state, INITIAL_STATE, { - errorMessage: 'Please only use alphanumeric usernames.', - }); - - case ACTION_LOGIN_DISABLE: - return copyInto(state, { - loginDisabled: true, - }); - - case ACTION_LOGIN_ENABLE: - return copyInto(state, { - loginDisabled: false, - }); - - case ACTION_LOGIN_SUCCESS: - return copyInto(state, { - authed: true, - loginDisabled: false, - username: action.username, - password: action.password, - languages: action.languages, - settings: action.settings, - errorMessage: null, - }); - - case ACTION_LOGIN_FAILURE: - return Object.assign({}, state, INITIAL_STATE, { - errorMessage: 'Login failed.', - loginDisabled: true, - }); - - case ACTION_LOGIN_REQUEST: - return copyInto(state, { - authed: false, - loginDisabled: true, - }); - - case ACTION_ADD_LANGUAGE_REQUEST: - return copyInto(state, { - pendingLanguages: true, - }); - - case ACTION_ADD_LANGUAGE_SUCCESS: - return copyInto(state, { - pendingLanguages: false, - languages: action.languages, - }); - - case ACTION_ADD_LANGUAGE_FAILURE: - return copyInto(state, { - pendingLanguages: false, - }); - - case ACTION_REMOVE_LANGUAGE_REQUEST: - return copyInto(state, { - pendingLanguages: true, - }); - - case ACTION_REMOVE_LANGUAGE_SUCCESS: - return copyInto(state, { - pendingLanguages: false, - languages: action.languages, - }); - - case ACTION_REMOVE_LANGUAGE_FAILURE: - return copyInto(state, { - pendingLanguages: false, - }); - - case ACTION_SUBMIT_SENTENCES_REQUEST: - return copyInto(state, { - sentenceSubmissionFailures: [], - }); - - case ACTION_SUBMIT_SENTENCES_FAILURE_SINGLE: - return copyInto(state, { - sentenceSubmissionFailures: action.errors, - }); - - case ACTION_SUBMIT_SENTENCES_SUCCESS: - return copyInto(state, { - sentences: mergeArray(state.sentences, action.sentences), - errorMessage: '', - }); - - case ACTION_PARSE_SENTENCES_FAILURE: - return copyInto(state, { - errorMessage: action.error.message, - }); - - case ACTION_SETTINGS_CHANGED: - return copyInto( - state, - { - settings: copyInto(state.settings, action.newSettings), - settingsChangedFailureMessage: '', - }, - ); - - case ACTION_SETTINGS_CHANGED_FAILURE: - return copyInto(state, { - settingsChangedFailureMessage: 'Could not change settings. Please try again.', - }); - - default: - return state; - } -} diff --git a/web/src/reducers/languages.js b/web/src/reducers/languages.js new file mode 100644 index 00000000..8c0b447a --- /dev/null +++ b/web/src/reducers/languages.js @@ -0,0 +1,52 @@ +import { + ACTION_ADD_LANGUAGE_REQUEST, + ACTION_ADD_LANGUAGE_SUCCESS, + ACTION_ADD_LANGUAGE_FAILURE, + ACTION_REMOVE_LANGUAGE_REQUEST, + ACTION_REMOVE_LANGUAGE_SUCCESS, + ACTION_REMOVE_LANGUAGE_FAILURE, +} from '../actions/languages'; + +export const INITIAL_STATE = { + languages: [], + pendingLanguages: false, +}; + +export default function(state = INITIAL_STATE, action) { + switch(action.type) { + case ACTION_ADD_LANGUAGE_REQUEST: + return Object.assign({}, state, { + pendingLanguages: true, + }); + + case ACTION_ADD_LANGUAGE_SUCCESS: + return Object.assign({}, state, { + pendingLanguages: false, + languages: action.languages, + }); + + case ACTION_ADD_LANGUAGE_FAILURE: + return Object.assign({}, state, { + pendingLanguages: false, + }); + + case ACTION_REMOVE_LANGUAGE_REQUEST: + return Object.assign({}, state, { + pendingLanguages: true, + }); + + case ACTION_REMOVE_LANGUAGE_SUCCESS: + return Object.assign({}, state, { + pendingLanguages: false, + languages: action.languages, + }); + + case ACTION_REMOVE_LANGUAGE_FAILURE: + return Object.assign({}, state, { + pendingLanguages: false, + }); + + default: + return state; + } +} diff --git a/web/src/reducers/login.js b/web/src/reducers/login.js new file mode 100644 index 00000000..8722208b --- /dev/null +++ b/web/src/reducers/login.js @@ -0,0 +1,69 @@ +import { + ACTION_LOGOUT, + ACTION_LOGIN_REQUEST, + ACTION_LOGIN_SUCCESS, + ACTION_LOGIN_FAILURE, + ACTION_LOGIN_CHECK_USERNAME_FAILED, + ACTION_LOGIN_ENABLE, + ACTION_LOGIN_DISABLE, + ACTION_LOGIN_CHECK_USERNAME_SUCCESS, +} from '../actions/login'; + +export const INITIAL_STATE = { + authed: false, + username: null, + password: null, + loginDisabled: true, + errorMessage: null, +}; + +export default function(state = INITIAL_STATE, action) { + switch(action.type) { + case ACTION_LOGOUT: + return Object.assign({}, state, INITIAL_STATE); + + case ACTION_LOGIN_CHECK_USERNAME_SUCCESS: + return Object.assign({}, state, { + errorMessage: null, + }); + + case ACTION_LOGIN_CHECK_USERNAME_FAILED: + return Object.assign({}, state, INITIAL_STATE, { + errorMessage: 'Please only use alphanumeric usernames.', + }); + + case ACTION_LOGIN_DISABLE: + return Object.assign({}, state, { + loginDisabled: true, + }); + + case ACTION_LOGIN_ENABLE: + return Object.assign({}, state, { + loginDisabled: false, + }); + + case ACTION_LOGIN_SUCCESS: + return Object.assign({}, state, { + authed: true, + loginDisabled: false, + username: action.username, + password: action.password, + errorMessage: null, + }); + + case ACTION_LOGIN_FAILURE: + return Object.assign({}, state, INITIAL_STATE, { + errorMessage: 'Login failed.', + loginDisabled: true, + }); + + case ACTION_LOGIN_REQUEST: + return Object.assign({}, state, { + authed: false, + loginDisabled: true, + }); + + default: + return state; + } +} diff --git a/web/src/reducers/sentences.js b/web/src/reducers/sentences.js new file mode 100644 index 00000000..224ae42b --- /dev/null +++ b/web/src/reducers/sentences.js @@ -0,0 +1,48 @@ +import { + ACTION_SUBMIT_SENTENCES_REQUEST, + ACTION_SUBMIT_SENTENCES_SUCCESS, + ACTION_SUBMIT_SENTENCES_FAILURE_SINGLE, +} from '../actions/sentences'; + +import { + ACTION_PARSE_SENTENCES_FAILURE, +} from '../actions/parsing'; + +export const INITIAL_STATE = { + sentences: [], + errorMessage: null, + sentenceSubmissionFailures: [], +}; + +function mergeArray(arr1, arr2) { + arr1 && arr1.forEach(item => (arr2.indexOf(item) === -1 && arr2.push(item))); + return arr2; +} + +export default function(state = INITIAL_STATE, action) { + switch(action.type) { + case ACTION_SUBMIT_SENTENCES_REQUEST: + return Object.assign({}, state, { + sentenceSubmissionFailures: [], + }); + + case ACTION_SUBMIT_SENTENCES_FAILURE_SINGLE: + return Object.assign({}, state, { + sentenceSubmissionFailures: action.errors, + }); + + case ACTION_SUBMIT_SENTENCES_SUCCESS: + return Object.assign({}, state, { + sentences: mergeArray(state.sentences, action.sentences), + errorMessage: '', + }); + + case ACTION_PARSE_SENTENCES_FAILURE: + return Object.assign({}, state, { + errorMessage: action.error.message, + }); + + default: + return state; + } +} diff --git a/web/src/reducers/settings.js b/web/src/reducers/settings.js new file mode 100644 index 00000000..26397dce --- /dev/null +++ b/web/src/reducers/settings.js @@ -0,0 +1,25 @@ +import { + ACTION_SETTINGS_CHANGED, + ACTION_SETTINGS_CHANGED_FAILURE, +} from '../actions/settings'; + +export const INITIAL_STATE = { + errorMessage: '', +}; + +export default function(state = INITIAL_STATE, action) { + switch(action.type) { + case ACTION_SETTINGS_CHANGED: + return Object.assign({}, state, action.newSettings, { + errorMessage: '', + }); + + case ACTION_SETTINGS_CHANGED_FAILURE: + return Object.assign({}, state, { + errorMessage: 'Could not change settings. Please try again.', + }); + + default: + return state; + } +} diff --git a/web/src/tests/login.test.js b/web/src/tests/login.test.js index 5a86489f..89c70b31 100644 --- a/web/src/tests/login.test.js +++ b/web/src/tests/login.test.js @@ -7,7 +7,7 @@ import { getStore } from './testUtils'; test('Login button is disabled without input values', () => { const initialState = { - app: { + login: { authed: false, username: null, errormessage: null,