Skip to content
Snippets Groups Projects
Commit 038d94b6 authored by Alexandru Munteanu's avatar Alexandru Munteanu
Browse files

Refactor authors reducer

parent e4f7ae95
No related branches found
No related tags found
No related merge requests found
Showing
with 166 additions and 82 deletions
...@@ -5,7 +5,10 @@ import { Icon } from '@pubsweet/ui' ...@@ -5,7 +5,10 @@ import { Icon } from '@pubsweet/ui'
import { DropTarget } from 'react-dnd' import { DropTarget } from 'react-dnd'
import { NativeTypes } from 'react-dnd-html5-backend' import { NativeTypes } from 'react-dnd-html5-backend'
import { compose, getContext, withHandlers, withState } from 'recompose' import { compose, getContext, withHandlers, withState } from 'recompose'
import { SortableList } from 'pubsweet-components-faraday/src/components' import {
SortableList,
Spinner,
} from 'pubsweet-components-faraday/src/components'
import FileItem from './FileItem' import FileItem from './FileItem'
import FilePicker from './FilePicker' import FilePicker from './FilePicker'
...@@ -73,9 +76,7 @@ const FileSection = ({ ...@@ -73,9 +76,7 @@ const FileSection = ({
</div> </div>
</FilePicker> </FilePicker>
) : ( ) : (
<div className={classnames(classes.rotate, classes.icon)}> <Spinner />
<Icon size={16}>loader</Icon>
</div>
)} )}
</div> </div>
<span className={classnames(classes.error)}>{error}</span> <span className={classnames(classes.error)}>{error}</span>
......
...@@ -60,48 +60,3 @@ ...@@ -60,48 +60,3 @@
.is-over { .is-over {
background-color: #ddd; background-color: #ddd;
} }
@keyframes rotating {
from {
-o-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-o-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-webkit-keyframes rotating {
from {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
.rotate {
-webkit-animation: rotating 1.5s linear infinite;
-moz-animation: rotating 1.5s linear infinite;
-ms-animation: rotating 1.5s linear infinite;
-o-animation: rotating 1.5s linear infinite;
animation: rotating 1.5s linear infinite;
}
.icon {
align-items: center;
display: flex;
justify-content: center;
margin: 0 5px 0 0;
}
...@@ -65,7 +65,6 @@ export default ({ ...@@ -65,7 +65,6 @@ export default ({
) )
}, },
)} )}
<div className={classnames(classes.buttons)}> <div className={classnames(classes.buttons)}>
<Button onClick={isFirst ? () => history.push('/') : prevStep}> <Button onClick={isFirst ? () => history.push('/') : prevStep}>
{isFirst {isFirst
......
...@@ -7,7 +7,9 @@ import { reduxForm } from 'redux-form' ...@@ -7,7 +7,9 @@ import { reduxForm } from 'redux-form'
import { compose, withProps } from 'recompose' import { compose, withProps } from 'recompose'
import { selectCurrentUser } from 'xpub-selectors' import { selectCurrentUser } from 'xpub-selectors'
import { Spinner } from '../UIComponents/'
import classes from './AuthorList.local.scss' import classes from './AuthorList.local.scss'
import { getAuthorFetching } from '../../redux/authors'
import { MenuItem, ValidatedTextField } from './FormItems' import { MenuItem, ValidatedTextField } from './FormItems'
const countries = [ const countries = [
...@@ -22,7 +24,13 @@ const emailRegex = new RegExp(/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/) ...@@ -22,7 +24,13 @@ const emailRegex = new RegExp(/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/)
const emailValidator = value => const emailValidator = value =>
emailRegex.test(value) ? undefined : 'Invalid email' emailRegex.test(value) ? undefined : 'Invalid email'
const AuthorAdder = ({ authors, editMode, setEditMode, handleSubmit }) => ( const AuthorAdder = ({
authors,
editMode,
setEditMode,
handleSubmit,
isFetching,
}) => (
<div className={classnames(classes.adder)}> <div className={classnames(classes.adder)}>
<Button onClick={setEditMode(true)} primary> <Button onClick={setEditMode(true)} primary>
{authors.length === 0 ? '+ Add submitting author' : '+ Add author'} {authors.length === 0 ? '+ Add submitting author' : '+ Add author'}
...@@ -54,9 +62,13 @@ const AuthorAdder = ({ authors, editMode, setEditMode, handleSubmit }) => ( ...@@ -54,9 +62,13 @@ const AuthorAdder = ({ authors, editMode, setEditMode, handleSubmit }) => (
</div> </div>
<div className={classnames(classes['form-buttons'])}> <div className={classnames(classes['form-buttons'])}>
<Button onClick={setEditMode(false)}>Cancel</Button> <Button onClick={setEditMode(false)}>Cancel</Button>
<Button onClick={handleSubmit} primary> {!isFetching ? (
Save <Button onClick={handleSubmit} primary>
</Button> Save
</Button>
) : (
<Spinner />
)}
</div> </div>
</div> </div>
)} )}
...@@ -66,6 +78,7 @@ const AuthorAdder = ({ authors, editMode, setEditMode, handleSubmit }) => ( ...@@ -66,6 +78,7 @@ const AuthorAdder = ({ authors, editMode, setEditMode, handleSubmit }) => (
export default compose( export default compose(
connect(state => ({ connect(state => ({
currentUser: selectCurrentUser(state), currentUser: selectCurrentUser(state),
isFetching: getAuthorFetching(state),
})), })),
withProps(({ currentUser: { admin, username, email }, authors }) => { withProps(({ currentUser: { admin, username, email }, authors }) => {
if (!admin && authors.length === 0) { if (!admin && authors.length === 0) {
......
import React from 'react' import React from 'react'
import classnames from 'classnames' import classnames from 'classnames'
import { compose } from 'recompose'
import { Button } from '@pubsweet/ui' import { Button } from '@pubsweet/ui'
import { connect } from 'react-redux'
import { reduxForm } from 'redux-form' import { reduxForm } from 'redux-form'
import { Spinner } from '../UIComponents'
import { getAuthorFetching } from '../../redux/authors'
import { ValidatedTextField, MenuItem } from './FormItems' import { ValidatedTextField, MenuItem } from './FormItems'
import classes from './AuthorList.local.scss' import classes from './AuthorList.local.scss'
...@@ -19,7 +23,7 @@ const emailRegex = new RegExp(/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/) ...@@ -19,7 +23,7 @@ const emailRegex = new RegExp(/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/)
const emailValidator = value => const emailValidator = value =>
emailRegex.test(value) ? undefined : 'Invalid email' emailRegex.test(value) ? undefined : 'Invalid email'
const AuthorEdit = ({ setAuthorEdit, handleSubmit }) => ( const AuthorEdit = ({ isFetching, setAuthorEdit, handleSubmit }) => (
<div className={classnames(classes['editor-body'])}> <div className={classnames(classes['editor-body'])}>
<div className={classnames(classes.row)}> <div className={classnames(classes.row)}>
<ValidatedTextField isRequired label="First name" name="edit.firstName" /> <ValidatedTextField isRequired label="First name" name="edit.firstName" />
...@@ -44,26 +48,35 @@ const AuthorEdit = ({ setAuthorEdit, handleSubmit }) => ( ...@@ -44,26 +48,35 @@ const AuthorEdit = ({ setAuthorEdit, handleSubmit }) => (
<div className={classnames(classes['form-buttons'])}> <div className={classnames(classes['form-buttons'])}>
<Button onClick={setAuthorEdit(-1)}>Cancel</Button> <Button onClick={setAuthorEdit(-1)}>Cancel</Button>
<Button onClick={handleSubmit} primary> {!isFetching ? (
Save <Button onClick={handleSubmit} primary>
</Button> Save
</Button>
) : (
<Spinner />
)}
</div> </div>
</div> </div>
) )
export default reduxForm({ export default compose(
form: 'edit', connect(state => ({
onSubmit: ( isFetching: getAuthorFetching(state),
values, })),
dispatch, reduxForm({
{ setAuthorEdit, setAuthors, authors, index, changeForm }, form: 'edit',
) => { onSubmit: (
const newAuthors = [ values,
...authors.slice(0, index), dispatch,
values.edit, { setAuthorEdit, setAuthors, authors, index, changeForm },
...authors.slice(index + 1), ) => {
] const newAuthors = [
setAuthorEdit(-1)() ...authors.slice(0, index),
setAuthors(newAuthors) values.edit,
}, ...authors.slice(index + 1),
})(AuthorEdit) ]
setAuthorEdit(-1)()
setAuthors(newAuthors)
},
}),
)(AuthorEdit)
import React from 'react'
import classnames from 'classnames'
import { Icon } from '@pubsweet/ui'
import classes from './Spinner.local.scss'
const Spinner = () => (
<div className={classnames(classes.rotate, classes.icon)}>
<Icon size={16}>loader</Icon>
</div>
)
export default Spinner
@keyframes rotating {
from {
-o-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-o-transform: rotate(360deg);
-ms-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-webkit-keyframes rotating {
from {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
.rotate {
-webkit-animation: rotating 1.8s linear infinite;
-moz-animation: rotating 1.8s linear infinite;
-ms-animation: rotating 1.8s linear infinite;
-o-animation: rotating 1.8s linear infinite;
animation: rotating 1.8s linear infinite;
}
.icon {
align-items: center;
display: flex;
justify-content: center;
margin: 0 5px 0 0;
}
export { default as Logo } from './Logo' export { default as Logo } from './Logo'
export { default as Spinner } from './Spinner'
export { default as Dropdown } from './Dropdown' export { default as Dropdown } from './Dropdown'
export { default as SortableList } from './SortableList/SortableList' export { default as SortableList } from './SortableList/SortableList'
export { default as AuthorList } from './AuthorList/AuthorList' export { default as AuthorList } from './AuthorList/AuthorList'
export { Dropdown, Logo } from './UIComponents' export { Dropdown, Logo, Spinner } from './UIComponents'
export { DragHandle } from './AuthorList/FormItems' export { DragHandle } from './AuthorList/FormItems'
import { get } from 'lodash'
import * as api from 'pubsweet-client/src/helpers/api' import * as api from 'pubsweet-client/src/helpers/api'
// constants // constants
export const SET_AUTHORS = 'authors/SET_AUTHORS' const REQUEST = 'authors/REQUEST'
const FAILURE = 'authors/FAILURE'
const SUCCESS = 'authors/SUCCESS'
// actions // actions
export const addAuthor = (author, collectionId, fragmentId) => dispatch => export const authorRequest = () => ({
api.create( type: REQUEST,
`/collections/${collectionId}/fragments/${fragmentId}/authors`, })
author,
) export const authorFaiure = error => ({
type: FAILURE,
error,
})
export const authorSuccess = () => ({
type: SUCCESS,
})
export const addAuthor = (author, collectionId, fragmentId) => dispatch => {
dispatch(authorRequest())
return api
.create(
`/collections/${collectionId}/fragments/${fragmentId}/authors`,
author,
)
.then(author => {
dispatch(authorSuccess())
return author
})
.catch(err => dispatch(authorFaiure(err)))
}
// selectors // selectors
export const getFragmentAuthors = (state, fragmentId) =>
get(state, `authors.${fragmentId}`) || []
export const getAuthorFetching = state => state.authors.isFetching
export const getAuthorError = state => state.authors.error
const initialState = { isFetching: false, error: null }
export default (state = {}, action) => { export default (state = initialState, action) => {
switch (action.type) { switch (action.type) {
case 'UPDATE_FRAGMENT_REQUEST':
case REQUEST:
return {
...initialState,
isFetching: true,
}
case FAILURE:
return {
...initialState,
error: action.error,
}
case 'UPDATE_FRAGMENT_SUCCESS':
case SUCCESS:
return initialState
default: default:
return state return state
} }
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment