mirror of
https://github.com/timepill/timepill-app.git
synced 2025-04-30 18:09: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',
|
name: 'FollowUser',
|
||||||
options: {
|
options: {
|
||||||
bottomTabs: {
|
bottomTabs: {
|
||||||
visible: false
|
visible: false,
|
||||||
|
|
||||||
|
// hide bottom tab for android
|
||||||
|
drawBehind: true,
|
||||||
|
animate: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,38 @@
|
||||||
import React, {Component} from 'react';
|
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 {
|
export default class FollowPage extends Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
index: 0,
|
||||||
|
routes: [
|
||||||
|
{ key: 'following', title: '我关注的' },
|
||||||
|
{ key: 'followedBy', title: '关注我的' }
|
||||||
|
]
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static options(passProps) {
|
static options(passProps) {
|
||||||
return {
|
return {
|
||||||
topBar: {
|
topBar: {
|
||||||
|
noBorder: true, // ios
|
||||||
|
elevation: 0, // android
|
||||||
|
|
||||||
title: {
|
title: {
|
||||||
text: '关注用户'
|
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() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<View style={localStyle.container}>
|
<TabView style={localStyle.container}
|
||||||
<Text style={localStyle.welcome}>Follow User Page !</Text>
|
initialLayout={{
|
||||||
</View>
|
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({
|
const localStyle = StyleSheet.create({
|
||||||
container: {
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
backgroundColor: '#F5FCFF',
|
|
||||||
},
|
|
||||||
welcome: {
|
welcome: {
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
margin: 10,
|
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')
|
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) {
|
async function call(method, api, body, _timeout = 10000) {
|
||||||
let token = await TokenManager.getUserToken();
|
let token = await TokenManager.getUserToken();
|
||||||
|
@ -159,8 +167,13 @@ export default {
|
||||||
IS_IPHONEX,
|
IS_IPHONEX,
|
||||||
|
|
||||||
login,
|
login,
|
||||||
|
|
||||||
getTodayDiaries,
|
getTodayDiaries,
|
||||||
getFollowDiaries,
|
getFollowDiaries,
|
||||||
|
|
||||||
getDiaryComments,
|
getDiaryComments,
|
||||||
getSelfNotebooks
|
getSelfNotebooks,
|
||||||
|
|
||||||
|
getRelationUsers,
|
||||||
|
getRelationReverseUsers
|
||||||
}
|
}
|
Loading…
Reference in a new issue