mirror of
https://github.com/timepill/timepill-app.git
synced 2025-04-30 09:59:31 +08:00
1. 整理首页日志列表,关注页日志列表,日记本日记列表
2. 提取通用列表尾组件 3. 解决向下滑屏追加数据后列表抖动问题
This commit is contained in:
parent
beb5c0ec21
commit
bb0a75e15f
7 changed files with 250 additions and 143 deletions
|
@ -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}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
});
|
|
@ -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>
|
||||
|
|
68
src/component/listFooter.js
Normal file
68
src/component/listFooter.js
Normal 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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
});
|
|
@ -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: {
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue