From 997a24b3ef8f90e2309a882607a1d6101d7a1e0e Mon Sep 17 00:00:00 2001 From: kdxcxs Date: Sat, 5 Dec 2020 14:45:27 +0800 Subject: [PATCH 01/12] chore: set height of screenshot in readme to 500 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 415d16d..9792289 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ 哟慕课 APP 是一个第三方的优慕课客户端 -![screenshot](./screenshot.jpg) +screenshot ## 🎞 背景 -- 2.30.2 From a5e1d8b06a987fb4dc192bdf3a9f528ca6c77fa4 Mon Sep 17 00:00:00 2001 From: kdxcxs Date: Sat, 5 Dec 2020 17:22:29 +0800 Subject: [PATCH 02/12] refactor(YooForum): change `getTopicList` function --- src/component/YooForum.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/component/YooForum.js b/src/component/YooForum.js index 521d21c..4b496e5 100644 --- a/src/component/YooForum.js +++ b/src/component/YooForum.js @@ -14,6 +14,7 @@ export default class YooForum extends Component { this.state = { hint: '准备中', topics: [], + fetchedListPage: 0, }; this.initForum = this.initForum.bind(this); this.getTopicList = this.getTopicList.bind(this); @@ -87,9 +88,7 @@ export default class YooForum extends Component { 'http://eol.ctbu.edu.cn/meol/jpk/course/layout/newpage/index.jsp?courseId=46445', }, ) - .then(() => { - this.getTopicList(); - }) + .then(this.getTopicList) .catch(ShowErrorToast); }) .catch(ShowErrorToast); @@ -101,7 +100,8 @@ export default class YooForum extends Component { .catch(ShowErrorToast); } - getTopicList(page = 1) { + getTopicList() { + const page = this.state.fetchedListPage + 1; this.setState({hint: '获取数据中'}); gbkFetch( 'GET', @@ -128,7 +128,11 @@ export default class YooForum extends Component { current.next().text(), ); }); - this.setState({topics: [...this.state.topics, ...newTopics], hint: ''}); + this.setState({ + topics: [...this.state.topics, ...newTopics], + hint: '', + fetchedListPage: page, + }); }); } -- 2.30.2 From bda9548b66fa17287e66d0b4fa17b2ec6ac6d5fa Mon Sep 17 00:00:00 2001 From: kdxcxs Date: Sat, 5 Dec 2020 17:40:14 +0800 Subject: [PATCH 03/12] refactor(YooForum): make topic list a separate function component --- src/ui/YooForumUI.js | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/ui/YooForumUI.js b/src/ui/YooForumUI.js index 5c91e20..6e78311 100644 --- a/src/ui/YooForumUI.js +++ b/src/ui/YooForumUI.js @@ -45,6 +45,17 @@ const styles = StyleSheet.create({ }, }); +function ForumTopic(props) { + return props.topics.map((topic, key) => ( + + )); +} + export default class YooForumUI extends Component { constructor(props) { super(props); @@ -134,14 +145,11 @@ export default class YooForumUI extends Component { this.setState({currentPosition: event.nativeEvent.contentOffset.y}) }> {this.props.hint === '' ? ( - this.props.topics.map((topic, key) => ( - - )) + ) : ( Date: Sat, 5 Dec 2020 18:15:34 +0800 Subject: [PATCH 04/12] feat(YooForum): support getting more topics in the bottom of topic list --- src/component/YooForum.js | 14 ++++++++++++-- src/ui/YooForumUI.js | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/component/YooForum.js b/src/component/YooForum.js index 4b496e5..458c0bb 100644 --- a/src/component/YooForum.js +++ b/src/component/YooForum.js @@ -15,6 +15,7 @@ export default class YooForum extends Component { hint: '准备中', topics: [], fetchedListPage: 0, + gettingTopicList: false, }; this.initForum = this.initForum.bind(this); this.getTopicList = this.getTopicList.bind(this); @@ -102,7 +103,8 @@ export default class YooForum extends Component { getTopicList() { const page = this.state.fetchedListPage + 1; - this.setState({hint: '获取数据中'}); + page === 1 && this.setState({hint: '获取数据中'}); + this.setState({gettingTopicList: true}); gbkFetch( 'GET', `http://eol.ctbu.edu.cn/meol/common/faq/forum.jsp?viewtype=thread&forumid=102211&cateId=0&s_gotopage=${page}`, @@ -132,11 +134,19 @@ export default class YooForum extends Component { topics: [...this.state.topics, ...newTopics], hint: '', fetchedListPage: page, + gettingTopicList: false, }); }); } render() { - return ; + return ( + + ); } } diff --git a/src/ui/YooForumUI.js b/src/ui/YooForumUI.js index 6e78311..c144d91 100644 --- a/src/ui/YooForumUI.js +++ b/src/ui/YooForumUI.js @@ -8,6 +8,7 @@ import { Image, Animated, Dimensions, + Pressable, } from 'react-native'; import YooForumTopic from '../component/YooForumTopic'; import YooReply from './YooReply'; @@ -43,6 +44,10 @@ const styles = StyleSheet.create({ indicator: { marginTop: 64, }, + showMoreText: { + fontSize: 18, + margin: 8, + }, }); function ForumTopic(props) { @@ -56,6 +61,20 @@ function ForumTopic(props) { )); } +function ShowMore(props) { + return ( + props.getTopicList()}> + {props.gettingTopicList ? ( + 加载中 + ) : ( + 加载更多 + )} + + ); +} + export default class YooForumUI extends Component { constructor(props) { super(props); @@ -145,11 +164,17 @@ export default class YooForumUI extends Component { this.setState({currentPosition: event.nativeEvent.contentOffset.y}) }> {this.props.hint === '' ? ( - + + + + ) : ( Date: Sat, 5 Dec 2020 18:24:56 +0800 Subject: [PATCH 05/12] fix(YooForum): now the position of reply is changed with topic title height instead of a fixed value --- src/ui/YooForumTopicUI.js | 7 ++++++- src/ui/YooForumUI.js | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/ui/YooForumTopicUI.js b/src/ui/YooForumTopicUI.js index ef8e4ef..7e4e6df 100644 --- a/src/ui/YooForumTopicUI.js +++ b/src/ui/YooForumTopicUI.js @@ -32,6 +32,7 @@ export default class YooForumTopicUI extends Component { this.state = { detailShowing: false, layoutY: 0, + layoutHeight: 0, translate: new Animated.ValueXY({x: 0, y: 0}), }; this.topicHeaderRef = createRef(); @@ -50,6 +51,7 @@ export default class YooForumTopicUI extends Component { this.state.layoutY, this.onAnimationFinished, this.props.replies, + this.state.layoutHeight, ); } else { this.props.hideDetail(this.state.translate, this.onAnimationFinished); @@ -65,7 +67,10 @@ export default class YooForumTopicUI extends Component { ]} onPress={this.onPress} onLayout={(event) => { - this.setState({layoutY: event.nativeEvent.layout.y}); + this.setState({ + layoutY: event.nativeEvent.layout.y, + layoutHeight: event.nativeEvent.layout.height, + }); }}> {this.props.topic.owner} diff --git a/src/ui/YooForumUI.js b/src/ui/YooForumUI.js index c144d91..8dba2ba 100644 --- a/src/ui/YooForumUI.js +++ b/src/ui/YooForumUI.js @@ -90,14 +90,14 @@ export default class YooForumUI extends Component { this.hideDetail = this.hideDetail.bind(this); } - showDetail(translate, layoutY, onAnimationFinished, replies) { + showDetail(translate, layoutY, onAnimationFinished, replies, layoutHeight) { this.replyRef.current.scrollTo({x: 0, y: 0, animated: false}); this.setState({ scrollEnabled: false, currentReplies: replies, }); this.state.replyTranslateY.setValue( - this.state.currentPosition + 100 + screenHeight, + this.state.currentPosition + layoutHeight + screenHeight, ); Animated.parallel([ Animated.timing(translate, { @@ -114,7 +114,7 @@ export default class YooForumUI extends Component { useNativeDriver: true, }), Animated.timing(this.state.replyTranslateY, { - toValue: this.state.currentPosition + 100, + toValue: this.state.currentPosition + layoutHeight + 8, duration: 250, delay: 250, useNativeDriver: true, -- 2.30.2 From 9b11b7447704f61b1084050cb3186bcc63b902c1 Mon Sep 17 00:00:00 2001 From: kdxcxs Date: Sat, 5 Dec 2020 18:28:29 +0800 Subject: [PATCH 06/12] chore: update TODO --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9792289..a7cd94e 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,8 @@ ## 📄 TODO - [ ] 弹出键盘不遮挡输入框 -- [ ] 根据像素比例设置登录背景图片大小 -- [ ] 添加应用图标 +- [ ] ~~根据像素比例设置登录背景图片大小~~ +- [x] 添加应用图标 - [ ] 适配 iOS 登录失败 toast - [ ] 在启动时检测是否已登录 - [ ] Splash 检测网络出错后在 Splash UI 中显示错误并不跳转 @@ -29,5 +29,5 @@ - [ ] 解决当动画进行时点击话题标题动画会被打断的问题 - [ ] 支持显示回复图片 - [ ] 在回复中显示头像 -- [ ] 当滑动到页面底部时显示"加载更多" +- [x] 当滑动到页面底部时显示"加载更多" -- 2.30.2 From 2f7f2e13f16cbf52f8ac1912a177c1288ae87e41 Mon Sep 17 00:00:00 2001 From: kdxcxs Date: Sat, 5 Dec 2020 19:50:53 +0800 Subject: [PATCH 07/12] fix(YooLogin): avoid input from being hidden under virtual keyboard For some reason, components is not reachable as children of `Animated.View`, so I create some Animated components to replace the originals. --- README.md | 2 +- src/ui/YooLoginUI.js | 71 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 65 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a7cd94e..4899d49 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ ## 📄 TODO -- [ ] 弹出键盘不遮挡输入框 +- [x] 弹出键盘不遮挡输入框 - [ ] ~~根据像素比例设置登录背景图片大小~~ - [x] 添加应用图标 - [ ] 适配 iOS 登录失败 toast diff --git a/src/ui/YooLoginUI.js b/src/ui/YooLoginUI.js index 2bb6132..0989653 100644 --- a/src/ui/YooLoginUI.js +++ b/src/ui/YooLoginUI.js @@ -8,6 +8,8 @@ import { Text, ActivityIndicator, ToastAndroid, + Animated, + Keyboard, } from 'react-native'; const styles = StyleSheet.create({ @@ -45,15 +47,37 @@ const styles = StyleSheet.create({ fontSize: 26, }, }); +const AnimatedInput = Animated.createAnimatedComponent(TextInput); +const AnimatedImage = Animated.createAnimatedComponent(Image); +const AnimatedTouchableOpacity = Animated.createAnimatedComponent(TouchableOpacity); export default class YooLoginUI extends Component { constructor(props) { super(props); this.state = { loading: false, + translateY: new Animated.Value(0), }; this.onButtonPress = this.onButtonPress.bind(this); this.onLoginFailed = this.onLoginFailed.bind(this); + this.onKeyboardDidShow = this.onKeyboardDidShow.bind(this); + this.onKeyboardDidHide = this.onKeyboardDidHide.bind(this); + } + + componentDidMount() { + this.keyboardDidShowListener = Keyboard.addListener( + 'keyboardDidShow', + this.onKeyboardDidShow, + ); + this.keyboardDidHideListener = Keyboard.addListener( + 'keyboardDidHide', + this.onKeyboardDidHide, + ); + } + + componentWillUnmount() { + this.keyboardDidShowListener.remove(); + this.keyboardDidHideListener.remove(); } onButtonPress() { @@ -68,31 +92,64 @@ export default class YooLoginUI extends Component { ToastAndroid.show(hint, ToastAndroid.SHORT); } + onKeyboardDidShow() { + Animated.timing(this.state.translateY, { + toValue: -320, + duration: 250, + useNativeDriver: true, + }).start(); + } + + onKeyboardDidHide() { + Animated.timing(this.state.translateY, { + toValue: 0, + duration: 250, + useNativeDriver: true, + }).start(); + } + render() { return ( - - + this.props.setUsername(username)} /> - this.props.setPassword(password)} /> - + {this.state.loading ? ( ) : ( 登录 )} - + ); } -- 2.30.2 From eea7e722bd863873de701e286ae470d63ab561c9 Mon Sep 17 00:00:00 2001 From: kdxcxs Date: Sat, 5 Dec 2020 20:06:13 +0800 Subject: [PATCH 08/12] chore: update TODO --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4899d49..8ace531 100644 --- a/README.md +++ b/README.md @@ -30,4 +30,5 @@ - [ ] 支持显示回复图片 - [ ] 在回复中显示头像 - [x] 当滑动到页面底部时显示"加载更多" +- [ ] 支持通过 Android BackHandler 从话题详情返回话题列表 -- 2.30.2 From c139df8fdfc5c9f1053abc488f13d1b373715329 Mon Sep 17 00:00:00 2001 From: kdxcxs Date: Sun, 6 Dec 2020 13:34:01 +0800 Subject: [PATCH 09/12] fix(YooBackground): make background image also move in the first 10 seconds --- src/ui/YooBackground.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/ui/YooBackground.js b/src/ui/YooBackground.js index df8a9a6..ae44fbf 100644 --- a/src/ui/YooBackground.js +++ b/src/ui/YooBackground.js @@ -10,6 +10,17 @@ export default function YooBackground() { zIndex: -1, }, }); + // The animation won't play for the first 10 seconds + // if we just use setInterval. So we start the animation + // at the component mounted just for the first 10 seconds. + Animated.timing(imgPosition, { + toValue: { + x: -Math.floor(Math.random() * 5000), + y: -Math.floor(Math.random() * 3000), + }, + duration: 10000, + useNativeDriver: true, + }).start(); setInterval(() => { Animated.timing(imgPosition, { toValue: { -- 2.30.2 From 09c29e1ba57fc9d14ce21c51eebcd8c40319fa86 Mon Sep 17 00:00:00 2001 From: kdxcxs Date: Sun, 6 Dec 2020 13:56:39 +0800 Subject: [PATCH 10/12] refactor: pass `this` to YooForumUI when YooForumTopicUI is clicked As feature is growing fast, it needs to pass to many args, so i decide to pass this instead. --- src/ui/YooForumTopicUI.js | 10 ++-------- src/ui/YooForumUI.js | 35 +++++++++++++++++++++++++---------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/ui/YooForumTopicUI.js b/src/ui/YooForumTopicUI.js index 7e4e6df..183df9b 100644 --- a/src/ui/YooForumTopicUI.js +++ b/src/ui/YooForumTopicUI.js @@ -46,15 +46,9 @@ export default class YooForumTopicUI extends Component { onPress() { if (!this.state.detailShowing) { - this.props.showDetail( - this.state.translate, - this.state.layoutY, - this.onAnimationFinished, - this.props.replies, - this.state.layoutHeight, - ); + this.props.showDetail(this); } else { - this.props.hideDetail(this.state.translate, this.onAnimationFinished); + this.props.hideDetail(); } } diff --git a/src/ui/YooForumUI.js b/src/ui/YooForumUI.js index 8dba2ba..a5cf94f 100644 --- a/src/ui/YooForumUI.js +++ b/src/ui/YooForumUI.js @@ -88,22 +88,29 @@ export default class YooForumUI extends Component { this.replyRef = createRef(); this.showDetail = this.showDetail.bind(this); this.hideDetail = this.hideDetail.bind(this); + this.currentForumTopicUI = null; } - showDetail(translate, layoutY, onAnimationFinished, replies, layoutHeight) { + showDetail(ForumTopicUI) { + this.currentForumTopicUI = ForumTopicUI; this.replyRef.current.scrollTo({x: 0, y: 0, animated: false}); this.setState({ scrollEnabled: false, - currentReplies: replies, + currentReplies: this.currentForumTopicUI.props.replies, }); this.state.replyTranslateY.setValue( - this.state.currentPosition + layoutHeight + screenHeight, + this.state.currentPosition + + this.currentForumTopicUI.state.layoutHeight + + screenHeight, ); Animated.parallel([ - Animated.timing(translate, { + Animated.timing(this.currentForumTopicUI.state.translate, { toValue: { x: screenWidth, - y: this.state.currentPosition - layoutY + 8, + y: + this.state.currentPosition - + this.currentForumTopicUI.state.layoutY + + 8, }, duration: 250, useNativeDriver: true, @@ -114,18 +121,21 @@ export default class YooForumUI extends Component { useNativeDriver: true, }), Animated.timing(this.state.replyTranslateY, { - toValue: this.state.currentPosition + layoutHeight + 8, + toValue: + this.state.currentPosition + + this.currentForumTopicUI.state.layoutHeight + + 8, duration: 250, delay: 250, useNativeDriver: true, }), - ]).start(onAnimationFinished); + ]).start(this.currentForumTopicUI.onAnimationFinished); } - hideDetail(translate, onAnimationFinished) { + hideDetail() { this.setState({scrollEnabled: true}); Animated.parallel([ - Animated.timing(translate, { + Animated.timing(this.currentForumTopicUI.state.translate, { toValue: { x: 0, y: 0, @@ -143,7 +153,12 @@ export default class YooForumUI extends Component { duration: 250, useNativeDriver: true, }), - ]).start(onAnimationFinished); + ]).start( + function () { + this.currentForumTopicUI.onAnimationFinished(); + this.currentForumTopicUI = null; + }.bind(this), + ); } render() { -- 2.30.2 From 5c121dec37312ff3646b20ce2736cf427293cba5 Mon Sep 17 00:00:00 2001 From: kdxcxs Date: Sun, 6 Dec 2020 14:04:46 +0800 Subject: [PATCH 11/12] feat: support go back to topic list by Android back action --- README.md | 2 +- src/ui/YooForumUI.js | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8ace531..dceaa97 100644 --- a/README.md +++ b/README.md @@ -30,5 +30,5 @@ - [ ] 支持显示回复图片 - [ ] 在回复中显示头像 - [x] 当滑动到页面底部时显示"加载更多" -- [ ] 支持通过 Android BackHandler 从话题详情返回话题列表 +- [x] 支持通过 Android BackHandler 从话题详情返回话题列表 diff --git a/src/ui/YooForumUI.js b/src/ui/YooForumUI.js index a5cf94f..86b4099 100644 --- a/src/ui/YooForumUI.js +++ b/src/ui/YooForumUI.js @@ -9,6 +9,7 @@ import { Animated, Dimensions, Pressable, + BackHandler, } from 'react-native'; import YooForumTopic from '../component/YooForumTopic'; import YooReply from './YooReply'; @@ -88,9 +89,24 @@ export default class YooForumUI extends Component { this.replyRef = createRef(); this.showDetail = this.showDetail.bind(this); this.hideDetail = this.hideDetail.bind(this); + this.handleAndroidBack = this.handleAndroidBack.bind(this); this.currentForumTopicUI = null; } + componentDidMount() { + this.backHandler = BackHandler.addEventListener( + 'hardwareBackPress', + this.handleAndroidBack, + ); + } + + handleAndroidBack() { + if (this.currentForumTopicUI){ + this.hideDetail(); + return true; + } + } + showDetail(ForumTopicUI) { this.currentForumTopicUI = ForumTopicUI; this.replyRef.current.scrollTo({x: 0, y: 0, animated: false}); -- 2.30.2 From 6952c7b97c10bede9b93f39781f7406f86eb2793 Mon Sep 17 00:00:00 2001 From: kdxcxs Date: Sun, 6 Dec 2020 14:25:14 +0800 Subject: [PATCH 12/12] feat: support confirming exiting app by Android back action --- src/ui/YooForumUI.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ui/YooForumUI.js b/src/ui/YooForumUI.js index 86b4099..053de93 100644 --- a/src/ui/YooForumUI.js +++ b/src/ui/YooForumUI.js @@ -10,6 +10,7 @@ import { Dimensions, Pressable, BackHandler, + ToastAndroid, } from 'react-native'; import YooForumTopic from '../component/YooForumTopic'; import YooReply from './YooReply'; @@ -85,6 +86,7 @@ export default class YooForumUI extends Component { translateX: new Animated.Value(0), currentReplies: [], replyTranslateY: new Animated.Value(0), + lastPressingBack: 0, }; this.replyRef = createRef(); this.showDetail = this.showDetail.bind(this); @@ -101,9 +103,15 @@ export default class YooForumUI extends Component { } handleAndroidBack() { - if (this.currentForumTopicUI){ + if (this.currentForumTopicUI) { this.hideDetail(); return true; + } else if (new Date().getTime() - this.state.lastPressingBack <= 1000) { + BackHandler.exitApp(); + } else { + this.setState({lastPressingBack: new Date().getTime()}); + ToastAndroid.show('再按一次以退出Yoomooc', ToastAndroid.SHORT); + return true; } } -- 2.30.2