UNPKG

react-native-ultimate-listview

Version:

A high performance FlatList providing customised pull-to-refresh | auto-pagination & infinite-scrolling | gridview layout | swipeable-row. The truly ultimate version that I have done the most tricky part for you, just simply follow the instructions shown

299 lines (281 loc) 8.41 kB
import React from 'react' import { ActivityIndicator, Animated, AsyncStorage, Dimensions, Easing, ScrollView, StyleSheet, Text, View } from 'react-native' import dateFormat from './util' const { width, height } = Dimensions.get('window') const DATE_KEY = 'ultimateRefreshDate' const RefreshStatus = { pullToRefresh: 0, releaseToRefresh: 1, refreshing: 2 } const PaginationStatus = { firstLoad: 0, waiting: 1, allLoaded: 2 } export default class RefreshableScrollView extends ScrollView { static defaultProps = { horizontal: false, scrollEnabled: true, header: null, refreshable: true, refreshableTitlePull: 'Pull to refresh', refreshableTitleRefreshing: 'Loading...', refreshableTitleRelease: 'Release to load', customRefreshView: null, displayDate: false, dateFormat: 'yyyy-MM-dd hh:mm', dateTitle: 'Last updated: ', arrowImageSource: require('./downArrow.png'), arrowImageStyle: undefined, refreshViewStyle: undefined, dateStyle: undefined, refreshViewHeight: 80, insideOfUltimateListView: false } _offsetY = 0 _isRefreshing = false _dragFlag = false constructor(props) { super(props) this.state = { arrowAngle: new Animated.Value(0), refreshStatus: RefreshStatus.refreshing, refreshTitle: this.props.refreshableTitleRefreshing, date: this.props.date, showRefreshHeader: false } } async componentDidMount() { console.warn('The advancedRefreshView is not ready for Android at this moment. \n\nIf the items are less than the height of device screen, the refreshView will not disappear. \n\nPlease consider setting the refreshableMode={Platform.OS === "ios" ? "advanced" : "basic"}, or feel free to send me a PR to resolve this problem. \n\nThanks a lot.') try { let result = await AsyncStorage.getItem(DATE_KEY) if (result) { result = parseInt(result, 10) this.setState({ date: dateFormat(new Date(result), this.props.dateFormat) }) } else { this.setState({ date: dateFormat(new Date(), this.props.dateFormat) }) } } catch (err) { console.log(err) } } onScroll = (event) => { // console.log('onScroll()'); const { y } = event.nativeEvent.contentOffset const { refreshViewHeight } = this.props if (y <= refreshViewHeight) { this._offsetY = y - refreshViewHeight } if (this._dragFlag) { if (!this._isRefreshing) { if (y <= 10) { if (this.state.refreshStatus !== RefreshStatus.releaseToRefresh) { this.setState({ refreshStatus: RefreshStatus.releaseToRefresh, refreshTitle: this.props.refreshableTitleRelease }) Animated.timing(this.state.arrowAngle, { toValue: 1, duration: 50, easing: Easing.inOut(Easing.quad) }).start() } } else if (this.state.refreshStatus !== RefreshStatus.pullToRefresh) { this.setState({ refreshStatus: RefreshStatus.pullToRefresh, refreshTitle: this.props.refreshableTitlePull }) Animated.timing(this.state.arrowAngle, { toValue: 0, duration: 50, easing: Easing.inOut(Easing.quad) }).start() } } } else if (y <= 5) { setTimeout(() => this._scrollview.scrollTo({ x: 0, y: refreshViewHeight, animated: false }), 100) } if (this.props.onScroll) { this.props.onScroll(event) } } onScrollBeginDrag = (event) => { // console.log('onScrollBeginDrag()'); this._dragFlag = true const { refreshViewHeight } = this.props this._offsetY = event.nativeEvent.contentOffset.y - refreshViewHeight if (this.props.onScrollBeginDrag) { this.props.onScrollBeginDrag(event) } } onScrollEndDrag = (event) => { this._dragFlag = false const { y } = event.nativeEvent.contentOffset const { refreshViewHeight } = this.props this._offsetY = y - refreshViewHeight // console.log('onScrollEndDrag()' + y); if (!this._isRefreshing) { if (this.state.refreshStatus === RefreshStatus.releaseToRefresh) { this._isRefreshing = true this.setState({ refreshStatus: RefreshStatus.refreshing, refreshTitle: this.props.refreshableTitleRefreshing }) this._scrollview.scrollTo({ x: 0, y: 0, animated: true }) if (this.props.insideOfUltimateListView) { this.props.onRefresh() } else { this.props.onRefresh(() => { this.onRefreshEnd() }) } } else if (y <= refreshViewHeight) { this._scrollview.scrollTo({ x: 0, y: refreshViewHeight, animated: true }) } } else if (y <= refreshViewHeight) { this._scrollview.scrollTo({ x: 0, y: 0, animated: true }) } if (this.props.onScrollEndDrag) { this.props.onScrollEndDrag(event) } } scrollTo = (option) => { this._scrollview.scrollTo(option) } scrollToEnd = (option) => { this._scrollview.scrollToEnd(option) } onRefreshEnd = () => { // console.log('onRefreshEnd()'); if (this.state.refreshStatus === RefreshStatus.refreshing) { this._isRefreshing = false const now = new Date().getTime() this.setState({ showRefreshHeader: true }) setTimeout(() => { if (this._scrollview.scrollTo) { this._scrollview.scrollTo({ x: 0, y: this.props.refreshViewHeight, animated: true }) } this.setState({ refreshStatus: RefreshStatus.pullToRefresh, refreshTitle: this.props.refreshableTitlePull, date: dateFormat(now, this.props.dateFormat) }) }, 1000) AsyncStorage.setItem(DATE_KEY, now.toString()) Animated.timing(this.state.arrowAngle, { toValue: 0, duration: 50, easing: Easing.inOut(Easing.quad) }).start() } } renderRefreshHeader() { if (this.props.customRefreshView) { return ( <View style={[defaultHeaderStyles.header, this.props.refreshViewStyle]}> {this.props.customRefreshView(this.state.refreshStatus, this._offsetY)} </View> ) } return ( <View style={[defaultHeaderStyles.header, this.props.refreshViewStyle, { height: this.props.refreshViewHeight }]} > <View style={defaultHeaderStyles.status}> {this.renderSpinner()} <Text style={defaultHeaderStyles.statusTitle}>{this.state.refreshTitle}</Text> </View> {this.props.displayDate && <Text style={[defaultHeaderStyles.date, this.props.dateStyle]} >{this.props.dateTitle + this.state.date} </Text> } </View> ) } renderSpinner() { if (this.state.refreshStatus === RefreshStatus.refreshing) { return ( <ActivityIndicator style={{ marginRight: 10 }} /> ) } return ( <Animated.Image source={this.props.arrowImageSource} resizeMode="contain" style={[defaultHeaderStyles.arrow, this.props.arrowImageStyle, { transform: [{ rotateX: this.state.arrowAngle.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '-180deg'] }) }] }]} /> ) } render() { return ( <ScrollView ref={c => this._scrollview = c} {...this.props} scrollEventThrottle={16} onScroll={this.onScroll} contentContainerStyle={{ minHeight: height }} // onMomentumScrollEnd={this.onScrollEndDrag} onScrollEndDrag={this.onScrollEndDrag} onScrollBeginDrag={this.onScrollBeginDrag} > {this.renderRefreshHeader()} {this.props.children} </ScrollView> ) } } const defaultHeaderStyles = StyleSheet.create({ header: { width, height: 80, alignItems: 'center', justifyContent: 'center' }, status: { flexDirection: 'row', alignItems: 'center' }, arrow: { width: 23, height: 23, marginRight: 10, opacity: 0.4 }, statusTitle: { fontSize: 13, color: '#333333' }, date: { fontSize: 11, color: '#333333', marginTop: 5 } })