diff --git a/Examples/UIExplorer/XHRExample.js b/Examples/UIExplorer/XHRExample.js index 2a3b1ebbb..d142222c9 100644 --- a/Examples/UIExplorer/XHRExample.js +++ b/Examples/UIExplorer/XHRExample.js @@ -128,6 +128,7 @@ class FormUploader extends React.Component { super(props); this.state = { isUploading: false, + uploadProgress: null, randomPhoto: null, textParams: [], }; @@ -217,6 +218,14 @@ class FormUploader extends React.Component { this.state.textParams.forEach( (param) => formdata.append(param.name, param.value) ); + if (xhr.upload) { + xhr.upload.onprogress = (event) => { + console.log('upload onprogress', event); + if (event.lengthComputable) { + this.setState({uploadProgress: event.loaded / event.total}); + } + }; + } xhr.send(formdata); this.setState({isUploading: true}); } @@ -251,6 +260,10 @@ class FormUploader extends React.Component { )); var uploadButtonLabel = this.state.isUploading ? 'Uploading...' : 'Upload'; + var uploadProgress = this.state.uploadProgress; + if (uploadProgress !== null) { + uploadButtonLabel += ' ' + Math.round(uploadProgress * 100) + '%'; + } var uploadButton = ( {uploadButtonLabel} diff --git a/Libraries/Network/RCTHTTPRequestHandler.m b/Libraries/Network/RCTHTTPRequestHandler.m index 6a6b385af..c89a4fbc9 100644 --- a/Libraries/Network/RCTHTTPRequestHandler.m +++ b/Libraries/Network/RCTHTTPRequestHandler.m @@ -75,6 +75,16 @@ RCT_EXPORT_MODULE() #pragma mark - NSURLSession delegate +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + didSendBodyData:(int64_t)bytesSent + totalBytesSent:(int64_t)totalBytesSent +totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend +{ + [[_delegates objectForKey:task] URLRequest:task didUploadProgress:(double)totalBytesSent total:(double)totalBytesExpectedToSend]; +} + + - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)task didReceiveResponse:(NSURLResponse *)response diff --git a/Libraries/Network/RCTNetworking.m b/Libraries/Network/RCTNetworking.m index df102e594..d73f1fb4d 100644 --- a/Libraries/Network/RCTNetworking.m +++ b/Libraries/Network/RCTNetworking.m @@ -184,6 +184,11 @@ typedef void (^RCTDataLoaderCallback)(NSData *data, NSString *MIMEType, NSError return [self initWithRequest:nil handler:nil callback:nil]; } +- (void)URLRequest:(id)requestToken didUploadProgress:(double)progress total:(double)total +{ + RCTAssert([requestToken isEqual:_requestToken], @"Shouldn't ever happen"); +} + - (void)URLRequest:(id)requestToken didReceiveResponse:(NSURLResponse *)response { RCTAssert([requestToken isEqual:_requestToken], @"Shouldn't ever happen"); @@ -395,6 +400,17 @@ RCT_EXPORT_MODULE() #pragma mark - RCTURLRequestDelegate +- (void)URLRequest:(id)requestToken didUploadProgress:(double)progress total:(double)total +{ + dispatch_async(_methodQueue, ^{ + RCTActiveURLRequest *request = [_activeRequests objectForKey:requestToken]; + RCTAssert(request != nil, @"Unrecognized request token: %@", requestToken); + + NSArray *responseJSON = @[request.requestID, @(progress), @(total)]; + [_bridge.eventDispatcher sendDeviceEventWithName:@"didUploadProgress" body:responseJSON]; + }); +} + - (void)URLRequest:(id)requestToken didReceiveResponse:(NSURLResponse *)response { dispatch_async(_methodQueue, ^{ diff --git a/Libraries/Network/XMLHttpRequest.ios.js b/Libraries/Network/XMLHttpRequest.ios.js index 6eb586c26..da020c808 100644 --- a/Libraries/Network/XMLHttpRequest.ios.js +++ b/Libraries/Network/XMLHttpRequest.ios.js @@ -21,15 +21,23 @@ class XMLHttpRequest extends XMLHttpRequestBase { _requestId: ?number; _subscriptions: [any]; + upload: { + onprogress?: (event: Object) => void; + }; constructor() { super(); this._requestId = null; this._subscriptions = []; + this.upload = {}; } _didCreateRequest(requestId: number): void { this._requestId = requestId; + this._subscriptions.push(RCTDeviceEventEmitter.addListener( + 'didUploadProgress', + (args) => this._didUploadProgress.call(this, args[0], args[1], args[2]) + )); this._subscriptions.push(RCTDeviceEventEmitter.addListener( 'didReceiveNetworkResponse', (args) => this._didReceiveResponse.call(this, args[0], args[1], args[2]) @@ -44,6 +52,17 @@ class XMLHttpRequest extends XMLHttpRequestBase { )); } + _didUploadProgress(requestId: number, progress: number, total: number): void { + if (requestId === this._requestId && this.upload.onprogress) { + var event = { + lengthComputable: true, + loaded: progress, + total, + }; + this.upload.onprogress(event); + } + } + _didReceiveResponse(requestId: number, status: number, responseHeaders: ?Object): void { if (requestId === this._requestId) { this.status = status; diff --git a/React/Base/RCTURLRequestDelegate.h b/React/Base/RCTURLRequestDelegate.h index 3ca5b0e01..48473b84b 100644 --- a/React/Base/RCTURLRequestDelegate.h +++ b/React/Base/RCTURLRequestDelegate.h @@ -15,6 +15,12 @@ */ @protocol RCTURLRequestDelegate +/** + * Call this when you first receives a response from the server. This should + * include response headers, etc. + */ +- (void)URLRequest:(id)requestToken didUploadProgress:(double)progress total:(double)total; + /** * Call this when you first receives a response from the server. This should * include response headers, etc.