diff --git a/App.js b/App.js
index 760cf7c..f13860a 100644
--- a/App.js
+++ b/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';
diff --git a/index.js b/index.js
index 27b645d..a4e8a09 100644
--- a/index.js
+++ b/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());
+ }
}
});
diff --git a/src/Icon.png b/src/Icon.png
new file mode 100644
index 0000000..fd4b831
Binary files /dev/null and b/src/Icon.png differ
diff --git a/src/component/diary/diaryAction.js b/src/component/diary/diaryAction.js
new file mode 100644
index 0000000..ecd0e64
--- /dev/null
+++ b/src/component/diary/diaryAction.js
@@ -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
+}
\ No newline at end of file
diff --git a/src/component/diary/diaryBrief.js b/src/component/diary/diaryBrief.js
index fe38fbe..4702d86 100644
--- a/src/component/diary/diaryBrief.js
+++ b/src/component/diary/diaryBrief.js
@@ -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 {
{
this.editable
- ?
+ ?
diff --git a/src/component/diary/diaryList.js b/src/component/diary/diaryList.js
index 98ef05e..c469707 100644
--- a/src/component/diary/diaryList.js
+++ b/src/component/diary/diaryList.js
@@ -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 (
this._onDiaryPress(item)}>
- this._onUserIconPress(item)}
- onDiaryAction={() => this._onDiaryAction(item)}
onPhotoPress={() => this._onPhotoPress(item.photoUrl)}
>
diff --git a/src/component/image/imageAction.js b/src/component/image/imageAction.js
index 3f4077f..889ad91 100644
--- a/src/component/image/imageAction.js
+++ b/src/component/image/imageAction.js
@@ -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;
}
diff --git a/src/component/notebook/notebookDiaryList.js b/src/component/notebook/notebookDiaryList.js
index ec3ba3e..88746cd 100644
--- a/src/component/notebook/notebookDiaryList.js
+++ b/src/component/notebook/notebookDiaryList.js
@@ -169,7 +169,8 @@ export default class NotebookDiaryList extends Component {
},
passProps: {
diary: diary,
- editable: this.editable
+ editable: this.editable,
+ expired: this.notebook.isExpired
}
}
});
diff --git a/src/component/passwordInput.js b/src/component/passwordInput.js
new file mode 100644
index 0000000..28a2b05
--- /dev/null
+++ b/src/component/passwordInput.js
@@ -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 (
+
+
+ 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()
+ }
+
+
+ )
+
+ }
+
+ _getInputItem() {
+ let inputItem = [];
+ let text = this.state.text;
+
+ for(let i=0; i
+ {i < text.length ? : null}
+
+ );
+ } else {
+ inputItem.push(
+
+ {i < text.length ? : null}
+ )
+ }
+ }
+
+ 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
+ }
+});
\ No newline at end of file
diff --git a/src/component/userIcon.js b/src/component/userIcon.js
index 18fa4c5..483bd70 100644
--- a/src/component/userIcon.js
+++ b/src/component/userIcon.js
@@ -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}
/>
diff --git a/src/component/userIntro.js b/src/component/userIntro.js
index 94a5acd..4784a59 100644
--- a/src/component/userIntro.js
+++ b/src/component/userIntro.js
@@ -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
diff --git a/src/page/AboutPage.js b/src/page/AboutPage.js
new file mode 100644
index 0000000..75c853e
--- /dev/null
+++ b/src/page/AboutPage.js
@@ -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 (
+
+
+
+ 版本: {Api.VERSION}{label}
+ {this.state.news.date} 更新日志
+ {this.state.news.info}
+
+
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/page/DiaryDetailPage.js b/src/page/DiaryDetailPage.js
index 4ab81af..72553e1 100644
--- a/src/page/DiaryDetailPage.js
+++ b/src/page/DiaryDetailPage.js
@@ -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 (
this.scroll = r}
@@ -144,16 +165,16 @@ export default class DiaryDetailPage extends Component {
>
this.diaryFull = r}
+ {...this.props}
diary={this.state.diary}
refreshData={() => this.state.diary}
- editable={this.state.editable}
- {...this.props}
+ editable={this.state.editable || isMine}
>
{
- this.state.selfInfo && this.state.diary ? (
+ !this.state.expired ? (
this.commentInput = r}
diary={this.state.diary}
selfInfo={this.state.selfInfo}
diff --git a/src/page/FeedbackPage.js b/src/page/FeedbackPage.js
new file mode 100644
index 0000000..15aa8a0
--- /dev/null
+++ b/src/page/FeedbackPage.js
@@ -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 (
+
+ this.setState({content: text})}
+ />
+
+
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/page/FollowPage.js b/src/page/FollowPage.js
index d9cb66a..e3ec75b 100644
--- a/src/page/FollowPage.js
+++ b/src/page/FollowPage.js
@@ -49,7 +49,6 @@ export default class FollowPage extends Component {
}
}
});
-
}
render() {
diff --git a/src/page/NotebookDetailPage.js b/src/page/NotebookDetailPage.js
index 36b0fc1..cbf801c 100644
--- a/src/page/NotebookDetailPage.js
+++ b/src/page/NotebookDetailPage.js
@@ -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 (
- this.diaryList = r}
+ notebook={this.props.notebook}
{...this.props}>
diff --git a/src/page/PasswordPage.js b/src/page/PasswordPage.js
new file mode 100644
index 0000000..7b13c29
--- /dev/null
+++ b/src/page/PasswordPage.js
@@ -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 (
+
+
+ {this.state.title}
+
+ {
+ this.props.type == 'setting' && this.props.operation != 'cancel'
+ ? (
+ 提示: 从后台切切换前台时不需要输入密码
+ ) : null
+ }
+ {
+ this.props.type == 'setting' ? null : (
+
+
+
+ 忘记密码?通过登录重设
+
+
+
+ )
+ }
+
+
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/page/SettingPage.js b/src/page/SettingPage.js
new file mode 100644
index 0000000..6b300e1
--- /dev/null
+++ b/src/page/SettingPage.js
@@ -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 (
+
+
+ this.jumpTo('UserInfoEdit')}>
+ 修改个人信息
+
+
+
+
+
+ 启动密码
+
+
+
+
+
+ 提醒推送
+
+
+
+
+
+ {
+ Api.IS_IOS ? (
+
+
+ Linking.openURL("https://itunes.apple.com/us/app/jiao-nang-ri-ji/id1142102323?l=zh&ls=1&mt=8")}
+ >
+ 去 App Store 评价
+
+
+
+
+ ) : null
+ }
+
+ this.jumpTo('Feedback')}>
+ 意见反馈
+
+
+
+
+ this.jumpTo('About')}>
+ 关于
+ {
+ this.state.hasUpdateNews
+ ? (
+
+ 1
+
+ )
+ : null
+ }
+
+
+
+
+
+
+ 退出登录
+
+
+
+ );
+ }
+}
+
+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'
+ }
+});
\ No newline at end of file
diff --git a/src/page/UserInfoEditPage.js b/src/page/UserInfoEditPage.js
new file mode 100644
index 0000000..6bc022d
--- /dev/null
+++ b/src/page/UserInfoEditPage.js
@@ -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 (
+
+
+ {
+ this.state.user ? (
+
+
+ 头像
+
+
+
+
+
+
+
+ this.jumpTo('UserNameEdit')}>
+ 名字
+
+ {this.state.user.name}
+
+
+
+
+
+ this.jumpTo('UserIntroEdit')}>
+ 个人简介
+
+
+
+ ) : null
+ }
+
+ );
+ }
+}
+
+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
+ }
+});
+
diff --git a/src/page/UserIntroEditPage.js b/src/page/UserIntroEditPage.js
new file mode 100644
index 0000000..d209825
--- /dev/null
+++ b/src/page/UserIntroEditPage.js
@@ -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 (
+
+
+ this.setState({intro: text})}
+ multiline={true}
+ />
+
+
+ );
+ }
+}
+
+
+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'
+ }
+});
+
diff --git a/src/page/UserNameEditPage.js b/src/page/UserNameEditPage.js
new file mode 100644
index 0000000..a1f9203
--- /dev/null
+++ b/src/page/UserNameEditPage.js
@@ -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 (
+
+
+
+ 名字
+ this.setState({name: text})}
+ />
+
+
+
+ );
+ }
+}
+
+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
+ }
+});
diff --git a/src/page/UserPage.js b/src/page/UserPage.js
index 6ebb469..caa45c2 100644
--- a/src/page/UserPage.js
+++ b/src/page/UserPage.js
@@ -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: () => this.userIntro = r}
user={this.user}
/>,
diary: () => {
@@ -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
}
\ No newline at end of file
diff --git a/src/util/event.js b/src/util/event.js
index bab9487..d9641d3 100644
--- a/src/util/event.js
+++ b/src/util/event.js
@@ -4,6 +4,8 @@ export default {
updateDiarys: 'updateDiarys',
updateComments: 'updateComments',
- commentPressed: 'commentPressed'
-
+ commentPressed: 'commentPressed',
+ passwordUpdated: 'passwordUpdated',
+ updateNewsRead: 'updateNewsRead',
+ userInfoUpdated: 'userInfoUpdated'
}
\ No newline at end of file