diff --git a/README.md b/README.md
index 415d16d..dceaa97 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
哟慕课 APP 是一个第三方的优慕课客户端
-![screenshot](./screenshot.jpg)
+
## 🎞 背景
@@ -18,9 +18,9 @@
## 📄 TODO
-- [ ] 弹出键盘不遮挡输入框
-- [ ] 根据像素比例设置登录背景图片大小
-- [ ] 添加应用图标
+- [x] 弹出键盘不遮挡输入框
+- [ ] ~~根据像素比例设置登录背景图片大小~~
+- [x] 添加应用图标
- [ ] 适配 iOS 登录失败 toast
- [ ] 在启动时检测是否已登录
- [ ] Splash 检测网络出错后在 Splash UI 中显示错误并不跳转
@@ -29,5 +29,6 @@
- [ ] 解决当动画进行时点击话题标题动画会被打断的问题
- [ ] 支持显示回复图片
- [ ] 在回复中显示头像
-- [ ] 当滑动到页面底部时显示"加载更多"
+- [x] 当滑动到页面底部时显示"加载更多"
+- [x] 支持通过 Android BackHandler 从话题详情返回话题列表
diff --git a/src/component/YooForum.js b/src/component/YooForum.js
index 521d21c..458c0bb 100644
--- a/src/component/YooForum.js
+++ b/src/component/YooForum.js
@@ -14,6 +14,8 @@ export default class YooForum extends Component {
this.state = {
hint: '准备中',
topics: [],
+ fetchedListPage: 0,
+ gettingTopicList: false,
};
this.initForum = this.initForum.bind(this);
this.getTopicList = this.getTopicList.bind(this);
@@ -87,9 +89,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,8 +101,10 @@ export default class YooForum extends Component {
.catch(ShowErrorToast);
}
- getTopicList(page = 1) {
- this.setState({hint: '获取数据中'});
+ getTopicList() {
+ const page = this.state.fetchedListPage + 1;
+ 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}`,
@@ -128,11 +130,23 @@ 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,
+ gettingTopicList: false,
+ });
});
}
render() {
- return ;
+ return (
+
+ );
}
}
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: {
diff --git a/src/ui/YooForumTopicUI.js b/src/ui/YooForumTopicUI.js
index ef8e4ef..183df9b 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();
@@ -45,14 +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.props.showDetail(this);
} else {
- this.props.hideDetail(this.state.translate, this.onAnimationFinished);
+ this.props.hideDetail();
}
}
@@ -65,7 +61,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 5c91e20..053de93 100644
--- a/src/ui/YooForumUI.js
+++ b/src/ui/YooForumUI.js
@@ -8,6 +8,9 @@ import {
Image,
Animated,
Dimensions,
+ Pressable,
+ BackHandler,
+ ToastAndroid,
} from 'react-native';
import YooForumTopic from '../component/YooForumTopic';
import YooReply from './YooReply';
@@ -43,8 +46,37 @@ const styles = StyleSheet.create({
indicator: {
marginTop: 64,
},
+ showMoreText: {
+ fontSize: 18,
+ margin: 8,
+ },
});
+function ForumTopic(props) {
+ return props.topics.map((topic, key) => (
+
+ ));
+}
+
+function ShowMore(props) {
+ return (
+ props.getTopicList()}>
+ {props.gettingTopicList ? (
+ 加载中
+ ) : (
+ 加载更多
+ )}
+
+ );
+}
+
export default class YooForumUI extends Component {
constructor(props) {
super(props);
@@ -54,26 +86,55 @@ 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);
this.hideDetail = this.hideDetail.bind(this);
+ this.handleAndroidBack = this.handleAndroidBack.bind(this);
+ this.currentForumTopicUI = null;
}
- showDetail(translate, layoutY, onAnimationFinished, replies) {
+ componentDidMount() {
+ this.backHandler = BackHandler.addEventListener(
+ 'hardwareBackPress',
+ this.handleAndroidBack,
+ );
+ }
+
+ handleAndroidBack() {
+ 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;
+ }
+ }
+
+ 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 + 100 + 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,
@@ -84,18 +145,21 @@ export default class YooForumUI extends Component {
useNativeDriver: true,
}),
Animated.timing(this.state.replyTranslateY, {
- toValue: this.state.currentPosition + 100,
+ 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,
@@ -113,7 +177,12 @@ export default class YooForumUI extends Component {
duration: 250,
useNativeDriver: true,
}),
- ]).start(onAnimationFinished);
+ ]).start(
+ function () {
+ this.currentForumTopicUI.onAnimationFinished();
+ this.currentForumTopicUI = null;
+ }.bind(this),
+ );
}
render() {
@@ -134,14 +203,17 @@ export default class YooForumUI extends Component {
this.setState({currentPosition: event.nativeEvent.contentOffset.y})
}>
{this.props.hint === '' ? (
- this.props.topics.map((topic, key) => (
-
+
- ))
+
+
) : (
-
-
+ this.props.setUsername(username)}
/>
- this.props.setPassword(password)}
/>
-
+
{this.state.loading ? (
) : (
登录
)}
-
+
);
}