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
3c8d338345
commit
d6331e6447
27 changed files with 1518 additions and 115 deletions
5
App.js
5
App.js
|
@ -13,7 +13,10 @@ import {
|
|||
Animated,
|
||||
LayoutAnimation,
|
||||
InteractionManager,
|
||||
Alert, StatusBar, DeviceEventEmitter, Linking
|
||||
Alert,
|
||||
StatusBar,
|
||||
DeviceEventEmitter,
|
||||
Linking
|
||||
} from 'react-native';
|
||||
import {Input} from "react-native-elements";
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
|
83
index.js
83
index.js
|
@ -19,6 +19,60 @@ for(let pageName in PageList) {
|
|||
Navigation.registerComponent(pageName, () => PageList[pageName]);
|
||||
}
|
||||
|
||||
function loginByAccount() {
|
||||
Navigation.setRoot({
|
||||
root: {
|
||||
stack: {
|
||||
children: [{
|
||||
component: {
|
||||
name: 'Timepill',
|
||||
options: {
|
||||
topBar: {
|
||||
visible: false,
|
||||
|
||||
// hide top bar for android
|
||||
drawBehind: true,
|
||||
animate: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loginByPassword() {
|
||||
Navigation.setRoot({
|
||||
root: {
|
||||
stack: {
|
||||
children: [{
|
||||
component: {
|
||||
name: 'Password',
|
||||
options: {
|
||||
topBar: {
|
||||
title: {
|
||||
text: '请输入密码'
|
||||
}
|
||||
},
|
||||
bottomTabs: {
|
||||
visible: false,
|
||||
|
||||
// hide bottom tab for android
|
||||
drawBehind: true,
|
||||
animate: true
|
||||
}
|
||||
},
|
||||
passProps: {
|
||||
type: 'login'
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Navigation.events().registerAppLaunchedListener(async () => {
|
||||
|
||||
try {
|
||||
|
@ -30,29 +84,16 @@ Navigation.events().registerAppLaunchedListener(async () => {
|
|||
let token = await Token.getUserToken();
|
||||
// let token;
|
||||
if(!token) {
|
||||
Navigation.setRoot({
|
||||
root: {
|
||||
stack: {
|
||||
children: [{
|
||||
component: {
|
||||
name: 'Timepill',
|
||||
options: {
|
||||
topBar: {
|
||||
visible: false,
|
||||
|
||||
// hide top bar for android
|
||||
drawBehind: true,
|
||||
animate: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
loginByAccount();
|
||||
|
||||
} else {
|
||||
Navigation.setRoot(BottomNav.config());
|
||||
const password = await Token.getLoginPassword();
|
||||
if(password) {
|
||||
loginByPassword();
|
||||
|
||||
} else {
|
||||
Navigation.setRoot(BottomNav.config());
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
|
BIN
src/Icon.png
Normal file
BIN
src/Icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.6 KiB |
61
src/component/diary/diaryAction.js
Normal file
61
src/component/diary/diaryAction.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
import React, {Component} from 'react';
|
||||
import {DeviceEventEmitter, Alert} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import Api from '../../util/api';
|
||||
import Msg from '../../util/msg';
|
||||
import Event from '../../util/event';
|
||||
|
||||
|
||||
function action(componentId, diary, callbacks) {
|
||||
ActionSheet.showActionSheetWithOptions({
|
||||
options:['修改','删除', '取消'],
|
||||
cancelButtonIndex: 2,
|
||||
destructiveButtonIndex: 1
|
||||
|
||||
}, (index) => {
|
||||
if(index === 0) {
|
||||
Navigation.push(componentId, {
|
||||
component: {
|
||||
name: 'Write',
|
||||
options: {
|
||||
bottomTabs: {
|
||||
visible: false,
|
||||
|
||||
// hide bottom tab for android
|
||||
drawBehind: true,
|
||||
animate: true
|
||||
}
|
||||
},
|
||||
passProps: {
|
||||
diary: diary
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} else if (index === 1) {
|
||||
Alert.alert('提示', '确认删除日记?', [
|
||||
{text: '删除', style: 'destructive', onPress: () => {
|
||||
Api.deleteDiary(diary.id)
|
||||
.then(() => {
|
||||
DeviceEventEmitter.emit(Event.updateDiarys, 'del');
|
||||
|
||||
Msg.showMsg('日记已删除');
|
||||
if(callbacks && callbacks.onDelete){
|
||||
callbacks.onDelete();
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
Msg.showMsg('日记删除失败' + e.message);
|
||||
})
|
||||
.done();
|
||||
}},
|
||||
{text: '取消', onPress: () => {}},
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
action
|
||||
}
|
|
@ -7,6 +7,8 @@ import Color from '../../style/color';
|
|||
import UserIcon from '../userIcon';
|
||||
import Photo from '../photo';
|
||||
|
||||
import DiaryAction from './diaryAction';
|
||||
|
||||
|
||||
export default class DiaryBrief extends Component {
|
||||
|
||||
|
@ -26,6 +28,10 @@ export default class DiaryBrief extends Component {
|
|||
return this.showField.indexOf(field) >= 0;
|
||||
}
|
||||
|
||||
onDiaryAction() {
|
||||
DiaryAction.action(this.props.componentId, this.diary);
|
||||
}
|
||||
|
||||
render() {
|
||||
let diary = this.diary;
|
||||
if(!diary) {
|
||||
|
@ -79,7 +85,7 @@ export default class DiaryBrief extends Component {
|
|||
<View style={{flex: 1}} />
|
||||
{
|
||||
this.editable
|
||||
? <TouchableOpacity onPress={this.props.onDiaryAction}>
|
||||
? <TouchableOpacity onPress={this.onDiaryAction.bind(this)}>
|
||||
<Ionicons name="ios-more" size={24}
|
||||
color={Color.inactiveText}
|
||||
style={localStyle.moreIcon} />
|
||||
|
|
|
@ -90,62 +90,12 @@ export default class DiaryList extends Component {
|
|||
diary: diary,
|
||||
user: diary.user,
|
||||
|
||||
editable: this.editable,
|
||||
onDiaryAction: this._onDiaryAction.bind(this)
|
||||
editable: this.editable
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_onDiaryAction(diary) {
|
||||
ActionSheet.showActionSheetWithOptions({
|
||||
options:['修改','删除', '取消'],
|
||||
cancelButtonIndex: 2,
|
||||
destructiveButtonIndex: 1
|
||||
|
||||
}, (index) => {
|
||||
if(index === 0) {
|
||||
Navigation.push(this.props.componentId, {
|
||||
component: {
|
||||
name: 'Write',
|
||||
options: {
|
||||
bottomTabs: {
|
||||
visible: false,
|
||||
|
||||
// hide bottom tab for android
|
||||
drawBehind: true,
|
||||
animate: true
|
||||
}
|
||||
},
|
||||
passProps: {
|
||||
diary: diary
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} else if (index === 1) {
|
||||
Alert.alert('提示', '确认删除日记?', [
|
||||
{text: '删除', style: 'destructive', onPress: () => {
|
||||
Api.deleteDiary(diary.id)
|
||||
.then(() => {
|
||||
let filterDiaries = this.state.diaries.filter((it) => it.id !== diary.id);
|
||||
this.setState({
|
||||
diaries: filterDiaries
|
||||
});
|
||||
|
||||
Msg.showMsg('日记已删除');
|
||||
})
|
||||
.catch(e => {
|
||||
Msg.showMsg('日记删除失败');
|
||||
})
|
||||
.done();
|
||||
}},
|
||||
{text: '取消', onPress: () => {}},
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_onPhotoPress(photoUrl) {
|
||||
Navigation.push(this.props.componentId, {
|
||||
component: {
|
||||
|
@ -253,12 +203,12 @@ export default class DiaryList extends Component {
|
|||
renderItem={({item}) => {
|
||||
return (
|
||||
<Touchable onPress={() => this._onDiaryPress(item)}>
|
||||
<DiaryBrief diary={item}
|
||||
<DiaryBrief {...this.props}
|
||||
diary={item}
|
||||
showField={this.props.showField}
|
||||
editable={this.editable}
|
||||
|
||||
onUserIconPress={() => this._onUserIconPress(item)}
|
||||
onDiaryAction={() => this._onDiaryAction(item)}
|
||||
onPhotoPress={() => this._onPhotoPress(item.photoUrl)}
|
||||
>
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ async function resize(uri, oWidth, oHeight, maxPixel) {
|
|||
height = Math.sqrt(oHeight * maxPixel / oWidth);
|
||||
}
|
||||
|
||||
const newUri = await ImageResizer.createResizedImage(uri, width, height);
|
||||
const newUri = await ImageResizer.createResizedImage(uri, width, height, 'JPEG', 75);
|
||||
return 'file://' + newUri.uri;
|
||||
}
|
||||
|
||||
|
|
|
@ -169,7 +169,8 @@ export default class NotebookDiaryList extends Component {
|
|||
},
|
||||
passProps: {
|
||||
diary: diary,
|
||||
editable: this.editable
|
||||
editable: this.editable,
|
||||
expired: this.notebook.isExpired
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
142
src/component/passwordInput.js
Normal file
142
src/component/passwordInput.js
Normal file
|
@ -0,0 +1,142 @@
|
|||
import React, {Component} from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
View,
|
||||
TextInput,
|
||||
TouchableHighlight,
|
||||
InteractionManager,
|
||||
Keyboard,
|
||||
Alert
|
||||
} from 'react-native';
|
||||
|
||||
import Api from '../util/api'
|
||||
|
||||
|
||||
export default class PasswordInput extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
text: ''
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
this._onPress();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
componentWillUnMount() {
|
||||
Keyboard.dismiss();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.setState({
|
||||
text: ''
|
||||
})
|
||||
}
|
||||
|
||||
_onPress() {
|
||||
|
||||
setTimeout(() => {
|
||||
this.inputText.focus();
|
||||
}, 500);
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TouchableHighlight activeOpacity={1} underlayColor='transparent'
|
||||
onPress={this._onPress.bind(this)}
|
||||
>
|
||||
<View style={[localStyle.container, this.props.style]}>
|
||||
<TextInput ref={(r) => this.inputText = r}
|
||||
style={localStyle.textInput}
|
||||
|
||||
maxLength={this.props.maxLength}
|
||||
autoFocus={false}
|
||||
keyboardType={Api.IS_IOS ? "number-pad" : 'numeric'}
|
||||
blurOnSubmit={false}
|
||||
|
||||
value={this.state.text}
|
||||
onChangeText={
|
||||
(text) => {
|
||||
this.setState({text});
|
||||
if(text.length === this.props.maxLength) {
|
||||
this.props.onEnd(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
/>
|
||||
{
|
||||
this._getInputItem()
|
||||
}
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
_getInputItem() {
|
||||
let inputItem = [];
|
||||
let text = this.state.text;
|
||||
|
||||
for(let i=0; i<parseInt(this.props.maxLength); i++) {
|
||||
if(i == 0) {
|
||||
inputItem.push(
|
||||
<View key={i} style={[localStyle.inputItem, this.props.inputItemStyle]}>
|
||||
{i < text.length ? <View style={[localStyle.iconStyle, this.props.iconStyle]}/> : null}
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
inputItem.push(
|
||||
<View key={i} style={[localStyle.inputItem, localStyle.inputItemBorderLeftWidth, this.props.inputItemStyle]}>
|
||||
{i < text.length ? <View style={[localStyle.iconStyle, this.props.iconStyle]}/> : null}
|
||||
</View>)
|
||||
}
|
||||
}
|
||||
|
||||
return inputItem;
|
||||
}
|
||||
}
|
||||
|
||||
const localStyle = StyleSheet.create({
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
borderWidth: 1,
|
||||
borderColor: '#ccc',
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: 5
|
||||
},
|
||||
textInput: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: 1,
|
||||
height:1,
|
||||
padding: 0,
|
||||
margin: 0
|
||||
},
|
||||
inputItem: {
|
||||
height: 45,
|
||||
width: 45,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'white'
|
||||
},
|
||||
inputItemBorderLeftWidth: {
|
||||
borderLeftWidth: 1,
|
||||
borderColor: '#ccc'
|
||||
},
|
||||
iconStyle: {
|
||||
width: 16,
|
||||
height: 16,
|
||||
backgroundColor: '#222',
|
||||
borderRadius: 8
|
||||
}
|
||||
});
|
|
@ -5,6 +5,13 @@ import {Avatar} from "react-native-elements";
|
|||
|
||||
export default class UserIcon extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
iconUrl : props.iconUrl
|
||||
};
|
||||
}
|
||||
|
||||
_defaultOnPress() {
|
||||
// empty
|
||||
}
|
||||
|
@ -15,7 +22,7 @@ export default class UserIcon extends Component {
|
|||
containerStyle={localStyle.container}
|
||||
width={this.props.width || 40}
|
||||
height={this.props.height || 40}
|
||||
source={{uri: this.props.iconUrl}}
|
||||
source={{uri: this.state.iconUrl}}
|
||||
onPress={this.props.onPress ? this.props.onPress : this._defaultOnPress.bind(this)}
|
||||
activeOpacity={0.7}
|
||||
/>
|
||||
|
|
|
@ -23,14 +23,19 @@ export default class UserIntro extends Component {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
this.loadUser();
|
||||
});
|
||||
Api.getSelfInfoByStore()
|
||||
.then(user => {
|
||||
this.selfInfo = user;
|
||||
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
this.refresh();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
loadUser() {
|
||||
let user = this.state.user;
|
||||
(user ? Api.getUserInfo(user.id) : Api.getSelfInfoByStore())
|
||||
refresh() {
|
||||
let userId = this.state.user ? this.state.user.id : this.selfInfo.id;
|
||||
Api.getUserInfo(userId)
|
||||
.then(user => {
|
||||
this.setState({
|
||||
user: user
|
||||
|
|
60
src/page/AboutPage.js
Normal file
60
src/page/AboutPage.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
import React, { Component } from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
View,
|
||||
Text,
|
||||
Image,
|
||||
DeviceEventEmitter
|
||||
} from 'react-native';
|
||||
|
||||
import Api from '../util/api';
|
||||
import TokenManager from '../util/token';
|
||||
import Event from '../util/event';
|
||||
import Color from '../style/color';
|
||||
import UpdateInfo from '../updateInfo';
|
||||
|
||||
|
||||
export default class AboutPage extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
info: null,
|
||||
news: UpdateInfo
|
||||
};
|
||||
}
|
||||
|
||||
static options(passProps) {
|
||||
return {
|
||||
topBar: {
|
||||
title: {
|
||||
text: '关于'
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
TokenManager.setUpdateVersion(UpdateInfo.version)
|
||||
.then(() => {
|
||||
DeviceEventEmitter.emit(Event.updateNewsRead);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const label = this.state.info ? ` (${this.state.info.label})` : null;
|
||||
|
||||
return (
|
||||
<View style={{flex: 1, backgroundColor: 'white'}}>
|
||||
<View style={{flex: 1, padding: 15, alignItems: 'center', paddingTop: 80}}>
|
||||
<Image source={require('../Icon.png')}
|
||||
style={{width: 128, height: 128, borderRadius: 28, borderWidth: 1, borderColor:"#d9d9d9"}} />
|
||||
<Text style={{paddingTop: 20, paddingBottom: 60}}>版本: {Api.VERSION}{label}</Text>
|
||||
<Text style={{color: Color.inactiveText}}>{this.state.news.date} 更新日志</Text>
|
||||
<Text style={{lineHeight: 20}}>{this.state.news.info}</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -18,7 +18,8 @@ import Api from '../util/api'
|
|||
import Event from '../util/event';
|
||||
|
||||
import DiaryFull from '../component/diary/diaryFull';
|
||||
import CommentInput from '../component/comment/commentInput'
|
||||
import DiaryAction from '../component/diary/diaryAction';
|
||||
import CommentInput from '../component/comment/commentInput';
|
||||
|
||||
|
||||
export default class DiaryDetailPage extends Component {
|
||||
|
@ -35,31 +36,40 @@ export default class DiaryDetailPage extends Component {
|
|||
user: props.user,
|
||||
|
||||
editable: props.editable || false,
|
||||
expired: props.expired || false,
|
||||
needScrollToBottom: false
|
||||
}
|
||||
|
||||
this.onDiaryAction = props.onDiaryAction || (() => {});
|
||||
}
|
||||
|
||||
static options(passProps) {
|
||||
return {
|
||||
topBar: {
|
||||
title: {
|
||||
text: '日记详情'
|
||||
},
|
||||
rightButtons: [{
|
||||
id: 'navButtonMore',
|
||||
icon: Icon.navButtonMore,
|
||||
|
||||
color: Color.primary // android
|
||||
}]
|
||||
let topBar = {
|
||||
title: {
|
||||
text: '日记详情'
|
||||
}
|
||||
}
|
||||
|
||||
if(!passProps.expired) {
|
||||
topBar.rightButtons = [{
|
||||
id: 'navButtonMore',
|
||||
icon: Icon.navButtonMore,
|
||||
|
||||
color: Color.primary // android
|
||||
}]
|
||||
}
|
||||
|
||||
return {
|
||||
topBar
|
||||
};
|
||||
}
|
||||
|
||||
navigationButtonPressed({buttonId}) {
|
||||
if(this.state.editable) {
|
||||
this.onDiaryAction(this.state.diary);
|
||||
if(this.state.editable || this.state.diary.user_id == this.state.selfInfo.id) {
|
||||
let componentId = this.props.componentId;
|
||||
DiaryAction.action(componentId, this.state.diary, {
|
||||
onDelete: () => {
|
||||
Navigation.pop(componentId);
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
ActionSheet.showActionSheetWithOptions({
|
||||
|
@ -90,7 +100,9 @@ export default class DiaryDetailPage extends Component {
|
|||
}).done();
|
||||
|
||||
this.diaryListener = DeviceEventEmitter.addListener(Event.updateDiarys, (param) => {
|
||||
this.refreshDiary();
|
||||
if(param != 'del') {
|
||||
this.refreshDiary();
|
||||
}
|
||||
});
|
||||
|
||||
this.commentListener = DeviceEventEmitter.addListener(Event.updateComments, (param) => {
|
||||
|
@ -131,6 +143,15 @@ export default class DiaryDetailPage extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
if(!this.state.selfInfo || !this.state.diary) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let isMine = false;
|
||||
if(this.state.selfInfo.id == this.state.diary.user_id) {
|
||||
isMine = true;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={localStyle.wrap}>
|
||||
<ScrollView ref={(r)=>this.scroll = r}
|
||||
|
@ -144,16 +165,16 @@ export default class DiaryDetailPage extends Component {
|
|||
>
|
||||
|
||||
<DiaryFull ref={(r) => this.diaryFull = r}
|
||||
{...this.props}
|
||||
diary={this.state.diary}
|
||||
refreshData={() => this.state.diary}
|
||||
editable={this.state.editable}
|
||||
{...this.props}
|
||||
editable={this.state.editable || isMine}
|
||||
></DiaryFull>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
{
|
||||
this.state.selfInfo && this.state.diary ? (
|
||||
!this.state.expired ? (
|
||||
<CommentInput ref={(r) => this.commentInput = r}
|
||||
diary={this.state.diary}
|
||||
selfInfo={this.state.selfInfo}
|
||||
|
|
70
src/page/FeedbackPage.js
Normal file
70
src/page/FeedbackPage.js
Normal file
|
@ -0,0 +1,70 @@
|
|||
import React, { Component } from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
View,
|
||||
Text,
|
||||
DeviceEventEmitter,
|
||||
TextInput
|
||||
} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
import {Button} from "react-native-elements";
|
||||
|
||||
import Color from '../style/color';
|
||||
import Api from '../util/api';
|
||||
import Msg from '../util/msg';
|
||||
|
||||
|
||||
export default class FeedbackPage extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
content: ''
|
||||
}
|
||||
}
|
||||
|
||||
static options(passProps) {
|
||||
return {
|
||||
topBar: {
|
||||
title: {
|
||||
text: '意见反馈'
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
send() {
|
||||
Api.feedback(this.state.content)
|
||||
.then(() => {
|
||||
Msg.showMsg('感谢反馈 :)');
|
||||
Navigation.pop(this.props.componentId);
|
||||
})
|
||||
.catch(e => {
|
||||
Msg.showMsg('反馈失败');
|
||||
})
|
||||
.done();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={{flex: 1, backgroundColor: Color.spaceBackground}}>
|
||||
<TextInput
|
||||
style={{padding: 10, height: 240, margin: 10, backgroundColor: '#fff', textAlignVertical:'top'}}
|
||||
underlineColorAndroid='transparent'
|
||||
selectionColor={Color.light}
|
||||
autoCorrect={false}
|
||||
|
||||
multiline={true}
|
||||
maxLength={1000}
|
||||
|
||||
value={this.state.content}
|
||||
onChangeText={(text) => this.setState({content: text})}
|
||||
/>
|
||||
<Button borderRadius={999} title={'发送'} backgroundColor={Color.primary}
|
||||
onPress={this.send.bind(this)}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -49,7 +49,6 @@ export default class FollowPage extends Component {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import React, {Component} from 'react';
|
||||
import {StyleSheet, Text, View, ScrollView} from 'react-native';
|
||||
import {StyleSheet, Text, View, DeviceEventEmitter} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import Color from '../style/color';
|
||||
import {Icon} from '../style/icon';
|
||||
import Event from '../util/event';
|
||||
import NotebookDiaryList from '../component/notebook/notebookDiaryList';
|
||||
|
||||
|
||||
|
@ -46,10 +47,21 @@ export default class NotebookDetailPage extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.diaryListener = DeviceEventEmitter.addListener(Event.updateDiarys, (param) => {
|
||||
this.diaryList.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.diaryListener.remove();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<NotebookDiaryList notebook={this.props.notebook}
|
||||
<NotebookDiaryList ref={(r) => this.diaryList = r}
|
||||
notebook={this.props.notebook}
|
||||
{...this.props}>
|
||||
</NotebookDiaryList>
|
||||
</View>
|
||||
|
|
208
src/page/PasswordPage.js
Normal file
208
src/page/PasswordPage.js
Normal file
|
@ -0,0 +1,208 @@
|
|||
import React, { Component } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
Alert,
|
||||
DeviceEventEmitter,
|
||||
TouchableOpacity,
|
||||
Keyboard
|
||||
} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import Color from '../style/color';
|
||||
import Api from '../util/api'
|
||||
import TokenManager from '../util/token';
|
||||
import Event from '../util/event';
|
||||
import Msg from '../util/msg';
|
||||
|
||||
import BottomNav from '../nav/bottomNav';
|
||||
import PasswordInput from "../component/passwordInput";
|
||||
|
||||
|
||||
export default class PasswordPage extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
title: '',
|
||||
oldPassword: null,
|
||||
step: 1,
|
||||
|
||||
password: null
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
TokenManager.getLoginPassword()
|
||||
.then(pwd => {
|
||||
if(this.props.type == 'setting') {
|
||||
if(this.props.operation == 'setnew') {
|
||||
this.setState({
|
||||
title: '请输入新密码'
|
||||
});
|
||||
|
||||
} else if(this.props.operation == 'cancel'){
|
||||
this.setState({
|
||||
title: '请输入密码',
|
||||
oldPassword: pwd
|
||||
});
|
||||
}
|
||||
|
||||
} else if(this.props.type == 'login') {
|
||||
this.setState({
|
||||
oldPassword: pwd
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_onEnd(password) {
|
||||
if(this.props.type == 'setting') {
|
||||
if(this.props.operation == 'setnew') {
|
||||
this._setting(password);
|
||||
|
||||
} else if(this.props.operation == 'cancel') {
|
||||
this._clearPassword(password);
|
||||
}
|
||||
|
||||
} else if(this.props.type == 'login') {
|
||||
this._login(password);
|
||||
}
|
||||
}
|
||||
|
||||
_setting(password) {
|
||||
if (this.state.step == 1) {
|
||||
if(!password.match(/^\d+$/)) {
|
||||
Alert.alert('提示', '只能设置数字密码');
|
||||
|
||||
} else {
|
||||
|
||||
this.setState({
|
||||
title: '请再次输入密码',
|
||||
password: password,
|
||||
step: 2
|
||||
});
|
||||
}
|
||||
|
||||
this.refs.input.clear();
|
||||
|
||||
} else if (this.state.step == 2) {
|
||||
if(this.state.password !== password) {
|
||||
Alert.alert('设置失败', '两次输入的密码不相同,请重新输入');
|
||||
this.refs.input.clear();
|
||||
|
||||
this.setState({
|
||||
title: '请输入新密码',
|
||||
password: null,
|
||||
step: 1
|
||||
});
|
||||
|
||||
} else {
|
||||
TokenManager.setLoginPassword(password)
|
||||
.then(() => {
|
||||
Keyboard.dismiss();
|
||||
Msg.showMsg('密码已设置');
|
||||
|
||||
DeviceEventEmitter.emit(Event.passwordUpdated);
|
||||
Navigation.pop(this.props.componentId);
|
||||
})
|
||||
.catch(e => {
|
||||
Alert.alert('错误', '设置密码失败:' + e.message);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_login(password) {
|
||||
if(!this.state.oldPassword) {
|
||||
Alert.alert('错误', '密码加载失败');
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.state.oldPassword === password) {
|
||||
Navigation.setRoot(BottomNav.config());
|
||||
|
||||
} else {
|
||||
Alert.alert('失败', '密码错误');
|
||||
}
|
||||
|
||||
this.refs.input.clear();
|
||||
}
|
||||
|
||||
_clearPassword(password) {
|
||||
if(!this.state.oldPassword) {
|
||||
Alert.alert('错误', '密码加载失败');
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.state.oldPassword !== password) {
|
||||
Alert.alert('提示', '密码不正确');
|
||||
this.refs.input.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
TokenManager.setLoginPassword('')
|
||||
.then(() => {
|
||||
Keyboard.dismiss();
|
||||
Msg.showMsg('密码已清除');
|
||||
|
||||
DeviceEventEmitter.emit(Event.passwordUpdated);
|
||||
Navigation.pop(this.props.componentId);
|
||||
|
||||
}).catch(() => {
|
||||
Alert.alert('错误', '清除密码失败');
|
||||
})
|
||||
}
|
||||
|
||||
toLogin() {
|
||||
Navigation.setRoot({
|
||||
root: {
|
||||
stack: {
|
||||
children: [{
|
||||
component: {
|
||||
name: 'Timepill',
|
||||
options: {
|
||||
topBar: {
|
||||
visible: false,
|
||||
|
||||
// hide top bar for android
|
||||
drawBehind: true,
|
||||
animate: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={{flex: 1, backgroundColor: '#EFEFF4'}}>
|
||||
<View style={{flex: 1, alignItems: 'center', marginTop: 60}}>
|
||||
<Text style={{fontSize: 24}}>{this.state.title}</Text>
|
||||
<PasswordInput ref='input' style={{marginTop: 50}} maxLength={4} onEnd={this._onEnd.bind(this)}/>
|
||||
{
|
||||
this.props.type == 'setting' && this.props.operation != 'cancel'
|
||||
? (
|
||||
<Text style={{marginTop: 50, fontSize: 11, color: Color.inactiveText}}>提示: 从后台切切换前台时不需要输入密码</Text>
|
||||
) : null
|
||||
}
|
||||
{
|
||||
this.props.type == 'setting' ? null : (
|
||||
<View style={{flex: 1, alignItems: 'center', paddingTop: 22}}>
|
||||
<TouchableOpacity onPress={this.toLogin}>
|
||||
<Text style={{fontSize: 14, color: Color.primary, padding: 10}}>
|
||||
忘记密码?通过登录重设
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
272
src/page/SettingPage.js
Normal file
272
src/page/SettingPage.js
Normal file
|
@ -0,0 +1,272 @@
|
|||
import React, {Component} from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
Switch,
|
||||
Alert,
|
||||
Linking,
|
||||
TouchableOpacity,
|
||||
DeviceEventEmitter
|
||||
} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
import Ionicons from 'react-native-vector-icons/Ionicons';
|
||||
|
||||
import Api from '../util/api';
|
||||
import TokenManager from '../util/token';
|
||||
import Color from '../style/color';
|
||||
|
||||
|
||||
export default class SettingPage extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
hasPassword: false,
|
||||
hasUpdateNews: false,
|
||||
|
||||
settings: {}
|
||||
}
|
||||
}
|
||||
|
||||
static options(passProps) {
|
||||
return {
|
||||
topBar: {
|
||||
title: {
|
||||
text: '设置'
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.refreshPasswordState();
|
||||
this.passwordListener = DeviceEventEmitter.addListener('passwordUpdated', this.refreshPasswordState.bind(this));
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.passwordListener.remove();
|
||||
}
|
||||
|
||||
refreshPasswordState() {
|
||||
TokenManager.getLoginPassword()
|
||||
.then((pwd) => this.setState({
|
||||
hasPassword: pwd != null && pwd.length > 0
|
||||
}));
|
||||
};
|
||||
|
||||
changePassword = () => {
|
||||
let titleText = '';
|
||||
if(!this.state.hasPassword) {
|
||||
titleText = '设置启动密码';
|
||||
} else {
|
||||
titleText = '取消启动密码';
|
||||
}
|
||||
|
||||
Navigation.push(this.props.componentId, {
|
||||
component: {
|
||||
name: 'Password',
|
||||
options: {
|
||||
topBar: {
|
||||
title: {
|
||||
text: titleText
|
||||
}
|
||||
},
|
||||
bottomTabs: {
|
||||
visible: false,
|
||||
|
||||
// hide bottom tab for android
|
||||
drawBehind: true,
|
||||
animate: true
|
||||
}
|
||||
},
|
||||
passProps: {
|
||||
type: 'setting',
|
||||
operation: !this.state.hasPassword ? 'setnew' : 'cancel'
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
changePush = (val) => {
|
||||
//
|
||||
}
|
||||
|
||||
jumpTo(pageName) {
|
||||
Navigation.push(this.props.componentId, {
|
||||
component: {
|
||||
name: pageName,
|
||||
options: {
|
||||
bottomTabs: {
|
||||
visible: false,
|
||||
|
||||
// hide bottom tab for android
|
||||
drawBehind: true,
|
||||
animate: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
logout() {
|
||||
Alert.alert('提示','确认退出登录?',[
|
||||
{text: '退出', style: 'destructive', onPress: () => {
|
||||
Api.logout();
|
||||
Navigation.setRoot({
|
||||
root: {
|
||||
stack: {
|
||||
children: [{
|
||||
component: {
|
||||
name: 'Timepill',
|
||||
options: {
|
||||
topBar: {
|
||||
visible: false,
|
||||
|
||||
// hide top bar for android
|
||||
drawBehind: true,
|
||||
animate: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
}},
|
||||
{text: '取消', onPress: () => {}}
|
||||
]);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={localStyle.wrap}>
|
||||
<View style={localStyle.group}>
|
||||
<TouchableOpacity style={localStyle.item} onPress={() => this.jumpTo('UserInfoEdit')}>
|
||||
<Text style={localStyle.title}>修改个人信息</Text>
|
||||
<Ionicons name="ios-arrow-forward" style={localStyle.arrow} size={18} color='#0076FF'/>
|
||||
</TouchableOpacity>
|
||||
<View style={localStyle.line} />
|
||||
|
||||
<View style={localStyle.item}>
|
||||
<Text style={localStyle.title}>启动密码</Text>
|
||||
<Switch value={this.state.hasPassword}
|
||||
trackColor={Api.IS_ANDROID ? Color.textSelect : null}
|
||||
thumbColor={Api.IS_ANDROID && this.state.hasPassword ? Color.light : null}
|
||||
onValueChange={this.changePassword} />
|
||||
</View>
|
||||
<View style={localStyle.line} />
|
||||
|
||||
<View style={localStyle.item}>
|
||||
<Text style={localStyle.title}>提醒推送</Text>
|
||||
<Switch value={this.state.settings['pushMessage']}
|
||||
trackColor={Api.IS_ANDROID ? Color.textSelect : null}
|
||||
thumbColor={Api.IS_ANDROID && this.state.settings['pushMessage'] ? Color.light : null}
|
||||
onValueChange={this.changePush} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={localStyle.group}>
|
||||
{
|
||||
Api.IS_IOS ? (
|
||||
<View>
|
||||
<TouchableOpacity style={localStyle.item}
|
||||
onPress={() =>
|
||||
Linking.openURL("https://itunes.apple.com/us/app/jiao-nang-ri-ji/id1142102323?l=zh&ls=1&mt=8")}
|
||||
>
|
||||
<Text style={localStyle.title}>去 App Store 评价</Text>
|
||||
<Ionicons name="ios-arrow-forward" style={localStyle.arrow} size={18}/>
|
||||
</TouchableOpacity>
|
||||
<View style={localStyle.line} />
|
||||
</View>
|
||||
) : null
|
||||
}
|
||||
|
||||
<TouchableOpacity style={localStyle.item}
|
||||
onPress={() => this.jumpTo('Feedback')}>
|
||||
<Text style={localStyle.title}>意见反馈</Text>
|
||||
<Ionicons name="ios-arrow-forward" style={localStyle.arrow} size={18}/>
|
||||
</TouchableOpacity>
|
||||
<View style={localStyle.line} />
|
||||
|
||||
<TouchableOpacity style={localStyle.item}
|
||||
onPress={() => this.jumpTo('About')}>
|
||||
<Text style={localStyle.title}>关于</Text>
|
||||
{
|
||||
this.state.hasUpdateNews
|
||||
? (
|
||||
<View style={localStyle.badge}>
|
||||
<Text style={localStyle.badgeText}>1</Text>
|
||||
</View>
|
||||
)
|
||||
: null
|
||||
}
|
||||
<Ionicons name="ios-arrow-forward" style={localStyle.arrow} size={18}/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<View style={[localStyle.group, { marginTop: 45 }]}>
|
||||
<TouchableOpacity style={localStyle.item}
|
||||
onPress={this.logout.bind(this)}
|
||||
>
|
||||
<Text style={localStyle.button}>退出登录</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const localStyle = StyleSheet.create({
|
||||
wrap: {
|
||||
flex: 1,
|
||||
backgroundColor: '#EFEFF4'
|
||||
},
|
||||
group: {
|
||||
marginTop: 30,
|
||||
backgroundColor: 'white',
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderColor: '#c8c7cc'
|
||||
},
|
||||
item: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: 15,
|
||||
height: 45
|
||||
},
|
||||
title: {
|
||||
fontSize: 16,
|
||||
color: '#222',
|
||||
flex: 1
|
||||
},
|
||||
line: {
|
||||
marginLeft: 15,
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderColor: '#c8c7cc'
|
||||
},
|
||||
arrow: {
|
||||
paddingTop: 1,
|
||||
color: Color.inactiveText
|
||||
},
|
||||
button: {
|
||||
flex: 1,
|
||||
textAlign: 'center',
|
||||
color: '#d9534f',
|
||||
fontSize: 16
|
||||
},
|
||||
badge: {
|
||||
backgroundColor: 'red',
|
||||
paddingHorizontal:8,
|
||||
paddingVertical: 2,
|
||||
borderRadius: 12,
|
||||
marginRight: 10
|
||||
},
|
||||
badgeText: {
|
||||
color: 'white',
|
||||
fontSize: 12,
|
||||
fontFamily: 'Arial'
|
||||
}
|
||||
});
|
187
src/page/UserInfoEditPage.js
Normal file
187
src/page/UserInfoEditPage.js
Normal file
|
@ -0,0 +1,187 @@
|
|||
import React, { Component } from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
View,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
Image,
|
||||
DeviceEventEmitter,
|
||||
Keyboard
|
||||
} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
import Ionicons from 'react-native-vector-icons/Ionicons';
|
||||
|
||||
import Color from '../style/color';
|
||||
import Token from '../util/token'
|
||||
import Api from '../util/api';
|
||||
import Event from '../util/event';
|
||||
import Msg from '../util/msg';
|
||||
|
||||
import Loading from '../component/loading'
|
||||
import ImageAction from '../component/image/imageAction'
|
||||
|
||||
|
||||
export default class UserInfoEditPage extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
user: null,
|
||||
uploading: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.loadUser();
|
||||
this.userInfoListener = DeviceEventEmitter.addListener(Event.userInfoUpdated, this.loadUser.bind(this));
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.userInfoListener.remove();
|
||||
}
|
||||
|
||||
loadUser() {
|
||||
Api.getSelfInfoByStore()
|
||||
.then(user => {
|
||||
this.setState({user});
|
||||
});
|
||||
}
|
||||
|
||||
editIcon() {
|
||||
ImageAction.action({
|
||||
width: 640,
|
||||
height: 640,
|
||||
cropping: true
|
||||
|
||||
}, -1, 640 * 640, (e, imageUri) => {
|
||||
|
||||
if(e) {
|
||||
Msg.showMsg('操作失败:' + e.message);
|
||||
} else {
|
||||
this.uploadIcon(imageUri);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
uploadIcon(uri) {
|
||||
this.setState({uploading: true});
|
||||
Api.updateUserIcon(uri)
|
||||
.then(async user => {
|
||||
await Token.setUserInfo(user);
|
||||
this.loadUser();
|
||||
|
||||
Msg.showMsg('头像保存成功');
|
||||
DeviceEventEmitter.emit(Event.userInfoUpdated);
|
||||
|
||||
})
|
||||
.catch(e => {
|
||||
Msg.showMsg('头像更新失败:' + e.message);
|
||||
})
|
||||
.done(() => {
|
||||
this.setState({uploading: false});
|
||||
});
|
||||
}
|
||||
|
||||
jumpTo(pageName) {
|
||||
Navigation.push(this.props.componentId, {
|
||||
component: {
|
||||
name: pageName,
|
||||
options: {
|
||||
bottomTabs: {
|
||||
visible: false,
|
||||
|
||||
// hide bottom tab for android
|
||||
drawBehind: true,
|
||||
animate: true
|
||||
}
|
||||
},
|
||||
passProps: {
|
||||
user: this.state.user
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={{flex: 1, backgroundColor: '#EFEFF4'}}>
|
||||
<Loading visible={this.state.uploading}></Loading>
|
||||
{
|
||||
this.state.user ? (
|
||||
<View style={localStyle.group}>
|
||||
<TouchableOpacity style={localStyle.item} onPress={this.editIcon.bind(this)}>
|
||||
<Text style={localStyle.title}>头像</Text>
|
||||
<View style={localStyle.right}>
|
||||
<Image source={{uri: this.state.user.iconUrl}} style={{width: 28, height: 28, borderRadius: 14}} />
|
||||
<Ionicons name="ios-arrow-forward" style={localStyle.arrow} size={18}/>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
<View style={localStyle.line} />
|
||||
|
||||
<TouchableOpacity style={localStyle.item} onPress={() => this.jumpTo('UserNameEdit')}>
|
||||
<Text style={localStyle.title}>名字</Text>
|
||||
<View style={localStyle.right}>
|
||||
<Text style={localStyle.value}>{this.state.user.name}</Text>
|
||||
<Ionicons name="ios-arrow-forward" style={localStyle.arrow} size={18}/>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
<View style={localStyle.line} />
|
||||
|
||||
<TouchableOpacity style={localStyle.item} onPress={() => this.jumpTo('UserIntroEdit')}>
|
||||
<Text style={localStyle.title}>个人简介</Text>
|
||||
<Ionicons name="ios-arrow-forward" style={localStyle.arrow} size={18}/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
) : null
|
||||
}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const localStyle = StyleSheet.create({
|
||||
group: {
|
||||
marginTop: 30,
|
||||
backgroundColor: 'white',
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderColor: '#c8c7cc'
|
||||
},
|
||||
item: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: 15,
|
||||
height: 45
|
||||
},
|
||||
title: {
|
||||
fontSize: 16,
|
||||
color: '#222'
|
||||
},
|
||||
line: {
|
||||
marginLeft: 15,
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderColor: '#c8c7cc'
|
||||
},
|
||||
right: {
|
||||
flexDirection:'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
arrow: {
|
||||
paddingTop: 1,
|
||||
color: Color.inactiveText,
|
||||
paddingLeft: 15
|
||||
},
|
||||
value: {
|
||||
fontSize: 16,
|
||||
color: Color.inactiveText
|
||||
},
|
||||
button: {
|
||||
flex: 1,
|
||||
textAlign: 'center',
|
||||
color: '#d9534f',
|
||||
fontSize: 16
|
||||
}
|
||||
});
|
||||
|
126
src/page/UserIntroEditPage.js
Normal file
126
src/page/UserIntroEditPage.js
Normal file
|
@ -0,0 +1,126 @@
|
|||
import React, { Component } from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
View,
|
||||
Alert,
|
||||
Text,
|
||||
TextInput,
|
||||
DeviceEventEmitter,
|
||||
Keyboard
|
||||
} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import Color from '../style/color';
|
||||
import {Icon} from '../style/icon';
|
||||
import Msg from '../util/msg';
|
||||
import Api from '../util/api';
|
||||
import Token from '../util/token';
|
||||
import Event from '../util/event';
|
||||
|
||||
|
||||
export default class EditIntroPage extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
Navigation.events().bindComponent(this);
|
||||
|
||||
this.state = {
|
||||
intro: props.user.intro
|
||||
}
|
||||
}
|
||||
|
||||
static options(passProps) {
|
||||
return {
|
||||
topBar: {
|
||||
title: {
|
||||
text: '修改简介'
|
||||
},
|
||||
rightButtons: [{
|
||||
id: 'save',
|
||||
icon: Icon.navButtonSave
|
||||
}]
|
||||
},
|
||||
bottomTabs: {
|
||||
visible: false,
|
||||
|
||||
// hide bottom tab for android
|
||||
drawBehind: true,
|
||||
animate: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
setTimeout(() => {
|
||||
this.refs.input.focus();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
navigationButtonPressed({buttonId}) {
|
||||
this.saveUserIntro();
|
||||
}
|
||||
|
||||
saveUserIntro() {
|
||||
const len = this.state.intro.length;
|
||||
if(len === 0) {
|
||||
Alert.alert('提示', '简介不能为空');
|
||||
return;
|
||||
} else if (len > 500) {
|
||||
Alert.alert('提示', '简介不能超过500个字');
|
||||
return;
|
||||
}
|
||||
|
||||
Api.updateUserInfo(this.props.user.name, this.state.intro)
|
||||
.then(async user => {
|
||||
Keyboard.dismiss();
|
||||
Msg.showMsg('修改成功');
|
||||
|
||||
await Token.setUserInfo(user);
|
||||
DeviceEventEmitter.emit(Event.userInfoUpdated);
|
||||
|
||||
Navigation.pop(this.props.componentId);
|
||||
})
|
||||
.catch(e => {
|
||||
Msg.showMsg('修改失败');
|
||||
})
|
||||
.done();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={{flex: 1, backgroundColor: '#EFEFF4'}}>
|
||||
<View style={localStyle.group}>
|
||||
<TextInput ref='input'
|
||||
underlineColorAndroid='transparent'
|
||||
style={localStyle.textInput}
|
||||
selectionColor={Color.primary}
|
||||
|
||||
value={this.state.intro}
|
||||
onChangeText={(text) => this.setState({intro: text})}
|
||||
multiline={true}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const localStyle = StyleSheet.create({
|
||||
group: {
|
||||
marginTop: 30,
|
||||
backgroundColor: 'white',
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderColor: '#c8c7cc'
|
||||
},
|
||||
textInput: {
|
||||
flexGrow: 1,
|
||||
fontSize: 16,
|
||||
margin: 15,
|
||||
color: Color.text,
|
||||
height: 200,
|
||||
textAlignVertical: 'top'
|
||||
}
|
||||
});
|
||||
|
137
src/page/UserNameEditPage.js
Normal file
137
src/page/UserNameEditPage.js
Normal file
|
@ -0,0 +1,137 @@
|
|||
import React, { Component } from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
View,
|
||||
Alert,
|
||||
Text,
|
||||
TextInput,
|
||||
DeviceEventEmitter,
|
||||
Keyboard
|
||||
} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import Color from '../style/color';
|
||||
import {Icon} from '../style/icon';
|
||||
import Msg from '../util/msg';
|
||||
import Api from '../util/api';
|
||||
import Token from '../util/token';
|
||||
import Event from '../util/event';
|
||||
|
||||
|
||||
export default class UserNameEditPage extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
Navigation.events().bindComponent(this);
|
||||
|
||||
this.state = {
|
||||
name: props.user.name
|
||||
}
|
||||
}
|
||||
|
||||
static options(passProps) {
|
||||
return {
|
||||
topBar: {
|
||||
title: {
|
||||
text: '修改名字'
|
||||
},
|
||||
rightButtons: [{
|
||||
id: 'save',
|
||||
icon: Icon.navButtonSave
|
||||
}]
|
||||
},
|
||||
bottomTabs: {
|
||||
visible: false,
|
||||
|
||||
// hide bottom tab for android
|
||||
drawBehind: true,
|
||||
animate: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
setTimeout(() => {
|
||||
this.refs.input.focus();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
navigationButtonPressed({buttonId}) {
|
||||
this.saveUserName();
|
||||
}
|
||||
|
||||
saveUserName() {
|
||||
const len = this.state.name.length;
|
||||
if (len === 0) {
|
||||
Alert.alert('提示', '名字不能为空');
|
||||
return;
|
||||
} else if (len > 10) {
|
||||
Alert.alert('提示', '名字不能超过10个字');
|
||||
return;
|
||||
}
|
||||
|
||||
Api.updateUserInfo(this.state.name, this.props.user.intro)
|
||||
.then(async user => {
|
||||
Keyboard.dismiss();
|
||||
Msg.showMsg('修改成功');
|
||||
|
||||
await Token.setUserInfo(user);
|
||||
DeviceEventEmitter.emit(Event.userInfoUpdated);
|
||||
|
||||
Navigation.pop(this.props.componentId);
|
||||
})
|
||||
.catch(e => {
|
||||
Msg.showMsg('修改失败');
|
||||
})
|
||||
.done();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={{flex: 1, backgroundColor: '#EFEFF4'}}>
|
||||
<View style={localStyle.group}>
|
||||
<View style={localStyle.item}>
|
||||
<Text style={localStyle.title}>名字</Text>
|
||||
<TextInput ref='input'
|
||||
style={localStyle.textInput}
|
||||
underlineColorAndroid='transparent'
|
||||
selectionColor={Color.primary}
|
||||
|
||||
value={this.state.name}
|
||||
onChangeText={(text) => this.setState({name: text})}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const localStyle = StyleSheet.create({
|
||||
group: {
|
||||
marginTop: 30,
|
||||
backgroundColor: 'white',
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderColor: '#c8c7cc'
|
||||
},
|
||||
item: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: 15,
|
||||
height: 45
|
||||
},
|
||||
title: {
|
||||
fontSize: 16,
|
||||
color: '#222'
|
||||
},
|
||||
textInput: {
|
||||
flex: 1,
|
||||
fontSize: 16,
|
||||
marginLeft: 15,
|
||||
padding: 0,
|
||||
height: 24,
|
||||
color: Color.content
|
||||
}
|
||||
});
|
|
@ -9,7 +9,7 @@ import {
|
|||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import Api from '../util/api';
|
||||
import {Icon} from '../style/icon'
|
||||
import {Icon} from '../style/icon';
|
||||
import Event from "../util/event";
|
||||
import Color from '../style/color';
|
||||
|
||||
|
@ -101,6 +101,22 @@ export default class UserPage extends Component {
|
|||
.catch(e => {
|
||||
Alert.alert('取消关注失败');
|
||||
}).done();
|
||||
|
||||
} else if(buttonId == 'setting') {
|
||||
Navigation.push(this.props.componentId, {
|
||||
component: {
|
||||
name: 'Setting',
|
||||
options: {
|
||||
bottomTabs: {
|
||||
visible: false,
|
||||
|
||||
// hide bottom tab for android
|
||||
drawBehind: true,
|
||||
animate: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,11 +146,16 @@ export default class UserPage extends Component {
|
|||
this.diaryListener = DeviceEventEmitter.addListener(Event.updateDiarys, (param) => {
|
||||
this.diaryList.refresh();
|
||||
});
|
||||
|
||||
this.userInfoListener = DeviceEventEmitter.addListener(Event.userInfoUpdated, (param) => {
|
||||
this.userIntro.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.notebookListener.remove();
|
||||
this.diaryListener.remove();
|
||||
this.userInfoListener.remove();
|
||||
}
|
||||
|
||||
_renderLabel = props => ({route}) => {
|
||||
|
@ -166,6 +187,7 @@ export default class UserPage extends Component {
|
|||
|
||||
_renderScene = SceneMap({
|
||||
userIntro: () => <UserIntro
|
||||
ref={(r) => this.userIntro = r}
|
||||
user={this.user}
|
||||
/>,
|
||||
diary: () => <DiaryList
|
||||
|
|
|
@ -23,7 +23,6 @@ import {Icon} from '../style/icon';
|
|||
import Color from '../style/color';
|
||||
import Api from '../util/api';
|
||||
import Msg from '../util/msg';
|
||||
import Token from '../util/token'
|
||||
import Event from "../util/event";
|
||||
|
||||
import NotebookLine from '../component/notebook/notebookLine';
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
export default {
|
||||
About: require("./AboutPage.js").default,
|
||||
DiaryDetail: require("./DiaryDetailPage.js").default,
|
||||
Empty: require("./EmptyPage.js").default,
|
||||
Feedback: require("./FeedbackPage.js").default,
|
||||
Follow: require("./FollowPage.js").default,
|
||||
FollowUser: require("./FollowUserPage.js").default,
|
||||
Home: require("./HomePage.js").default,
|
||||
|
@ -8,7 +10,12 @@ NotebookDetail: require("./NotebookDetailPage.js").default,
|
|||
NotebookEdit: require("./NotebookEditPage.js").default,
|
||||
NotificationHistory: require("./NotificationHistoryPage.js").default,
|
||||
Notification: require("./NotificationPage.js").default,
|
||||
Password: require("./PasswordPage.js").default,
|
||||
Photo: require("./PhotoPage.js").default,
|
||||
Setting: require("./SettingPage.js").default,
|
||||
UserInfoEdit: require("./UserInfoEditPage.js").default,
|
||||
UserIntroEdit: require("./UserIntroEditPage.js").default,
|
||||
UserNameEdit: require("./UserNameEditPage.js").default,
|
||||
User: require("./UserPage.js").default,
|
||||
Write: require("./WritePage.js").default
|
||||
}
|
11
src/updateInfo.js
Normal file
11
src/updateInfo.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
export default {
|
||||
version: 5,
|
||||
date: '4月9日',
|
||||
info: `
|
||||
1.增加草稿本
|
||||
2.修复桌面图标提示点不掉的问题
|
||||
3.优化话题UI
|
||||
4.优化 iPhone X 兼容
|
||||
5.设置页增加关注用户入口
|
||||
6.点击 Tab 图标回到顶部,再次点击刷新列表
|
||||
`}
|
|
@ -18,6 +18,7 @@ const IS_IPHONEX = isIphoneX();
|
|||
|
||||
|
||||
const baseUrl = 'http://open.timepill.net/api';
|
||||
const v2Url = 'http://v2.timepill.net/api';
|
||||
|
||||
|
||||
async function login(username, password) {
|
||||
|
@ -42,6 +43,12 @@ async function login(username, password) {
|
|||
}
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
TokenManager.setUserToken('');
|
||||
TokenManager.setUserInfo(false);
|
||||
TokenManager.setLoginPassword('');
|
||||
}
|
||||
|
||||
async function getSelfInfo() {
|
||||
return call('GET', '/users/my');
|
||||
}
|
||||
|
@ -54,6 +61,20 @@ async function getUserInfo(id) {
|
|||
return call('GET', '/users/' + id)
|
||||
}
|
||||
|
||||
async function updateUserIcon(photoUri) {
|
||||
return upload('POST', '/users/icon', {
|
||||
icon: {uri: photoUri, name: 'image.jpg', type: 'image/jpg'}
|
||||
});
|
||||
}
|
||||
|
||||
async function updateUserInfo(name, intro) {
|
||||
return call('PUT', '/users', {
|
||||
name: name,
|
||||
intro: intro
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
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) => {
|
||||
|
@ -216,6 +237,10 @@ async function report(user_id, diary_id) {
|
|||
});
|
||||
}
|
||||
|
||||
async function feedback(content) {
|
||||
return callV2('POST', '/feedback', {content});
|
||||
}
|
||||
|
||||
|
||||
async function upload(method, api, body) {
|
||||
let token = await TokenManager.getUserToken();
|
||||
|
@ -245,9 +270,8 @@ async function upload(method, api, body) {
|
|||
, 60000);
|
||||
}
|
||||
|
||||
async function call(method, api, body, _timeout = 10000) {
|
||||
async function call(method, api, body = null, _timeout = 10000) {
|
||||
let token = await TokenManager.getUserToken();
|
||||
|
||||
return timeout(fetch(baseUrl + api, {
|
||||
method: method,
|
||||
headers: {
|
||||
|
@ -268,6 +292,31 @@ async function call(method, api, body, _timeout = 10000) {
|
|||
, _timeout);
|
||||
}
|
||||
|
||||
async function callV2(method, api, body = null, _timeout = 10000) {
|
||||
let token = await TokenManager.getToken();
|
||||
return timeout(fetch(v2Url + api, {
|
||||
method: method,
|
||||
headers: {
|
||||
'Authorization': token,
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'X-TP-OS': OS,
|
||||
'X-TP-OS-Version': OS_VERSION,
|
||||
'X-TP-Version': VERSION,
|
||||
'X-Device-ID': DEVICE_ID,
|
||||
},
|
||||
body: body ? JSON.stringify(body) : null
|
||||
})
|
||||
.then((response) => {
|
||||
return response;
|
||||
})
|
||||
.then(checkStatus)
|
||||
.then(parseJSON)
|
||||
.catch(handleCatch)
|
||||
|
||||
,_timeout);
|
||||
}
|
||||
|
||||
async function checkStatus(response) {
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
return response;
|
||||
|
@ -332,8 +381,12 @@ export default {
|
|||
IS_IPHONEX,
|
||||
|
||||
login,
|
||||
logout,
|
||||
getSelfInfoByStore,
|
||||
getUserInfo,
|
||||
|
||||
updateUserIcon,
|
||||
updateUserInfo,
|
||||
|
||||
getTodayDiaries,
|
||||
getFollowDiaries,
|
||||
|
@ -367,5 +420,6 @@ export default {
|
|||
addDiary,
|
||||
updateDiary,
|
||||
|
||||
report
|
||||
report,
|
||||
feedback
|
||||
}
|
|
@ -4,6 +4,8 @@ export default {
|
|||
updateDiarys: 'updateDiarys',
|
||||
updateComments: 'updateComments',
|
||||
|
||||
commentPressed: 'commentPressed'
|
||||
|
||||
commentPressed: 'commentPressed',
|
||||
passwordUpdated: 'passwordUpdated',
|
||||
updateNewsRead: 'updateNewsRead',
|
||||
userInfoUpdated: 'userInfoUpdated'
|
||||
}
|
Loading…
Reference in a new issue