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;
return (
<View style={localStyle.box}>
<View style={[localStyle.box, this.props.style]}>
{(user && user.iconUrl)
? <UserIcon iconUrl={user.iconUrl}></UserIcon> : null}

View file

@ -14,6 +14,7 @@ import Msg from '../../util/msg';
import Api from '../../util/api';
import Touchable from '../touchable';
import ListFooter from '../listFooter';
import DiaryBrief from './diaryBrief';
@ -24,13 +25,14 @@ export default class DiaryList extends Component {
this.dataSource = props.dataSource;
this.state = {
isLoading: true,
diaries: [],
hasMore: 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) {
return;
}
this.setState({hasMore: false, refreshing: true, refreshFailed: false});
this.dataSource.refresh(loadMore)
this.setState({refreshing: true, refreshFailed: false});
this.dataSource.refresh()
.then(result => {
if(!result) {
throw {
message: 'refresh no result'
message: 'refresh diary no result'
}
} else {
let diaries = this.state.diaries;
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) {
Msg.showMsg('没有新内容');
@ -64,40 +66,54 @@ export default class DiaryList extends Component {
this.setState({
diaries: result.list ? result.list : [],
hasMore: result.more,
refreshFailed: false
});
}
}).catch(e => {
if (e.code === 401) {
/*
this.props.navigator.showModal({
screen: "App"
});
*/
}
this.setState({
diaries: [],
hasMore: false,
refreshFailed: true
});
}).done(() => {
this.setState({
isLoading: false,
refreshing: false
});
});
}
async loadMore() {
if (this.state.refreshing) {
if (this.state.loadingMore) {
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() {
@ -111,8 +127,6 @@ export default class DiaryList extends Component {
return item.id.toString()
}}
ListHeaderComponent={this.state.isLoading ? null : this.props.header}
renderItem={({item}) => {
return (
<Touchable onPress={() => this.props.onDiaryPress(item)}>
@ -123,12 +137,25 @@ export default class DiaryList extends Component {
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}
onRefresh={this.refresh.bind(this)}
automaticallyAdjustContentInsets={true}
onEndReachedThreshold={2}
onEndReached={this.state.hasMore ? this.loadMore.bind(this) : null}
>
@ -136,38 +163,6 @@ export default class DiaryList extends Component {
</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({
@ -176,11 +171,5 @@ const localStyle = StyleSheet.create({
},
list: {
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 UserIcon from '../userIcon';
import ListFooter from '../listFooter';
export default class FollowUserList extends Component {
@ -18,10 +19,13 @@ export default class FollowUserList extends Component {
this.state = {
users: [],
hasMore: 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) {
return;
}
this.setState({hasMore: false, refreshing: true, refreshFailed: false});
this.dataSource.refresh(loadMore)
this.setState({refreshing: true, refreshFailed: false});
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 => {
if(!result) {
throw {
@ -50,32 +88,23 @@ export default class FollowUserList extends Component {
this.setState({
users: result.list ? result.list : [],
hasMore: result.more,
refreshFailed: false
loadFailed: false
});
}
}).catch(e => {
this.setState({
users: [],
hasMore: false,
refreshFailed: true
loadFailed: true
});
}).done(() => {
this.setState({
refreshing: false
loadingMore: false
});
});
}
loadMore() {
if (this.state.refreshing) {
return;
}
this.refresh(true);
}
render() {
return (
<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}
onRefresh={this.refresh.bind(this)}
onEndReachedThreshold={5}
onEndReachedThreshold={2}
onEndReached={this.state.hasMore ? this.loadMore.bind(this) : null}
/>
</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 DiaryBrief from '../diary/diaryBrief';
import ListFooter from '../listFooter';
import NotebookDiaryData from '../../dataLoader/notebookDiaryData';
@ -28,10 +29,13 @@ export default class NotebookDiaryList extends Component {
this.state = {
diaries: [],
hasMore: 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;
}
refresh(loadMore = false) {
this.setState({refreshing: true});
this.dataSource.refresh(this.notebook.id, loadMore)
.then(result => {
console.log('get notebook diaries:', result);
refresh() {
if (this.state.refreshing) {
return;
}
this.setState({refreshing: true, refreshFailed: false});
this.dataSource.refresh(this.notebook.id)
.then(result => {
if(!result) {
throw {
message: 'refresh no result'
message: 'refresh notebookDiary no result'
}
} else {
let diaries = this.formatDiaries(result.list);
this.setState({
diaries,
hasMore: result.more,
refreshFailed: false
});
}
}).catch(e => {
this.setState({
diaries: [],
hasMore: false,
refreshFailed: true
});
@ -111,11 +114,38 @@ export default class NotebookDiaryList extends Component {
}
loadMore() {
if (this.state.refreshing) {
if (this.state.loadingMore) {
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() {
@ -139,53 +169,34 @@ export default class NotebookDiaryList extends Component {
</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) =>
<View key={`${sectionID}-${rowID}`} style={localStyle.itemSeparator} />}
SectionSeparatorComponent={() => <View style={localStyle.sectionSeparator} />}
onEndReachedThreshold={2}
onEndReached={this.state.hasMore ? this.loadMore.bind(this) : null}
/>
</View>
) : 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({
@ -206,11 +217,5 @@ const localStyle = StyleSheet.create({
sectionSeparator: {
borderBottomWidth: StyleSheet.hairlineWidth,
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({
wrap: {
flex: 1,
backgroundColor: '#fff',
paddingTop: 20
backgroundColor: '#fff'
},
header: {
paddingLeft: 20,
paddingTop: 20,
flexDirection: "row"
},
title: {

View file

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