Skip to content
Snippets Groups Projects
Commit b2f3a3cb authored by Bogdan Cochior's avatar Bogdan Cochior
Browse files

Merge branch 'faraday-master' of gitlab.coko.foundation:xpub/xpub into faraday-master

parents 75e587f1 2ba2837d
No related branches found
No related tags found
No related merge requests found
Showing
with 645 additions and 7 deletions
import React from 'react'
import classnames from 'classnames'
import classes from './FileDropzone.local.scss'
const FileDropzone = ({ ...props }) => (
<div className={classnames(classes.dropzone)}>
<span>Drag items here or use the upload button</span>
</div>
)
// export default compose(
// DropTarget(
// 'item',
// {
// drop(props) {
// console.log('s-a dat drop', props)
// },
// },
// (connect, monitor) => ({
// connectDropTarget: connect.dropTarget(),
// }),
// ),
// )(FileDropzone)
export default FileDropzone
.dropzone {
align-items: center;
display: flex;
height: 60px;
justify-content: center;
margin: 10px 0;
span {
color: #888;
font-size: 14px;
}
}
import React from 'react'
import classnames from 'classnames'
import { Icon } from '@pubsweet/ui'
import classes from './FileItem.local.scss'
const parseFileSize = size => {
const kbSize = size / 1000
const mbSize = kbSize / 1000
const gbSize = mbSize / 1000
if (Math.floor(gbSize)) {
return `${Math.floor(gbSize)} GB`
} else if (Math.floor(mbSize)) {
return `${Math.floor(mbSize)} MB`
} else if (Math.floor(kbSize)) {
return `${Math.floor(kbSize)} kB`
}
return `${size} bytes`
}
const FileItem = ({ dragHandle, name, size, id, removeFile, ...rest }) => (
<div className={classnames(classes['file-item'])}>
{dragHandle}
<div className={classnames(classes.info)}>
<span>{name}</span>
<span>{parseFileSize(size)}</span>
</div>
<div className={classnames(classes.buttons)}>
<a href={rest.signedUrl} target="_blank">
<Icon color="#666">eye</Icon>
</a>
<button onClick={removeFile(id)} title="Delete">
<Icon color="#666">trash-2</Icon>
</button>
</div>
</div>
)
export default FileItem
.file-item {
align-items: center;
border: 1px solid black;
display: flex;
margin: 5px;
.info {
border-right: 1px solid black;
display: flex;
flex: 1;
justify-content: space-between;
padding: 2px 10px 2px 0;
}
.buttons {
align-items: center;
display: flex;
justify-content: center;
margin: 0 10px;
a {
align-items: center;
display: flex;
}
button {
background-color: transparent;
border: none;
cursor: pointer;
&:active,
&:focus {
outline: none;
}
}
}
}
import React, { Component } from 'react'
class FilePicker extends Component {
handleUpload = e => {
const { onUpload } = this.props
onUpload(e.target.files[0])
this.fileInput.value = null
}
render() {
const { children } = this.props
return (
<div>
<input
onChange={this.handleUpload}
ref={input => (this.fileInput = input)}
style={{ display: 'none' }}
type="file"
/>
{React.cloneElement(children, {
onClick: e => {
e.preventDefault()
this.fileInput.click()
},
})}
</div>
)
}
}
export default FilePicker
import React from 'react'
import { compose } from 'recompose'
import classnames from 'classnames'
import { Icon } from '@pubsweet/ui'
import { DropTarget } from 'react-dnd'
import { NativeTypes } from 'react-dnd-html5-backend'
import { SortableList } from 'pubsweet-components-faraday/src/components'
import FileItem from './FileItem'
import FilePicker from './FilePicker'
import FileDropzone from './FileDropzone'
import classes from './FileSection.local.scss'
const DragHandle = () => (
<div className={classnames(classes['drag-handle'])}>
<Icon size={14}>chevron_up</Icon>
<Icon size={10}>menu</Icon>
<Icon size={14}>chevron_down</Icon>
</div>
)
const FileSection = ({
title,
files,
listId,
isOver,
isLast,
isFirst,
addFile,
canDrop,
dropItems,
moveItem,
isFileOver,
removeFile,
connectFileDrop,
connectDropTarget,
}) =>
connectFileDrop(
connectDropTarget(
<div
className={classnames({
[classes['drop-section']]: true,
[classes['no-border-top']]: !isFirst,
[classes['dashed-border']]: !isLast,
[classes['is-over']]: isFileOver || (isOver && canDrop),
})}
>
<div className={classnames(classes.header)}>
<span className={classnames(classes.title)}>{title}</span>
<FilePicker onUpload={addFile}>
<div className={classnames(classes['upload-button'])}>
<Icon>file-plus</Icon>
</div>
</FilePicker>
</div>
<SortableList
beginDragProps={['id', 'index', 'name', 'listId']}
dragHandle={DragHandle}
dropItem={dropItems}
items={files}
listId={listId}
listItem={FileItem}
moveItem={moveItem}
removeFile={removeFile}
/>
<FileDropzone />
</div>,
),
)
export default compose(
DropTarget(
'item',
{
drop({ changeList, listId: toListId }, monitor) {
const { listId: fromListId, id } = monitor.getItem()
if (toListId === fromListId) return
changeList(fromListId, toListId, id)
},
canDrop({ listId: toListId }, monitor) {
const { listId: fromListId } = monitor.getItem()
return toListId !== fromListId
},
},
(connect, monitor) => ({
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
canDrop: monitor.canDrop(),
}),
),
DropTarget(
NativeTypes.FILE,
{
drop({ addFile }, monitor) {
const [file] = monitor.getItem().files
addFile(file)
},
},
(connect, monitor) => ({
connectFileDrop: connect.dropTarget(),
isFileOver: monitor.isOver(),
}),
),
)(FileSection)
.drop-section {
border: 1px solid black;
display: flex;
flex-direction: column;
padding: 5px;
.header {
align-items: center;
display: flex;
justify-content: flex-start;
.upload-button {
cursor: pointer;
display: flex;
margin-left: 5px;
}
.title {
margin: 5px;
text-transform: uppercase;
}
}
}
.no-border-top {
border-top: none;
}
.dashed-border {
border-bottom: 1px dashed black;
}
.drag-handle {
align-items: center;
border-right: 1px solid black;
cursor: move;
display: flex;
flex-direction: column;
justify-content: center;
margin-right: 10px;
padding: 3px;
}
.is-over {
background-color: #ddd;
}
import React from 'react'
import { get } from 'lodash'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { actions } from 'pubsweet-client'
import { withRouter } from 'react-router-dom'
import { compose, withHandlers, getContext, lifecycle } from 'recompose'
import { SortableList } from 'pubsweet-components-faraday/src/components'
import FileSection from './FileSection'
import { uploadFile, setFiles, getFiles, deleteFile } from '../redux/files'
const Files = ({
files,
addFile,
moveItem,
removeFile,
changeList,
dropItems,
...rest
}) => (
<div>
<FileSection
addFile={addFile('manuscripts')}
changeList={changeList}
dropItems={dropItems}
files={get(files, 'manuscripts') || []}
isFirst
listId="manuscripts"
moveItem={moveItem('manuscripts')}
removeFile={removeFile('manuscripts')}
title="Main manuscript"
/>
<FileSection
addFile={addFile('supplementary')}
changeList={changeList}
files={get(files, 'supplementary') || []}
listId="supplementary"
moveItem={moveItem('supplementary')}
removeFile={removeFile('supplementary')}
title="Supplementarry files"
/>
<FileSection
addFile={addFile('coverLetter')}
changeList={changeList}
files={get(files, 'coverLetter') || []}
isLast
listId="coverLetter"
moveItem={moveItem('coverLetter')}
removeFile={removeFile('coverLetter')}
title="Cover letter"
/>
</div>
)
export default compose(
withRouter,
getContext({ version: PropTypes.object, project: PropTypes.object }),
connect(
state => ({
isFetching: state.files.isFetching,
files: getFiles(state),
}),
{
uploadFile,
updateFragment: actions.updateFragment,
setFiles,
deleteFile,
},
),
lifecycle({
componentDidMount() {
const { setFiles, version } = this.props
setFiles(version.files.manuscripts, 'manuscripts')
setFiles(version.files.supplementary, 'supplementary')
setFiles(version.files.coverLetter, 'coverLetter')
},
}),
withHandlers({
dropItems: ({ files, updateFragment, project, version }) => () => {
updateFragment(project, {
submitted: new Date(),
...version,
files,
})
},
changeList: ({ files, setFiles, updateFragment, project, version }) => (
fromListId,
toListId,
id,
) => {
const swappedFile = files[fromListId].find(f => f.id === id)
const fromFiles = files[fromListId].filter(f => f.id !== id)
const toFiles = [...files[toListId], swappedFile]
setFiles(fromFiles, fromListId)
setFiles(toFiles, toListId)
updateFragment(project, {
submitted: new Date(),
...version,
files: {
...version.files,
[fromListId]: fromFiles,
[toListId]: toFiles,
},
})
},
addFile: ({
files,
setFiles,
uploadFile,
updateFragment,
project,
version,
}) => type => file => {
uploadFile(file, type).then(fileResponse => {
setFiles([...files[type], fileResponse], type)
updateFragment(project, {
submitted: new Date(),
...version,
files: {
...version.files,
[type]: version.files[type]
? [...version.files[type], fileResponse]
: [fileResponse],
},
})
})
},
moveItem: ({
setFiles,
files,
project,
version,
updateFragment,
}) => type => (dragIndex, hoverIndex) => {
const newFiles = SortableList.moveItem(files[type], dragIndex, hoverIndex)
setFiles(newFiles, type)
},
removeFile: ({
setFiles,
files,
deleteFile,
project,
version,
updateFragment,
}) => type => id => e => {
e.preventDefault()
deleteFile(id)
const newFiles = files[type].filter(f => f.id !== id)
setFiles(newFiles, type)
updateFragment(project, {
submitted: new Date(),
...version,
files: {
...version.files,
[type]: newFiles,
},
})
},
}),
)(Files)
.file-item {
align-items: center;
border: 1px solid black;
display: flex;
margin: 5px;
.info {
border-right: 1px solid black;
display: flex;
flex: 1;
padding: 2px 0;
}
.buttons {
align-items: center;
display: flex;
justify-content: center;
margin: 0 5px;
button {
border: none;
cursor: pointer;
&:active,
&:focus {
outline: none;
}
}
}
}
......@@ -6,6 +6,8 @@ import { withJournal } from 'xpub-journal'
import { ConnectPage } from 'xpub-connect'
import { selectCollection, selectFragment } from 'xpub-selectors'
import { compose, withHandlers, withState, withContext } from 'recompose'
import HTML5Backend from 'react-dnd-html5-backend'
import { DragDropContext } from 'react-dnd'
import Wizard from './Wizard'
......@@ -78,4 +80,5 @@ export default compose(
toggleConfirmation,
}),
),
DragDropContext(HTML5Backend),
)(Wizard)
......@@ -5,6 +5,7 @@ import { ValidatedField, Button } from '@pubsweet/ui'
import classes from './WizardStep.local.scss'
import AutosaveIndicator from './AutosaveIndicator'
import Files from './Files'
export default ({
children: stepChildren,
......@@ -65,6 +66,7 @@ export default ({
)
},
)}
<Files />
<div className={classnames(classes.buttons)}>
<Button onClick={isFirst ? () => history.push('/') : prevStep}>
{isFirst
......
......@@ -4,6 +4,7 @@ module.exports = {
reducers: {
wizardConversion: () => require('./redux/conversion').default,
autosave: () => require('./redux/autosave').default,
files: () => require('./redux/files').default,
},
},
}
import request, { remove } from 'pubsweet-client/src/helpers/api'
const initialState = {
isFetching: false,
error: null,
files: {
manuscripts: [],
supplementary: [],
coverLetter: [],
},
}
const UPLOAD_REQUEST = 'files/UPLOAD_REQUEST'
const UPLOAD_FAILURE = 'files/UPLOAD_FAILURE'
const UPLOAD_SUCCESS = 'files/UPLOAD_SUCCESS'
const REMOVE_REQUEST = 'files/REMOVE_REQUEST'
const REMOVE_FAILURE = 'files/REMOVE_FAILURE'
const REMOVE_SUCCESS = 'files/REMOVE_SUCCESS'
const SET_FILES = 'files/SET_FILES'
export const setFiles = (files, fileType) => ({
type: SET_FILES,
files,
fileType,
})
const uploadRequest = () => ({
type: UPLOAD_REQUEST,
})
const uploadFailure = error => ({
type: UPLOAD_FAILURE,
error,
})
const uploadSuccess = () => ({
type: UPLOAD_SUCCESS,
})
const createFileData = (file, type) => {
const data = new FormData()
data.append('fileType', type)
data.append('file', file)
return {
method: 'POST',
headers: {
Accept: 'text/plain',
},
body: data,
}
}
const removeRequest = () => ({
type: REMOVE_REQUEST,
})
const removeFailure = error => ({
type: REMOVE_FAILURE,
error,
})
const removeSuccess = () => ({
type: REMOVE_SUCCESS,
})
export const getFiles = state => state.files.files
export const uploadFile = (file, type) => dispatch => {
dispatch(uploadRequest())
return request('/aws-upload', createFileData(file, type))
.then(r => {
dispatch(uploadSuccess())
return r
})
.catch(err => dispatch(uploadFailure(err.message)))
}
export const deleteFile = fileId => dispatch => {
dispatch(removeRequest())
return remove(`/aws-delete/${fileId}`)
.then(r => {
dispatch(removeSuccess())
return r
})
.catch(err => dispatch(removeFailure(err.message)))
}
export default (state = initialState, action) => {
switch (action.type) {
case UPLOAD_REQUEST:
return {
...state,
isFetching: true,
error: null,
}
case SET_FILES:
return {
...state,
files: {
...state.files,
[action.fileType]: action.files,
},
}
case UPLOAD_FAILURE:
return {
...state,
isFetching: false,
error: action.error,
}
case UPLOAD_SUCCESS:
return {
...state,
isFetching: false,
error: null,
}
default:
return state
}
}
export { default as files } from './files'
export { default as autosave } from './autosave'
export { default as conversion } from './conversion'
......@@ -63,6 +63,7 @@ const Authors = ({
/>
) : (
<SortableList
beginDragProps={['index']}
dragHandle={DragHandle}
dropItem={dropItem}
editedAuthor={editedAuthor}
......
......@@ -31,9 +31,9 @@ export default ({
key={a.firstName}
{...a}
countryParser={countryParser}
index={index}
parseAuthorType={parseAuthorType}
removeAuthor={removeAuthor}
index={index}
{...rest}
/>
),
......
import React from 'react'
import { pick } from 'lodash'
import { compose } from 'recompose'
import { findDOMNode } from 'react-dom'
import HTML5Backend from 'react-dnd-html5-backend'
import { DragSource, DropTarget, DragDropContext } from 'react-dnd'
import { DragSource, DropTarget } from 'react-dnd'
const itemSource = {
beginDrag(props) {
return { index: props.index }
return pick(props, props.beginDragProps)
},
}
const itemTarget = {
hover({ moveItem, index }, monitor, component) {
const dragIndex = monitor.getItem().index
hover({ moveItem, index, listId }, monitor, component) {
const { index: dragIndex, listId: toListId } = monitor.getItem()
const hoverIndex = index
if (listId !== toListId) {
return
}
// Don't replace items with themselves
if (dragIndex === hoverIndex) {
return
......@@ -124,4 +128,5 @@ SortableList.moveItem = (items, dragIndex, hoverIndex) => {
]
}
export default DragDropContext(HTML5Backend)(SortableList)
// export default DragDropContext(HTML5Backend)(SortableList)
export default SortableList
......@@ -2,3 +2,4 @@ export { default as SortableList } from './SortableList/SortableList'
export { default as AuthorList } from './AuthorList/AuthorList'
export { Dropdown, Logo } from './UIComponents'
export { DragHandle } from './AuthorList/FormItems'
_build/
api/
logs/
node_modules/
uploads/
.env.*
.env
config/local*.*
\ No newline at end of file
## xPub-aws
A server package that handles AWS
\ No newline at end of file
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