欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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弹框的自定义展示。