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
265 lines (248 loc) • 7.05 kB
JavaScript
import React from 'react'
import { ActivityIndicator, Animated, AsyncStorage, Easing, ScrollView, StyleSheet, Text, View } from 'react-native'
import dateFormat from './util'
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.pullToRefresh,
refreshTitle: this.props.refreshableTitlePull,
date: this.props.date
}
}
async componentDidMount() {
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
this._offsetY = y
if (this._dragFlag) {
if (!this._isRefreshing) {
const height = this.props.refreshViewHeight
if (y <= -height) {
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 {
this.setState({
refreshStatus: RefreshStatus.pullToRefresh,
refreshTitle: this.props.refreshableTitlePull
})
Animated.timing(this.state.arrowAngle, {
toValue: 0,
duration: 50,
easing: Easing.inOut(Easing.quad)
}).start()
}
}
}
if (this.props.onScroll) {
this.props.onScroll(event)
}
}
onScrollBeginDrag = (event) => {
// console.log('onScrollBeginDrag()');
this._dragFlag = true
this._offsetY = event.nativeEvent.contentOffset.y
if (this.props.onScrollBeginDrag) {
this.props.onScrollBeginDrag(event)
}
}
onScrollEndDrag = (event) => {
// console.log('onScrollEndDrag()');
this._dragFlag = false
const { y } = event.nativeEvent.contentOffset
this._offsetY = y
const height = this.props.refreshViewHeight
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: -height, animated: true })
if (this.props.insideOfUltimateListView) {
this.props.onRefresh()
} else {
this.props.onRefresh(() => {
this.onRefreshEnd()
})
}
}
} else if (y <= 0) {
this._scrollview.scrollTo({ x: 0, y: -height, 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({
refreshStatus: RefreshStatus.pullToRefresh,
refreshTitle: this.props.refreshableTitlePull,
date: dateFormat(now, this.props.dateFormat)
})
AsyncStorage.setItem(DATE_KEY, now.toString())
Animated.timing(this.state.arrowAngle, {
toValue: 0,
duration: 50,
easing: Easing.inOut(Easing.quad)
}).start()
this._scrollview.scrollTo({ x: 0, y: 0, animated: true })
}
}
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]}>
<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}
onScrollEndDrag={this.onScrollEndDrag}
onScrollBeginDrag={this.onScrollBeginDrag}
>
{this.renderRefreshHeader()}
{this.props.children}
</ScrollView>
)
}
}
const defaultHeaderStyles = StyleSheet.create({
header: {
position: 'absolute',
top: -80,
left: 0,
right: 0,
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
}
})