From 628609a0691335a3542e6ee069fc16b17c57b3f9 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Wed, 10 Jun 2015 13:16:16 -0700 Subject: [PATCH] [ReactNative] XHR FormData upload example --- .../UIExplorer.xcodeproj/project.pbxproj | 30 +++ Examples/UIExplorer/XHRExample.js | 232 +++++++++++++++++- 2 files changed, 260 insertions(+), 2 deletions(-) diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj index 1976634a60..28361d706d 100644 --- a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj +++ b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj @@ -45,6 +45,7 @@ 14D6D7281B2222EF001FB087 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */; }; 14D6D7291B2222EF001FB087 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14AADF041AC3DB95002390C9 /* libReact.a */; }; 14DC67F41AB71881001358AB /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14DC67F11AB71876001358AB /* libRCTPushNotification.a */; }; + 3578590A1B28D2CF00341EDB /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 357859011B28D2C500341EDB /* libRCTLinking.a */; }; 834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 834C36D21AF8DA610019C93C /* libRCTSettings.a */; }; D85B829E1AB6D5D7003F4FE2 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */; }; /* End PBXBuildFile section */ @@ -120,6 +121,13 @@ remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = RCTPushNotification; }; + 357859001B28D2C500341EDB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTLinking; + }; 58005BED1ABA80530062E044 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */; @@ -192,6 +200,7 @@ 14D6D7101B220EB3001FB087 /* libOCMock.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libOCMock.a; sourceTree = ""; }; 14DC67E71AB71876001358AB /* RCTPushNotification.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTPushNotification.xcodeproj; path = ../../Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj; sourceTree = ""; }; 14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = ""; }; + 357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = ""; }; 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTTest.xcodeproj; path = ../../Libraries/RCTTest/RCTTest.xcodeproj; sourceTree = ""; }; D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = ../../Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = ""; }; /* End PBXFileReference section */ @@ -226,6 +235,7 @@ 134454601AAFCABD003F0779 /* libRCTAdSupport.a in Frameworks */, 134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */, 13417FE91AA91432003F314A /* libRCTImage.a in Frameworks */, + 3578590A1B28D2CF00341EDB /* libRCTLinking.a in Frameworks */, 1341802C1AA9178B003F314A /* libRCTNetwork.a in Frameworks */, 14DC67F41AB71881001358AB /* libRCTPushNotification.a in Frameworks */, 834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */, @@ -255,6 +265,7 @@ 134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */, 134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */, 13417FE31AA91428003F314A /* RCTImage.xcodeproj */, + 357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */, 134180261AA91779003F314A /* RCTNetwork.xcodeproj */, 13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */, 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */, @@ -432,6 +443,14 @@ name = Products; sourceTree = ""; }; + 357858F91B28D2C400341EDB /* Products */ = { + isa = PBXGroup; + children = ( + 357859011B28D2C500341EDB /* libRCTLinking.a */, + ); + name = Products; + sourceTree = ""; + }; 58005BE51ABA80530062E044 /* Products */ = { isa = PBXGroup; children = ( @@ -581,6 +600,10 @@ ProductGroup = 13417FE41AA91428003F314A /* Products */; ProjectRef = 13417FE31AA91428003F314A /* RCTImage.xcodeproj */; }, + { + ProductGroup = 357858F91B28D2C400341EDB /* Products */; + ProjectRef = 357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */; + }, { ProductGroup = 134180271AA91779003F314A /* Products */; ProjectRef = 134180261AA91779003F314A /* RCTNetwork.xcodeproj */; @@ -687,6 +710,13 @@ remoteRef = 14DC67F01AB71876001358AB /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 357859011B28D2C500341EDB /* libRCTLinking.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTLinking.a; + remoteRef = 357859001B28D2C500341EDB /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 58005BEE1ABA80530062E044 /* libRCTTest.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; diff --git a/Examples/UIExplorer/XHRExample.js b/Examples/UIExplorer/XHRExample.js index c5b350c70c..db172efc1b 100644 --- a/Examples/UIExplorer/XHRExample.js +++ b/Examples/UIExplorer/XHRExample.js @@ -17,11 +17,17 @@ var React = require('react-native'); var { + AlertIOS, + CameraRoll, + Image, + LinkingIOS, + PixelRatio, ProgressViewIOS, StyleSheet, - View, Text, + TextInput, TouchableHighlight, + View, } = React; class Downloader extends React.Component { @@ -109,6 +115,177 @@ class Downloader extends React.Component { } } +var PAGE_SIZE = 20; + +class FormUploader extends React.Component { + + constructor(props) { + super(props); + this.state = { + isUploading: false, + randomPhoto: null, + textParams: [], + }; + this._isMounted = true; + this._fetchRandomPhoto = this._fetchRandomPhoto.bind(this); + this._addTextParam = this._addTextParam.bind(this); + this._upload = this._upload.bind(this); + + this._fetchRandomPhoto(); + } + + _fetchRandomPhoto() { + CameraRoll.getPhotos( + {first: PAGE_SIZE}, + (data) => { + console.log('isMounted', this._isMounted); + if (!this._isMounted) { + return; + } + var edges = data.edges; + var edge = edges[Math.floor(Math.random() * edges.length)]; + var randomPhoto = edge && edge.node && edge.node.image; + if (randomPhoto) { + this.setState({randomPhoto}); + } + }, + (error) => undefined + ); + } + + _addTextParam() { + var textParams = this.state.textParams; + textParams.push({name: '', value: ''}); + this.setState({textParams}); + } + + componentWillUnmount() { + this._isMounted = false; + } + + _onTextParamNameChange(index, text) { + var textParams = this.state.textParams; + textParams[index].name = text; + this.setState({textParams}); + } + + _onTextParamValueChange(index, text) { + var textParams = this.state.textParams; + textParams[index].value = text; + this.setState({textParams}); + } + + _upload() { + var xhr = new XMLHttpRequest(); + xhr.open('POST', 'http://posttestserver.com/post.php'); + xhr.onload = () => { + this.setState({isUploading: false}); + if (xhr.status !== 200) { + AlertIOS.alert( + 'Upload failed', + 'Expected HTTP 200 OK response, got ' + xhr.status + ); + return; + } + if (!xhr.responseText) { + AlertIOS.alert( + 'Upload failed', + 'No response payload.' + ); + return; + } + var index = xhr.responseText.indexOf('http://www.posttestserver.com/'); + if (index === -1) { + AlertIOS.alert( + 'Upload failed', + 'Invalid response payload.' + ); + return; + } + var url = xhr.responseText.slice(index).split('\n')[0]; + LinkingIOS.openURL(url); + }; + var formdata = new FormData(); + if (this.state.randomPhoto) { + formdata.append('image', {...this.state.randomPhoto, name: 'image.jpg'}); + } + this.state.textParams.forEach( + (param) => formdata.append(param.name, param.value) + ); + xhr.send(formdata); + this.setState({isUploading: true}); + } + + render() { + var image = null; + if (this.state.randomPhoto) { + image = ( + + ); + } + var textItems = this.state.textParams.map((item, index) => ( + + + = + + + )); + var uploadButtonLabel = this.state.isUploading ? 'Uploading...' : 'Upload'; + var uploadButton = ( + + {uploadButtonLabel} + + ); + if (!this.state.isUploading) { + uploadButton = ( + + {uploadButton} + + ); + } + return ( + + + + Random photo from your library + ( + update + ) + + {image} + + {textItems} + + + Add a text param + + + + {uploadButton} + + + ); + } +} + + exports.framework = 'React'; exports.title = 'XMLHttpRequest'; exports.description = 'XMLHttpRequest'; @@ -117,6 +294,11 @@ exports.examples = [{ render() { return ; } +}, { + title: 'multipart/form-data Upload', + render() { + return ; + } }]; var styles = StyleSheet.create({ @@ -126,6 +308,52 @@ var styles = StyleSheet.create({ }, button: { backgroundColor: '#eeeeee', - padding: 10, + padding: 8, + }, + paramRow: { + flexDirection: 'row', + paddingVertical: 8, + alignItems: 'center', + borderBottomWidth: 1 / PixelRatio.get(), + borderBottomColor: 'grey', + }, + photoLabel: { + flex: 1, + }, + randomPhoto: { + width: 50, + height: 50, + }, + textButton: { + color: 'blue', + }, + addTextParamButton: { + marginTop: 8, + }, + textInput: { + flex: 1, + borderRadius: 3, + borderColor: 'grey', + borderWidth: 1, + height: 30, + paddingLeft: 8, + }, + equalSign: { + paddingHorizontal: 4, + }, + uploadButton: { + marginTop: 16, + }, + uploadButtonBox: { + flex: 1, + paddingVertical: 12, + alignItems: 'center', + backgroundColor: 'blue', + borderRadius: 4, + }, + uploadButtonLabel: { + color: 'white', + fontSize: 16, + fontWeight: '500', }, });