1. 空日记列表组件

2. 日记列表尾组件整理
3. 清理不必要组件
4. 照片日记修改保存后跳转
This commit is contained in:
xuwenyang 2019-05-31 01:50:50 +08:00
parent c45b638b79
commit 3c8d338345
18 changed files with 295 additions and 209 deletions

2
App.js
View file

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

View file

@ -1,12 +1,12 @@
import React, {Component} from 'react';
import {StyleSheet, Text, View} from 'react-native';
import {StyleSheet, Text, View, TouchableOpacity, Alert} from 'react-native';
import Ionicons from 'react-native-vector-icons/Ionicons';
import ActionSheet from 'react-native-actionsheet-api';
import moment from 'moment';
import Color from '../../style/color';
import UserIcon from '../userIcon';
import CommentActionIcon from './commentActionIcon';
export default class Comment extends Component {
@ -18,7 +18,7 @@ export default class Comment extends Component {
return (
<View>
<View style={localStyle.box}>
<UserIcon iconUrl={user.iconUrl}></UserIcon>
<UserIcon iconUrl={user.iconUrl} onPress={this.props.onUserIconPress}></UserIcon>
<View style={localStyle.body}>
<View style={localStyle.title}>
@ -40,7 +40,9 @@ export default class Comment extends Component {
{
editable
? <CommentActionIcon onPress={this.props.onCommentAction}></CommentActionIcon>
? <TouchableOpacity onPress={this.props.onCommentAction} style={localStyle.moreIcon}>
<Ionicons name="ios-more" size={24} color={Color.inactiveText} />
</TouchableOpacity>
: null
}
@ -91,5 +93,12 @@ const localStyle = StyleSheet.create({
backgroundColor: Color.line,
marginHorizontal: 16,
marginLeft: 56
},
moreIcon: {
position: 'absolute',
bottom: 0,
right: 10,
paddingHorizontal: 12,
paddingVertical: 5
}
});

View file

@ -1,37 +0,0 @@
import React, {Component} from 'react';
import {StyleSheet, Text, View, TouchableOpacity, Alert} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import ActionSheet from 'react-native-actionsheet-api';
import Color from '../../style/color'
export default class CommentActionIcon extends Component {
_defaultOnPress() {
}
render() {
return (
<TouchableOpacity style={localStyle.moreIcon}
onPress={this.props.onPress ? this.props.onPress : this._defaultOnPress.bind(this)}
>
<Icon name="ios-more"
size={24}
color={Color.inactiveText} />
</TouchableOpacity>
);
}
}
const localStyle = StyleSheet.create({
moreIcon: {
position: 'absolute',
bottom: 0,
right: 10,
paddingHorizontal: 12,
paddingVertical: 5
}
});

View file

@ -1,36 +0,0 @@
import React, {Component} from 'react';
import {StyleSheet, Text, View} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import Color from '../../style/color'
export default class CommentIcon extends Component {
render() {
return (
<View style={localStyle.commentIconBox}>
<Icon name="ios-text-outline"
size={18}
color={Color.inactiveText}
style={localStyle.buttonIcon} />
<Text style={localStyle.commentIconText}>{this.props.count}</Text>
</View>
);
}
}
const localStyle = StyleSheet.create({
commentIconBox: {
flexDirection: "row"
},
buttonIcon: {
marginRight: 8,
marginLeft: 2
},
commentIconIext: {
fontSize: 15,
color: Color.inactiveText
}
});

View file

@ -10,6 +10,7 @@ import {
Keyboard,
DeviceEventEmitter
} from 'react-native';
import {Navigation} from 'react-native-navigation';
import {Divider} from "react-native-elements";
import Touchable from '../touchable';
@ -57,6 +58,26 @@ export default class CommentList extends Component {
DeviceEventEmitter.emit(Event.commentPressed, comment);
}
_onUserIconPress(comment) {
Navigation.push(this.props.componentId, {
component: {
name: 'User',
options: {
bottomTabs: {
visible: false,
// hide bottom tab for android
drawBehind: true,
animate: true
}
},
passProps: {
user: comment.user
}
}
});
}
_onCommentAction(comment) {
ActionSheet.showActionSheetWithOptions({
options:['删除回复', '取消'],
@ -107,6 +128,7 @@ export default class CommentList extends Component {
return (
<Touchable onPress={() => this._onCommentPress(item)}>
<Comment comment={item} editable={this.editable}
onUserIconPress={() => this._onUserIconPress(item)}
onCommentAction={() => this._onCommentAction(item)}>
</Comment>
</Touchable>

View file

@ -1,34 +0,0 @@
import React, {Component} from 'react';
import {StyleSheet, Text, View, TouchableOpacity} from 'react-native';
import Ionicons from 'react-native-vector-icons/Ionicons';
import Color from '../../style/color'
export default class DiaryActionIcon extends Component {
_defaultOnPress() {
}
render() {
return (
<TouchableOpacity onPress={
this.props.onPress ? this.props.onPress : this._defaultOnPress.bind(this)
}>
<Ionicons name="ios-more"
size={24}
color={Color.inactiveText}
style={localStyle.moreIcon} />
</TouchableOpacity>
);
}
}
const localStyle = StyleSheet.create({
moreIcon: {
paddingVertical: 5,
paddingHorizontal: 5
}
});

View file

@ -1,14 +1,12 @@
import React, {Component} from 'react';
import {StyleSheet, Text, View} from 'react-native';
import {StyleSheet, Text, View, TouchableOpacity} from 'react-native';
import Ionicons from 'react-native-vector-icons/Ionicons';
import moment from 'moment';
import Color from '../../style/color';
import UserIcon from '../userIcon';
import Photo from '../photo';
import CommentIcon from '../comment/commentIcon';
import DiaryActionIcon from './diaryActionIcon';
export default class DiaryBrief extends Component {
@ -70,14 +68,22 @@ export default class DiaryBrief extends Component {
<View style={localStyle.actionBar}>
{
diary.comment_count > 0
? <CommentIcon count={diary.comment_count}></CommentIcon>
? <View style={localStyle.commentIconBox}>
<Ionicons name="ios-text-outline" size={18}
color={Color.inactiveText}
style={localStyle.buttonIcon} />
<Text style={localStyle.commentIconText}>{diary.comment_count}</Text>
</View>
: null
}
<View style={{flex: 1}} />
{
this.editable
? <DiaryActionIcon diaryId={diary.id}
onPress={this.props.onDiaryAction}></DiaryActionIcon>
? <TouchableOpacity onPress={this.props.onDiaryAction}>
<Ionicons name="ios-more" size={24}
color={Color.inactiveText}
style={localStyle.moreIcon} />
</TouchableOpacity>
: null
}
</View>
@ -130,5 +136,20 @@ const localStyle = StyleSheet.create({
height: 30,
marginTop: 10,
justifyContent: 'space-between'
},
moreIcon: {
paddingVertical: 5,
paddingHorizontal: 5
},
commentIconBox: {
flexDirection: "row"
},
buttonIcon: {
marginRight: 8,
marginLeft: 2
},
commentIconIext: {
fontSize: 15,
color: Color.inactiveText
}
});

View file

@ -109,6 +109,8 @@ export default class DiaryFull extends Component {
<CommentList ref={(r) => this.commentList = r}
diaryId={diary.id}
editable={this.state.editable}
onUserIconPress={this._onUserIconPress.bind(this)}
{...this.props}
></CommentList>
</View>

View file

@ -17,7 +17,12 @@ import Msg from '../../util/msg';
import Api from '../../util/api';
import Touchable from '../touchable';
import ListFooter from '../listFooter';
import {
ListFooterLoading,
ListFooterEnd,
ListFooterFailed
} from '../listFooter';
import {ListEmptyRefreshable} from '../listEmpty';
import DiaryBrief from './diaryBrief';
@ -30,6 +35,7 @@ export default class DiaryList extends Component {
this.dataSource = props.dataSource;
this.state = {
mounting: true,
diaries: [],
refreshing: false,
@ -91,7 +97,6 @@ export default class DiaryList extends Component {
});
}
// to be seperated as diaryAction component
_onDiaryAction(diary) {
ActionSheet.showActionSheetWithOptions({
options:['修改','删除', '取消'],
@ -113,10 +118,7 @@ export default class DiaryList extends Component {
}
},
passProps: {
diary: diary,
onSave: (diary) => {
//
}
diary: diary
}
}
});
@ -152,11 +154,11 @@ export default class DiaryList extends Component {
url: photoUrl
}
}
})
});
}
async refresh() {
if (this.state.refreshing) {
if(this.state.refreshing) {
return;
}
@ -169,14 +171,6 @@ export default class DiaryList extends Component {
}
} else {
let diaries = this.state.diaries;
let newDiaries = result.list;
if (diaries.length > 0 && newDiaries.length > 0
&& diaries[0].id === newDiaries[0].id) {
Msg.showMsg('没有新内容');
}
this.setState({
diaries: result.list ? result.list : [],
hasMore: result.more,
@ -191,6 +185,7 @@ export default class DiaryList extends Component {
}).done(() => {
this.setState({
mounting: false,
refreshing: false
});
});
@ -231,6 +226,20 @@ export default class DiaryList extends Component {
}
render() {
if(!this.state.mounting && (!this.state.diaries || this.state.diaries.length == 0)) {
let message = this.editable
? '今天还没有写日记,马上写一篇吧'
: '今天还没有人写日记';
return (
<ListEmptyRefreshable
error={this.state.refreshFailed}
message={message}
onPress={this.refresh.bind(this)}
></ListEmptyRefreshable>
);
}
return (
<View style={localStyle.container}>
<FlatList style={localStyle.list}
@ -266,14 +275,14 @@ export default class DiaryList extends Component {
}
if (this.state.loadFailed) {
return ListFooter.renderFooterFailed(this.loadMore.bind(this));
return <ListFooterFailed refresh={this.loadMore.bind(this)}></ListFooterFailed>;
}
if (!this.state.hasMore) {
return ListFooter.renderFooterEnd();
return <ListFooterEnd></ListFooterEnd>;
}
return ListFooter.renderFooterLoading();
return <ListFooterLoading></ListFooterLoading>;
}}
refreshing={this.state.refreshing}

View file

@ -7,7 +7,11 @@ import Touchable from '../touchable';
import Color from '../../style/color';
import UserIcon from '../userIcon';
import ListFooter from '../listFooter';
import {
ListFooterLoading,
ListFooterEnd,
ListFooterFailed
} from '../listFooter';
export default class FollowUserList extends Component {
@ -157,7 +161,7 @@ export default class FollowUserList extends Component {
return (
<Touchable key={item.id} onPress={() => this._onItemPress(item)}>
<View style={localStyle.box}>
<UserIcon iconUrl={item.iconUrl}></UserIcon>
<UserIcon iconUrl={item.iconUrl} onPress={() => this._onItemPress(item)}></UserIcon>
<Text style={localStyle.userName}>{item.name}</Text>
<Touchable onPress={() => this._onDeletePress(item)}>
<Ionicons name="md-close" size={20}
@ -175,14 +179,14 @@ export default class FollowUserList extends Component {
}
if (this.state.loadFailed) {
return ListFooter.renderFooterFailed(this.loadMore.bind(this));
return <ListFooterFailed refresh={this.loadMore.bind(this)}></ListFooterFailed>;
}
if (!this.state.hasMore) {
return ListFooter.renderFooterEnd();
return <ListFooterEnd></ListFooterEnd>;
}
return ListFooter.renderFooterLoading();
return <ListFooterLoading></ListFooterLoading>;
}}
refreshing={this.state.refreshing}

View file

@ -3,11 +3,9 @@ import ImagePicker from 'react-native-image-crop-picker'
import ImageResizer from 'react-native-image-resizer'
async function resize(uri, oWidth, oHeight) {
async function resize(uri, oWidth, oHeight, maxPixel) {
let width = oWidth;
let height = oHeight;
let maxPixel = 640 * 640;
let oPixel = oWidth * oHeight;
if(oPixel > maxPixel) {
@ -15,11 +13,11 @@ async function resize(uri, oWidth, oHeight) {
height = Math.sqrt(oHeight * maxPixel / oWidth);
}
const newUri = await ImageResizer.createResizedImage(uri, width, height, 'JPEG', 75);
const newUri = await ImageResizer.createResizedImage(uri, width, height);
return 'file://' + newUri.uri;
}
function action(imageOption, callback) {
function action(imageOption, maxSize, maxPixel, callback) {
ActionSheet.showActionSheetWithOptions({
options: ['拍照', '从相册选择', '取消'],
cancelButtonIndex: 2,
@ -37,13 +35,19 @@ function action(imageOption, callback) {
: ImagePicker.openPicker(imageOption)
)
.then(async (image) => {
let imageUri = await resize(image.path, image.width, image.height);
if(typeof callback == 'function') {
callback(imageUri);
let imageUri = image.path;
if(image.size > maxSize) {
imageUri = await resize(image.path, image.width, image.height, maxPixel);
}
callback(null, imageUri);
})
.catch(e => {
//
if(e.message === "User cancelled image selection") {
return;
}
callback(e);
})
.done();
}

View file

@ -0,0 +1,37 @@
import React, {Component} from 'react';
import {StyleSheet, Text, View} from 'react-native';
import {Button} from 'react-native-elements'
import Color from '../style/color';
const ListEmptyRefreshable = (props) => {
let text = props.error ? '出错了 :(' : props.message;
return (
<View style={localStyle.container}>
<Text style={localStyle.text}>{text}</Text>
<Button fontSize={14} borderRadius={999} backgroundColor={Color.primary}
title={'刷新一下'}
onPress={() => props.onPress ? props.onPress() : null} />
</View>
);
}
const localStyle = StyleSheet.create({
container: {
alignItems:'center',
justifyContent: 'center',
height: '100%'
},
text: {
paddingBottom: 15,
color: Color.text
}
});
export {
ListEmptyRefreshable
}

View file

@ -20,7 +20,7 @@ const localStyle = StyleSheet.create({
}
});
function renderFooterLoading() {
const ListFooterLoading = () => {
return (
<View style={localStyle.footer}>
<ActivityIndicator animating={true} color={Color.primary}
@ -29,7 +29,7 @@ function renderFooterLoading() {
);
}
function renderFooterEnd() {
const ListFooterEnd = () => {
return (
<View style={localStyle.footer}>
<Text style={{color: Color.inactiveText, fontSize: 12}}>
@ -39,15 +39,15 @@ function renderFooterEnd() {
);
}
function renderFooterFailed(refresh) {
let isRefreshable = refresh && typeof refresh == 'function';
const ListFooterFailed = (props) => {
let isRefreshable = props.refresh ? true : false;
return (
<View style={localStyle.footer}>
<TouchableOpacity style={{marginTop: 15}}
onPress={() => {
if(isRefreshable) {
refresh();
props.refresh();
}
}}
>
@ -60,9 +60,9 @@ function renderFooterFailed(refresh) {
}
export default {
renderFooterLoading,
renderFooterEnd,
renderFooterFailed
export {
ListFooterLoading,
ListFooterEnd,
ListFooterFailed
}

View file

@ -16,8 +16,12 @@ import Color from '../../style/color';
import Touchable from '../touchable';
import DiaryBrief from '../diary/diaryBrief';
import ListFooter from '../listFooter';
import NotebookDiaryData from '../../dataLoader/notebookDiaryData';
import {
ListFooterLoading,
ListFooterEnd,
ListFooterFailed
} from '../listFooter';
export default class NotebookDiaryList extends Component {
@ -200,14 +204,14 @@ export default class NotebookDiaryList extends Component {
}
if (this.state.loadFailed) {
return ListFooter.renderFooterFailed(this.loadMore.bind(this));
return <ListFooterFailed refresh={this.loadMore.bind(this)}></ListFooterFailed>;
}
if (!this.state.hasMore) {
return ListFooter.renderFooterEnd();
return <ListFooterEnd></ListFooterEnd>;
}
return ListFooter.renderFooterLoading();
return <ListFooterLoading></ListFooterLoading>;
}}
ItemSeparatorComponent={(sectionID, rowID, adjacentRowHighlighted) =>

View file

@ -78,9 +78,41 @@ export default class DiaryDetailPage extends Component {
componentDidMount() {
if(!this.state.diary && this.state.diaryId) {
Api.getDiary(this.state.diaryId)
this.refreshDiary();
}
Api.getSelfInfoByStore()
.then(user => {
this.setState({
selfInfo: user
});
}).done();
this.diaryListener = DeviceEventEmitter.addListener(Event.updateDiarys, (param) => {
this.refreshDiary();
});
this.commentListener = DeviceEventEmitter.addListener(Event.updateComments, (param) => {
this.setState({needScrollToBottom: true});
this.diaryFull.refreshComment();
Keyboard.dismiss();
});
}
componentWillUnmount(){
this.diaryListener.remove();
this.commentListener.remove();
}
refreshDiary() {
let diaryId = this.state.diaryId;
if(!diaryId) {
diaryId = this.state.diary.id;
}
Api.getDiary(diaryId)
.then(result => {
console.log('get diary:', result);
if(!result) {
throw {
message: 'get diary no result'
@ -98,25 +130,6 @@ export default class DiaryDetailPage extends Component {
});
}
Api.getSelfInfoByStore()
.then(user => {
this.setState({
selfInfo: user
});
}).done();
this.listener = DeviceEventEmitter.addListener(Event.updateComments, (param) => {
this.setState({needScrollToBottom: true});
this.diaryFull.refreshComment();
Keyboard.dismiss();
});
}
componentWillUnmount(){
this.listener.remove();
}
render() {
return (
<View style={localStyle.wrap}>

View file

@ -126,8 +126,7 @@ export default class NotebookEditPage extends Component {
Msg.showMsg('封面保存成功');
})
.catch(e => {
// Msg.showMsg('封面保存失败:' + e.message);
Alert.alert('封面保存失败:' + e.message);
Msg.showMsg('封面保存失败');
})
.done(() => {
this.setState({uploading: false});
@ -139,7 +138,15 @@ export default class NotebookEditPage extends Component {
width: 640,
height: 480,
cropping: true
}, this.uploadCover.bind(this));
}, -1, 600*600, (e, imageUri) => {
if(e) {
Msg.showMsg('操作失败');
} else {
this.uploadCover(imageUri);
}
});
}
_onDelete() {

View file

@ -6,6 +6,8 @@ import {Navigation} from 'react-native-navigation';
import Color from '../style/color'
import {Icon} from '../style/icon'
import {ListEmptyRefreshable} from '../component/listEmpty'
export default class NotificationPage extends Component {
@ -24,7 +26,7 @@ export default class NotificationPage extends Component {
id: 'history',
icon: Icon.navButtonTime,
color: '#aaa' // android
color: Color.primary // android
}]
}
};
@ -40,11 +42,7 @@ export default class NotificationPage extends Component {
render() {
return (
<View style={localStyle.container}>
<Text style={localStyle.text}>没有内容</Text>
<Button fontSize={14} borderRadius={999} backgroundColor={Color.primary}
title={'刷新'} onPress={() => {}} />
</View>
<ListEmptyRefreshable message={'没有提醒:)'}></ListEmptyRefreshable>
);
}
}

View file

@ -11,7 +11,8 @@ import {
Easing,
TouchableWithoutFeedback,
DeviceEventEmitter,
Alert
Alert,
Image
} from 'react-native';
import {Navigation} from 'react-native-navigation';
import KeyboardSpacer from "react-native-keyboard-spacer";
@ -26,6 +27,7 @@ import Token from '../util/token'
import Event from "../util/event";
import NotebookLine from '../component/notebook/notebookLine';
import ImageAction from '../component/image/imageAction'
export default class WritePage extends Component {
@ -42,6 +44,8 @@ export default class WritePage extends Component {
targetbookSubject: diary ? diary.notebook_subject : '',
content: diary ? diary.content : '',
photoSource: null,
photoUri: null,
modalVisible: false,
fadeAnimOpacity: new Animated.Value(0),
@ -189,19 +193,64 @@ export default class WritePage extends Component {
}).done();
}
_onPickPhoto() {
if(this.state.photoUri != null) {
ActionSheet.showActionSheetWithOptions({
options: ['预览照片', '删除照片', '取消'],
destructiveButtonIndex: 1,
cancelButtonIndex: 2
}, (index) => {
if(index == 0) {
Navigation.push(this.props.componentId, {
component: {
name: 'Photo',
passProps: {
url: this.state.photoUri
}
}
});
} else if(index == 1) {
this.setState({
photoSource: null,
photoUri: null
});
}
});
} else {
ImageAction.action({
cropping: false,
mediaType: 'photo'
}, 1024 * 1024 * 2, 2560 * 1920, (e, imageUri) => {
if(e) {
Msg.showMsg('操作失败');
} else {
this.setState({
photoSource: {
uri: imageUri,
isStatic: true
},
photoUri: imageUri
});
}
});
}
}
saveDiary() {
let photoUri = null;
let photoUri = this.state.photoUri;
let topic = this.props.topic ? 1 : 0;
(this.diary
? Api.updateDiary(this.diary.id, this.state.targetbookId, this.state.content)
: Api.addDiary(this.state.targetbookId, this.state.content, null, topic)
: Api.addDiary(this.state.targetbookId, this.state.content, photoUri, topic)
).then(result => {
Msg.showMsg('日记保存完成');
DeviceEventEmitter.emit(Event.updateDiarys);
this.props.onSave(result);
if(this.diary) {
Navigation.pop(this.props.componentId);
@ -220,7 +269,6 @@ export default class WritePage extends Component {
}
});
}
})
.catch(e => {
Msg.showMsg('保存失败');
@ -266,10 +314,8 @@ export default class WritePage extends Component {
<View style={{flex: 1}} />
<TouchableOpacity style={localStyle.photo} onPress={() => {}}>
<Ionicons name="ios-image-outline" size={30}
style={{paddingTop: 4}} color={Color.light} />
</TouchableOpacity>
{this.renderPhotoButton()}
</View>
{
@ -282,6 +328,23 @@ export default class WritePage extends Component {
);
}
renderPhotoButton() {
if(this.diary) {
return null;
}
let content = !this.state.photoSource
? (<Ionicons name="ios-image-outline" size={30} style={{paddingTop: 4}} color={Color.light} />)
: (<Image source={this.state.photoSource} style={{width: 30, height: 30, borderRadius: 3}} />);
return (
<TouchableOpacity style={localStyle.photo}
onPress={this._onPickPhoto.bind(this)}>
{content}
</TouchableOpacity>
);
}
renderModal() {
return (
<Modal animationType='none' transparent={true}