diff --git a/public/index.html b/public/index.html
index 5b61727..17dce82 100644
--- a/public/index.html
+++ b/public/index.html
@@ -18,13 +18,6 @@
-
-
-
-
diff --git a/src/actions/actionTypes.js b/src/actions/actionTypes.js
index ef50a86..db743a3 100644
--- a/src/actions/actionTypes.js
+++ b/src/actions/actionTypes.js
@@ -41,5 +41,9 @@ export const REGISTRATION_SUCCESS = 'REGISTRATION_SUCCESS';
export const REQUEST_REGISTRATION = 'REQUEST_REGISTRATION';
export const REGISTRATION_FAILURE = 'REGISTRATION_FAILURE';
+export const REQUEST_LANGUAGES = 'REQUEST_LANGUAGES';
+export const GET_LANGUAGES_SUCCESS = 'GET_LANGUAGES_SUCCESS';
+export const GET_LANGUAGES_FAILURE = 'GET_LANGUAGES_FAILURE';
+
export const CHECK_AUTH = 'CHECK_AUTH';
export const SET_AUTH_TO_FALSE = 'SET_AUTH_TO_FALSE';
diff --git a/src/actions/languageActions.js b/src/actions/languageActions.js
new file mode 100644
index 0000000..24687bc
--- /dev/null
+++ b/src/actions/languageActions.js
@@ -0,0 +1,61 @@
+import * as types from './actionTypes';
+import LanguageApi from '../api/languageApi';
+
+const languageApi = new LanguageApi();
+
+export function getLanguagesError(message) {
+ return {
+ type: types.GET_LANGUAGES_FAILURE,
+ isFetching: false,
+ isAuthenticated: false,
+ message
+ };
+}
+
+export function requestLanguages() {
+ return {
+ type: types.REQUEST_LANGUAGES,
+ isFetching: true,
+ languages: []
+ };
+}
+
+export function getLanguagesSuccess(languages) {
+ return {
+ type: types.GET_LANGUAGES_SUCCESS,
+ isFetching: false,
+ languages
+ };
+}
+
+export function getLanguages() {
+ return async (dispatch, getState) => {
+ try {
+ dispatch(requestLanguages());
+
+ const languages = await languageApi.getAllLanguages();
+
+ dispatch(getLanguagesSuccess(languages));
+ } catch (e) {
+ dispatch(getLanguagesError(e.message));
+ }
+ };
+}
+
+export function getLanguageNameByCode(code) {
+ return async (dispatch, getState) => {
+ try {
+ const languages = getState().languages.languages;
+ for (let i = 0; i < languages.languages.length; i++) {
+ const lang = languages.languages[i];
+
+ if (lang.code === code) {
+ return lang.name;
+ }
+ }
+
+ return null;
+ } catch (e) {
+ }
+ };
+}
diff --git a/src/actions/userActions.js b/src/actions/userActions.js
index 43cda3a..1e4f743 100644
--- a/src/actions/userActions.js
+++ b/src/actions/userActions.js
@@ -82,10 +82,9 @@ export function registeryError(message) {
};
}
-export function requestUpdateUser(creds) {
+export function requestUpdateUser() {
return {
type: types.REQUEST_UPDATE_USER,
- creds,
isFetching: true,
requiresAuth: true
};
@@ -95,8 +94,7 @@ export function updateUserSuccess(user) {
return {
type: types.UPDATE_USER_SUCCESS,
user,
- isFetching: false,
- requiresAuth: true
+ isFetching: false
};
}
diff --git a/src/api/languageApi.js b/src/api/languageApi.js
new file mode 100644
index 0000000..6e661bf
--- /dev/null
+++ b/src/api/languageApi.js
@@ -0,0 +1,9 @@
+import Api from './api';
+
+class LanguageApi extends Api {
+ async getAllLanguages() {
+ return await this.request(null, 'GET', `${this.basicUrl}/language`, true);
+ }
+}
+
+export default LanguageApi;
diff --git a/src/containers/ChecklistItemBlock.js b/src/containers/ChecklistItemBlock.js
index 6d8f4f5..70a76b5 100644
--- a/src/containers/ChecklistItemBlock.js
+++ b/src/containers/ChecklistItemBlock.js
@@ -47,7 +47,6 @@ class ChecklistItemBlock extends React.Component {
handleShare(event) {
event.stopPropagation();
- console.log('Share Checklist', this.props.checklist);
}
handleOkClick(event) {
diff --git a/src/containers/LanguageDialog.js b/src/containers/LanguageDialog.js
index 6a6a683..87bf302 100644
--- a/src/containers/LanguageDialog.js
+++ b/src/containers/LanguageDialog.js
@@ -9,7 +9,7 @@ import FormLabel from '@material-ui/core/FormLabel';
import FormGroup from '@material-ui/core/FormGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Checkbox from '../common/components/Checkbox';
-import { SupportedLanguages } from '../utils/enums';
+import Spinner from '../common/components/Spinner';
import { connect } from 'react-redux';
import * as userActions from '../actions/userActions';
import { bindActionCreators } from 'redux';
@@ -18,104 +18,54 @@ import { withStyles } from '@material-ui/core/styles';
const styles = theme => ({
dialogContent: {
width: '400px',
- height: '400px'
+ maxHeight: '400px'
}
});
class LanguageDialog extends React.Component {
- constructor(props) {
- super();
+ constructor(props, context) {
+ super(props, context);
this.state = {
- languages: this.filterLanguages(new SupportedLanguages().languages, props.user.user.languages),
- user: Object.assign({}, props.user.user)
+ user: Object.assign({}, this.props.user.user)
};
- this.filterLanguages = this.filterLanguages.bind(this);
+ this.originalLanguages = [...this.props.user.user.languages];
+
this.handleCancelClick = this.handleCancelClick.bind(this);
this.handleCheckboxChange = this.handleCheckboxChange.bind(this);
this.handleOkClick = this.handleOkClick.bind(this);
this.handleClose = this.handleClose.bind(this);
}
- filterLanguages(languages, userLanguages) {
- return (
- languages
- .map(l => {
- return {
- name: l.name,
- code: l.code,
- checked: userLanguages.includes(l.code)
- };
- })
- // Sort Alphabetically
- .sort((a, b) => {
- const nameA = a.name.toUpperCase();
- const nameB = b.name.toUpperCase();
-
- if (nameA < nameB) {
- return -1;
- }
-
- if (nameA > nameB) {
- return 1;
- }
-
- return 0;
- })
- // Checked at the top of the list
- .sort((a, b) => {
- if (a.checked && !b.checked) {
- return -1;
- }
- if (!a.checked && b.checked) {
- return 1;
- }
-
- return 0;
- })
- );
- }
-
handleCancelClick() {
this.props.onClose();
}
- handleCheckboxChange = languageName => event => {
- const languages = [...this.state.languages];
+ handleCheckboxChange(selectedLanguage) {
+ return event => {
+ const user = Object.assign({}, this.props.user.user);
- for (let i = 0; i < languages.length; i++) {
- const language = languages[i];
+ if(user.languages.includes(selectedLanguage.code)) {
+ const index = user.languages.indexOf(selectedLanguage.code);
- if (language.name === languageName) {
- language.checked = event.target.checked;
-
- break;
+ user.languages.splice(index, 1);
+ } else {
+ user.languages.push(selectedLanguage.code);
}
- }
-
- this.setState({
- languages
- });
- };
-
- handleOkClick(event) {
- const user = Object.assign({}, this.state.user);
- const languages = [...this.state.languages];
- const langCodes = languages.filter(l => l.checked).map(l => l.code);
-
- if (langCodes.length !== user.languages.length || !langCodes.every(l => user.languages.includes(l))) {
- user.languages = langCodes;
-
- this.props.actions.updateUser(user);
this.setState({
- languages: this.filterLanguages(languages, user.languages),
user
});
+ };
+ }
+ handleOkClick(event) {
+ if (isArraysEqual(this.originalLanguages, this.state.user.languages)) {
this.props.onClose();
} else {
+ this.props.userActions.updateUser(this.state.user);
+
this.props.onClose();
}
}
@@ -139,19 +89,22 @@ class LanguageDialog extends React.Component {
Select language
- {this.state.languages.map(language => (
-
- }
- />
- ))}
+ {this.props.languages.isFetching
+ ? ()
+ : this.props.languages.languages.map(language => (
+
+ }
+ />
+ ))
+ }
@@ -169,16 +122,27 @@ class LanguageDialog extends React.Component {
function mapStateToProps(state, ownProps) {
return {
- user: state.user
+ user: state.user,
+ languages: state.languages
};
}
function mapDispatchToProps(dispatch) {
return {
- actions: bindActionCreators(userActions, dispatch)
+ userActions: bindActionCreators(userActions, dispatch)
};
}
+function isArraysEqual(a, b) {
+ const arrayA = [...a];
+ const arrayB = [...b];
+
+ arrayA.sort();
+ arrayB.sort();
+
+ return arrayA.length === arrayB.length && arrayA.every((element, index) => element === arrayB[index]);
+}
+
LanguageDialog.propTypes = {
open: PropTypes.bool.isRequired,
classes: PropTypes.object.isRequired,
diff --git a/src/pages/ChecklistPage.js b/src/pages/ChecklistPage.js
index 918f044..b1f1169 100644
--- a/src/pages/ChecklistPage.js
+++ b/src/pages/ChecklistPage.js
@@ -14,6 +14,7 @@ import ExpansionPanel from '../components/ExpansionPanel';
import ExpansionPanelSummary from '../components/ExpansionPanelSummary';
import * as checklistActions from '../actions/checklistActions';
import * as userActions from '../actions/userActions';
+import * as languageActions from '../actions/languageActions';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { PropTypes } from 'prop-types';
@@ -48,9 +49,11 @@ class ChecklistPage extends React.Component {
constructor(props, context) {
super(props, context);
+ const hasUserAnyLanguages = this.props.user.user.languages.length === 0;
+
this.state = {
user: Object.assign({}, this.props.user.user),
- openLanguageDialog: this.props.user.user.languages.length === 0,
+ openLanguageDialog: hasUserAnyLanguages,
drawerIsOpened: false,
openElementDialog: false,
checklist: {
@@ -75,6 +78,7 @@ class ChecklistPage extends React.Component {
componentWillMount() {
this.props.checklistActions.loadChecklists();
+ this.props.languageActions.getLanguages();
}
handleLogoutClick(event) {
@@ -134,7 +138,7 @@ class ChecklistPage extends React.Component {
}
});
}
-
+
handAddNewChecklistButtonClick(event) {
this.setState({
openElementDialog: true
@@ -157,7 +161,7 @@ class ChecklistPage extends React.Component {
render() {
const { classes } = this.props;
- const isFetching = this.props.isFetching;
+ const isChecklistsFetching = this.props.isChecklistsFetching;
const isApiAddChecklist = this.props.isApiAddChecklist;
const drawerOptions = [
{
@@ -185,21 +189,21 @@ class ChecklistPage extends React.Component {
aria-label="Add checklist"
size="medium"
className={classes.addChecklistButton}
- onClick={isFetching ? undefined : this.handAddNewChecklistButtonClick}
+ onClick={isChecklistsFetching ? undefined : this.handAddNewChecklistButtonClick}
>
- {isFetching ? (
+ {isChecklistsFetching ? (
) : (
this.props.checklists.map(checklist => (
@@ -252,7 +256,8 @@ function mapStateToProps(state, ownProps) {
isAuthenticated: state.user.isAuthenticated,
user: state.user,
checklists: state.checklists.checklists,
- isFetching: state.checklists.isFetching,
+ languages: state.languages.languages,
+ isChecklistsFetching: state.checklists.isFetching,
isApiAddChecklist: state.checklists.isApiAddChecklist
};
}
@@ -260,16 +265,18 @@ function mapStateToProps(state, ownProps) {
function mapDispatchToProps(dispatch) {
return {
checklistActions: bindActionCreators(checklistActions, dispatch),
- userActions: bindActionCreators(userActions, dispatch)
+ userActions: bindActionCreators(userActions, dispatch),
+ languageActions: bindActionCreators(languageActions, dispatch)
};
}
ChecklistPage.propTypes = {
classes: PropTypes.object.isRequired,
- isFetching: PropTypes.bool.isRequired,
+ isChecklistsFetching: PropTypes.bool.isRequired,
isAuthenticated: PropTypes.bool.isRequired,
user: PropTypes.object.isRequired,
- checklists: PropTypes.array.isRequired
+ checklists: PropTypes.array.isRequired,
+ languages: PropTypes.array.isRequired
};
export default connect(
diff --git a/src/reducers/index.js b/src/reducers/index.js
index c568f69..45068ee 100644
--- a/src/reducers/index.js
+++ b/src/reducers/index.js
@@ -1,10 +1,12 @@
import { combineReducers } from 'redux';
import checklists from './checklistReducer';
import user from './userReducer';
+import languages from './languageReducer';
const rootReducer = combineReducers({
checklists,
- user
+ user,
+ languages
});
export default rootReducer;
diff --git a/src/reducers/initialState.js b/src/reducers/initialState.js
index 63e0d04..067dbad 100644
--- a/src/reducers/initialState.js
+++ b/src/reducers/initialState.js
@@ -9,5 +9,9 @@ export default {
isFetching: false,
user: localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user')) : null,
isAuthenticated: isTokenValid()
+ },
+ languages: {
+ languages: [],
+ isFetching: false
}
};
diff --git a/src/reducers/languageReducer.js b/src/reducers/languageReducer.js
new file mode 100644
index 0000000..9a731c7
--- /dev/null
+++ b/src/reducers/languageReducer.js
@@ -0,0 +1,27 @@
+import * as types from '../actions/actionTypes';
+import initialState from './initialState';
+
+export default function languageReducer(state = initialState.languages, action) {
+ switch (action.type) {
+ case types.REQUEST_LANGUAGES:
+ return Object.assign({}, state, {
+ isFetching: action.isFetching,
+ languages: action.languages,
+ user: action.creds
+ });
+ case types.GET_LANGUAGES_SUCCESS:
+ return Object.assign({}, state, {
+ isFetching: action.isFetching,
+ languages: action.languages,
+ user: action.user
+ });
+ case types.GET_LANGUAGES_FAILURE:
+ return Object.assign({}, state, {
+ isFetching: action.isFetching,
+ languages: action.languages,
+ errorMessage: action.message
+ });
+ default:
+ return state;
+ }
+}
diff --git a/src/reducers/userReducer.js b/src/reducers/userReducer.js
index 06f357e..78043c6 100644
--- a/src/reducers/userReducer.js
+++ b/src/reducers/userReducer.js
@@ -59,6 +59,20 @@ export default function userReducer(state = initialState.auth, action) {
isAuthenticated: action.isAuthenticated,
user: {}
});
+ case types.REQUEST_UPDATE_USER:
+ return Object.assign({}, state, {
+ isFetching: action.isFetching
+ });
+ case types.UPDATE_USER_SUCCESS:
+ return Object.assign({}, state, {
+ isFetching: action.isFetching,
+ user: action.user
+ });
+ case types.UPDATE_USER_FAILURE:
+ return Object.assign({}, state, {
+ isFetching: action.isFetching,
+ errorMessage: action.message
+ });
default:
return state;
}