RN code push自定义弹框
Posted susie-note
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RN code push自定义弹框相关的知识,希望对你有一定的参考价值。
最近在弄react native的code push热更新问题。开始是用的后台默默更新配置。由于微软服务器速度问题,经常遇到用户一直在下载中问题。而用户也不知道代码需要更新才能使用新功能,影响了正常业务流程。而目前公司也无力搭建自己的服务器和dns设置。所以比较快速的方案就是,前端自定义热更新弹框,在需要更新代码的情况下禁止用户向下操作。
ok,废话少说,直接上代码:
这是构建一个弹框,强制文案提示和非强制文案提示弹框。
/** * Created by susie on 2018/9/20. */ import React, { Component } from ‘react‘; import {View, Text, StyleSheet, Modal, TouchableOpacity, Image , Dimensions , Alert} from ‘react-native‘ import CodePush from "react-native-code-push" import Progress from ‘./CusProgressBar‘; import color from ‘../../styles/theme‘; import {showLoadingImg,hideLoadingImg,px2pt} from ‘../../utils/util‘; import Global from "../../constants/global"; let SCREEN_WIDTH = Dimensions.get(‘window‘).width;//宽 let SCREEN_HEIGHT = Dimensions.get(‘window‘).height;//高 let codePushOptions = { checkFrequency : CodePush.CheckFrequency.ON_APP_START, installMode: CodePush.InstallMode.IMMEDIATE } class CodePushModal extends Component { constructor(props) { super(props) this.currProgress = 0.0 this.syncMessage = ‘‘ this.state = { modalVisible: false, //是否有更新 isMandatory: false, //是否为强制更新 immediateUpdate: false, //是否在更新中 updateInfo: {} } } codePushStatusDidChange(syncStatus) { if (this.state.immediateUpdate) { switch(syncStatus) { case CodePush.SyncStatus.CHECKING_FOR_UPDATE: this.syncMessage = ‘Checking for update‘ break; case CodePush.SyncStatus.DOWNLOADING_PACKAGE: this.syncMessage = ‘Downloading package‘ break; case CodePush.SyncStatus.AWAITING_USER_ACTION: this.syncMessage = ‘Awaiting user action‘ break; case CodePush.SyncStatus.INSTALLING_UPDATE: this.syncMessage = ‘Installing update‘ break; case CodePush.SyncStatus.UP_TO_DATE: this.syncMessage = ‘App up to date.‘ break; case CodePush.SyncStatus.UPDATE_IGNORED: this.syncMessage = ‘Update cancelled by user‘ break; case CodePush.SyncStatus.UPDATE_INSTALLED: this.syncMessage = ‘Update installed and will be applied on restart.‘ break; case CodePush.SyncStatus.UNKNOWN_ERROR: this.syncMessage = ‘An unknown error occurred‘ //Toast.showError(‘更新出错,请重启应用!‘) this.setState({modalVisible: false}) CodePush.allowRestart(); break; } } } codePushDownloadDidProgress(progress) { var self = this; if(self.state.immediateUpdate){ self.currProgress = parseFloat(progress.receivedBytes / progress.totalBytes).toFixed(2); if(self.currProgress >= 1) { self.setState({modalVisible: false}) } else if(self.refs.progressBar) { self.refs.progressBar.progress = self.currProgress; self.refs.progressBar.buffer = self.currProgress; } } } syncImmediate() { CodePush.checkForUpdate().then((update) => { Global.isCheckCodePush = false; hideLoadingImg(); if (!update) { CodePush.allowRestart(); } else { this.setState({modalVisible: true, updateInfo: update, isMandatory: update.isMandatory}) } }).catch(function () { Global.isCheckCodePush = false; CodePush.allowRestart(); }) } componentWillMount() { Global.isCheckCodePush = true; showLoadingImg(); CodePush.disallowRestart() this.syncImmediate() } componentDidMount() { //CodePush.allowRestart() } _immediateUpdateNew() { this.setState({immediateUpdate: true}); let self = this; var timer = setTimeout(function () { CodePush.sync( { updateDialog: {}, installMode: CodePush.InstallMode.IMMEDIATE}, self.codePushStatusDidChange.bind(self), self.codePushDownloadDidProgress.bind(self) ) clearTimeout(timer); CodePush.allowRestart(); },10); } render() { return ( <View style={styles.container}> <Modal animationType={"none"} transparent={true} onRequestClose={() => {}} visible={this.state.modalVisible} > <View style={styles.modal}> <View style={styles.modalContainer}> { !this.state.immediateUpdate ? <View> <View style={styles.modalContent}> <View> <Text style={styles.modalTitle}>页面升级</Text> </View> <View style={styles.updateDes}> <Text style={styles.updateDesText}>升级内容:</Text> <Text style={styles.updateDesText}>{this.state.updateInfo.description}</Text> </View> <View style={styles.updateTip}> <Text style={styles.updateTipText}>本升级非APP更新,wifi环境下30s内即可完成</Text> </View> { !this.state.isMandatory ? <View style={styles.updateBtns}> <TouchableOpacity onPress={() => this.setState({modalVisible: false})}> <View style={[styles.btnRight,styles.btnLeft]}> <Text style={{fontSize: 16, color: ‘#989898‘}}>残忍拒绝</Text> </View> </TouchableOpacity> <TouchableOpacity style={styles.btnRight} onPress={() => this._immediateUpdateNew()} > <View style={styles.btnRightText}> <Text style={{fontSize: 16, color: color.theme}}>立即升级</Text> </View> </TouchableOpacity> </View> : <View style={styles.updateBtns}> <TouchableOpacity style={[styles.btnRight,styles.onlyBtn]} onPress={() => this._immediateUpdateNew()} > <View style={[styles.btnRightText,{marginHorizontal: 40}]}> <Text style={{fontSize: 16, color: color.theme, letterSpacing:1}}>立即升级</Text> </View> </TouchableOpacity> </View> } </View> </View> : <View style={styles.modalContent}> <View> <Text style={styles.modalTitle}>页面升级中</Text> </View> <View style={{ paddingVertical: 20, alignItems: ‘center‘}}> <Progress ref="progressBar" progressColor={‘#89C0FF‘} style={{ marginTop: 20, width: SCREEN_WIDTH - 80, marginLeft:10, marginRight:10 }} /> <View style={styles.updateTip}> <Text style={styles.updateTipText}>本升级非APP更新,wifi环境下30s内即可完成</Text> </View> </View> </View> } </View> </View> </Modal> </View> ); } } let modalW = SCREEN_WIDTH - 40; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: ‘center‘, alignItems: ‘center‘ }, modal: { height: SCREEN_HEIGHT, width: SCREEN_WIDTH, alignItems: ‘center‘, justifyContent: ‘center‘, backgroundColor: ‘rgba(0,0,0,0.3)‘ }, modalContainer: { marginHorizontal: px2pt(120), borderBottomLeftRadius: px2pt(20), borderBottomRightRadius: px2pt(20), }, modalContent:{ backgroundColor: ‘#FFF‘, borderRadius:5, width: modalW }, modalTitle:{ marginTop:px2pt(70), fontSize:px2pt(36), color:color.gray3, textAlign:‘center‘, width:‘100%‘ }, modalTips:{ fontSize:px2pt(28), color:color.darkOrange, textAlign:‘center‘, marginBottom:px2pt(10) }, updateDes:{ marginLeft:px2pt(40), marginRight:px2pt(40), marginTop:px2pt(30) }, updateDesText:{ fontSize:px2pt(32), color: color.gray6, lineHeight:px2pt(44) }, updateTip:{ alignItems: ‘center‘, marginTop: px2pt(40) }, updateTipText:{ fontSize: px2pt(28), color: color.gray6 }, updateBtns:{ flexDirection: ‘row‘, height: px2pt(100), alignItems: ‘center‘, marginTop: px2pt(40), borderTopColor: ‘#E6E6E6‘, borderTopWidth: 1 }, btnLeft:{ borderRightColor: ‘#E6E6E6‘, borderRightWidth: 1 }, btnRight:{ flexDirection: ‘row‘, alignItems: ‘center‘, width: modalW / 2, height:px2pt(100), justifyContent: ‘center‘ }, btnRightText:{ flex: 1, height:px2pt(80), alignItems: ‘center‘, justifyContent: ‘center‘ }, onlyBtn:{ width: modalW } }) export default CodePush(codePushOptions)(CodePushModal)
其中,注意installMode 的设置问题,在初始化CodePush参数时,就要设置。否则在非强制更新下会出现 不立即刷新的问题。
然后是进度条展示处理:
/** * Created by susie on 2018/9/20. */ import React, {Component}from ‘react‘ import {View, StyleSheet, Animated, Easing,Text}from ‘react-native‘ import LinearGradient from ‘react-native-linear-gradient‘ import PropTypes from ‘prop-types‘ export default class CusProgressBar extends Component { static propTypes = { ...View.propTypes, // 当前进度 progress: PropTypes.number, // second progress进度 buffer: PropTypes.number, // 进度条颜色 progressColor: PropTypes.string, // 进度动画时长 progressAniDuration: PropTypes.number, // buffer动画时长 bufferAniDuration: PropTypes.number } static defaultProps = { // 进度条颜色 progressColor: ‘white‘, // 进度条动画时长 progressAniDuration: 100, // buffer进度条动画时长 bufferAniDuration: 100 } constructor(props) { super(props) this._progressAni = new Animated.Value(0) this._bufferAni = new Animated.Value(0) } componentWillReceiveProps(nextProps) { this._progress = nextProps.progress this._buffer = nextProps.buffer } componentWillMount() { this._progress = this.props.progress this._buffer = this.props.buffer } render() { return ( <View style={[styles.container,this.props.style]} onLayout={this._onLayout.bind(this)}> <LinearGradient colors={[‘#daddff‘, ‘#d3eeff‘]} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={{position:‘absolute‘,borderRadius:10,width:‘100%‘,height:‘100%‘}}></LinearGradient> <Animated.View ref="progress" style={{ position:‘absolute‘, width: this._progressAni, borderRadius:10 }}> <LinearGradient colors={[‘#4669ff‘, ‘#3eefff‘]} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={{borderRadius:10,width:‘100%‘,height:‘100%‘}}></LinearGradient> </Animated.View> <Animated.Image ref="buffer" style={{ position:‘absolute‘, left: this._bufferAni, marginLeft:0, top : -3, width:35 }} source={require(‘../../styles/images/huojian.png‘)} /> </View> ) } _onLayout({nativeEvent: {layout:{width, height}}}) { // 防止多次调用,当第一次获取后,后面就不再去获取了 if (width > 0 && this.totalWidth !== width) { // 获取progress控件引用 let progress = this._getProgress() // 获取buffer控件引用 let buffer = this._getBuffer() // 获取父布局宽度 this.totalWidth = width //给progress控件设置高度 progress.setNativeProps({ style: { height: height } }) // 给buffer控件设置高度 buffer.setNativeProps({ style: { height: height+6 } }) } } _startAniProgress(progress) { if (this._progress >= 0 && this.totalWidth !== 0) { Animated.timing(this._progressAni, { toValue: progress * this.totalWidth, duration: this.props.progressAniDuration, easing: Easing.linear }).start() } } _startAniBuffer(buffer) { if (this._buffer >= 0 && this.totalWidth !== 0) { Animated.timing(this._bufferAni, { toValue: buffer * this.totalWidth, duration: this.props.bufferAniDuration, }).start() } } _getProgress() { if (typeof this.refs.progress.refs.node !== ‘undefined‘) { return this.refs.progress.refs.node } return this.refs.progress._component } _getBuffer() { if (typeof this.refs.buffer.refs.node !== ‘undefined‘) { return this.refs.buffer.refs.node; } return this.refs.buffer._component; } } Object.defineProperty(CusProgressBar.prototype, ‘progress‘, { set(value){ if (value >= 0 && this._progress !== value) { this._progress = value; this._startAniProgress(value); } }, get() { return this._progress; }, enumerable: true, }) Object.defineProperty(CusProgressBar.prototype, ‘buffer‘, { set(value){ if (value >= 0 && this._buffer !== value) { this._buffer = value; this._startAniBuffer(value); } }, get() { return this._buffer; }, enumerable: true, }) const styles = StyleSheet.create({ container: { height: 12 } })
以上就完成了自定义 code push弹框的自定义展示。
以上是关于RN code push自定义弹框的主要内容,如果未能解决你的问题,请参考以下文章