1. 提取DiaryList组件,首页和关注页复用

2. 设备相关常量固定在api.js,其他组件直接引用
This commit is contained in:
xuwenyang 2019-05-10 16:29:19 +08:00
parent 1ea2afe25c
commit 66b26e7562
8 changed files with 340 additions and 227 deletions

View file

@ -15,8 +15,8 @@ import BottomNav from './src/nav/bottomNav'
async function init() {
await loadIcon();
// let token = await Token.getUserToken();
let token;
let token = await Token.getUserToken();
// let token;
if (!token) {
Navigation.startSingleScreenApp({
screen: {

View file

@ -0,0 +1,196 @@
import React, {Component} from 'react';
import {
InteractionManager,
ActivityIndicator,
StyleSheet,
FlatList,
Text, View
} from 'react-native';
import {Divider} from "react-native-elements";
import Color from '../../style/color'
import Msg from '../../util/msg'
import Api from '../../util/api'
import Touchable from '../touchable'
import DiaryBrief from './diaryBrief'
export default class DiaryList extends Component {
constructor(props) {
super(props);
this.dataSource = props.dataSource;
this.state = {
isLoading: true,
diaries: [],
hasMore: false,
refreshing: false,
refreshFailed: false
};
}
componentDidMount() {
console.log('diaryList mounted.');
InteractionManager.runAfterInteractions(() => {
this.refresh();
});
}
async refresh(loadMore = false) {
if (this.state.refreshing) {
return;
}
this.setState({hasMore: false, refreshing: true, refreshFailed: false});
this.dataSource.refresh(loadMore)
.then(result => {
console.log('diaryList refresh:', result);
if(!result) {
throw {
message: 'refresh no result'
}
} else {
let diaries = this.state.diaries;
let newDiaries = result.list;
if (!loadMore && diaries.length > 0 && newDiaries.length > 0
&& diaries[0].id === newDiaries[0].id) {
Msg.showMsg('没有新内容');
}
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) {
return;
}
this.refresh(true);
}
_onDiaryPress(diary) {
/*
this.props.navigator.push({
screen: 'DiaryDetail',
title: '日记详情',
passProps: { diary: diary }
});
*/
}
render() {
return (
<View style={localStyle.container}>
<FlatList style={localStyle.list}
data={this.state.diaries}
keyExtractor={(item, index) => {
return item.id.toString()
}}
ListHeaderComponent={this.state.isLoading ? null : this.props.header}
renderItem={({item}) => {
return (
<Touchable onPress={() => this._onDiaryPress(item)}>
<DiaryBrief diary={item}></DiaryBrief>
</Touchable>
)
}}
ItemSeparatorComponent={({highlighted}) => <Divider style={{backgroundColor: '#eee'}}/>}
ListFooterComponent={this.renderFooter()}
refreshing={this.state.refreshing}
onRefresh={this.refresh.bind(this)}
automaticallyAdjustContentInsets={true}
onEndReachedThreshold={2}
onEndReached={this.state.hasMore ? this.loadMore.bind(this) : null}
>
</FlatList>
</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({
container: {
flex: 1
},
list: {
height: '100%'
},
footer: {
height: 60,
justifyContent: 'center',
alignItems: 'center',
paddingBottom: 15
}
});

View file

@ -1,30 +1,23 @@
import React, { Component } from 'react';
import { StyleSheet, Text, View, ActivityIndicator, Dimensions } from 'react-native';
import {StyleSheet, Text, View, ActivityIndicator} from 'react-native';
const { width, height } = Dimensions.get('window')
import Api from '../util/api'
export default class Loading extends Component {
constructor(props) {
super(props);
this.state = {
color: props.color ? props.color : '#aaa'
}
}
/*
show() {
this.setState({visible: true})
}
hide() {
this.setState({visible: false})
}
*/
render() {
if (this.props.visible) {
return (
<View style={styles.loading}>
<ActivityIndicator size="large" color="#aaa" />
<View style={localStyle.loading}>
<ActivityIndicator size="large" color={this.state.color} />
</View>
);
@ -34,13 +27,13 @@ export default class Loading extends Component {
}
}
const styles = StyleSheet.create({
const localStyle = StyleSheet.create({
loading: {
position: "absolute",
position: 'absolute',
left: 0,
width: width,
width: Api.DEVICE_WINDOW.width,
top: 200,
justifyContent: "center",
alignItems: "center"
justifyContent: 'center',
alignItems: 'center'
}
});

View file

@ -0,0 +1,30 @@
import Api from '../util/api'
const PAGE_SIZE = 21;
export default class FollowListData {
list: [];
last_id: 0;
async refresh(loadMore = false) {
let lastId = !loadMore ? 0 : this.last_id;
let data = await Api.getFollowDiaries(0, PAGE_SIZE, lastId);
let more = data.diaries.length === PAGE_SIZE;
if(!loadMore) {
this.list = data.diaries.slice(0, PAGE_SIZE - 1);
} else if(data.diaries.length > 0) {
this.list = this.list.concat(data.diaries.slice(0, PAGE_SIZE - 1));
}
this.last_id = more ? data.diaries[PAGE_SIZE - 1].id : 0;
return {
list: this.list,
more
};
}
}

View file

@ -1,33 +1,55 @@
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';
import {StyleSheet, Text, View} from 'react-native';
import Api from '../util/api'
import DiaryList from '../component/diary/diaryList'
import FollowListData from '../dataLoader/followListData';
export default class FollowPage extends Component {
constructor(props) {
super(props);
this.dataSource = new FollowListData();
}
renderHeader() {
return (
<View style={localStyle.header}>
<Text style={localStyle.title}>关注</Text>
</View>
);
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Follow Page !</Text>
</View>
);
return (
<View style={localStyle.wrap}>
<DiaryList ref={(r) => this.list = r}
dataSource={this.dataSource}
header={this.renderHeader.bind(this)}
navigator={this.props.navigator}
></DiaryList>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
const localStyle = StyleSheet.create({
wrap: {
flex: 1,
backgroundColor: '#fff',
paddingTop: Api.IS_IPHONEX || Api.IS_ANDROID ? 44 : 20
},
header: {
paddingLeft: 20,
flexDirection: "row"
},
title: {
flex: 1,
fontSize: 30,
color: '#000'
}
});

View file

@ -1,211 +1,55 @@
import React, {Component} from 'react';
import {
ActivityIndicator,
FlatList,
InteractionManager, Platform, StyleSheet, Text, TouchableOpacity, View,
Alert,
Dimensions
} from 'react-native';
import {Divider} from "react-native-elements";
import {isIphoneX} from 'react-native-iphone-x-helper'
import {StyleSheet, Text, View} from 'react-native';
import Color from '../style/color'
import Msg from '../util/msg'
import Api from '../util/api'
import Loading from '../component/loading'
import Touchable from '../component/touchable'
import DiaryBrief from '../component/diary/diaryBrief'
import DiaryFull from '../component/diary/diaryFull'
import HomeListData from '../entity/homeListData';
import DiaryList from '../component/diary/diaryList'
import HomeListData from '../dataLoader/homeListData';
const isIpx = isIphoneX();
const isAndroid = Platform.OS === 'android';
const HEADER_PADDING = Platform.OS === 'android' ? 20 : (isIpx ? 10 : 25);
const { width, height } = Dimensions.get('window')
export default class HomePage extends Component {
constructor(props) {
super(props);
this.dataSource = new HomeListData();
this.state = {
isLoading: true,
diaries: [],
hasMore: false,
refreshing: false,
refreshFailed: false
};
}
componentDidMount() {
InteractionManager.runAfterInteractions(() => {
this.refresh();
});
}
async refresh(loadMore = false) {
if (this.state.refreshing) {
return;
}
this.setState({refreshing: true, hasMore: false, refreshFailed: false});
this.dataSource.refresh(loadMore)
.then(data => {
console.log('homepage data:', data);
if(!data) {
throw {
message: 'empty data'
}
} else {
let diaries = this.state.diaries;
let newDiaries = data.list;
if (!loadMore && diaries.length > 0 && newDiaries.length > 0
&& diaries[0].id === newDiaries[0].id) {
Msg.showMsg('没有新内容');
}
this.setState({
diaries: data.list ? data.list : [],
hasMore: data.more,
refreshFailed: false
});
}
}).catch(e => {
if (e.code && 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) {
return;
}
this.refresh(true);
}
_checkResult(result) {
}
_onDiaryPress(diary) {
/*
this.props.navigator.push({
screen: 'DiaryDetail',
title: '日记详情',
passProps: { diary: diary }
});
*/
renderHeader() {
return (
<View style={localStyle.header}>
<Text style={localStyle.title}>胶囊日记</Text>
</View>
);
}
render() {
return (
<View style={localStyle.wrap}>
<Loading visible={this.state.isLoading}></Loading>
<DiaryList ref={(r) => this.list = r}
dataSource={this.dataSource}
header={this.renderHeader.bind(this)}
<FlatList style={localStyle.list}
navigator={this.props.navigator}
data={this.state.diaries}
keyExtractor={(item, index) => {
return item.id.toString()
}}
renderItem={({item}) => {
return (
<Touchable onPress={() => this._onDiaryPress(item)}>
<DiaryBrief diary={item}></DiaryBrief>
</Touchable>
)
}}
ItemSeparatorComponent={({highlighted}) => <Divider style={{backgroundColor: '#eee'}}/>}
ListFooterComponent={this.renderFooter()}
refreshing={this.state.refreshing}
onRefresh={this.refresh.bind(this)}
automaticallyAdjustContentInsets={true}
onEndReachedThreshold={2}
onEndReached={this.state.hasMore ? this.loadMore.bind(this) : null}
/>
></DiaryList>
</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={Platform.OS === 'android' ? 'large' : 'small'}/>
</View>
);
}
}
const localStyle = StyleSheet.create({
wrap: {
position: 'absolute',
backgroundColor: '#fff',
flex: 1,
top: 0,
bottom: 0,
paddingTop: isIpx || isAndroid ? 44 : 20
backgroundColor: '#fff',
paddingTop: Api.IS_IPHONEX || Api.IS_ANDROID ? 44 : 20
},
list: {
backgroundColor: 'white',
height: '100%'
header: {
paddingLeft: 20,
flexDirection: "row"
},
footer: {
height: 60,
justifyContent: 'center',
alignItems: 'center',
paddingBottom: 15
title: {
flex: 1,
fontSize: 30,
color: '#000'
}
});

View file

@ -1,12 +1,21 @@
import TokenManager from './token'
import {Platform, Dimensions} from 'react-native'
import DeviceInfo from 'react-native-device-info';
import {isIphoneX} from 'react-native-iphone-x-helper'
import TokenManager from './token'
const IS_ANDROID = Platform.OS === 'android';
const DEVICE_WINDOW = Dimensions.get('window')
const OS = DeviceInfo.getSystemName();
const OS_VERSION = DeviceInfo.getSystemVersion();
const DEVICE_ID = DeviceInfo.getUniqueID();
const VERSION = DeviceInfo.getVersion();
const IS_IPHONEX = isIphoneX();
const baseUrl = 'http://open.timepill.net/api';
@ -32,7 +41,7 @@ async function login(username, password) {
}
async function getSelfInfo() {
return call('GET', '/users/my')
return call('GET', '/users/my');
}
async function getTodayDiaries(page = 1, page_size = 20, first_id = '') {
@ -45,6 +54,16 @@ async function getTodayDiaries(page = 1, page_size = 20, first_id = '') {
});
}
async function getFollowDiaries(page = 1, page_size = 20, first_id = '') {
return call('GET', '/diaries/follow?page=' + page + '&page_size=' + page_size + `&first_id=${first_id}`)
.then((json) => {
json.page = Number(json.page);
json.page_size = Number(json.page_size);
return json;
});
}
@ -125,6 +144,15 @@ function handleCatch(err) {
export default {
IS_ANDROID,
DEVICE_WINDOW,
OS,
OS_VERSION,
DEVICE_ID,
VERSION,
IS_IPHONEX,
login,
getTodayDiaries
getTodayDiaries,
getFollowDiaries
}