Skip to content

Commit

Permalink
Convert attachment input to react component
Browse files Browse the repository at this point in the history
  • Loading branch information
Murilo Varela committed Jul 14, 2017
1 parent cdf8e46 commit ff8c292
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Changed
- Story tasks to react components
- Story attachments input to react component

### Fixed
- Fixing compatibility of docker-composer with webpack
Expand Down
81 changes: 81 additions & 0 deletions app/assets/javascripts/components/story/StoryAttachment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React from 'react';

class StoryAttachment extends React.Component {
constructor(props) {
super(props);
this.saveInput = (input) => { this.filesInput = input };
this.random = (Math.floor(Math.random() * 10000) + 1);
this.progressElementId = "documents_progress_" + this.random;
this.finishedElementId = "documents_finished_" + this.random;
this.attachinaryContainerId = "attachinary_container_" + this.random;
}

componentDidMount() {
const $filesInput = $(this.filesInput);
$filesInput.off('fileuploadprogressall');
$filesInput.on('fileuploadprogressall', (function(_this, _progressElementId, _finishedElementId) {
return function(e, data) {
var $progress = $('#' + _progressElementId);
if ( $progress.is(":hidden") ) {
$progress.show();
}

var progress = parseInt(data.loaded / data.total * 100, 10);
$progress.css('width', progress + "%");

if (progress == 100) {
$progress.css('width', "1px");
$progress.hide();

$('#' + _finishedElementId).show();
}
};
})(this, this.progressElementId, this.finishedElementId));
}

renderAttachmentInput() {
const { name, attachinaryProps } = this.props;
let attachinary = attachinaryProps(
'documents',
this.progressElementId,
this.finishedElementId,
this.attachinaryContainerId
);

return(
<input
type='file'
name={name}
className='attachinary-input'
ref={this.saveInput}
multiple={attachinary.multiple}
data-attachinary={attachinary.dataAttachinary}
data-form-data={attachinary.dataFormData}
data-url={attachinary.dataUrl}
/>
);
}

renderProgressBar() {
return(
<div
id={this.progressElementId}
className='attachinary_progress_bar'
/>
);
}

render() {
const { name, isReadonly } = this.props;
return(
<div className="uploads">
<label htmlFor={name}>{ I18n.t(`story.${name}`) }</label>
{ (!isReadonly) ? this.renderAttachmentInput() : null }
{ (!isReadonly) ? this.renderProgressBar() : null }
<div id={this.attachinaryContainerId} />
</div>
);
}
}

export default StoryAttachment;
57 changes: 42 additions & 15 deletions app/assets/javascripts/views/story_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import NoteForm from 'components/notes/NoteForm';
import StoryLabels from 'components/story/StoryLabels';
import StoryTasks from 'components/story/StoryTasks';
import TaskForm from 'components/tasks/TaskForm';
import StoryAttachment from 'components/story/StoryAttachment';

var Clipboard = require('clipboard');

Expand Down Expand Up @@ -405,21 +406,9 @@ module.exports = FormView.extend({

this.$el.append(
this.makeFormControl(function(div) {
var random = (Math.floor(Math.random() * 10000) + 1);
var progress_element_id = "documents_progress_" + random;
var finished_element_id = "documents_finished_" + random;
var attachinary_container_id = "attachinary_container_" + random;

$(div).append(this.label('attachments', I18n.t('story.attachments')));
$(div).addClass('uploads');
if(!this.isReadonly()) {
$(div).append(this.fileField("documents", progress_element_id, finished_element_id, attachinary_container_id));
$(div).append("<div id='" + progress_element_id + "' class='attachinary_progress_bar'></div>");
}
$(div).append('<div id="' + attachinary_container_id + '"></div>');

// FIXME: refactor to a separated AttachmentView or similar
// must run the plugin after the element is available in the DOM, not before, hence, the setTimeout
const $storyAttachments = $('<div class="story-attachments"></div>');
$(div).append($storyAttachments);

clearTimeout(window.executeAttachinaryTimeout);
window.executeAttachinaryTimeout = setTimeout(executeAttachinary, 1000);
})
Expand All @@ -443,6 +432,31 @@ module.exports = FormView.extend({
return this;
},

makeAttachinaryProps: function(name, progress_element_id, finished_element_id, attachinary_container_id) {
const field_name = name + ( ATTACHINARY_OPTIONS['html']['multiple'] ? '[]' : '' );
let files = this.model.get('documents');

if(files) {
files = files.map(function(d) { return d.file });
}

const options = $.extend(ATTACHINARY_OPTIONS['attachinary'], {
files_container_selector: '#' + attachinary_container_id,
'files': files
});
const dataAttachinary = JSON.stringify(options);
const dataFormData = JSON.stringify(ATTACHINARY_OPTIONS['html']['data']['form_data']);
const dataUrl = ATTACHINARY_OPTIONS['html']['data']['url'];
const multiple = (ATTACHINARY_OPTIONS['html']['multiple']) ? 'multiple' : '';

return {
dataAttachinary: dataAttachinary,
dataFormData: dataFormData,
dataUrl: dataUrl,
multiple: multiple
};
},

renderReactComponents: function() {
ReactDOM.render(
<StoryControls
Expand Down Expand Up @@ -477,6 +491,7 @@ module.exports = FormView.extend({
isNew={this.model.isNew()}
editingDescription={this.model.get('editingDescription')}
value={this.model.get("description")}
fileuploadprogressall={this.uploadProgressBar}
onChange={ (event) => this.onChangeModel(event.target.value, "description") }
onClick={this.editDescription}
/>,
Expand All @@ -499,6 +514,18 @@ module.exports = FormView.extend({
);
}

const attachments = this.$('.story-attachments')[0];
if(attachments) {
ReactDOM.render(
<StoryAttachment
name='attachments'
isReadonly={this.isReadonly()}
attachinaryProps={() => this.makeAttachinaryProps()}
/>,
attachments
);
}

this.renderSelects();
this.renderTasks();
this.renderNotes();
Expand Down

0 comments on commit ff8c292

Please sign in to comment.