hcmobile-sdk
Version:
mobile-sdk
317 lines (298 loc) • 8.76 kB
JavaScript
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) => {
if(this._isRefreshing){
return;
}
// 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()');
if(this._isRefreshing){
return;
}
this._dragFlag = true
const { refreshViewHeight } = this.props
this._offsetY = event.nativeEvent.contentOffset.y - refreshViewHeight
if (this.props.onScrollBeginDrag) {
this.props.onScrollBeginDrag(event)
}
}
onScrollEndDrag = (event) => {
if(this._isRefreshing){
return;
}
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) => {
if(this._isRefreshing){
return;
}
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(() => {
try {
if (this._scrollview.scrollTo) {
this._scrollview.scrollTo({ x: 0, y: this.props.refreshViewHeight, animated: true })
}
} catch( error){
console.log(error);
}
this._isRefreshing = false;
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,backgroundColor:'#eee' }]}
>
<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
}
})