mirror of
https://github.com/timepill/timepill-app.git
synced 2025-04-30 09:59:31 +08:00
1. 关注/被关注用户列表切换
2. 抽象出customedList组件,应用于关注用户页面的列表展现。逐步将试用到各类日志列表,减少列表类组件数量
This commit is contained in:
parent
7852b9e05a
commit
0b682dd777
7 changed files with 343 additions and 13 deletions
97
src/component/customedList.js
Normal file
97
src/component/customedList.js
Normal file
|
@ -0,0 +1,97 @@
|
|||
import React, {Component} from 'react';
|
||||
import {StyleSheet, Text, View, InteractionManager, FlatList} from 'react-native';
|
||||
|
||||
|
||||
export default class CustomedList extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.listType = props.listType || 'undefined';
|
||||
this.dataSource = props.dataSource;
|
||||
|
||||
this.state = {
|
||||
listData: [],
|
||||
hasMore: false,
|
||||
|
||||
refreshing: false,
|
||||
refreshFailed: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
this.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
refresh(loadMore = false) {
|
||||
if (this.state.refreshing) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({hasMore: false, refreshing: true, refreshFailed: false});
|
||||
this.dataSource.refresh(loadMore)
|
||||
.then(result => {
|
||||
if(!result) {
|
||||
throw {
|
||||
message: 'refresh ' + this.listType + ' no result'
|
||||
}
|
||||
|
||||
} else {
|
||||
console.log('refresh ' + this.listType + ' result:', result);
|
||||
|
||||
this.setState({
|
||||
listData: result.list ? result.list : [],
|
||||
hasMore: result.more,
|
||||
refreshFailed: false
|
||||
});
|
||||
}
|
||||
|
||||
}).catch(e => {
|
||||
this.setState({
|
||||
listData: [],
|
||||
hasMore: false,
|
||||
refreshFailed: true
|
||||
});
|
||||
|
||||
}).done(() => {
|
||||
this.setState({
|
||||
refreshing: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
loadMore() {
|
||||
if (this.state.refreshing) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.refresh(true);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<FlatList
|
||||
|
||||
data={this.state.listData}
|
||||
|
||||
keyExtractor={(item, index) => {
|
||||
return item.id ? item.id.toString() : index;
|
||||
}}
|
||||
|
||||
renderItem={this.props.renderItem}
|
||||
|
||||
refreshing={this.state.refreshing}
|
||||
onRefresh={this.refresh.bind(this)}
|
||||
|
||||
onEndReachedThreshold={5}
|
||||
onEndReached={this.state.hasMore ? this.loadMore.bind(this) : null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const localStyle = StyleSheet.create({
|
||||
|
||||
});
|
72
src/component/follow/followUserList.js
Normal file
72
src/component/follow/followUserList.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
import React, {Component} from 'react';
|
||||
import {StyleSheet, Text, View, InteractionManager, FlatList} from 'react-native';
|
||||
import Ionicons from 'react-native-vector-icons/Ionicons';
|
||||
|
||||
import Touchable from '../touchable';
|
||||
import Color from '../../style/color';
|
||||
|
||||
import UserIcon from '../userIcon';
|
||||
import CustomedList from '../customedList';
|
||||
|
||||
|
||||
export default class FollowUserList extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.dataSource = props.dataSource;
|
||||
this.listType = props.listType;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={localStyle.container}>
|
||||
<CustomedList listType={this.props.listType} style={localStyle.list}
|
||||
|
||||
dataSource={this.props.dataSource}
|
||||
|
||||
renderItem={({item}) => {
|
||||
return (
|
||||
<Touchable key={item.id} onPress={() => {}}>
|
||||
<View style={localStyle.box}>
|
||||
<UserIcon iconUrl={item.iconUrl}></UserIcon>
|
||||
<Text style={localStyle.userName}>{item.name}</Text>
|
||||
<Touchable onPress={() => {}}>
|
||||
<Ionicons name="md-close" size={20}
|
||||
style={localStyle.removeIcon}
|
||||
color={Color.inactiveText} />
|
||||
</Touchable>
|
||||
</View>
|
||||
</Touchable>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const localStyle = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1
|
||||
},
|
||||
list: {
|
||||
height: '100%'
|
||||
},
|
||||
box: {
|
||||
flexDirection: 'row',
|
||||
borderBottomWidth: 1,
|
||||
borderColor: Color.line,
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'white',
|
||||
paddingLeft: 25
|
||||
},
|
||||
userName: {
|
||||
flex: 1,
|
||||
color: Color.text,
|
||||
fontSize: 16
|
||||
},
|
||||
removeIcon: {
|
||||
padding: 20
|
||||
}
|
||||
});
|
30
src/dataLoader/followedByUserData.js
Normal file
30
src/dataLoader/followedByUserData.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
import Api from '../util/api'
|
||||
|
||||
|
||||
const PAGE_SIZE = 20;
|
||||
|
||||
export default class FollowedByUserData {
|
||||
|
||||
page: 1;
|
||||
list: [];
|
||||
|
||||
async refresh(loadMore = false) {
|
||||
let page = !loadMore ? 1 : this.page + 1;
|
||||
let data = await Api.getRelationReverseUsers(page, PAGE_SIZE)
|
||||
let more = data.users.length === PAGE_SIZE;
|
||||
|
||||
if(!loadMore) {
|
||||
this.page = page;
|
||||
this.list = data.users.slice(0, PAGE_SIZE - 1);
|
||||
|
||||
} else if(data.users.length > 0) {
|
||||
this.page = page;
|
||||
this.list = this.list.concat(data.users.slice(0, PAGE_SIZE - 1));
|
||||
}
|
||||
|
||||
return {
|
||||
list: this.list,
|
||||
more
|
||||
};
|
||||
}
|
||||
}
|
30
src/dataLoader/followingUserData.js
Normal file
30
src/dataLoader/followingUserData.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
import Api from '../util/api'
|
||||
|
||||
|
||||
const PAGE_SIZE = 20;
|
||||
|
||||
export default class FollowingUserData {
|
||||
|
||||
page: 1;
|
||||
list: [];
|
||||
|
||||
async refresh(loadMore = false) {
|
||||
let page = !loadMore ? 1 : this.page + 1;
|
||||
let data = await Api.getRelationUsers(page, PAGE_SIZE)
|
||||
let more = data.users.length === PAGE_SIZE;
|
||||
|
||||
if(!loadMore) {
|
||||
this.page = page;
|
||||
this.list = data.users.slice(0, PAGE_SIZE - 1);
|
||||
|
||||
} else if(data.users.length > 0) {
|
||||
this.page = page;
|
||||
this.list = this.list.concat(data.users.slice(0, PAGE_SIZE - 1));
|
||||
}
|
||||
|
||||
return {
|
||||
list: this.list,
|
||||
more
|
||||
};
|
||||
}
|
||||
}
|
|
@ -54,7 +54,11 @@ export default class FollowPage extends Component {
|
|||
name: 'FollowUser',
|
||||
options: {
|
||||
bottomTabs: {
|
||||
visible: false
|
||||
visible: false,
|
||||
|
||||
// hide bottom tab for android
|
||||
drawBehind: true,
|
||||
animate: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,38 @@
|
|||
import React, {Component} from 'react';
|
||||
import {StyleSheet, Text, View} from 'react-native';
|
||||
import {StyleSheet, Text, View, Animated} from 'react-native';
|
||||
import {
|
||||
PagerScroll,
|
||||
TabView,
|
||||
TabBar,
|
||||
SceneMap
|
||||
} from 'react-native-tab-view';
|
||||
|
||||
import Api from '../util/api';
|
||||
import Color from '../style/color';
|
||||
import FollowUserList from '../component/follow/followUserList'
|
||||
import FollowingUserData from '../dataLoader/followingUserData'
|
||||
import FollowedByUserData from '../dataLoader/followedByUserData'
|
||||
|
||||
|
||||
export default class FollowPage extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
index: 0,
|
||||
routes: [
|
||||
{ key: 'following', title: '我关注的' },
|
||||
{ key: 'followedBy', title: '关注我的' }
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
static options(passProps) {
|
||||
return {
|
||||
topBar: {
|
||||
noBorder: true, // ios
|
||||
elevation: 0, // android
|
||||
|
||||
title: {
|
||||
text: '关注用户'
|
||||
}
|
||||
|
@ -18,25 +40,87 @@ export default class FollowPage extends Component {
|
|||
};
|
||||
}
|
||||
|
||||
_renderLabel = props => ({route}) => {
|
||||
let routes = props.navigationState.routes;
|
||||
let index = props.navigationState.index;
|
||||
|
||||
let color = route.key == routes[index].key ? Color.primary : '#222';
|
||||
|
||||
return (
|
||||
<Animated.Text
|
||||
style={[localStyle.label, {color}]}>
|
||||
{route.title}
|
||||
</Animated.Text>
|
||||
);
|
||||
};
|
||||
|
||||
_renderTabBar = props => {
|
||||
return (
|
||||
<TabBar
|
||||
{...props}
|
||||
style={localStyle.tabBar}
|
||||
indicatorStyle={localStyle.indicator}
|
||||
|
||||
renderLabel={this._renderLabel(props)}
|
||||
>
|
||||
</TabBar>
|
||||
);
|
||||
};
|
||||
|
||||
_renderScene = SceneMap({
|
||||
following: () => <FollowUserList
|
||||
listType={'followingUser'} dataSource={new FollowingUserData()}
|
||||
/>,
|
||||
followedBy: () => <FollowUserList
|
||||
listType={'followedByUser'} dataSource={new FollowedByUserData()}
|
||||
/>
|
||||
});
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={localStyle.container}>
|
||||
<Text style={localStyle.welcome}>Follow User Page !</Text>
|
||||
</View>
|
||||
<TabView style={localStyle.container}
|
||||
initialLayout={{
|
||||
height: 0,
|
||||
width: Api.DEVICE_WINDOW.width
|
||||
}}
|
||||
|
||||
renderPager={(props) => <PagerScroll {...props}/>} /* android */
|
||||
|
||||
renderTabBar={this._renderTabBar}
|
||||
renderScene={this._renderScene}
|
||||
|
||||
navigationState={this.state}
|
||||
onIndexChange={index => {
|
||||
this.setState({index});
|
||||
}}
|
||||
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const localStyle = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F5FCFF',
|
||||
},
|
||||
|
||||
welcome: {
|
||||
fontSize: 20,
|
||||
textAlign: 'center',
|
||||
margin: 10,
|
||||
marginTop: 35
|
||||
},
|
||||
|
||||
container: {
|
||||
flex: 1
|
||||
},
|
||||
tabBar: {
|
||||
backgroundColor: '#fff',
|
||||
paddingTop: 10,
|
||||
paddingBottom: 5
|
||||
},
|
||||
indicator: {
|
||||
backgroundColor: Color.primary
|
||||
},
|
||||
label: {
|
||||
fontSize: 13,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
});
|
||||
|
|
|
@ -72,6 +72,14 @@ async function getSelfNotebooks() {
|
|||
return call('GET', '/notebooks/my')
|
||||
}
|
||||
|
||||
async function getRelationUsers(page, page_size) {
|
||||
return call('GET', `/relation?page=${page}&page_size=${page_size}`);
|
||||
}
|
||||
|
||||
async function getRelationReverseUsers(page, page_size) {
|
||||
return call('GET', `/relation/reverse?page=${page}&page_size=${page_size}`);
|
||||
}
|
||||
|
||||
|
||||
async function call(method, api, body, _timeout = 10000) {
|
||||
let token = await TokenManager.getUserToken();
|
||||
|
@ -159,8 +167,13 @@ export default {
|
|||
IS_IPHONEX,
|
||||
|
||||
login,
|
||||
|
||||
getTodayDiaries,
|
||||
getFollowDiaries,
|
||||
|
||||
getDiaryComments,
|
||||
getSelfNotebooks
|
||||
getSelfNotebooks,
|
||||
|
||||
getRelationUsers,
|
||||
getRelationReverseUsers
|
||||
}
|
Loading…
Reference in a new issue