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})} + /> +