RN code push自定义弹框
程序员文章站
2022-06-24 22:03:51
最近在弄react native的code push热更新问题。开始是用的后台默默更新配置。由于微软服务器速度问题,经常遇到用户一直在下载中问题。而用户也不知道代码需要更新才能使用新功能,影响了正常业务流程。而目前公司也无力搭建自己的服务器和dns设置。所以比较快速的方案就是,前端自定义热更新弹框, ......
最近在弄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弹框的自定义展示。
上一篇: js的查找和过滤方法教程