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
6ff2670a3c
commit
aa23c49880
15 changed files with 287 additions and 106 deletions
2
App.js
2
App.js
|
@ -87,7 +87,7 @@ const localStyle = StyleSheet.create({
|
|||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
paddingTop: 100,
|
||||
paddingTop: 65,
|
||||
paddingHorizontal: 15
|
||||
},
|
||||
bottom: {
|
||||
|
|
7
index.js
7
index.js
|
@ -35,7 +35,12 @@ Navigation.events().registerAppLaunchedListener(async () => {
|
|||
stack: {
|
||||
children: [{
|
||||
component: {
|
||||
name: 'Timepill'
|
||||
name: 'Timepill',
|
||||
options: {
|
||||
topBar: {
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
|
|
@ -14,12 +14,21 @@ export default class DiaryFull extends Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.diary = props.diary;
|
||||
this.editable = props.editable || false;
|
||||
this.state = {
|
||||
diary: props.diary,
|
||||
editable: props.editable || false
|
||||
}
|
||||
}
|
||||
|
||||
refreshDiaryContent() {
|
||||
// empty
|
||||
if(!this.props.refreshData) {
|
||||
return;
|
||||
}
|
||||
|
||||
let diary = this.props.refreshData();
|
||||
if(diary) {
|
||||
this.setState({diary})
|
||||
}
|
||||
}
|
||||
|
||||
async refreshComment() {
|
||||
|
@ -27,7 +36,7 @@ export default class DiaryFull extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
let diary = this.diary;
|
||||
let diary = this.state.diary;
|
||||
if(!diary) {
|
||||
return null;
|
||||
}
|
||||
|
@ -66,7 +75,7 @@ export default class DiaryFull extends Component {
|
|||
|
||||
<CommentList ref={(r) => this.commentList = r}
|
||||
diaryId={diary.id}
|
||||
editable={this.editable}
|
||||
editable={this.state.editable}
|
||||
></CommentList>
|
||||
|
||||
</View>
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
import ActionSheet from 'react-native-actionsheet-api';
|
||||
import ImagePicker from 'react-native-image-crop-picker'
|
||||
import ImageResizer from 'react-native-image-resizer'
|
||||
|
||||
|
||||
async function resize(uri, oWidth, oHeight) {
|
||||
let width = oWidth;
|
||||
let height = oHeight;
|
||||
|
||||
let maxPixel = 640 * 640;
|
||||
let oPixel = oWidth * oHeight;
|
||||
|
||||
if(oPixel > maxPixel) {
|
||||
width = Math.sqrt(oWidth * maxPixel / oHeight);
|
||||
height = Math.sqrt(oHeight * maxPixel / oWidth);
|
||||
}
|
||||
|
||||
const newUri = await ImageResizer.createResizedImage(uri, width, height, 'JPEG', 75);
|
||||
return 'file://' + newUri.uri;
|
||||
}
|
||||
|
||||
function action(imageOption, callback) {
|
||||
ActionSheet.showActionSheetWithOptions({
|
||||
options: ['拍照', '从相册选择', '取消'],
|
||||
cancelButtonIndex: 2,
|
||||
title: '选择图片'
|
||||
|
||||
}, (index) => {
|
||||
if(index == 0 || index == 1) {
|
||||
if(!imageOption) {
|
||||
return;
|
||||
}
|
||||
|
||||
(
|
||||
index == 0
|
||||
? ImagePicker.openCamera(imageOption)
|
||||
: ImagePicker.openPicker(imageOption)
|
||||
)
|
||||
.then(async (image) => {
|
||||
let imageUri = await resize(image.path, image.width, image.height);
|
||||
if(typeof callback == 'function') {
|
||||
callback(imageUri);
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
//
|
||||
})
|
||||
.done();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
action
|
||||
}
|
0
src/component/listEmpty.js
Normal file
0
src/component/listEmpty.js
Normal file
|
@ -25,7 +25,6 @@ export default class LoginForm extends Component {
|
|||
try {
|
||||
isLoginSucc = await Api.login(this.state.username, this.state.password);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
errMsg = err.message;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,6 @@ export default class NotebookDiaryList extends Component {
|
|||
return maplist;
|
||||
|
||||
}, {});
|
||||
console.log('reduce result:', reducedDiaries);
|
||||
|
||||
let result = [];
|
||||
for(let key in reducedDiaries) {
|
||||
|
|
|
@ -24,7 +24,7 @@ export default class NotebookLine extends Component {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
notebooks = this.props.refreshData()
|
||||
let notebooks = this.props.refreshData()
|
||||
|
||||
if(notebooks && notebooks.length > 0) {
|
||||
this.setState({
|
||||
|
|
|
@ -6,18 +6,58 @@ import Touchable from '../touchable';
|
|||
import Color from '../../style/color'
|
||||
|
||||
|
||||
function unique(array) {
|
||||
let n = [];
|
||||
for(let i=0; i<array.length; i++) {
|
||||
if(n.indexOf(array[i]) == -1) {
|
||||
n.push(array[i])
|
||||
};
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
export default class Notification extends Component {
|
||||
|
||||
render() {
|
||||
let msg = this.props.msg;
|
||||
if(msg && msg.type) {
|
||||
if(msg.type == 1) {
|
||||
return this.renderComment(msg);
|
||||
|
||||
} else if(msg.type == 2) {
|
||||
return this.renderFollow(msg);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
renderComment(msg) {
|
||||
const users = unique(msg.list.map(it => it.content.author.name)).join('、');
|
||||
const body = `${users} 回复了你`;
|
||||
|
||||
return (
|
||||
<Touchable onPress={() => {}}>
|
||||
<Touchable key={msg.link_id} onPress={() => this.props.onCommentPress(msg)}>
|
||||
<View style={localStyle.container}>
|
||||
<Ionicons name="ios-text" size={16}
|
||||
style={localStyle.icon} color={Color.light} />
|
||||
<Text style={localStyle.text}>{'XXX 回复了你|关注了你'}</Text>
|
||||
<Ionicons name="ios-text" size={16} style={localStyle.icon} color={Color.light} />
|
||||
<Text style={localStyle.text}>{body}</Text>
|
||||
</View>
|
||||
</Touchable>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
renderFollow(msg) {
|
||||
const body = `${msg.content.user.name} 关注了你`;
|
||||
|
||||
return (
|
||||
<Touchable key={msg.link_id} onPress={() => this.props.onFollowPress(msg)}>
|
||||
<View style={localStyle.container}>
|
||||
<Ionicons name="ios-heart" size={16} style={localStyle.icon} color='#d9534f' />
|
||||
<Text key={msg.link_id} style={localStyle.text}>{body}</Text>
|
||||
</View>
|
||||
</Touchable>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React, {Component} from 'react';
|
||||
import {StyleSheet, Text, View, FlatList, InteractionManager} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import Api from '../../util/api';
|
||||
import Notification from './notification';
|
||||
|
@ -26,17 +27,83 @@ export default class NotificationList extends Component {
|
|||
this.setState({refreshing: true});
|
||||
Api.getMessagesHistory()
|
||||
.then(notifications => {
|
||||
console.log('notifications:', notifications);
|
||||
if(notifications) {
|
||||
let reducedNoti = notifications.reduce((ret, v) => {
|
||||
if(v.type == 1) {
|
||||
let items = ret.filter(x => x.type === 1 && x.link_id === v.link_id);
|
||||
if(items.length > 0) {
|
||||
items[0].list.push(v);
|
||||
|
||||
} else {
|
||||
ret.push({
|
||||
type: 1,
|
||||
link_id: v.link_id,
|
||||
created: v.created,
|
||||
list: [v]
|
||||
});
|
||||
}
|
||||
|
||||
} else if (v.type == 2) {
|
||||
ret.push(v);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}, []);
|
||||
|
||||
console.log('notifications:', reducedNoti);
|
||||
|
||||
this.setState({
|
||||
notifications
|
||||
notifications: reducedNoti
|
||||
});
|
||||
}
|
||||
|
||||
}).done(() => {
|
||||
this.setState({refreshing: false});
|
||||
});
|
||||
}
|
||||
|
||||
_onCommentPress(msg) {
|
||||
Navigation.push(this.props.componentId, {
|
||||
component: {
|
||||
name: 'DiaryDetail',
|
||||
options: {
|
||||
bottomTabs: {
|
||||
visible: false,
|
||||
|
||||
// hide bottom tab for android
|
||||
drawBehind: true,
|
||||
animate: true
|
||||
}
|
||||
},
|
||||
passProps: {
|
||||
diaryId: msg.link_id,
|
||||
editable: true
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_onFollowPress(msg) {
|
||||
Navigation.push(this.props.componentId, {
|
||||
component: {
|
||||
name: 'User',
|
||||
options: {
|
||||
bottomTabs: {
|
||||
visible: false,
|
||||
|
||||
// hide bottom tab for android
|
||||
drawBehind: true,
|
||||
animate: true
|
||||
}
|
||||
},
|
||||
passProps: {
|
||||
user: msg.content.user
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
let hasData = this.state.notifications && this.state.notifications.length > 0;
|
||||
return hasData ? (
|
||||
|
@ -44,12 +111,15 @@ export default class NotificationList extends Component {
|
|||
data={this.state.notifications}
|
||||
|
||||
keyExtractor={(item, index) => {
|
||||
return item.id ? item.id.toString() : index
|
||||
return item.id ? item.id.toString() : `${index}`
|
||||
}}
|
||||
|
||||
renderItem={({item}) => {
|
||||
return (
|
||||
<Notification></Notification>
|
||||
<Notification msg={item}
|
||||
onCommentPress={this._onCommentPress.bind(this)}
|
||||
onFollowPress={this._onFollowPress.bind(this)}
|
||||
></Notification>
|
||||
);
|
||||
}}
|
||||
|
||||
|
@ -64,3 +134,6 @@ export default class NotificationList extends Component {
|
|||
const localStyle = StyleSheet.create({
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -22,14 +22,11 @@ export default class RegisterForm extends Component {
|
|||
async register() {
|
||||
let isRegisterSucc, errMsg = '注册失败';
|
||||
|
||||
this.props.setLoading(true);
|
||||
try {
|
||||
isRegisterSucc = await Api.register(this.state.nickname, this.state.username, this.state.password);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
errMsg = err.message;
|
||||
}
|
||||
this.props.setLoading(false);
|
||||
|
||||
return {
|
||||
isRegisterSucc,
|
||||
|
@ -40,12 +37,7 @@ export default class RegisterForm extends Component {
|
|||
_checkResult(result) {
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
if(result.isRegisterSucc) {
|
||||
Navigation.startSingleScreenApp({
|
||||
screen: {
|
||||
screen: 'Home',
|
||||
title: 'Home Title',
|
||||
}
|
||||
});
|
||||
Navigation.setRoot(BottomNav.config());
|
||||
|
||||
} else {
|
||||
Alert.alert(
|
||||
|
@ -61,7 +53,23 @@ export default class RegisterForm extends Component {
|
|||
}
|
||||
|
||||
_clickRegister() {
|
||||
this.register().then(this._checkResult);
|
||||
this.props.setLoading(true);
|
||||
this.register().then(result => {
|
||||
this.props.setLoading(false);
|
||||
this._checkResult(result);
|
||||
});
|
||||
}
|
||||
|
||||
_nicknameSubmit() {
|
||||
this.refs.inputEmail.focus();
|
||||
}
|
||||
|
||||
_usernameSubmit() {
|
||||
this.refs.registerPw.focus();
|
||||
}
|
||||
|
||||
_passwordSubmit() {
|
||||
this._clickRegister();
|
||||
}
|
||||
|
||||
render() {return (
|
||||
|
@ -74,7 +82,7 @@ export default class RegisterForm extends Component {
|
|||
selectionColor={Color.primary}
|
||||
underlineColorAndroid='transparent'
|
||||
keyboardType='email-address'
|
||||
value={''}
|
||||
value={this.state.nickname}
|
||||
|
||||
autoCorrect={false}
|
||||
autoFocus={false}
|
||||
|
@ -84,35 +92,35 @@ export default class RegisterForm extends Component {
|
|||
placeholder='名字'
|
||||
returnKeyType='next'
|
||||
|
||||
onChangeText={() => {}}
|
||||
onSubmitEditing={() => {}}
|
||||
onChangeText={(text) => this.setState({nickname: text})}
|
||||
onSubmitEditing={this._nicknameSubmit.bind(this)}
|
||||
/>
|
||||
|
||||
<FormInput
|
||||
<FormInput ref='inputEmail'
|
||||
|
||||
selectionColor={Color.primary}
|
||||
underlineColorAndroid='transparent'
|
||||
keyboardType='email-address'
|
||||
value={''}
|
||||
value={this.state.username}
|
||||
|
||||
autoCorrect={false}
|
||||
autoFocus={false}
|
||||
autoCapitalize='none'
|
||||
|
||||
placeholderTextColor={Color.inactiveText}
|
||||
placeholder='账号邮箱'
|
||||
placeholder='邮箱'
|
||||
returnKeyType='next'
|
||||
|
||||
onChangeText={() => {}}
|
||||
onChangeText={(text) => this.setState({username: text})}
|
||||
onSubmitEditing={() => {}}
|
||||
/>
|
||||
|
||||
<FormInput
|
||||
<FormInput ref='registerPw'
|
||||
|
||||
selectionColor={Color.primary}
|
||||
underlineColorAndroid='transparent'
|
||||
|
||||
value={''}
|
||||
value={this.state.password}
|
||||
secureTextEntry={true}
|
||||
selectTextOnFocus={true}
|
||||
autoCorrect={false}
|
||||
|
@ -121,8 +129,8 @@ export default class RegisterForm extends Component {
|
|||
placeholderTextColor={Color.inactiveText}
|
||||
returnKeyType='done'
|
||||
|
||||
onChangeText={() => {}}
|
||||
onSubmitEditing={() => {}}
|
||||
onChangeText={(text) => this.setState({password: text})}
|
||||
onSubmitEditing={this._passwordSubmit.bind(this)}
|
||||
/>
|
||||
</View>
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ export default class DiaryDetailPage extends Component {
|
|||
this.state = {
|
||||
selfInfo: null,
|
||||
|
||||
diaryId: props.diaryId,
|
||||
diary: props.diary,
|
||||
user: props.user,
|
||||
|
||||
|
@ -76,6 +77,27 @@ export default class DiaryDetailPage extends Component {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
if(!this.state.diary && this.state.diaryId) {
|
||||
Api.getDiary(this.state.diaryId)
|
||||
.then(result => {
|
||||
console.log('get diary:', result);
|
||||
if(!result) {
|
||||
throw {
|
||||
message: 'get diary no result'
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
diary: result
|
||||
}, () => {
|
||||
this.diaryFull.refreshDiaryContent();
|
||||
})
|
||||
})
|
||||
.done(() => {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
Api.getSelfInfoByStore()
|
||||
.then(user => {
|
||||
this.setState({
|
||||
|
@ -110,13 +132,14 @@ export default class DiaryDetailPage extends Component {
|
|||
|
||||
<DiaryFull ref={(r) => this.diaryFull = r}
|
||||
diary={this.state.diary}
|
||||
refreshData={() => this.state.diary}
|
||||
editable={this.state.editable}
|
||||
></DiaryFull>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
{
|
||||
this.state.selfInfo ? (
|
||||
this.state.selfInfo && this.state.diary ? (
|
||||
<CommentInput ref={(r) => this.commentInput = r}
|
||||
diary={this.state.diary}
|
||||
selfInfo={this.state.selfInfo}
|
||||
|
|
|
@ -24,6 +24,7 @@ import Event from "../util/event";
|
|||
|
||||
import Loading from '../component/loading'
|
||||
import DateInput from '../component/dateInput'
|
||||
import ImageAction from '../component/image/imageAction'
|
||||
|
||||
|
||||
export default class NotebookEditPage extends Component {
|
||||
|
@ -111,71 +112,34 @@ export default class NotebookEditPage extends Component {
|
|||
this.setState({saving: false});
|
||||
}
|
||||
|
||||
|
||||
async resizePhoto(uri, oWidth, oHeight) {
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
|
||||
let maxPixel = 640 * 640;
|
||||
let oPixel = oWidth * oHeight;
|
||||
|
||||
if(oPixel > maxPixel) {
|
||||
width = Math.sqrt(oWidth * maxPixel / oHeight);
|
||||
height = Math.sqrt(oHeight * maxPixel / oWidth);
|
||||
|
||||
} else {
|
||||
width = oWidth;
|
||||
height = oHeight;
|
||||
}
|
||||
|
||||
const newUri = await ImageResizer.createResizedImage(uri, width, height, 'JPEG', 75);
|
||||
return 'file://' + newUri.uri;
|
||||
}
|
||||
|
||||
async _uploadCover(uri, width, height) {
|
||||
const newUri = await this.resizePhoto(uri, width, height);
|
||||
|
||||
try {
|
||||
uploadCover(uri) {
|
||||
this.setState({uploading: true});
|
||||
let result = await Api.updateNotebookCover(this.props.notebook.id, newUri);
|
||||
if(result) {
|
||||
Msg.showMsg('封面保存成功');
|
||||
|
||||
} else {
|
||||
Api.updateNotebookCover(this.props.notebook.id, uri)
|
||||
.then(result => {
|
||||
if(!result) {
|
||||
throw {
|
||||
message: 'upload notebook cover failed'
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
Msg.showMsg('封面保存失败');
|
||||
}
|
||||
|
||||
DeviceEventEmitter.emit(Event.updateNotebooks);
|
||||
Msg.showMsg('封面保存成功');
|
||||
})
|
||||
.catch(e => {
|
||||
// Msg.showMsg('封面保存失败:' + e.message);
|
||||
Alert.alert('封面保存失败:' + e.message);
|
||||
})
|
||||
.done(() => {
|
||||
this.setState({uploading: false});
|
||||
});
|
||||
}
|
||||
|
||||
_onEditCover() {
|
||||
ActionSheet.showActionSheetWithOptions({
|
||||
options: ['拍照', '从相册选择', '取消'],
|
||||
cancelButtonIndex: 2,
|
||||
title: '设置封面'
|
||||
|
||||
}, (index) => {
|
||||
if (index != 2) {
|
||||
let imageOption = {
|
||||
ImageAction.action({
|
||||
width: 640,
|
||||
height: 480,
|
||||
cropping: true
|
||||
};
|
||||
|
||||
let imageSelect = index == 0
|
||||
? ImagePicker.openCamera(imageOption) : ImagePicker.openPicker(imageOption);
|
||||
|
||||
imageSelect.then(image => {
|
||||
this._uploadCover(image.path, image.width, image.height);
|
||||
})
|
||||
}
|
||||
});
|
||||
}, this.uploadCover.bind(this));
|
||||
}
|
||||
|
||||
_onDelete() {
|
||||
|
|
|
@ -28,7 +28,7 @@ export default class NotificationHistoryPage extends Component {
|
|||
render() {
|
||||
return (
|
||||
<View style={localStyle.container}>
|
||||
<NotificationList></NotificationList>
|
||||
<NotificationList {...this.props}></NotificationList>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -141,6 +141,10 @@ async function getMessagesHistory() {
|
|||
return call('GET', '/tip/history');
|
||||
}
|
||||
|
||||
async function getDiary(id) {
|
||||
return call('GET', '/diaries/' + id);
|
||||
}
|
||||
|
||||
async function deleteDiary(id) {
|
||||
return call('DELETE', '/diaries/' + id);
|
||||
}
|
||||
|
@ -214,7 +218,7 @@ async function report(user_id, diary_id) {
|
|||
|
||||
|
||||
async function upload(method, api, body) {
|
||||
let token = await TokenManager.getToken();
|
||||
let token = await TokenManager.getUserToken();
|
||||
let formData = new FormData();
|
||||
for(let prop of Object.keys(body)) {
|
||||
formData.append(prop, body[prop]);
|
||||
|
@ -335,6 +339,8 @@ export default {
|
|||
getFollowDiaries,
|
||||
getNotebookDiaries,
|
||||
getUserTodayDiaries,
|
||||
|
||||
getDiary,
|
||||
deleteDiary,
|
||||
|
||||
getDiaryComments,
|
||||
|
|
Loading…
Reference in a new issue