1. 提醒历史列表

2. 注册(未测试)
3. 图片选择器(未测试)
This commit is contained in:
xuwenyang 2019-05-30 02:46:55 +08:00
parent 6ff2670a3c
commit aa23c49880
15 changed files with 287 additions and 106 deletions

2
App.js
View file

@ -87,7 +87,7 @@ const localStyle = StyleSheet.create({
},
content: {
flex: 1,
paddingTop: 100,
paddingTop: 65,
paddingHorizontal: 15
},
bottom: {

View file

@ -35,7 +35,12 @@ Navigation.events().registerAppLaunchedListener(async () => {
stack: {
children: [{
component: {
name: 'Timepill'
name: 'Timepill',
options: {
topBar: {
visible: false
}
}
}
}]
}

View file

@ -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>

View file

@ -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
}

View file

View 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;
}

View file

@ -68,7 +68,6 @@ export default class NotebookDiaryList extends Component {
return maplist;
}, {});
console.log('reduce result:', reducedDiaries);
let result = [];
for(let key in reducedDiaries) {

View file

@ -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({

View file

@ -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>
)
}
}

View file

@ -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);
this.setState({
notifications
});
} 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: 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({
});

View file

@ -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>

View file

@ -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}

View file

@ -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 {
this.setState({uploading: true});
let result = await Api.updateNotebookCover(this.props.notebook.id, newUri);
if(result) {
Msg.showMsg('封面保存成功');
} else {
throw {
message: 'upload notebook cover failed'
uploadCover(uri) {
this.setState({uploading: true});
Api.updateNotebookCover(this.props.notebook.id, uri)
.then(result => {
if(!result) {
throw {
message: 'upload notebook cover failed'
}
}
}
} catch (e) {
Msg.showMsg('封面保存失败');
}
this.setState({uploading: false});
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 = {
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);
})
}
});
ImageAction.action({
width: 640,
height: 480,
cropping: true
}, this.uploadCover.bind(this));
}
_onDelete() {

View file

@ -28,7 +28,7 @@ export default class NotificationHistoryPage extends Component {
render() {
return (
<View style={localStyle.container}>
<NotificationList></NotificationList>
<NotificationList {...this.props}></NotificationList>
</View>
);
}

View file

@ -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,