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
4076fa7333
commit
2951ea054c
11 changed files with 380 additions and 49 deletions
10
package-lock.json
generated
10
package-lock.json
generated
|
@ -9830,6 +9830,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"moment": {
|
||||||
|
"version": "2.24.0",
|
||||||
|
"resolved": "http://registry.npm.taobao.org/moment/download/moment-2.24.0.tgz",
|
||||||
|
"integrity": "sha1-DQVdU/UFKqZTyfbraLtdEr9cK1s="
|
||||||
|
},
|
||||||
"morgan": {
|
"morgan": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"resolved": "http://registry.npm.taobao.org/morgan/download/morgan-1.9.1.tgz",
|
"resolved": "http://registry.npm.taobao.org/morgan/download/morgan-1.9.1.tgz",
|
||||||
|
@ -10814,6 +10819,11 @@
|
||||||
"prop-types": "^15.5.8"
|
"prop-types": "^15.5.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-native-iphone-x-helper": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "http://registry.npm.taobao.org/react-native-iphone-x-helper/download/react-native-iphone-x-helper-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-fbylMJMPfBzoYzzI/RO6lBApkuE="
|
||||||
|
},
|
||||||
"react-native-navigation": {
|
"react-native-navigation": {
|
||||||
"version": "1.1.376",
|
"version": "1.1.376",
|
||||||
"resolved": "https://registry.npm.taobao.org/react-native-navigation/download/react-native-navigation-1.1.376.tgz",
|
"resolved": "https://registry.npm.taobao.org/react-native-navigation/download/react-native-navigation-1.1.376.tgz",
|
||||||
|
|
|
@ -9,10 +9,12 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-native-community/async-storage": "^1.3.4",
|
"@react-native-community/async-storage": "^1.3.4",
|
||||||
"base-64": "^0.1.0",
|
"base-64": "^0.1.0",
|
||||||
|
"moment": "^2.24.0",
|
||||||
"react": "16.8.3",
|
"react": "16.8.3",
|
||||||
"react-native": "0.59.5",
|
"react-native": "0.59.5",
|
||||||
"react-native-device-info": "^1.6.1",
|
"react-native-device-info": "^1.6.1",
|
||||||
"react-native-elements": "^0.19.0",
|
"react-native-elements": "^0.19.0",
|
||||||
|
"react-native-iphone-x-helper": "^1.0.2",
|
||||||
"react-native-navigation": "^1.1.376",
|
"react-native-navigation": "^1.1.376",
|
||||||
"react-native-vector-icons": "^4.5.0"
|
"react-native-vector-icons": "^4.5.0"
|
||||||
},
|
},
|
||||||
|
|
75
src/component/diary/diaryBrief.js
Normal file
75
src/component/diary/diaryBrief.js
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import {Platform, StyleSheet, Text, View} from 'react-native';
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
import Color from '../../style/color'
|
||||||
|
import UserIcon from './userIcon'
|
||||||
|
|
||||||
|
|
||||||
|
export default class DiaryBrief extends Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let diary = this.props.diary;
|
||||||
|
let user = diary.user;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={localStyle.box}>
|
||||||
|
<UserIcon iconUrl={user.iconUrl}></UserIcon>
|
||||||
|
<View style={localStyle.body}>
|
||||||
|
<View style={localStyle.title}>
|
||||||
|
<Text style={localStyle.titleName} numberOfLines={1}>
|
||||||
|
{user.name}
|
||||||
|
</Text>
|
||||||
|
<Text style={[localStyle.titleText, {flex: 1}]} numberOfLines={1}>
|
||||||
|
《{diary.notebook_subject}》
|
||||||
|
</Text>
|
||||||
|
<Text style={localStyle.titleText}>
|
||||||
|
{moment(diary.created).format('H:mm')}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Text style={localStyle.content} numberOfLines={5}>
|
||||||
|
{diary.content.trim()}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const localStyle = StyleSheet.create({
|
||||||
|
box: {
|
||||||
|
flexDirection: "row",
|
||||||
|
overflow: "hidden",
|
||||||
|
paddingHorizontal: 15,
|
||||||
|
paddingTop: 15
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
flexDirection: "column",
|
||||||
|
flexGrow: 1,
|
||||||
|
flexShrink: 1,
|
||||||
|
paddingTop: 2
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "flex-end",
|
||||||
|
paddingBottom: 5
|
||||||
|
},
|
||||||
|
titleName: {
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: Color.text,
|
||||||
|
fontSize: 14
|
||||||
|
},
|
||||||
|
titleText: {
|
||||||
|
fontSize: 12,
|
||||||
|
color: Color.inactiveText
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
flexGrow: 1,
|
||||||
|
lineHeight: 24,
|
||||||
|
color: Color.text,
|
||||||
|
fontSize: 15,
|
||||||
|
textAlignVertical: 'bottom',
|
||||||
|
paddingBottom: 15
|
||||||
|
}
|
||||||
|
});
|
75
src/component/diary/diaryFull.js
Normal file
75
src/component/diary/diaryFull.js
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import {Platform, StyleSheet, Text, View} from 'react-native';
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
import Color from '../../style/color'
|
||||||
|
import UserIcon from './userIcon'
|
||||||
|
|
||||||
|
|
||||||
|
export default class DiaryFull extends Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let diary = this.props.diary;
|
||||||
|
let user = diary.user;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={localStyle.box}>
|
||||||
|
<UserIcon iconUrl={user.iconUrl}></UserIcon>
|
||||||
|
<View style={localStyle.body}>
|
||||||
|
<View style={localStyle.title}>
|
||||||
|
<Text style={localStyle.titleName} numberOfLines={1}>
|
||||||
|
{user.name}
|
||||||
|
</Text>
|
||||||
|
<Text style={[localStyle.titleText, {flex: 1}]} numberOfLines={1}>
|
||||||
|
《{diary.notebook_subject}》
|
||||||
|
</Text>
|
||||||
|
<Text style={localStyle.titleText}>
|
||||||
|
{moment(diary.created).format('H:mm')}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Text style={localStyle.content}>
|
||||||
|
{diary.content}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const localStyle = StyleSheet.create({
|
||||||
|
box: {
|
||||||
|
flexDirection: "row",
|
||||||
|
overflow: "hidden",
|
||||||
|
paddingHorizontal: 15,
|
||||||
|
paddingTop: 15
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
flexDirection: "column",
|
||||||
|
flexGrow: 1,
|
||||||
|
flexShrink: 1,
|
||||||
|
paddingTop: 2
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "flex-end",
|
||||||
|
paddingBottom: 5
|
||||||
|
},
|
||||||
|
titleName: {
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: Color.text,
|
||||||
|
fontSize: 14
|
||||||
|
},
|
||||||
|
titleText: {
|
||||||
|
fontSize: 12,
|
||||||
|
color: Color.inactiveText
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
flexGrow: 1,
|
||||||
|
lineHeight: 24,
|
||||||
|
color: Color.text,
|
||||||
|
fontSize: 15,
|
||||||
|
textAlignVertical: 'bottom',
|
||||||
|
paddingBottom: 15
|
||||||
|
}
|
||||||
|
});
|
29
src/component/diary/userIcon.js
Normal file
29
src/component/diary/userIcon.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import {Platform, StyleSheet, Text, View} from 'react-native';
|
||||||
|
import {Avatar} from "react-native-elements";
|
||||||
|
|
||||||
|
|
||||||
|
export default class UserIcon extends Component {
|
||||||
|
|
||||||
|
_defaultOnPress() {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Avatar small rounded
|
||||||
|
containerStyle={localStyle.container}
|
||||||
|
source={{uri: this.props.iconUrl}}
|
||||||
|
onPress={this.props.onPress ? this.props.onPress : this._defaultOnPress.bind(this)}
|
||||||
|
activeOpacity={0.7}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const localStyle = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
marginTop: 3,
|
||||||
|
marginRight: 8,
|
||||||
|
}
|
||||||
|
});
|
|
@ -38,9 +38,8 @@ const styles = StyleSheet.create({
|
||||||
loading: {
|
loading: {
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
|
||||||
width: width,
|
width: width,
|
||||||
height: 400,
|
top: 200,
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center"
|
alignItems: "center"
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,8 +71,6 @@ export default class LoginForm extends Component {
|
||||||
|
|
||||||
render() {return (
|
render() {return (
|
||||||
<View>
|
<View>
|
||||||
|
|
||||||
|
|
||||||
<Text style={localStyle.title}>{'欢迎来到胶囊日记'}</Text>
|
<Text style={localStyle.title}>{'欢迎来到胶囊日记'}</Text>
|
||||||
|
|
||||||
<View style={localStyle.form}>
|
<View style={localStyle.form}>
|
||||||
|
|
22
src/component/touchable.js
Normal file
22
src/component/touchable.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import React from "react";
|
||||||
|
import {Platform, TouchableNativeFeedback, TouchableOpacity, View} from "react-native";
|
||||||
|
|
||||||
|
|
||||||
|
let TouchableIOS = (props) => {
|
||||||
|
return <TouchableOpacity activeOpacity={0.7} {...props}/>
|
||||||
|
};
|
||||||
|
|
||||||
|
let TouchableAndroid = (props) => {
|
||||||
|
return (
|
||||||
|
<TouchableNativeFeedback background={TouchableNativeFeedback.SelectableBackground()} useForeground={true} {...props}>
|
||||||
|
<View>
|
||||||
|
{props.children}
|
||||||
|
</View>
|
||||||
|
</TouchableNativeFeedback>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
const Touchable = Platform.OS === 'android' ? TouchableAndroid : TouchableIOS;
|
||||||
|
Touchable.propTypes = (Platform.OS === 'android' ? null : TouchableOpacity.propTypes);
|
||||||
|
|
||||||
|
export default Touchable
|
39
src/entity/homeListData.js
Normal file
39
src/entity/homeListData.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import Api from '../util/api'
|
||||||
|
|
||||||
|
|
||||||
|
const PAGE_SIZE = 21;
|
||||||
|
|
||||||
|
export default class HomeListData {
|
||||||
|
|
||||||
|
list: [];
|
||||||
|
last_id: 0;
|
||||||
|
|
||||||
|
async refresh() {
|
||||||
|
this.last_id = 0;
|
||||||
|
|
||||||
|
let data = await Api.getTodayDiaries(0, PAGE_SIZE, this.last_id);
|
||||||
|
let more = data.diaries.length === PAGE_SIZE;
|
||||||
|
this.list = data.diaries.slice(0, PAGE_SIZE - 1);
|
||||||
|
this.last_id = more ? data.diaries[PAGE_SIZE - 1].id : 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
list: this.list,
|
||||||
|
more
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async load_more() {
|
||||||
|
let data = await Api.getTodayDiaries(0, PAGE_SIZE, this.last_id);
|
||||||
|
let more = data.diaries.length === PAGE_SIZE;
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,50 +1,119 @@
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import {Platform, StyleSheet, Text, View} from 'react-native';
|
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 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';
|
||||||
|
|
||||||
|
|
||||||
|
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 {
|
export default class HomePage extends Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
onPress() {
|
this.dataSource = new HomeListData();
|
||||||
/*
|
this.state = {
|
||||||
this.props.navigator.push({
|
isLoading: true,
|
||||||
screen: 'Home'
|
diaries: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
InteractionManager.runAfterInteractions(() => {
|
||||||
|
this.refresh()
|
||||||
|
.then(data => {
|
||||||
|
console.log('homepage data:', data);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
isLoading: false,
|
||||||
|
diaries: data && data.list ? data.list : []
|
||||||
});
|
});
|
||||||
|
|
||||||
Navigation.startSingleScreenApp({
|
}).catch(e => {
|
||||||
screen: {
|
if (e.code && e.code === 401) {
|
||||||
screen: 'Home',
|
this.props.navigator.showModal({
|
||||||
title: 'Home Title',
|
screen: "App"
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
diaries: []
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async refresh() {
|
||||||
|
return await this.dataSource.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onDiaryPress(diary) {
|
||||||
|
/*
|
||||||
|
this.props.navigator.push({
|
||||||
|
screen: 'DiaryDetail',
|
||||||
|
title: '日记详情',
|
||||||
|
passProps: { diary: diary }
|
||||||
|
});
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={localStyle.wrap}>
|
||||||
<Text style={styles.welcome}>Home Page !</Text>
|
<Loading visible={this.state.isLoading}></Loading>
|
||||||
|
|
||||||
|
<FlatList style={localStyle.list}
|
||||||
|
|
||||||
|
data={this.state.diaries}
|
||||||
|
|
||||||
|
keyExtractor={(item, index) => {
|
||||||
|
return item.id.toString()
|
||||||
|
}}
|
||||||
|
|
||||||
|
renderItem={({item}) => {
|
||||||
|
return (
|
||||||
|
<Touchable onPress={() => this._onDiaryPress(item)}>
|
||||||
|
<DiaryFull diary={item}></DiaryFull>
|
||||||
|
</Touchable>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
|
||||||
|
ItemSeparatorComponent={({highlighted}) => <Divider style={{backgroundColor: '#eee'}}/>}
|
||||||
|
|
||||||
|
automaticallyAdjustContentInsets={true}
|
||||||
|
onEndReachedThreshold={2}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const localStyle = StyleSheet.create({
|
||||||
container: {
|
wrap: {
|
||||||
|
position: 'absolute',
|
||||||
|
backgroundColor: '#fff',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: 'center',
|
top: 0,
|
||||||
alignItems: 'center',
|
bottom: 0,
|
||||||
backgroundColor: '#F5FCFF',
|
paddingTop: isIpx || isAndroid ? 44 : 20
|
||||||
},
|
|
||||||
welcome: {
|
|
||||||
fontSize: 20,
|
|
||||||
textAlign: 'center',
|
|
||||||
margin: 10,
|
|
||||||
},
|
|
||||||
instructions: {
|
|
||||||
textAlign: 'center',
|
|
||||||
color: '#333333',
|
|
||||||
marginBottom: 5,
|
|
||||||
},
|
},
|
||||||
|
list: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
height: '100%'
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -35,6 +35,19 @@ async function getSelfInfo() {
|
||||||
return call('GET', '/users/my')
|
return call('GET', '/users/my')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getTodayDiaries(page = 1, page_size = 20, first_id = '') {
|
||||||
|
return call('GET', '/diaries/today?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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
|
@ -58,16 +71,6 @@ async function call(method, api, body, _timeout = 10000) {
|
||||||
, _timeout);
|
, _timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
function timeout(promise, time) {
|
|
||||||
return Promise.race([
|
|
||||||
promise,
|
|
||||||
new Promise(function (resolve, reject) {
|
|
||||||
setTimeout(() => reject(new Error('request timeout')), time)
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function checkStatus(response) {
|
async function checkStatus(response) {
|
||||||
if (response.status >= 200 && response.status < 300) {
|
if (response.status >= 200 && response.status < 300) {
|
||||||
return response;
|
return response;
|
||||||
|
@ -94,6 +97,15 @@ async function checkStatus(response) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function timeout(promise, time) {
|
||||||
|
return Promise.race([
|
||||||
|
promise,
|
||||||
|
new Promise(function (resolve, reject) {
|
||||||
|
setTimeout(() => reject(new Error('request timeout')), time)
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
function parseJSON(response) {
|
function parseJSON(response) {
|
||||||
if (response.headers.get('content-type') === 'application/json') {
|
if (response.headers.get('content-type') === 'application/json') {
|
||||||
return response.json();
|
return response.json();
|
||||||
|
@ -113,5 +125,6 @@ function handleCatch(err) {
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
login
|
login,
|
||||||
|
getTodayDiaries
|
||||||
}
|
}
|
Loading…
Reference in a new issue