diff --git a/android/app/build.gradle b/android/app/build.gradle index 700db09..cd91b28 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -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 } diff --git a/android/gradle.properties b/android/gradle.properties index 89e0d99..ccb748f 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -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 \ No newline at end of file diff --git a/src/component/comment/comment.js b/src/component/comment/comment.js index 5fdb75d..b037835 100644 --- a/src/component/comment/comment.js +++ b/src/component/comment/comment.js @@ -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: { diff --git a/src/component/diary/diaryBrief.js b/src/component/diary/diaryBrief.js index baad127..ed42f0c 100644 --- a/src/component/diary/diaryBrief.js +++ b/src/component/diary/diaryBrief.js @@ -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 ( + this.props.onDiaryPress ? this.props.onDiaryPress(this.state.diary) : null}> {(user && user.iconUrl && this.show('userIcon')) ? : null} @@ -80,7 +91,9 @@ export default class DiaryBrief extends Component { + clickable={!this.expired} + refreshBack={this.refreshDiary.bind(this)} + > { @@ -96,6 +109,7 @@ export default class DiaryBrief extends Component { + ); } } diff --git a/src/component/diary/diaryFull.js b/src/component/diary/diaryFull.js index 1641ec7..a917ae8 100644 --- a/src/component/diary/diaryFull.js +++ b/src/component/diary/diaryFull.js @@ -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 { this._onPhotoPress(diary.photoUrl)}> + + + + @@ -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' } }); diff --git a/src/component/diary/diaryIconOkB.js b/src/component/diary/diaryIconOkB.js index 8b3a6c1..5856fc1 100644 --- a/src/component/diary/diaryIconOkB.js +++ b/src/component/diary/diaryIconOkB.js @@ -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, diff --git a/src/component/diary/diaryList.js b/src/component/diary/diaryList.js index 6a5710b..e6a246a 100644 --- a/src/component/diary/diaryList.js +++ b/src/component/diary/diaryList.js @@ -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 ( - 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 ( - this._onDiaryPress(item)}> - this._onUserIconPress(item)} - onPhotoPress={() => this._onPhotoPress(item.photoUrl)} - > + onDiaryPress={this._onDiaryPress.bind(this, index)} + onUserIconPress={() => this._onUserIconPress(item)} + onPhotoPress={() => this._onPhotoPress(item.photoUrl)} - - + refreshBack={this.refreshOne.bind(this, index)} + > + ) }} diff --git a/src/component/notebook/notebookDiaryList.js b/src/component/notebook/notebookDiaryList.js index 4e86401..dccf81d 100644 --- a/src/component/notebook/notebookDiaryList.js +++ b/src/component/notebook/notebookDiaryList.js @@ -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 ( item.id} + keyExtractor={(item, index) => { + return item.id + item.updated + item.comment_count + item.like_count; + }} sections={this.state.diaries} renderItem={(rowData) => { - return ( this._onDiaryPress(rowData.item)}> - - } + return ( + - ); + ); }} renderSectionHeader={(info) => { diff --git a/src/component/userIntro.js b/src/component/userIntro.js index 4784a59..9a612b2 100644 --- a/src/component/userIntro.js +++ b/src/component/userIntro.js @@ -52,28 +52,29 @@ export default class UserIntro extends Component { } render() { + if(this.state.isLoading) { + return ; + } + const user = this.state.user; + return user ? ( + + + + {user.name} + - return this.state.isLoading - ? - : ( - - - - {user.name} - + { + user.intro && user.intro.length > 0 + ? ({user.intro}) : null + } + + + {moment(user.created).format('YYYY年M月D日')}加入胶囊 + - { - user.intro && user.intro.length > 0 - ? ({user.intro}) : null - } - - - {moment(user.created).format('YYYY年M月D日')}加入胶囊 - - - - ); + + ) : null; } } diff --git a/src/page/DiaryDetailPage.js b/src/page/DiaryDetailPage.js index 72553e1..572d50d 100644 --- a/src/page/DiaryDetailPage.js +++ b/src/page/DiaryDetailPage.js @@ -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 { this.diaryFull = r} {...this.props} diary={this.state.diary} - refreshData={() => this.state.diary} editable={this.state.editable || isMine} + expired={this.state.expired} > diff --git a/src/page/HomePage.js b/src/page/HomePage.js index 6c18b73..b8491f1 100644 --- a/src/page/HomePage.js +++ b/src/page/HomePage.js @@ -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 { { this.state.showSplash ? this.renderModal() : ( - this.list = r} + this.diaryList = r} dataSource={this.dataSource} listHeader={this.renderHeader.bind(this)} refreshHeader={this.refreshTopic.bind(this)} diff --git a/src/page/UserPage.js b/src/page/UserPage.js index caa45c2..582d8e4 100644 --- a/src/page/UserPage.js +++ b/src/page/UserPage.js @@ -193,6 +193,7 @@ export default class UserPage extends Component { diary: () => this.diaryList = r} dataSource={this.dataSource} + showField={['subject', 'createdTime']} editable={!this.user} {...this.props} />, diff --git a/src/util/api.js b/src/util/api.js index 2103045..1b75aa2 100644 --- a/src/util/api.js +++ b/src/util/api.js @@ -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 } \ No newline at end of file diff --git a/src/util/update.js b/src/util/update.js new file mode 100644 index 0000000..9be9e93 --- /dev/null +++ b/src/util/update.js @@ -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 +} \ No newline at end of file