Commit 10234f83 authored by Yannis Barlas's avatar Yannis Barlas
Browse files

Merge branch 'multiple-file-upload' into 'master'

Multiple file upload

See merge request !1
parents e67fe195 6c51e227
......@@ -5,7 +5,9 @@ import { connect } from 'react-redux'
// TODO -- clean up this import
import Actions from 'pubsweet-client/src/actions'
import Division from './Division'
import FileUploader from './FileUploader/FileUploader'
import TeamManagerModal from './TeamManager/TeamManagerModal'
import styles from './styles/bookBuilder.local.scss'
......@@ -22,10 +24,12 @@ export class BookBuilder extends React.Component {
this._getRoles = this._getRoles.bind(this)
this._isProductionEditor = this._isProductionEditor.bind(this)
this.setProductionEditor = this.setProductionEditor.bind(this)
this.updateUploadStatus = this.updateUploadStatus.bind(this)
this.state = {
outerContainer: {},
showTeamManager: false
showTeamManager: false,
uploading: {}
}
}
......@@ -166,6 +170,10 @@ export class BookBuilder extends React.Component {
)
}
updateUploadStatus (status) {
this.setState({ uploading: status })
}
render () {
const { book, chapters } = this.props
const { createFragment, deleteFragment, ink, updateFragment } = this.props.actions
......@@ -194,8 +202,10 @@ export class BookBuilder extends React.Component {
let teamManagerButton = ''
if (isProductionEditor) {
teamManagerButton = (
<div className={styles.teamManagerBtn}
onClick={this._toggleTeamManager}>
<div
className={styles.teamManagerBtn}
onClick={this._toggleTeamManager}
>
<a>team manager</a>
</div>
)
......@@ -204,17 +214,30 @@ export class BookBuilder extends React.Component {
const productionEditor = _.get(book, 'productionEditor.username') || 'unassigned'
const teamManagerModal = this.renderTeamManagerModal()
// console.log('render bb')
return (
<div className='bootstrap modal pubsweet-component pubsweet-component-scroll'>
<div className={styles.bookBuilder}>
<div
className='col-lg-offset-2 col-lg-8 col-md-8 col-sm-12 col-xs-12'
ref='outerContainer'>
ref='outerContainer'
>
<h1>{this.props.book.title}</h1>
<div className={styles.productionEditorContainer}>
<span>Production Editor: &nbsp; { productionEditor } </span>
<FileUploader
backChapters={backChapters}
bodyChapters={bodyChapters}
book={book}
convert={ink}
create={createFragment}
frontChapters={frontChapters}
update={updateFragment}
updateUploadStatus={this.updateUploadStatus}
/>
{teamManagerButton}
<div className={styles.separator} />
</div>
......@@ -230,6 +253,7 @@ export class BookBuilder extends React.Component {
title='Front Matter'
type='front'
update={updateFragment}
uploadStatus={this.state.uploading}
/>
<div className={styles.sectionDivider} />
......@@ -245,6 +269,7 @@ export class BookBuilder extends React.Component {
title='Body'
type='body'
update={updateFragment}
uploadStatus={this.state.uploading}
/>
<div className={styles.sectionDivider} />
......@@ -260,6 +285,7 @@ export class BookBuilder extends React.Component {
title='Back Matter'
type='back'
update={updateFragment}
uploadStatus={this.state.uploading}
/>
</div>
......@@ -283,26 +309,28 @@ BookBuilder.propTypes = {
}
function mapStateToProps (state, ownProps) {
let book = _.find(state.collections, function (c) {
const book = _.find(state.collections, (c) => {
return c.id === ownProps.params.id
})
let chapters = _.sortBy(_.filter(state.fragments, function (f) {
const chapters = _.sortBy(_.filter(state.fragments, (f) => {
return f.book === book.id && f.id && !f.deleted
}), 'index')
let teams = state.teams
let users = state.users.users
let user = state.currentUser.user
// console.log(chapters)
const teams = state.teams
const users = state.users.users
const user = state.currentUser.user
let error = state.error
const error = state.error
return {
book: book || {},
chapters: chapters,
teams: teams,
users: users,
user: user,
chapters,
teams,
users,
user,
// userRoles: state.auth.roles,
errorMessage: error
}
......
......@@ -75,9 +75,12 @@ export class Chapter extends React.Component {
remove,
roles,
title,
type
type,
uploading
} = this.props
// console.log('ch upl', this.props.uploading)
const { isUploadInProgress } = this.state
const listItemStyle = {
......@@ -103,7 +106,7 @@ export class Chapter extends React.Component {
<FirstRow
book={book}
chapter={chapter}
isUploadInProgress={isUploadInProgress}
isUploadInProgress={isUploadInProgress || uploading}
outerContainer={outerContainer}
remove={remove}
roles={roles}
......@@ -143,7 +146,8 @@ Chapter.propTypes = {
roles: React.PropTypes.array,
title: React.PropTypes.string.isRequired,
type: React.PropTypes.string.isRequired,
update: React.PropTypes.func.isRequired
update: React.PropTypes.func.isRequired,
uploading: React.PropTypes.bool
}
// combine them, as each chapter can be both a source and a target
......
......@@ -67,6 +67,7 @@ class ChapterTitle extends React.Component {
renderUploadIndicator () {
const { isUploadInProgress } = this.props
// console.log('is uploading', isUploadInProgress)
if (!isUploadInProgress) return null
......@@ -132,6 +133,7 @@ class ChapterTitle extends React.Component {
{ hasContent }
{ title }
{/* { this.props.chapter.index } */}
{ uploadIndicator }
{ renameEmptyError }
......
......@@ -17,7 +17,7 @@ class ChapterSecondRow extends React.Component {
<div className={styles.noPadding + ' col-lg-2 col-md-12 col-sm-12 col-xs-12'}>
<UploadButton
accept='.docx'
accept='.doc,.docx'
chapter={chapter}
convertFile={convertFile}
modalContainer={outerContainer}
......
......@@ -130,12 +130,17 @@ export class Division extends React.Component {
}
render () {
const { book, chapters, ink, outerContainer, roles, title, type, update } = this.props
const { book, chapters, ink, outerContainer, roles, title, type, update, uploadStatus } = this.props
const { _onAddClick, _onRemove, _onMove } = this
// console.log('div upl', uploadStatus)
const chapterType = (type === 'body') ? 'chapter' : 'component'
const chapterInstances = _.map(chapters, function (c, i) {
// console.log('c id', c.id)
// console.log(uploadStatus[c.id])
// console.log('')
return (
<Chapter
book={book}
......@@ -151,6 +156,7 @@ export class Division extends React.Component {
title={c.title}
type={c.subCategory}
update={update}
uploading={uploadStatus[c.id]}
/>
)
})
......@@ -222,7 +228,8 @@ Division.propTypes = {
roles: React.PropTypes.array,
title: React.PropTypes.string.isRequired,
type: React.PropTypes.string.isRequired,
update: React.PropTypes.func.isRequired
update: React.PropTypes.func.isRequired,
uploadStatus: React.PropTypes.object
}
export default DragDropContext(HTML5Backend)(Division)
import React from 'react'
import { each, get, keys, pickBy, sortBy } from 'lodash'
import styles from '../styles/bookBuilder.local.scss'
class FileUploader extends React.Component {
constructor (props) {
super(props)
this.onChange = this.onChange.bind(this)
this.state = {
counter: {
front: null,
body: null,
back: null
},
uploading: {}
}
}
handleUploadStatusChange (fragmentId, bool) {
const { uploading } = this.state
uploading[fragmentId] = bool
this.setState({
uploading
})
}
onChange (event) {
event.preventDefault()
const {
backChapters,
bodyChapters,
book,
convert,
create,
frontChapters,
update,
updateUploadStatus
} = this.props
const divisionMapper = {
a: {
chapterList: frontChapters,
division: 'front'
},
b: {
chapterList: bodyChapters,
division: 'body'
},
c: {
chapterList: backChapters,
division: 'back'
}
}
const originalFiles = event.target.files
const files = sortBy(originalFiles, 'name') // ensure order
each(keys(divisionMapper), (key) => {
const division = divisionMapper[key]
const { counter } = this.state
const baseCounter = get(division, 'chapterList.length') || 0
counter[division.division] = baseCounter
this.setState(counter)
})
each(files, (file, i) => {
const name = file.name.replace(/\.[^/.]+$/, '') // remove file extension
const nameSpecifier = name.slice(0, 1) // get division from name
// mark last file
let last
if ((i + 1) === files.length) last = true
// default to body
let division
if (!divisionMapper[nameSpecifier]) {
division = 'body'
} else {
division = divisionMapper[nameSpecifier].division
}
let subCategory
if (division !== 'body') {
subCategory = 'component'
} else {
if (name.slice(5, 9) === 'Part') {
subCategory = 'part'
} else {
subCategory = 'chapter'
}
}
const index = this.state.counter[division]
const nextIndex = index + 1
const { counter } = this.state
counter[division] = nextIndex
this.setState({ counter })
const fragment = {
book: book.id,
subCategory,
division,
alignment: {
left: false,
right: false
},
progress: {
style: 0,
edit: 0,
review: 0,
clean: 0
},
lock: null,
index,
kind: 'chapter',
title: name,
status: 'unpublished',
author: '',
source: '',
comments: {},
trackChanges: false
}
// setTimeout(() => {
create(book, fragment).then((res) => {
const fragmentId = res.fragment.id
if (last) this.input.value = '' // reset input
this.handleUploadStatusChange(fragmentId, true)
updateUploadStatus(this.state.uploading)
convert(file).then((response) => {
const patch = {
id: fragmentId,
source: response.converted
}
update(book, patch)
this.handleUploadStatusChange(fragmentId, false)
updateUploadStatus(this.state.uploading)
}).catch((error) => {
console.log(error)
this.handleUploadStatusChange(fragmentId, false)
updateUploadStatus(this.state.uploading)
})
}).catch((error) => {
console.log('create fragment error', error)
})
// }, i * 50)
})
}
render () {
const containerStyles = {
padding: '0'
}
const inputStyles = {
display: 'none'
}
const labelStyles = {
color: '#fff',
cursor: 'pointer',
fontWeight: '500',
margin: 'auto 0',
padding: ' 0 30px'
}
const { uploading } = this.state
const uploadingOnly = pickBy(uploading, (value, key) => {
return (value === true)
})
const currentlyUploading = keys(uploadingOnly).length
let labelText
if (currentlyUploading > 0) {
labelText = `converting ${currentlyUploading} files`
} else {
labelText = 'upload files'
}
return (
<div
className={styles.teamManagerBtn}
style={containerStyles}
>
<label
htmlFor='file-uploader'
style={labelStyles}
>
{ labelText }
</label>
<input
accept='.doc,.docx'
id='file-uploader'
multiple
name='file-uploader'
onChange={this.onChange}
ref={(c) => { this.input = c }}
style={inputStyles}
type='file'
/>
</div>
)
}
}
FileUploader.propTypes = {
backChapters: React.PropTypes.array.isRequired,
bodyChapters: React.PropTypes.array.isRequired,
book: React.PropTypes.object.isRequired,
convert: React.PropTypes.func.isRequired,
create: React.PropTypes.func.isRequired,
frontChapters: React.PropTypes.array.isRequired,
update: React.PropTypes.func.isRequired,
updateUploadStatus: React.PropTypes.func.isRequired
}
export default FileUploader
......@@ -7,7 +7,7 @@ $white: #fff;
.bookBuilder {
@import 'animate';
counter-reset: chapter;
// counter-reset: chapter;
font-family: 'Fira Sans';
h1 {
......@@ -266,10 +266,10 @@ $white: #fff;
.isChapter {
padding-left: 3%!important;
h3::before {
counter-increment: chapter;
content: "" counter(chapter) ". ";
}
// h3::before {
// counter-increment: chapter;
// content: "" counter(chapter) ". ";
// }
}
.isPart {
......
Markdown is supported
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