1. 日记详情页增加ok绷

2. 点击底栏首页按钮向上滚动至顶部
3. android更新检测
This commit is contained in:
xuwenyang 2019-06-20 06:20:34 +08:00
parent 239c7bc790
commit 5a4a8c9f78
14 changed files with 247 additions and 96 deletions

View file

@ -152,7 +152,7 @@ dependencies {
implementation project(':react-native-navigation') implementation project(':react-native-navigation')
implementation fileTree(dir: "libs", include: ["*.jar"]) implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}" implementation 'androidx.appcompat:appcompat:1.0.0'
implementation "com.facebook.react:react-native:+" // From node_modules implementation "com.facebook.react:react-native:+" // From node_modules
} }

View file

@ -16,3 +16,6 @@
# This option should only be used with decoupled projects. More details, visit # This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true # org.gradle.parallel=true
android.useAndroidX=true
android.enableJetifier=true

View file

@ -79,13 +79,15 @@ const localStyle = StyleSheet.create({
}, },
titleText: { titleText: {
fontSize: 12, fontSize: 12,
color: Color.inactiveText color: Color.inactiveText,
paddingRight: 10
}, },
content: { content: {
flexGrow: 1, flexGrow: 1,
lineHeight: 26, lineHeight: 26,
color: Color.text, color: Color.text,
fontSize: 15, fontSize: 15,
paddingRight: 5,
marginBottom: 10 marginBottom: 10
}, },
line: { line: {

View file

@ -3,6 +3,7 @@ import {StyleSheet, Text, View, TouchableOpacity} from 'react-native';
import Ionicons from 'react-native-vector-icons/Ionicons'; import Ionicons from 'react-native-vector-icons/Ionicons';
import moment from 'moment'; import moment from 'moment';
import Touchable from '../touchable';
import Color from '../../style/color'; import Color from '../../style/color';
import UserIcon from '../userIcon'; import UserIcon from '../userIcon';
import Photo from '../photo'; import Photo from '../photo';
@ -17,8 +18,11 @@ export default class DiaryBrief extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.diary = props.diary; this.state = {
this.diary.isExpired = props.isExpired || false; diary: props.diary
}
this.expired = props.expired || false;
this.editable = props.editable || false; this.editable = props.editable || false;
this.showField = ['userIcon', 'userName', 'subject', 'createdTime']; this.showField = ['userIcon', 'userName', 'subject', 'createdTime'];
@ -32,11 +36,17 @@ export default class DiaryBrief extends Component {
} }
onDiaryAction() { onDiaryAction() {
DiaryAction.action(this.props.componentId, this.diary); DiaryAction.action(this.props.componentId, this.state.diary);
}
refreshDiary(diary) {
if(diary && this.props.refreshBack) {
this.props.refreshBack(diary);
}
} }
render() { render() {
let diary = this.diary; let diary = this.state.diary;
if(!diary) { if(!diary) {
return null; return null;
} }
@ -44,6 +54,7 @@ export default class DiaryBrief extends Component {
let user = diary.user; let user = diary.user;
return ( return (
<Touchable onPress={() => this.props.onDiaryPress ? this.props.onDiaryPress(this.state.diary) : null}>
<View style={[localStyle.box, this.props.style]}> <View style={[localStyle.box, this.props.style]}>
{(user && user.iconUrl && this.show('userIcon')) {(user && user.iconUrl && this.show('userIcon'))
? <UserIcon iconUrl={user.iconUrl} onPress={this.props.onUserIconPress}></UserIcon> : null} ? <UserIcon iconUrl={user.iconUrl} onPress={this.props.onUserIconPress}></UserIcon> : null}
@ -80,7 +91,9 @@ export default class DiaryBrief extends Component {
<DiaryIconOkB diaryId={diary.id} <DiaryIconOkB diaryId={diary.id}
count={diary.like_count} count={diary.like_count}
active={diary.liked} active={diary.liked}
clickable={!diary.isExpired}></DiaryIconOkB> clickable={!this.expired}
refreshBack={this.refreshDiary.bind(this)}
></DiaryIconOkB>
</View> </View>
<View style={{flex: 1}} /> <View style={{flex: 1}} />
{ {
@ -96,6 +109,7 @@ export default class DiaryBrief extends Component {
</View> </View>
</View> </View>
</Touchable>
); );
} }
} }

View file

@ -8,6 +8,7 @@ import UserIcon from '../userIcon';
import Photo from '../photo'; import Photo from '../photo';
import CommentList from '../comment/commentList'; import CommentList from '../comment/commentList';
import DiaryIconOkB from './diaryIconOkB';
export default class DiaryFull extends Component { export default class DiaryFull extends Component {
@ -17,16 +18,12 @@ export default class DiaryFull extends Component {
this.state = { this.state = {
diary: props.diary, diary: props.diary,
editable: props.editable || false editable: props.editable || false,
expired: props.expired || false
} }
} }
refreshDiaryContent() { refreshDiaryContent(diary) {
if(!this.props.refreshData) {
return;
}
let diary = this.props.refreshData();
if(diary) { if(diary) {
this.setState({diary}) this.setState({diary})
} }
@ -103,6 +100,14 @@ export default class DiaryFull extends Component {
</Text> </Text>
<Photo uri={diary.photoThumbUrl} onPress={() => this._onPhotoPress(diary.photoUrl)}></Photo> <Photo uri={diary.photoThumbUrl} onPress={() => this._onPhotoPress(diary.photoUrl)}></Photo>
<View style={localStyle.actionBar}>
<DiaryIconOkB diaryId={diary.id}
count={diary.like_count}
active={diary.liked}
clickable={!this.state.expired}
></DiaryIconOkB>
</View>
</View> </View>
</View> </View>
@ -125,7 +130,7 @@ const localStyle = StyleSheet.create({
overflow: "hidden", overflow: "hidden",
paddingHorizontal: 15, paddingHorizontal: 15,
paddingTop: 15, paddingTop: 15,
marginBottom: 30 marginBottom: 1
}, },
body: { body: {
flexDirection: "column", flexDirection: "column",
@ -137,6 +142,7 @@ const localStyle = StyleSheet.create({
title: { title: {
flexDirection: "row", flexDirection: "row",
alignItems: "flex-end", alignItems: "flex-end",
paddingRight: 10,
paddingBottom: 5 paddingBottom: 5
}, },
titleName: { titleName: {
@ -153,6 +159,14 @@ const localStyle = StyleSheet.create({
lineHeight: 24, lineHeight: 24,
color: Color.text, color: Color.text,
fontSize: 15, fontSize: 15,
paddingRight: 5,
textAlignVertical: 'bottom' textAlignVertical: 'bottom'
},
actionBar: {
flexDirection: 'row',
width: '100%',
height: 30,
marginTop: 15,
justifyContent: 'flex-end'
} }
}); });

View file

@ -1,9 +1,17 @@
import React, {Component} from 'react'; import React, {Component} from 'react';
import {StyleSheet, Text, View, Image, TouchableOpacity} from 'react-native'; import {
StyleSheet,
Text,
View,
Image,
TouchableOpacity,
DeviceEventEmitter
} from 'react-native';
import Color from '../../style/color'; import Color from '../../style/color';
import Api from '../../util/api'; import Api from '../../util/api';
import Msg from '../../util/msg'; import Msg from '../../util/msg';
import Event from '../../util/event';
export default class DiaryIconOkB extends Component { export default class DiaryIconOkB extends Component {
@ -17,6 +25,19 @@ export default class DiaryIconOkB extends Component {
active: props.active || false, active: props.active || false,
clickable: props.clickable && true clickable: props.clickable && true
} }
this.refreshBack = props.refreshBack || null;
}
componentWillReceiveProps(nextProps) {
this.setState({
diaryId: nextProps.diaryId || null,
count: nextProps.count || 0,
active: nextProps.active || false,
clickable: nextProps.clickable && true
});
this.refreshBack = nextProps.refreshBack || null;
} }
onPress() { onPress() {
@ -27,32 +48,25 @@ export default class DiaryIconOkB extends Component {
let count = this.state.count; let count = this.state.count;
let isActive = this.state.active; let isActive = this.state.active;
if(!isActive) { (!isActive ? Api.likeDiary(this.state.diaryId) : Api.cancelLikeDiary(this.state.diaryId))
Api.likeDiary(this.state.diaryId) .then(re => {
.then(re => { if(this.refreshBack) {
this.setState({ Api.getDiary(this.state.diaryId)
count: count + 1, .then(result => {
active: true if(result) {
this.refreshBack(result);
}
}) })
}) .done();
.catch(e => {
Msg.showMsg('操作失败');
})
.done();
} else { } else {
Api.cancelLikeDiary(this.state.diaryId) DeviceEventEmitter.emit(Event.updateDiarys);
.then(re => { }
this.setState({ })
count: count - 1, .catch(e => {
active: false Msg.showMsg('操作失败');
}) })
}) .done();
.catch(e => {
Msg.showMsg('操作失败');
})
.done();
}
} }
render() { render() {
@ -82,8 +96,7 @@ export default class DiaryIconOkB extends Component {
const localStyle = StyleSheet.create({ const localStyle = StyleSheet.create({
wrap: { wrap: {
flexDirection: 'row', flexDirection: 'row'
marginRight: 6
}, },
icon: { icon: {
width: 18, width: 18,

View file

@ -52,6 +52,12 @@ export default class DiaryList extends Component {
}); });
} }
scrollToTop() {
this.list.scrollToOffset({
offset: 0
});
}
_onUserIconPress(diary) { _onUserIconPress(diary) {
Navigation.push(this.props.componentId, { Navigation.push(this.props.componentId, {
component: { component: {
@ -72,7 +78,7 @@ export default class DiaryList extends Component {
}); });
} }
_onDiaryPress(diary) { _onDiaryPress(index, diary) {
Navigation.push(this.props.componentId, { Navigation.push(this.props.componentId, {
component: { component: {
name: 'DiaryDetail', name: 'DiaryDetail',
@ -89,7 +95,8 @@ export default class DiaryList extends Component {
diary: diary, diary: diary,
user: diary.user, user: diary.user,
editable: this.editable editable: this.editable,
refreshBack: this.refreshOne.bind(this, index)
} }
} }
}); });
@ -106,6 +113,18 @@ export default class DiaryList extends Component {
}); });
} }
refreshOne(index, diary) {
if(diary) {
let list = this.state.diaries;
diary.user = list[index].user;
list[index] = diary;
this.setState({
diaries: list
});
}
}
async refresh() { async refresh() {
if(this.state.refreshing) { if(this.state.refreshing) {
return; return;
@ -195,7 +214,7 @@ export default class DiaryList extends Component {
return ( return (
<View style={localStyle.container}> <View style={localStyle.container}>
<FlatList style={localStyle.list} <FlatList ref={(r) => this.list = r} style={localStyle.list}
data={this.state.diaries} data={this.state.diaries}
@ -203,20 +222,20 @@ export default class DiaryList extends Component {
return item.id + item.updated + item.comment_count + item.like_count; return item.id + item.updated + item.comment_count + item.like_count;
}} }}
renderItem={({item}) => { renderItem={({item, index}) => {
return ( return (
<Touchable onPress={() => this._onDiaryPress(item)}> <DiaryBrief {...this.props}
<DiaryBrief {...this.props} diary={item}
diary={item} showField={this.props.showField}
showField={this.props.showField} editable={this.editable}
editable={this.editable}
onUserIconPress={() => this._onUserIconPress(item)} onDiaryPress={this._onDiaryPress.bind(this, index)}
onPhotoPress={() => this._onPhotoPress(item.photoUrl)} onUserIconPress={() => this._onUserIconPress(item)}
> onPhotoPress={() => this._onPhotoPress(item.photoUrl)}
</DiaryBrief> refreshBack={this.refreshOne.bind(this, index)}
</Touchable> >
</DiaryBrief>
) )
}} }}

View file

@ -29,11 +29,12 @@ export default class NotebookDiaryList extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.editable = props.editable || false;
this.notebook = props.notebook; this.notebook = props.notebook;
this.editable = props.editable || false;
this.dataSource = new NotebookDiaryData(); this.dataSource = new NotebookDiaryData();
this.state = { this.state = {
rawlist: [],
diaries: [], diaries: [],
refreshing: false, refreshing: false,
@ -84,6 +85,19 @@ export default class NotebookDiaryList extends Component {
return result; return result;
} }
refreshOne(index, diary) {
console.log('index, diary:', index, diary);
if(diary) {
let list = this.state.rawlist;
diary.user = list[index].user;
list[index] = diary;
this.setState({
diaries: this.formatDiaries(list)
});
}
}
refresh() { refresh() {
if (this.state.refreshing) { if (this.state.refreshing) {
return; return;
@ -100,6 +114,8 @@ export default class NotebookDiaryList extends Component {
} else { } else {
let diaries = this.formatDiaries(result.list); let diaries = this.formatDiaries(result.list);
this.setState({ this.setState({
rawlist: result.list,
diaries, diaries,
hasMore: result.more, hasMore: result.more,
refreshFailed: false refreshFailed: false
@ -134,6 +150,8 @@ export default class NotebookDiaryList extends Component {
} else { } else {
let diaries = this.formatDiaries(result.list); let diaries = this.formatDiaries(result.list);
this.setState({ this.setState({
rawlist: result.list,
diaries, diaries,
hasMore: result.more, hasMore: result.more,
loadFailed: false loadFailed: false
@ -153,7 +171,7 @@ export default class NotebookDiaryList extends Component {
}); });
} }
_onDiaryPress(diary) { _onDiaryPress(index, diary) {
Navigation.push(this.props.componentId, { Navigation.push(this.props.componentId, {
component: { component: {
name: 'DiaryDetail', name: 'DiaryDetail',
@ -169,7 +187,9 @@ export default class NotebookDiaryList extends Component {
passProps: { passProps: {
diary: diary, diary: diary,
editable: this.editable, editable: this.editable,
expired: this.notebook.isExpired expired: this.notebook.isExpired,
refreshBack: this.refreshOne.bind(this, index)
} }
} }
}); });
@ -180,23 +200,31 @@ export default class NotebookDiaryList extends Component {
return null; return null;
} }
let isExpired = this.notebook.isExpired; let expired = this.notebook.isExpired;
return ( return (
<View style={localStyle.container}> <View style={localStyle.container}>
<SectionList <SectionList
keyExtractor={(item, index) => item.id} keyExtractor={(item, index) => {
return item.id + item.updated + item.comment_count + item.like_count;
}}
sections={this.state.diaries} sections={this.state.diaries}
renderItem={(rowData) => { renderItem={(rowData) => {
return (<Touchable onPress={() => this._onDiaryPress(rowData.item)}> return (
<DiaryBrief diary={rowData.item} isExpired={isExpired} <DiaryBrief
showField={['createdTime']}> diary={rowData.item}
} showField={['createdTime']}
expired={expired}
onDiaryPress={this._onDiaryPress.bind(this, rowData.index)}
refreshBack={this.refreshOne.bind(this, rowData.index)}
>
</DiaryBrief> </DiaryBrief>
</Touchable>); );
}} }}
renderSectionHeader={(info) => { renderSectionHeader={(info) => {

View file

@ -52,28 +52,29 @@ export default class UserIntro extends Component {
} }
render() { render() {
if(this.state.isLoading) {
return <Loading visible={this.state.isLoading}></Loading>;
}
const user = this.state.user; const user = this.state.user;
return user ? (
<ScrollView style={localStyle.container} automaticallyAdjustContentInsets={false}>
<View style={localStyle.userIcon}>
<UserIcon width={90} height={90} iconUrl={user.coverUrl} />
<Text style={localStyle.userTitle}>{user.name}</Text>
</View>
return this.state.isLoading {
? <Loading visible={this.state.isLoading}></Loading> user.intro && user.intro.length > 0
: ( ? (<Text style={localStyle.introText}>{user.intro}</Text>) : null
<ScrollView style={localStyle.container} automaticallyAdjustContentInsets={false}> }
<View style={localStyle.userIcon}>
<UserIcon width={90} height={90} iconUrl={user.coverUrl} /> <Text style={localStyle.joinTime}>
<Text style={localStyle.userTitle}>{user.name}</Text> {moment(user.created).format('YYYY年M月D日')}加入胶囊
</View> </Text>
{ </ScrollView>
user.intro && user.intro.length > 0 ) : null;
? (<Text style={localStyle.introText}>{user.intro}</Text>) : null
}
<Text style={localStyle.joinTime}>
{moment(user.created).format('YYYY年M月D日')}加入胶囊
</Text>
</ScrollView>
);
} }
} }

View file

@ -131,15 +131,10 @@ export default class DiaryDetailPage extends Component {
} }
} }
this.setState({ this.props.refreshBack(result);
diary: result this.diaryFull.refreshDiaryContent(result);
}, () => {
this.diaryFull.refreshDiaryContent();
})
}) })
.done(() => { .done();
});
} }
render() { render() {
@ -167,8 +162,8 @@ export default class DiaryDetailPage extends Component {
<DiaryFull ref={(r) => this.diaryFull = r} <DiaryFull ref={(r) => this.diaryFull = r}
{...this.props} {...this.props}
diary={this.state.diary} diary={this.state.diary}
refreshData={() => this.state.diary}
editable={this.state.editable || isMine} editable={this.state.editable || isMine}
expired={this.state.expired}
></DiaryFull> ></DiaryFull>
</ScrollView> </ScrollView>

View file

@ -16,6 +16,7 @@ import ActionSheet from 'react-native-actionsheet-api';
import Color from '../style/color' import Color from '../style/color'
import Api from '../util/api'; import Api from '../util/api';
import Update from '../util/update';
import DiaryList from '../component/diary/diaryList' import DiaryList from '../component/diary/diaryList'
import HomeDiaryData from '../dataLoader/homeDiaryData'; import HomeDiaryData from '../dataLoader/homeDiaryData';
@ -49,6 +50,24 @@ export default class HomePage extends Component {
} else { } else {
this.closeSplash(); this.closeSplash();
} }
this.bottomTabEventListener = Navigation.events().registerBottomTabSelectedListener(
({ selectedTabIndex, unselectedTabIndex }) => {
if(selectedTabIndex == unselectedTabIndex && selectedTabIndex == 0) {
this.diaryList.scrollToTop();
}
}
);
if(Api.IS_ANDROID) {
setTimeout(() => {
Update.updateAndroid();
}, 2000);
}
}
componentWillUnmount() {
this.bottomTabEventListener.remove();
} }
startTimer() { startTimer() {
@ -189,7 +208,7 @@ export default class HomePage extends Component {
<View style={localStyle.wrap}> <View style={localStyle.wrap}>
{ {
this.state.showSplash ? this.renderModal() : ( this.state.showSplash ? this.renderModal() : (
<DiaryList ref={(r) => this.list = r} <DiaryList ref={(r) => this.diaryList = r}
dataSource={this.dataSource} dataSource={this.dataSource}
listHeader={this.renderHeader.bind(this)} listHeader={this.renderHeader.bind(this)}
refreshHeader={this.refreshTopic.bind(this)} refreshHeader={this.refreshTopic.bind(this)}

View file

@ -193,6 +193,7 @@ export default class UserPage extends Component {
diary: () => <DiaryList diary: () => <DiaryList
ref={(r) => this.diaryList = r} ref={(r) => this.diaryList = r}
dataSource={this.dataSource} dataSource={this.dataSource}
showField={['subject', 'createdTime']}
editable={!this.user} editable={!this.user}
{...this.props} {...this.props}
/>, />,

View file

@ -339,6 +339,10 @@ async function feedback(content) {
return callV2('POST', '/feedback', {content}); return callV2('POST', '/feedback', {content});
} }
async function getUpdateInfo() {
return callV2('GET', '/updateInfo');
}
async function upload(method, api, body) { async function upload(method, api, body) {
let token = await Token.getUserToken(); let token = await Token.getUserToken();
@ -529,5 +533,6 @@ export default {
deleteNotebook, deleteNotebook,
report, report,
feedback feedback,
getUpdateInfo
} }

37
src/util/update.js Normal file
View file

@ -0,0 +1,37 @@
import React, {Component} from 'react';
import {
Alert,
Linking
} from 'react-native';
import Api from './api';
import Msg from './msg';
async function updateAndroid() {
try {
let info = await Api.getUpdateInfo();
// console.log('update info:', info, Api.VERSION);
if(info.lastestVersion > Api.VERSION) {
Alert.alert(
'发现新版本 v' + info.lastestVersion,
info.message,
[
{text: '以后再说', onPress: () => {}},
{text: '更新', onPress: () => downloadApk(info.apkUrl, info.lastestVersion)},
],
{cancelable: false}
)
}
} catch(e) {}
}
async function downloadApk(url, version) {
Linking.openURL(url);
Msg.showMsg('从浏览器下载更新包');
}
export default {
updateAndroid,
downloadApk
}