Skip to content

Instantly share code, notes, and snippets.

@cameronbourke
Last active May 9, 2016 02:01
Show Gist options
  • Save cameronbourke/df4241c75f402aa2f1a1dda89e6182bf to your computer and use it in GitHub Desktop.
Save cameronbourke/df4241c75f402aa2f1a1dda89e6182bf to your computer and use it in GitHub Desktop.
import React, { StyleSheet, Alert, TouchableOpacity } from 'react-native';
import codePush from 'react-native-code-push';
import Promise from 'promise';
const { PENDING } = codePush.UpdateState;
const { IMMEDIATE } = codePush.InstallMode;
export const downloadNewVersion = (downloadProgressCallback) => {
return new Promise((resolve, reject) => {
// Queries the CodePush service to see whether the configured app deployment has an update available.
// By default, it will use the deployment key that is configured in your Info.plist file (iOS), or MainActivity.java file (Android)
codePush.checkForUpdate().then((remotePackage) => {
/*
update === null can be due to the following reasons:
- The configured deployment doesn't contain any releases, and therefore, nothing to update.
- The latest release within the configured deployment is targeting a different binary version than what you're currently running (either older or newer).
- The currently running app already has the latest release from the configured deployment, and therefore, doesn't need it again.
*/
if (!remotePackage) {
getNewLocalVersion.then((localPackage) => {
if (!localPackage) return resolve({ message: 'The app is up to date!' });
resolve(localPackage);
});
} else {
// remoteUpdate is an instance of a RemotePackage which contains details
// about an update that is available for download from the CodePush server
const downloadPackage = remotePackage.download(downloadProgressCallback);
// downloadProgressCallback is periodically called with a DownloadProgress object
// than looks like { totalBytes: Number, receivedBytes: Number }
downloadPackage.then((localPackage) => {
if (localPackage) return resolve({ newVersion: localPackage });
reject({ message: 'New version failed to download' });
});
};
});
});
};
export const getNewLocalVersion = () => {
return new Promise((resolve, reject) => {
codePush.getUpdateMetadata(PENDING)
.then(resolve)
.catch(reject);
});
};
// call in the root component's on componentDidMount so that
// it is only called once (or just call in this file)
codePush.notifyAppReady();
/* EXAMPLE USAGE
--------------------------------
...
handleNewAppVersion (newAppVersion) {
this.setState({ newAppVersion })
}
...
const {newAppVersion} = this.state;
<UpdateAppButton
showWhenUpdateAvailable={true} // default to true instead ??
onNewVersion={this.handleNewAppVersion}>
<Text>Update to {newAppVersion.versionNumber}></Text>
</UpdateAppButton>
*/
export class UpdateAppButton extends React.Component {
constructor () {
super();
this.state = {
newVersion: null,
};
}
componentDidMount () {
downloadNewVersion().then(this._handleNewVersion)
.catch(console.log.bind(this, 'err'));
}
_handleNewVersion (newVersion) {
this.setState({ newVersion });
}
handleUpdatePress (visible) {
const { newVersion } = this.state;
const alertMessage = `Version ${newVersion.description} now available. Do you want to upgrade now? Note: Upgrading the app will restart the app and any changes not saved will be lost.`;
Alert.alert('New Version Available', alertMessage, [{
text: 'Update Now',
onPress: () => this.install(),
}, {
text: 'Cancel',
onPress: () => console.log('Cancel Pressed'),
style: 'cancel',
}]);
}
install () {
this.state.newVersion.install(IMMEDIATE);
}
render () {
const { children, style } = this.props;
if (!this.state.newVersion) return null;
return (
<TouchableOpacity
style={[style, styles.updateApp]}
onPress={() => this.handleUpdatePress(true)}>
{children || <Text>Update</Text>}
</TouchableOpacity>
);
}
};
UpdateAppButton.defaultProps = {
// yet to be determined
};
UpdateAppButton.propTypes = {
// yet to be determined
};
export class AppVersion extends React.Component {
constructor () {
super();
this.state = {
versionNumber: '',
};
}
componentDidMount () {
getNewLocalVersion().then(this._handleLocalVersion);
}
_handleLocalVersion ({ description }) {
// the hack here is that we are using the description to specify the latest
// app version and not app version that was compiled with the binary
this.setState({ versionNumber: description });
}
render () {
return (
<span style={[styles.appVersion, this.props.style]}>
{this.state.versionNumber}
</span>
);
}
}
AppVersion.defaultProps = {
// yet to be determined
};
AppVersion.propTypes = {
// yet to be determined
};
// styles for both components
const styles = StyleSheet.create({
updateApp: {
borderRadius: 5,
flex: 1,
height: 44,
alignSelf: 'stretch',
justifyContent: 'center',
overflow: 'hidden',
},
appVersion: {
flex: 1,
},
});
export default UpdateAppButton;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment