1. 整理首页日志列表,关注页日志列表,日记本日记列表

2. 提取通用列表尾组件
3. 解决向下滑屏追加数据后列表抖动问题
This commit is contained in:
xuwenyang 2019-05-20 01:32:33 +08:00
parent beb5c0ec21
commit bb0a75e15f
7 changed files with 250 additions and 143 deletions

View file

@ -18,7 +18,7 @@ export default class DiaryBrief extends Component {
let editable = this.props.editable; let editable = this.props.editable;
return ( return (
<View style={localStyle.box}> <View style={[localStyle.box, this.props.style]}>
{(user && user.iconUrl) {(user && user.iconUrl)
? <UserIcon iconUrl={user.iconUrl}></UserIcon> : null} ? <UserIcon iconUrl={user.iconUrl}></UserIcon> : null}

View file

@ -14,6 +14,7 @@ import Msg from '../../util/msg';
import Api from '../../util/api'; import Api from '../../util/api';
import Touchable from '../touchable'; import Touchable from '../touchable';
import ListFooter from '../listFooter';
import DiaryBrief from './diaryBrief'; import DiaryBrief from './diaryBrief';
@ -24,13 +25,14 @@ export default class DiaryList extends Component {
this.dataSource = props.dataSource; this.dataSource = props.dataSource;
this.state = { this.state = {
isLoading: true,
diaries: [], diaries: [],
hasMore: false,
refreshing: false, refreshing: false,
refreshFailed: false refreshFailed: false,
hasMore: true,
loadingMore: false,
loadFailed: false
}; };
} }
@ -40,23 +42,23 @@ export default class DiaryList extends Component {
}); });
} }
async refresh(loadMore = false) { async refresh() {
if (this.state.refreshing) { if (this.state.refreshing) {
return; return;
} }
this.setState({hasMore: false, refreshing: true, refreshFailed: false}); this.setState({refreshing: true, refreshFailed: false});
this.dataSource.refresh(loadMore) this.dataSource.refresh()
.then(result => { .then(result => {
if(!result) { if(!result) {
throw { throw {
message: 'refresh no result' message: 'refresh diary no result'
} }
} else { } else {
let diaries = this.state.diaries; let diaries = this.state.diaries;
let newDiaries = result.list; let newDiaries = result.list;
if (!loadMore && diaries.length > 0 && newDiaries.length > 0 if (diaries.length > 0 && newDiaries.length > 0
&& diaries[0].id === newDiaries[0].id) { && diaries[0].id === newDiaries[0].id) {
Msg.showMsg('没有新内容'); Msg.showMsg('没有新内容');
@ -64,40 +66,54 @@ export default class DiaryList extends Component {
this.setState({ this.setState({
diaries: result.list ? result.list : [], diaries: result.list ? result.list : [],
hasMore: result.more,
refreshFailed: false refreshFailed: false
}); });
} }
}).catch(e => { }).catch(e => {
if (e.code === 401) {
/*
this.props.navigator.showModal({
screen: "App"
});
*/
}
this.setState({ this.setState({
diaries: [],
hasMore: false,
refreshFailed: true refreshFailed: true
}); });
}).done(() => { }).done(() => {
this.setState({ this.setState({
isLoading: false,
refreshing: false refreshing: false
}); });
}); });
} }
async loadMore() { async loadMore() {
if (this.state.refreshing) { if (this.state.loadingMore) {
return; return;
} }
this.refresh(true); this.setState({loadingMore: true, loadFailed: false});
this.dataSource.refresh(true)
.then(result => {
if(!result) {
throw {
message: 'loadMore diary no result'
}
} else {
this.setState({
diaries: result.list ? result.list : [],
hasMore: result.more,
loadFailed: false
});
}
}).catch(e => {
this.setState({
hasMore: false,
loadFailed: true
});
}).done(() => {
this.setState({
loadingMore: false
});
});
} }
render() { render() {
@ -111,8 +127,6 @@ export default class DiaryList extends Component {
return item.id.toString() return item.id.toString()
}} }}
ListHeaderComponent={this.state.isLoading ? null : this.props.header}
renderItem={({item}) => { renderItem={({item}) => {
return ( return (
<Touchable onPress={() => this.props.onDiaryPress(item)}> <Touchable onPress={() => this.props.onDiaryPress(item)}>
@ -123,12 +137,25 @@ export default class DiaryList extends Component {
ItemSeparatorComponent={({highlighted}) => <Divider style={{backgroundColor: '#eee'}}/>} ItemSeparatorComponent={({highlighted}) => <Divider style={{backgroundColor: '#eee'}}/>}
ListFooterComponent={this.renderFooter()} ListFooterComponent={() => {
if (this.state.refreshing || this.state.loadingMore || this.state.diaries.length == 0) {
return null;
}
if (this.state.loadFailed) {
return ListFooter.renderFooterFailed(this.loadMore.bind(this));
}
if (!this.state.hasMore) {
return ListFooter.renderFooterEnd();
}
return ListFooter.renderFooterLoading();
}}
refreshing={this.state.refreshing} refreshing={this.state.refreshing}
onRefresh={this.refresh.bind(this)} onRefresh={this.refresh.bind(this)}
automaticallyAdjustContentInsets={true}
onEndReachedThreshold={2} onEndReachedThreshold={2}
onEndReached={this.state.hasMore ? this.loadMore.bind(this) : null} onEndReached={this.state.hasMore ? this.loadMore.bind(this) : null}
> >
@ -136,38 +163,6 @@ export default class DiaryList extends Component {
</View> </View>
); );
} }
renderFooter() {
if (this.state.refreshing || this.state.diaries.length === 0) {
return null;
}
if (this.state.refreshFailed) {
return (
<View style={localStyle.footer}>
<TouchableOpacity style={{marginTop: 15}}
onPress={() => {this.loadMore();}}>
<Text style={{color: Color.primary}}>加载失败,请点击重试</Text>
</TouchableOpacity>
</View>
);
}
if (!this.state.hasMore) {
return (
<View style={localStyle.footer}>
<Text style={{color: Color.inactiveText, fontSize: 12}}> THE END </Text>
</View>
);
}
return (
<View style={localStyle.footer}>
<ActivityIndicator animating={true} color={Color.primary}
size={Api.IS_ANDROID ? 'large' : 'small'}/>
</View>
);
}
} }
const localStyle = StyleSheet.create({ const localStyle = StyleSheet.create({
@ -176,11 +171,5 @@ const localStyle = StyleSheet.create({
}, },
list: { list: {
height: '100%' height: '100%'
},
footer: {
height: 60,
justifyContent: 'center',
alignItems: 'center',
paddingBottom: 15
} }
}); });

View file

@ -6,6 +6,7 @@ import Touchable from '../touchable';
import Color from '../../style/color'; import Color from '../../style/color';
import UserIcon from '../userIcon'; import UserIcon from '../userIcon';
import ListFooter from '../listFooter';
export default class FollowUserList extends Component { export default class FollowUserList extends Component {
@ -18,10 +19,13 @@ export default class FollowUserList extends Component {
this.state = { this.state = {
users: [], users: [],
hasMore: false,
refreshing: false, refreshing: false,
refreshFailed: false refreshFailed: false,
hasMore: true,
loadingMore: false,
loadFailed: false
}; };
} }
@ -31,13 +35,47 @@ export default class FollowUserList extends Component {
}); });
} }
refresh(loadMore = false) { refresh() {
if (this.state.refreshing) { if (this.state.refreshing) {
return; return;
} }
this.setState({hasMore: false, refreshing: true, refreshFailed: false}); this.setState({refreshing: true, refreshFailed: false});
this.dataSource.refresh(loadMore) this.dataSource.refresh()
.then(result => {
if(!result) {
throw {
message: 'refresh ' + this.listType + ' no result'
}
} else {
console.log('refresh ' + this.listType + ' result:', result);
this.setState({
users: result.list ? result.list : [],
refreshFailed: false
});
}
}).catch(e => {
this.setState({
refreshFailed: true
});
}).done(() => {
this.setState({
refreshing: false
});
});
}
loadMore() {
if (this.state.loadingMore) {
return;
}
this.setState({loadingMore: true, loadFailed: false});
this.dataSource.refresh(true)
.then(result => { .then(result => {
if(!result) { if(!result) {
throw { throw {
@ -50,32 +88,23 @@ export default class FollowUserList extends Component {
this.setState({ this.setState({
users: result.list ? result.list : [], users: result.list ? result.list : [],
hasMore: result.more, hasMore: result.more,
refreshFailed: false loadFailed: false
}); });
} }
}).catch(e => { }).catch(e => {
this.setState({ this.setState({
users: [],
hasMore: false, hasMore: false,
refreshFailed: true loadFailed: true
}); });
}).done(() => { }).done(() => {
this.setState({ this.setState({
refreshing: false loadingMore: false
}); });
}); });
} }
loadMore() {
if (this.state.refreshing) {
return;
}
this.refresh(true);
}
render() { render() {
return ( return (
<View style={localStyle.container}> <View style={localStyle.container}>
@ -103,10 +132,26 @@ export default class FollowUserList extends Component {
); );
}} }}
ListFooterComponent={() => {
if (this.state.refreshing || this.state.loadingMore || this.state.users.length == 0) {
return null;
}
if (this.state.loadFailed) {
return ListFooter.renderFooterFailed(this.loadMore.bind(this));
}
if (!this.state.hasMore) {
return ListFooter.renderFooterEnd();
}
return ListFooter.renderFooterLoading();
}}
refreshing={this.state.refreshing} refreshing={this.state.refreshing}
onRefresh={this.refresh.bind(this)} onRefresh={this.refresh.bind(this)}
onEndReachedThreshold={5} onEndReachedThreshold={2}
onEndReached={this.state.hasMore ? this.loadMore.bind(this) : null} onEndReached={this.state.hasMore ? this.loadMore.bind(this) : null}
/> />
</View> </View>

View file

@ -0,0 +1,68 @@
import React, {Component} from 'react';
import {
ActivityIndicator,
TouchableOpacity,
StyleSheet,
Text,
View
} from 'react-native';
import Color from '../style/color';
import Api from '../util/api';
const localStyle = StyleSheet.create({
footer: {
height: 60,
justifyContent: 'center',
alignItems: 'center',
paddingBottom: 15
}
});
function renderFooterLoading() {
return (
<View style={localStyle.footer}>
<ActivityIndicator animating={true} color={Color.primary}
size={Api.IS_ANDROID ? 'large' : 'small'} />
</View>
);
}
function renderFooterEnd() {
return (
<View style={localStyle.footer}>
<Text style={{color: Color.inactiveText, fontSize: 12}}>
THE END
</Text>
</View>
);
}
function renderFooterFailed(refresh) {
let isRefreshable = refresh && typeof refresh == 'function';
return (
<View style={localStyle.footer}>
<TouchableOpacity style={{marginTop: 15}}
onPress={() => {
if(isRefreshable) {
refresh();
}
}}
>
<Text style={{color: Color.primary}}>
加载失败{isRefreshable ? ',请点击重试' : ''}
</Text>
</TouchableOpacity>
</View>
);
}
export default {
renderFooterLoading,
renderFooterEnd,
renderFooterFailed
}

View file

@ -15,6 +15,7 @@ import Color from '../../style/color';
import Touchable from '../touchable'; import Touchable from '../touchable';
import DiaryBrief from '../diary/diaryBrief'; import DiaryBrief from '../diary/diaryBrief';
import ListFooter from '../listFooter';
import NotebookDiaryData from '../../dataLoader/notebookDiaryData'; import NotebookDiaryData from '../../dataLoader/notebookDiaryData';
@ -28,10 +29,13 @@ export default class NotebookDiaryList extends Component {
this.state = { this.state = {
diaries: [], diaries: [],
hasMore: false,
refreshing: false, refreshing: false,
refreshFailed: false refreshFailed: false,
hasMore: true,
loadingMore: false,
loadFailed: false
}; };
} }
@ -76,30 +80,29 @@ export default class NotebookDiaryList extends Component {
return result; return result;
} }
refresh(loadMore = false) { refresh() {
this.setState({refreshing: true}); if (this.state.refreshing) {
this.dataSource.refresh(this.notebook.id, loadMore) return;
.then(result => { }
console.log('get notebook diaries:', result);
this.setState({refreshing: true, refreshFailed: false});
this.dataSource.refresh(this.notebook.id)
.then(result => {
if(!result) { if(!result) {
throw { throw {
message: 'refresh no result' message: 'refresh notebookDiary no result'
} }
} else { } else {
let diaries = this.formatDiaries(result.list); let diaries = this.formatDiaries(result.list);
this.setState({ this.setState({
diaries, diaries,
hasMore: result.more,
refreshFailed: false refreshFailed: false
}); });
} }
}).catch(e => { }).catch(e => {
this.setState({ this.setState({
diaries: [],
hasMore: false,
refreshFailed: true refreshFailed: true
}); });
@ -111,11 +114,38 @@ export default class NotebookDiaryList extends Component {
} }
loadMore() { loadMore() {
if (this.state.refreshing) { if (this.state.loadingMore) {
return; return;
} }
this.refresh(true); this.setState({loadingMore: true, loadFailed: false});
this.dataSource.refresh(this.notebook.id, true)
.then(result => {
if(!result) {
throw {
message: 'loadMore notebookDiary no result'
}
} else {
let diaries = this.formatDiaries(result.list);
this.setState({
diaries,
hasMore: result.more,
loadFailed: false
});
}
}).catch(e => {
this.setState({
hasMore: false,
loadFailed: true
});
}).done(() => {
this.setState({
loadingMore: false
});
});
} }
render() { render() {
@ -139,53 +169,34 @@ export default class NotebookDiaryList extends Component {
</View>); </View>);
}} }}
ListFooterComponent={this.renderFooter.bind(this)} ListFooterComponent={() => {
if (this.state.refreshing || this.state.loadingMore || this.state.diaries.length == 0) {
return null;
}
automaticallyAdjustContentInsets={true} if (this.state.loadFailed) {
return ListFooter.renderFooterFailed(this.loadMore.bind(this));
}
if (!this.state.hasMore) {
return ListFooter.renderFooterEnd();
}
return ListFooter.renderFooterLoading();
}}
ItemSeparatorComponent={(sectionID, rowID, adjacentRowHighlighted) => ItemSeparatorComponent={(sectionID, rowID, adjacentRowHighlighted) =>
<View key={`${sectionID}-${rowID}`} style={localStyle.itemSeparator} />} <View key={`${sectionID}-${rowID}`} style={localStyle.itemSeparator} />}
SectionSeparatorComponent={() => <View style={localStyle.sectionSeparator} />} SectionSeparatorComponent={() => <View style={localStyle.sectionSeparator} />}
onEndReachedThreshold={2}
onEndReached={this.state.hasMore ? this.loadMore.bind(this) : null} onEndReached={this.state.hasMore ? this.loadMore.bind(this) : null}
/> />
</View> </View>
) : null; ) : null;
} }
renderFooter() {
if (this.state.refreshing || this.state.diaries.length === 0) {
return null;
}
if (this.state.refreshFailed) {
return (
<View style={localStyle.footer}>
<TouchableOpacity style={{marginTop: 15}}
onPress={() => {this.loadMore();}}>
<Text style={{color: Color.primary}}>加载失败,请点击重试</Text>
</TouchableOpacity>
</View>
);
}
if (!this.state.hasMore) {
return (
<View style={localStyle.footer}>
<Text style={{color: Color.inactiveText, fontSize: 12}}> THE END </Text>
</View>
);
}
return (
<View style={localStyle.footer}>
<ActivityIndicator animating={true} color={Color.primary}
size={Api.IS_ANDROID ? 'large' : 'small'}/>
</View>
);
}
} }
const localStyle = StyleSheet.create({ const localStyle = StyleSheet.create({
@ -206,11 +217,5 @@ const localStyle = StyleSheet.create({
sectionSeparator: { sectionSeparator: {
borderBottomWidth: StyleSheet.hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
borderColor: Color.line borderColor: Color.line
},
footer: {
height: 60,
justifyContent: 'center',
alignItems: 'center',
paddingBottom: 15
} }
}); });

View file

@ -55,11 +55,11 @@ export default class HomePage extends Component {
const localStyle = StyleSheet.create({ const localStyle = StyleSheet.create({
wrap: { wrap: {
flex: 1, flex: 1,
backgroundColor: '#fff', backgroundColor: '#fff'
paddingTop: 20
}, },
header: { header: {
paddingLeft: 20, paddingLeft: 20,
paddingTop: 20,
flexDirection: "row" flexDirection: "row"
}, },
title: { title: {

View file

@ -9,9 +9,9 @@ export default class NotebookDetailPage extends Component {
render() { render() {
return ( return (
<ScrollView style={{flex: 1}}> <View style={{flex: 1}}>
<NotebookDiaryList notebook={this.props.notebook}></NotebookDiaryList> <NotebookDiaryList notebook={this.props.notebook}></NotebookDiaryList>
</ScrollView> </View>
); );
} }
} }