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

View file

@ -16,3 +16,6 @@
# 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
# org.gradle.parallel=true
android.useAndroidX=true
android.enableJetifier=true

View file

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

View file

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

View file

@ -8,6 +8,7 @@ import UserIcon from '../userIcon';
import Photo from '../photo';
import CommentList from '../comment/commentList';
import DiaryIconOkB from './diaryIconOkB';
export default class DiaryFull extends Component {
@ -17,16 +18,12 @@ export default class DiaryFull extends Component {
this.state = {
diary: props.diary,
editable: props.editable || false
editable: props.editable || false,
expired: props.expired || false
}
}
refreshDiaryContent() {
if(!this.props.refreshData) {
return;
}
let diary = this.props.refreshData();
refreshDiaryContent(diary) {
if(diary) {
this.setState({diary})
}
@ -103,6 +100,14 @@ export default class DiaryFull extends Component {
</Text>
<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>
@ -125,7 +130,7 @@ const localStyle = StyleSheet.create({
overflow: "hidden",
paddingHorizontal: 15,
paddingTop: 15,
marginBottom: 30
marginBottom: 1
},
body: {
flexDirection: "column",
@ -137,6 +142,7 @@ const localStyle = StyleSheet.create({
title: {
flexDirection: "row",
alignItems: "flex-end",
paddingRight: 10,
paddingBottom: 5
},
titleName: {
@ -153,6 +159,14 @@ const localStyle = StyleSheet.create({
lineHeight: 24,
color: Color.text,
fontSize: 15,
paddingRight: 5,
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 {StyleSheet, Text, View, Image, TouchableOpacity} from 'react-native';
import {
StyleSheet,
Text,
View,
Image,
TouchableOpacity,
DeviceEventEmitter
} from 'react-native';
import Color from '../../style/color';
import Api from '../../util/api';
import Msg from '../../util/msg';
import Event from '../../util/event';
export default class DiaryIconOkB extends Component {
@ -17,6 +25,19 @@ export default class DiaryIconOkB extends Component {
active: props.active || false,
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() {
@ -27,32 +48,25 @@ export default class DiaryIconOkB extends Component {
let count = this.state.count;
let isActive = this.state.active;
if(!isActive) {
Api.likeDiary(this.state.diaryId)
.then(re => {
this.setState({
count: count + 1,
active: true
(!isActive ? Api.likeDiary(this.state.diaryId) : Api.cancelLikeDiary(this.state.diaryId))
.then(re => {
if(this.refreshBack) {
Api.getDiary(this.state.diaryId)
.then(result => {
if(result) {
this.refreshBack(result);
}
})
})
.catch(e => {
Msg.showMsg('操作失败');
})
.done();
.done();
} else {
Api.cancelLikeDiary(this.state.diaryId)
.then(re => {
this.setState({
count: count - 1,
active: false
})
})
.catch(e => {
Msg.showMsg('操作失败');
})
.done();
}
} else {
DeviceEventEmitter.emit(Event.updateDiarys);
}
})
.catch(e => {
Msg.showMsg('操作失败');
})
.done();
}
render() {
@ -82,8 +96,7 @@ export default class DiaryIconOkB extends Component {
const localStyle = StyleSheet.create({
wrap: {
flexDirection: 'row',
marginRight: 6
flexDirection: 'row'
},
icon: {
width: 18,

View file

@ -52,6 +52,12 @@ export default class DiaryList extends Component {
});
}
scrollToTop() {
this.list.scrollToOffset({
offset: 0
});
}
_onUserIconPress(diary) {
Navigation.push(this.props.componentId, {
component: {
@ -72,7 +78,7 @@ export default class DiaryList extends Component {
});
}
_onDiaryPress(diary) {
_onDiaryPress(index, diary) {
Navigation.push(this.props.componentId, {
component: {
name: 'DiaryDetail',
@ -89,7 +95,8 @@ export default class DiaryList extends Component {
diary: diary,
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() {
if(this.state.refreshing) {
return;
@ -195,7 +214,7 @@ export default class DiaryList extends Component {
return (
<View style={localStyle.container}>
<FlatList style={localStyle.list}
<FlatList ref={(r) => this.list = r} style={localStyle.list}
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;
}}
renderItem={({item}) => {
renderItem={({item, index}) => {
return (
<Touchable onPress={() => this._onDiaryPress(item)}>
<DiaryBrief {...this.props}
diary={item}
showField={this.props.showField}
editable={this.editable}
<DiaryBrief {...this.props}
diary={item}
showField={this.props.showField}
editable={this.editable}
onUserIconPress={() => this._onUserIconPress(item)}
onPhotoPress={() => this._onPhotoPress(item.photoUrl)}
>
onDiaryPress={this._onDiaryPress.bind(this, index)}
onUserIconPress={() => this._onUserIconPress(item)}
onPhotoPress={() => this._onPhotoPress(item.photoUrl)}
</DiaryBrief>
</Touchable>
refreshBack={this.refreshOne.bind(this, index)}
>
</DiaryBrief>
)
}}

View file

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

View file

@ -52,28 +52,29 @@ export default class UserIntro extends Component {
}
render() {
if(this.state.isLoading) {
return <Loading visible={this.state.isLoading}></Loading>;
}
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>
: (
<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>
{
user.intro && user.intro.length > 0
? (<Text style={localStyle.introText}>{user.intro}</Text>) : null
}
{
user.intro && user.intro.length > 0
? (<Text style={localStyle.introText}>{user.intro}</Text>) : null
}
<Text style={localStyle.joinTime}>
{moment(user.created).format('YYYY年M月D日')}加入胶囊
</Text>
<Text style={localStyle.joinTime}>
{moment(user.created).format('YYYY年M月D日')}加入胶囊
</Text>
</ScrollView>
);
</ScrollView>
) : null;
}
}

View file

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

View file

@ -16,6 +16,7 @@ import ActionSheet from 'react-native-actionsheet-api';
import Color from '../style/color'
import Api from '../util/api';
import Update from '../util/update';
import DiaryList from '../component/diary/diaryList'
import HomeDiaryData from '../dataLoader/homeDiaryData';
@ -49,6 +50,24 @@ export default class HomePage extends Component {
} else {
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() {
@ -189,7 +208,7 @@ export default class HomePage extends Component {
<View style={localStyle.wrap}>
{
this.state.showSplash ? this.renderModal() : (
<DiaryList ref={(r) => this.list = r}
<DiaryList ref={(r) => this.diaryList = r}
dataSource={this.dataSource}
listHeader={this.renderHeader.bind(this)}
refreshHeader={this.refreshTopic.bind(this)}

View file

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

View file

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