@@ -6,7 +6,12 @@ import {defineMessages, injectIntl, intlShape} from 'react-intl';
66
77import analytics from '../lib/analytics' ;
88import log from '../lib/log' ;
9- import { LoadingStates , onLoadedProject , onProjectUploadStarted } from '../reducers/project-state' ;
9+ import {
10+ LoadingStates ,
11+ getIsLoadingUpload ,
12+ onLoadedProject ,
13+ requestProjectUpload
14+ } from '../reducers/project-state' ;
1015
1116import {
1217 openLoadingProject ,
@@ -48,9 +53,31 @@ class SBFileUploader extends React.Component {
4853 'renderFileInput' ,
4954 'setFileInput' ,
5055 'handleChange' ,
51- 'handleClick'
56+ 'handleClick' ,
57+ 'onload' ,
58+ 'resetFileInput'
5259 ] ) ;
5360 }
61+ componentWillMount ( ) {
62+ this . reader = new FileReader ( ) ;
63+ this . reader . onload = this . onload ;
64+ this . resetFileInput ( ) ;
65+ }
66+ componentDidUpdate ( prevProps ) {
67+ if ( this . props . isLoadingUpload && ! prevProps . isLoadingUpload && this . fileToUpload && this . reader ) {
68+ this . reader . readAsArrayBuffer ( this . fileToUpload ) ;
69+ }
70+ }
71+ componentWillUnmount ( ) {
72+ this . reader = null ;
73+ this . resetFileInput ( ) ;
74+ }
75+ resetFileInput ( ) {
76+ this . fileToUpload = null ;
77+ if ( this . fileInput ) {
78+ this . fileInput . value = null ;
79+ }
80+ }
5481 getProjectTitleFromFilename ( fileInputFilename ) {
5582 if ( ! fileInputFilename ) return '' ;
5683 // only parse title from files like "filename.sb2" or "filename.sb3"
@@ -60,35 +87,43 @@ class SBFileUploader extends React.Component {
6087 }
6188 // called when user has finished selecting a file to upload
6289 handleChange ( e ) {
63- // Remove the hash if any (without triggering a hash change event or a reload)
64- history . replaceState ( { } , document . title , '.' ) ;
65- const reader = new FileReader ( ) ;
6690 const thisFileInput = e . target ;
67- reader . onload = ( ) => this . props . vm . loadProject ( reader . result )
68- . then ( ( ) => {
69- analytics . event ( {
70- category : 'project' ,
71- action : 'Import Project File' ,
72- nonInteraction : true
73- } ) ;
74- this . props . onLoadingFinished ( this . props . loadingState ) ;
75- // Reset the file input after project is loaded
76- // This is necessary in case the user wants to reload a project
77- thisFileInput . value = null ;
78- } )
79- . catch ( error => {
80- log . warn ( error ) ;
81- alert ( this . props . intl . formatMessage ( messages . loadError ) ) ; // eslint-disable-line no-alert
82- this . props . onLoadingFinished ( this . props . loadingState ) ;
83- // Reset the file input after project is loaded
84- // This is necessary in case the user wants to reload a project
85- thisFileInput . value = null ;
86- } ) ;
8791 if ( thisFileInput . files ) { // Don't attempt to load if no file was selected
92+ this . fileToUpload = thisFileInput . files [ 0 ] ;
93+ this . props . requestProjectUpload ( this . props . loadingState ) ;
94+ }
95+ }
96+ // called when file upload raw data is available in the reader
97+ onload ( ) {
98+ if ( this . reader ) {
8899 this . props . onLoadingStarted ( ) ;
89- reader . readAsArrayBuffer ( thisFileInput . files [ 0 ] ) ;
90- const uploadedProjectTitle = this . getProjectTitleFromFilename ( thisFileInput . files [ 0 ] . name ) ;
91- this . props . onUpdateProjectTitle ( uploadedProjectTitle ) ;
100+ const filename = this . fileToUpload && this . fileToUpload . name ;
101+ this . props . vm . loadProject ( this . reader . result )
102+ . then ( ( ) => {
103+ analytics . event ( {
104+ category : 'project' ,
105+ action : 'Import Project File' ,
106+ nonInteraction : true
107+ } ) ;
108+ // Remove the hash if any (without triggering a hash change event or a reload)
109+ history . replaceState ( { } , document . title , '.' ) ;
110+ this . props . onLoadingFinished ( this . props . loadingState , true ) ;
111+ // Reset the file input after project is loaded
112+ // This is necessary in case the user wants to reload a project
113+ if ( filename ) {
114+ const uploadedProjectTitle = this . getProjectTitleFromFilename ( filename ) ;
115+ this . props . onUpdateProjectTitle ( uploadedProjectTitle ) ;
116+ }
117+ this . resetFileInput ( ) ;
118+ } )
119+ . catch ( error => {
120+ log . warn ( error ) ;
121+ alert ( this . props . intl . formatMessage ( messages . loadError ) ) ; // eslint-disable-line no-alert
122+ this . props . onLoadingFinished ( this . props . loadingState , false ) ;
123+ // Reset the file input after project is loaded
124+ // This is necessary in case the user wants to reload a project
125+ this . resetFileInput ( ) ;
126+ } ) ;
92127 }
93128 }
94129 handleClick ( ) {
@@ -119,32 +154,36 @@ SBFileUploader.propTypes = {
119154 children : PropTypes . func ,
120155 className : PropTypes . string ,
121156 intl : intlShape . isRequired ,
157+ isLoadingUpload : PropTypes . bool ,
122158 loadingState : PropTypes . oneOf ( LoadingStates ) ,
123159 onLoadingFinished : PropTypes . func ,
124160 onLoadingStarted : PropTypes . func ,
125161 onUpdateProjectTitle : PropTypes . func ,
162+ requestProjectUpload : PropTypes . func ,
126163 vm : PropTypes . shape ( {
127164 loadProject : PropTypes . func
128165 } )
129166} ;
130167SBFileUploader . defaultProps = {
131168 className : ''
132169} ;
133- const mapStateToProps = state => ( {
134- loadingState : state . scratchGui . projectState . loadingState ,
135- vm : state . scratchGui . vm
136- } ) ;
170+ const mapStateToProps = state => {
171+ const loadingState = state . scratchGui . projectState . loadingState ;
172+ return {
173+ isLoadingUpload : getIsLoadingUpload ( loadingState ) ,
174+ loadingState : loadingState ,
175+ vm : state . scratchGui . vm
176+ } ;
177+ } ;
137178
138179const mapDispatchToProps = ( dispatch , ownProps ) => ( {
139- onLoadingFinished : loadingState => {
140- dispatch ( onLoadedProject ( loadingState , ownProps . canSave ) ) ;
180+ onLoadingFinished : ( loadingState , success ) => {
181+ dispatch ( onLoadedProject ( loadingState , ownProps . canSave , success ) ) ;
141182 dispatch ( closeLoadingProject ( ) ) ;
142183 dispatch ( closeFileMenu ( ) ) ;
143184 } ,
144- onLoadingStarted : ( ) => {
145- dispatch ( openLoadingProject ( ) ) ;
146- dispatch ( onProjectUploadStarted ( ) ) ;
147- }
185+ requestProjectUpload : loadingState => dispatch ( requestProjectUpload ( loadingState ) ) ,
186+ onLoadingStarted : ( ) => dispatch ( openLoadingProject ( ) )
148187} ) ;
149188
150189// Allow incoming props to override redux-provided props. Used to mock in tests.
0 commit comments