diff --git a/App.js b/App.js
index 62b5a4c..14832d3 100644
--- a/App.js
+++ b/App.js
@@ -23,6 +23,7 @@ import {Navigation} from 'react-native-navigation';
import Color from './src/style/color';
import Api from './src/util/api';
+import BottomNav from './src/nav/bottomNav';
import Loading from './src/component/loading';
import LoginForm from './src/component/loginForm';
@@ -55,14 +56,28 @@ export default class App extends Component {
});
}
+ _onSucc() {
+ Api.getSplashByStore()
+ .then(splash => {
+ Navigation.setRoot(BottomNav.config(splash));
+ })
+ .done();
+ }
+
render() {
return (
{this.state.isLoginPage
- ? ()
- : ()}
+ ? ()
+ : ()}
diff --git a/index.js b/index.js
index 9c488a0..d3c359e 100644
--- a/index.js
+++ b/index.js
@@ -9,10 +9,15 @@ import {Icon, loadIcon} from './src/style/icon';
import App from './App';
import Token from './src/util/token';
+import Api from './src/util/api';
import PageList from './src/page/_list';
import BottomNav from './src/nav/bottomNav';
+// for debug
+// console.disableYellowBox = true;
+
+
Navigation.registerComponent('Timepill', () => App);
// regist screens automatically
for(let pageName in PageList) {
@@ -81,6 +86,11 @@ Navigation.events().registerAppLaunchedListener(async () => {
Alert.alert("loadIcon err: " + err.toString());
}
+ try {
+ await Api.syncSplash();
+ } catch (err) {}
+
+
let token = await Token.getUserToken();
// let token;
if(!token) {
diff --git a/src/component/diary/diaryList.js b/src/component/diary/diaryList.js
index 18b0268..208ff70 100644
--- a/src/component/diary/diaryList.js
+++ b/src/component/diary/diaryList.js
@@ -10,7 +10,6 @@ import {
} from 'react-native';
import {Navigation} from 'react-native-navigation';
import {Divider} from "react-native-elements";
-import ActionSheet from 'react-native-actionsheet-api';
import Color from '../../style/color';
import Msg from '../../util/msg';
@@ -248,7 +247,7 @@ export default class DiaryList extends Component {
onEndReached={this.state.hasMore ? this.loadMore.bind(this) : null}
>
-
+
);
}
diff --git a/src/component/loginForm.js b/src/component/loginForm.js
index eb2ad0d..5f58e6b 100644
--- a/src/component/loginForm.js
+++ b/src/component/loginForm.js
@@ -37,7 +37,7 @@ export default class LoginForm extends Component {
_checkResult(result) {
InteractionManager.runAfterInteractions(() => {
if(result.isLoginSucc) {
- Navigation.setRoot(BottomNav.config());
+ this.props.onLoginSucc();
} else {
Alert.alert(
diff --git a/src/nav/bottomNav.js b/src/nav/bottomNav.js
index 1a2b62a..04a8bb7 100644
--- a/src/nav/bottomNav.js
+++ b/src/nav/bottomNav.js
@@ -1,7 +1,7 @@
import Color from '../style/color'
import {Icon} from '../style/icon'
-function config() {
+function config(splash) {
return {
root: {
bottomTabs: {
@@ -19,11 +19,23 @@ function config() {
name: 'Home',
options: {
topBar: {
- // visible: false,
+ visible: false,
+ animate: false
+
+ /*
title: {
text: '首页'
}
+ */
+ },
+ bottomTabs: {
+ visible: false,
+ animate: false
}
+
+ },
+ passProps: {
+ splash: splash
}
}
}],
diff --git a/src/page/AboutPage.js b/src/page/AboutPage.js
index 75c853e..d80bbfa 100644
--- a/src/page/AboutPage.js
+++ b/src/page/AboutPage.js
@@ -8,7 +8,7 @@ import {
} from 'react-native';
import Api from '../util/api';
-import TokenManager from '../util/token';
+import Token from '../util/token';
import Event from '../util/event';
import Color from '../style/color';
import UpdateInfo from '../updateInfo';
@@ -36,7 +36,7 @@ export default class AboutPage extends Component {
}
componentDidMount() {
- TokenManager.setUpdateVersion(UpdateInfo.version)
+ Token.setUpdateVersion(UpdateInfo.version)
.then(() => {
DeviceEventEmitter.emit(Event.updateNewsRead);
});
diff --git a/src/page/HomePage.js b/src/page/HomePage.js
index c83f627..6c18b73 100644
--- a/src/page/HomePage.js
+++ b/src/page/HomePage.js
@@ -1,6 +1,18 @@
import React, {Component} from 'react';
-import {StyleSheet, Text, View, TouchableOpacity, ImageBackground} from 'react-native';
+import {
+ StyleSheet,
+ Text,
+ View,
+ TouchableOpacity,
+ ImageBackground,
+ Animated,
+ Modal,
+ TouchableWithoutFeedback,
+ InteractionManager
+} from 'react-native';
import {Navigation} from 'react-native-navigation';
+import {Button} from 'react-native-elements';
+import ActionSheet from 'react-native-actionsheet-api';
import Color from '../style/color'
import Api from '../util/api';
@@ -15,11 +27,132 @@ export default class HomePage extends Component {
super(props);
this.dataSource = new HomeDiaryData();
+ let splash = props.splash;
this.state = {
+ hasSplash: splash ? true : false,
+
+ showSplash: true,
+ fadeInOpacity: new Animated.Value(0),
+
+ splashTime : 3,
+ splashImage: splash ? splash.image_url : null,
+ splashLink: splash ? splash.link : null,
+
topic: null
}
}
+ componentDidMount() {
+ if(this.state.hasSplash) {
+ this.openSplash();
+
+ } else {
+ this.closeSplash();
+ }
+ }
+
+ startTimer() {
+ this.timer = setInterval(() => {
+ let newTime = this.state.splashTime - 1;
+ this.setState({
+ splashTime: newTime
+ });
+
+ if(newTime == 0) {
+ this.closeSplash();
+ }
+
+ }, 1000);
+ }
+
+ openSplash() {
+ Animated.timing(
+ this.state.fadeInOpacity,
+ {
+ toValue: 1,
+ duration: 1000,
+ }
+
+ ).start(() => {
+ this.startTimer();
+ });
+ }
+
+ closeSplash() {
+ if(this.timer) {
+ clearTimeout(this.timer);
+ }
+
+ Animated.timing(
+ this.state.fadeInOpacity,
+ {
+ toValue: 0,
+ duration: 500,
+ }
+
+ ).start(() => {
+
+ Navigation.mergeOptions(this.props.componentId, {
+ topBar: {
+ visible: true,
+ title: {
+ text: '首页'
+ }
+ },
+ bottomTabs: {
+ visible: true
+ }
+ });
+
+ this.setState({
+ showSplash: false
+ })
+
+ });
+ }
+
+ onSplashPress() {
+ if(this.state.splashLink) {
+ if(this.timer) {
+ clearTimeout(this.timer);
+ }
+
+ Navigation.mergeOptions(this.props.componentId, {
+ topBar: {
+ visible: true,
+ title: {
+ text: '首页'
+ }
+ },
+ bottomTabs: {
+ visible: true
+ }
+ });
+
+ Navigation.push(this.props.componentId, {
+ component: {
+ name: 'WebView',
+ options: {
+ bottomTabs: {
+ visible: false,
+
+ // hide bottom tab for android
+ drawBehind: true,
+ animate: true
+ }
+ },
+ passProps: this.state.splashLink.passProps
+ }
+ }).then(() => {
+
+ this.setState({
+ showSplash: false,
+ fadeInOpacity: new Animated.Value(0)
+ });
+ });
+ }
+ }
+
refreshTopic() {
Api.getTodayTopic()
.then(topic => {
@@ -53,14 +186,19 @@ export default class HomePage extends Component {
render() {
return (
-
- this.list = r}
- dataSource={this.dataSource}
- listHeader={this.renderHeader.bind(this)}
- refreshHeader={this.refreshTopic.bind(this)}
- {...this.props}
- >
-
+
+ {
+ this.state.showSplash ? this.renderModal() : (
+ this.list = r}
+ dataSource={this.dataSource}
+ listHeader={this.renderHeader.bind(this)}
+ refreshHeader={this.refreshTopic.bind(this)}
+ {...this.props}
+ >
+ )
+ }
+
+
);
}
@@ -78,6 +216,36 @@ export default class HomePage extends Component {
) : null;
}
+
+ renderModal() {
+ return (
+ {}}
+ onRequestClose={() => {}}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
}
const localStyle = StyleSheet.create({
@@ -116,5 +284,30 @@ const localStyle = StyleSheet.create({
textShadowOffset: { width: 1, height: 1 },
textShadowRadius: 2,
shadowOpacity: 0.5
+ },
+
+ closeButtonWrap: {
+ flexDirection: 'row-reverse',
+ marginTop: Api.IS_IOS ? (Api.IS_IPHONEX ? 50 : 30) : 20
+ },
+ closeButtonContainer: {
+ width: 80,
+ backgroundColor: 'black',
+ opacity: 0.75,
+ borderRadius: 40,
+ marginRight: 15
+ },
+ closeButton: {
+ borderWidth: 1,
+ borderColor: 'black',
+ paddingVertical: 5,
+ paddingHorizontal: 0,
+ backgroundColor: 'black'
+ },
+ closeButtonText: {
+ fontWeight: 'bold',
+ fontSize: 14,
+ color: 'white',
+ fontFamily: 'Helvetica'
}
});
diff --git a/src/page/PasswordPage.js b/src/page/PasswordPage.js
index 7b13c29..769b33b 100644
--- a/src/page/PasswordPage.js
+++ b/src/page/PasswordPage.js
@@ -11,7 +11,7 @@ import {Navigation} from 'react-native-navigation';
import Color from '../style/color';
import Api from '../util/api'
-import TokenManager from '../util/token';
+import Token from '../util/token';
import Event from '../util/event';
import Msg from '../util/msg';
@@ -34,7 +34,7 @@ export default class PasswordPage extends Component {
}
componentDidMount() {
- TokenManager.getLoginPassword()
+ Token.getLoginPassword()
.then(pwd => {
if(this.props.type == 'setting') {
if(this.props.operation == 'setnew') {
@@ -99,7 +99,7 @@ export default class PasswordPage extends Component {
});
} else {
- TokenManager.setLoginPassword(password)
+ Token.setLoginPassword(password)
.then(() => {
Keyboard.dismiss();
Msg.showMsg('密码已设置');
@@ -142,7 +142,7 @@ export default class PasswordPage extends Component {
return;
}
- TokenManager.setLoginPassword('')
+ Token.setLoginPassword('')
.then(() => {
Keyboard.dismiss();
Msg.showMsg('密码已清除');
diff --git a/src/page/SettingPage.js b/src/page/SettingPage.js
index 6b300e1..6f6b47b 100644
--- a/src/page/SettingPage.js
+++ b/src/page/SettingPage.js
@@ -13,7 +13,7 @@ import {Navigation} from 'react-native-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
import Api from '../util/api';
-import TokenManager from '../util/token';
+import Token from '../util/token';
import Color from '../style/color';
@@ -50,7 +50,7 @@ export default class SettingPage extends Component {
}
refreshPasswordState() {
- TokenManager.getLoginPassword()
+ Token.getLoginPassword()
.then((pwd) => this.setState({
hasPassword: pwd != null && pwd.length > 0
}));
diff --git a/src/page/WebViewPage.js b/src/page/WebViewPage.js
new file mode 100644
index 0000000..79c373b
--- /dev/null
+++ b/src/page/WebViewPage.js
@@ -0,0 +1,93 @@
+import React, {Component} from 'react';
+import {
+ WebView,
+ Linking,
+ BackHandler
+} from 'react-native';
+import {Navigation} from 'react-native-navigation';
+
+import {Icon} from '../style/icon'
+
+
+export default class WebViewPage extends Component {
+
+ constructor(props) {
+ super(props);
+ Navigation.events().bindComponent(this);
+
+ this.webViewState = {
+ canGoBack: false,
+ canGoForward: false,
+ loading: true,
+ target: 0,
+ url: this.props.uri
+ };
+ }
+
+ static options(passProps) {
+ return {
+ topBar: {
+ title: {
+ text: '加载中...'
+ },
+ leftButtons: [{
+ id: 'back', icon: Icon.navButtonBack
+ }],
+ rightButtons: [{
+ id: "open",
+ icon: Icon.navButtonOpen
+ }]
+ }
+ };
+ }
+
+ componentWillMount() {
+ BackHandler.addEventListener('hardwareBackPress', this.goBack);
+ }
+
+ componentWillUnmount() {
+ BackHandler.removeEventListener('hardwareBackPress', this.goBack)
+ }
+
+ navigationButtonPressed({buttonId}) {
+ if(buttonId == 'back') {
+ this.goBack();
+
+ } else if(buttonId == 'open') {
+ Linking.openURL(this.webViewState.url);
+
+ }
+ }
+
+ goBack() {
+ if (this.webViewState.canGoBack) {
+ this.webView.injectJavaScript('window.history.back();');
+
+ } else {
+ Navigation.pop(this.props.componentId);
+ }
+ }
+
+ onNavigationStateChange(event) {
+ this.webViewState = event;
+
+ Navigation.mergeOptions(this.props.componentId, {
+ topBar: {
+ title: {
+ text: event.title
+ }
+ }
+ });
+
+ }
+
+ render() {
+ return (
+ this.webView = r}
+ style={{flex: 1}}
+ source={{uri: this.props.uri}}
+ onNavigationStateChange={this.onNavigationStateChange.bind(this)}
+ />
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/page/WritePage.js b/src/page/WritePage.js
index 61ba114..2079b87 100644
--- a/src/page/WritePage.js
+++ b/src/page/WritePage.js
@@ -18,7 +18,6 @@ import {Navigation} from 'react-native-navigation';
import KeyboardSpacer from "react-native-keyboard-spacer";
import Ionicons from 'react-native-vector-icons/Ionicons';
-import BottomNav from '../nav/bottomNav';
import {Icon} from '../style/icon';
import Color from '../style/color';
import Api from '../util/api';
diff --git a/src/page/_list.js b/src/page/_list.js
index 12d928a..433c50f 100644
--- a/src/page/_list.js
+++ b/src/page/_list.js
@@ -18,5 +18,6 @@ UserInfoEdit: require("./UserInfoEditPage.js").default,
UserIntroEdit: require("./UserIntroEditPage.js").default,
UserNameEdit: require("./UserNameEditPage.js").default,
User: require("./UserPage.js").default,
+WebView: require("./WebViewPage.js").default,
Write: require("./WritePage.js").default
}
\ No newline at end of file
diff --git a/src/util/api.js b/src/util/api.js
index b7b8f18..fc240cf 100644
--- a/src/util/api.js
+++ b/src/util/api.js
@@ -2,7 +2,7 @@ import {Platform, Dimensions} from 'react-native'
import DeviceInfo from 'react-native-device-info';
import {isIphoneX} from 'react-native-iphone-x-helper'
-import TokenManager from './token'
+import Token from './token'
const IS_ANDROID = Platform.OS === 'android';
@@ -22,19 +22,19 @@ const v2Url = 'http://v2.timepill.net/api';
async function login(username, password) {
- const token = TokenManager.generateToken(username, password);
- await TokenManager.setUserToken(token);
+ const token = Token.generateToken(username, password);
+ await Token.setUserToken(token);
try {
const userInfo = await getSelfInfo();
- await TokenManager.setUserInfo(userInfo);
- await TokenManager.setLoginPassword('');
+ await Token.setUserInfo(userInfo);
+ await Token.setLoginPassword('');
return userInfo;
} catch(err) {
- await TokenManager.setUserToken('');
+ await Token.setUserToken('');
if (err.code && err.code === 401) {
return false;
}
@@ -44,9 +44,38 @@ async function login(username, password) {
}
async function logout() {
- TokenManager.setUserToken('');
- TokenManager.setUserInfo(false);
- TokenManager.setLoginPassword('');
+ Token.setUserToken('');
+ Token.setUserInfo(false);
+ Token.setLoginPassword('');
+}
+
+async function getSplashByStore() {
+ try {
+ let info = await Token.get('splash');
+ console.log('api get splash:', info);
+ if(info) {
+ const splash = JSON.parse(info);
+
+ const now = Date.parse(new Date()) / 1000;
+ if((splash.start_time && splash.start_time > now) ||
+ (splash.end_time && now > splash.end_time)) {
+
+ return null;
+ }
+
+ return splash;
+ }
+
+ } catch (e) {}
+
+ return null;
+}
+
+async function syncSplash() {
+ const splash = await callV2('GET', '/splash');
+ await Token.set('splash', JSON.stringify(splash));
+
+ return splash;
}
async function getSelfInfo() {
@@ -54,7 +83,7 @@ async function getSelfInfo() {
}
async function getSelfInfoByStore() {
- return await TokenManager.getUserInfo();
+ return await Token.getUserInfo();
}
async function getUserInfo(id) {
@@ -258,7 +287,7 @@ async function feedback(content) {
async function upload(method, api, body) {
- let token = await TokenManager.getUserToken();
+ let token = await Token.getUserToken();
let formData = new FormData();
for(let prop of Object.keys(body)) {
formData.append(prop, body[prop]);
@@ -286,7 +315,7 @@ async function upload(method, api, body) {
}
async function call(method, api, body = null, _timeout = 10000) {
- let token = await TokenManager.getUserToken();
+ let token = await Token.getUserToken();
return timeout(fetch(baseUrl + api, {
method: method,
headers: {
@@ -308,7 +337,7 @@ async function call(method, api, body = null, _timeout = 10000) {
}
async function callV2(method, api, body = null, _timeout = 10000) {
- let token = await TokenManager.getToken();
+ let token = await Token.getUserToken();
return timeout(fetch(v2Url + api, {
method: method,
headers: {
@@ -397,6 +426,8 @@ export default {
login,
logout,
+ getSplashByStore,
+ syncSplash,
getSelfInfoByStore,
getUserInfo,