2018-03-11 16:32:13 +01:00
|
|
|
import React from 'react';
|
2018-05-03 20:58:31 +02:00
|
|
|
import AboutPage from '../page/about';
|
2018-03-11 16:32:13 +01:00
|
|
|
import DiscoverPage from '../page/discover';
|
|
|
|
import FilePage from '../page/file';
|
2018-06-08 10:13:45 +02:00
|
|
|
import FirstRunScreen from '../page/firstRun';
|
2018-08-28 12:59:14 +02:00
|
|
|
import RewardsPage from '../page/rewards';
|
2018-06-05 20:52:10 +02:00
|
|
|
import TrendingPage from '../page/trending';
|
2018-08-28 12:59:14 +02:00
|
|
|
import SearchPage from '../page/search';
|
2018-03-23 00:03:05 +01:00
|
|
|
import SettingsPage from '../page/settings';
|
2018-03-11 16:32:13 +01:00
|
|
|
import SplashScreen from '../page/splash';
|
2018-05-03 20:58:31 +02:00
|
|
|
import TransactionHistoryPage from '../page/transactionHistory';
|
|
|
|
import WalletPage from '../page/wallet';
|
2018-04-09 20:39:35 +02:00
|
|
|
import SearchInput from '../component/searchInput';
|
|
|
|
import {
|
|
|
|
addNavigationHelpers,
|
|
|
|
DrawerNavigator,
|
|
|
|
StackNavigator,
|
|
|
|
NavigationActions
|
|
|
|
} from 'react-navigation';
|
2018-03-11 16:32:13 +01:00
|
|
|
import { connect } from 'react-redux';
|
|
|
|
import { addListener } from '../utils/redux';
|
2018-04-24 21:32:17 +02:00
|
|
|
import {
|
|
|
|
AppState,
|
|
|
|
AsyncStorage,
|
|
|
|
BackHandler,
|
2018-05-25 09:13:05 +02:00
|
|
|
Linking,
|
2018-04-24 21:32:17 +02:00
|
|
|
NativeModules,
|
2018-05-03 20:58:31 +02:00
|
|
|
TextInput,
|
|
|
|
ToastAndroid
|
2018-04-24 21:32:17 +02:00
|
|
|
} from 'react-native';
|
2018-08-17 20:11:15 +02:00
|
|
|
import { doDeleteCompleteBlobs } from '../redux/actions/file';
|
2018-08-16 11:48:34 +02:00
|
|
|
import { SETTINGS, doHideNotification, doNotify, selectNotification } from 'lbry-redux';
|
|
|
|
import {
|
|
|
|
doUserEmailVerify,
|
|
|
|
doUserEmailVerifyFailure,
|
|
|
|
selectEmailToVerify,
|
|
|
|
selectEmailVerifyIsPending,
|
|
|
|
selectEmailVerifyErrorMessage,
|
|
|
|
selectUser
|
|
|
|
} from 'lbryinc';
|
2018-04-09 20:39:35 +02:00
|
|
|
import { makeSelectClientSetting } from '../redux/selectors/settings';
|
2018-08-16 11:48:34 +02:00
|
|
|
import { decode as atob } from 'base-64';
|
2018-08-08 19:11:25 +02:00
|
|
|
import Icon from 'react-native-vector-icons/FontAwesome5';
|
2018-08-16 11:48:34 +02:00
|
|
|
import Constants from '../constants';
|
2018-03-11 16:32:13 +01:00
|
|
|
import discoverStyle from '../styles/discover';
|
2018-04-09 20:39:35 +02:00
|
|
|
import searchStyle from '../styles/search';
|
2018-08-16 11:48:34 +02:00
|
|
|
import SearchRightHeaderIcon from '../component/searchRightHeaderIcon';
|
2018-03-11 16:32:13 +01:00
|
|
|
|
|
|
|
const discoverStack = StackNavigator({
|
|
|
|
Discover: {
|
|
|
|
screen: DiscoverPage,
|
|
|
|
navigationOptions: ({ navigation }) => ({
|
|
|
|
title: 'Discover',
|
2018-08-08 19:11:25 +02:00
|
|
|
headerLeft: <Icon name="bars" size={24} style={discoverStyle.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />,
|
2018-03-11 16:32:13 +01:00
|
|
|
})
|
|
|
|
},
|
|
|
|
File: {
|
|
|
|
screen: FilePage,
|
|
|
|
navigationOptions: {
|
2018-03-22 07:26:04 +01:00
|
|
|
header: null,
|
|
|
|
drawerLockMode: 'locked-closed'
|
2018-03-11 16:32:13 +01:00
|
|
|
}
|
2018-04-09 20:39:35 +02:00
|
|
|
},
|
|
|
|
Search: {
|
|
|
|
screen: SearchPage,
|
|
|
|
navigationOptions: ({ navigation }) => ({
|
2018-05-26 01:20:44 +02:00
|
|
|
drawerLockMode: 'locked-closed'
|
2018-04-09 20:39:35 +02:00
|
|
|
})
|
2018-03-11 16:32:13 +01:00
|
|
|
}
|
|
|
|
}, {
|
|
|
|
headerMode: 'screen',
|
|
|
|
});
|
|
|
|
|
2018-06-05 20:52:10 +02:00
|
|
|
const trendingStack = StackNavigator({
|
|
|
|
Trending: {
|
|
|
|
screen: TrendingPage,
|
|
|
|
navigationOptions: ({ navigation }) => ({
|
|
|
|
title: 'Trending',
|
2018-08-08 19:11:25 +02:00
|
|
|
headerLeft: <Icon name="bars" size={24} style={discoverStyle.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />,
|
2018-06-05 20:52:10 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2018-05-03 20:58:31 +02:00
|
|
|
const walletStack = StackNavigator({
|
|
|
|
Wallet: {
|
|
|
|
screen: WalletPage,
|
|
|
|
navigationOptions: ({ navigation }) => ({
|
|
|
|
title: 'Wallet',
|
2018-08-08 19:11:25 +02:00
|
|
|
headerLeft: <Icon name="bars" size={24} style={discoverStyle.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />,
|
2018-05-03 20:58:31 +02:00
|
|
|
})
|
|
|
|
},
|
|
|
|
TransactionHistory: {
|
|
|
|
screen: TransactionHistoryPage,
|
|
|
|
navigationOptions: {
|
|
|
|
title: 'Transaction History',
|
|
|
|
drawerLockMode: 'locked-closed'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, {
|
|
|
|
headerMode: 'screen'
|
|
|
|
});
|
|
|
|
|
2018-08-28 12:59:14 +02:00
|
|
|
const rewardsStack = StackNavigator({
|
|
|
|
Rewards: {
|
|
|
|
screen: RewardsPage,
|
|
|
|
navigationOptions: ({ navigation }) => ({
|
|
|
|
title: 'Rewards',
|
|
|
|
headerLeft: <Icon name="bars" size={24} style={discoverStyle.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2018-03-11 16:32:13 +01:00
|
|
|
const drawer = DrawerNavigator({
|
2018-05-10 22:07:18 +02:00
|
|
|
DiscoverStack: { screen: discoverStack },
|
2018-06-05 20:52:10 +02:00
|
|
|
TrendingStack: { screen: trendingStack },
|
2018-05-10 22:07:18 +02:00
|
|
|
WalletStack: { screen: walletStack },
|
2018-08-28 12:59:14 +02:00
|
|
|
Rewards: { screen: rewardsStack },
|
2018-04-23 06:10:18 +02:00
|
|
|
Settings: { screen: SettingsPage, navigationOptions: { drawerLockMode: 'locked-closed' } },
|
|
|
|
About: { screen: AboutPage, navigationOptions: { drawerLockMode: 'locked-closed' } }
|
2018-03-11 16:32:13 +01:00
|
|
|
}, {
|
|
|
|
drawerWidth: 300,
|
|
|
|
headerMode: 'none'
|
|
|
|
});
|
|
|
|
|
|
|
|
export const AppNavigator = new StackNavigator({
|
2018-06-08 10:13:45 +02:00
|
|
|
FirstRun: {
|
|
|
|
screen: FirstRunScreen,
|
|
|
|
navigationOptions: {
|
|
|
|
drawerLockMode: 'locked-closed'
|
|
|
|
}
|
|
|
|
},
|
2018-03-11 16:32:13 +01:00
|
|
|
Splash: {
|
2018-04-09 20:39:35 +02:00
|
|
|
screen: SplashScreen,
|
|
|
|
navigationOptions: {
|
|
|
|
drawerLockMode: 'locked-closed'
|
|
|
|
}
|
2018-03-11 16:32:13 +01:00
|
|
|
},
|
|
|
|
Main: {
|
|
|
|
screen: drawer
|
|
|
|
}
|
|
|
|
}, {
|
|
|
|
headerMode: 'none'
|
|
|
|
});
|
|
|
|
|
|
|
|
class AppWithNavigationState extends React.Component {
|
2018-05-03 20:58:31 +02:00
|
|
|
static supportedDisplayTypes = ['toast'];
|
2018-06-08 10:13:45 +02:00
|
|
|
|
2018-08-16 11:48:34 +02:00
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
this.state = {
|
|
|
|
emailVerifyDone: false
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-03-11 16:32:13 +01:00
|
|
|
componentWillMount() {
|
2018-03-23 00:03:05 +01:00
|
|
|
AppState.addEventListener('change', this._handleAppStateChange);
|
2018-03-11 16:32:13 +01:00
|
|
|
BackHandler.addEventListener('hardwareBackPress', function() {
|
2018-05-25 09:13:05 +02:00
|
|
|
const { dispatch, nav } = this.props;
|
2018-04-23 06:01:33 +02:00
|
|
|
// There should be a better way to check this
|
2018-05-10 22:07:18 +02:00
|
|
|
if (nav.routes.length > 0) {
|
2018-07-24 15:37:31 +02:00
|
|
|
if (nav.routes[0].routes && nav.routes[0].routes.length > 0) {
|
|
|
|
const subRoutes = nav.routes[0].routes[0].routes;
|
|
|
|
const lastRoute = subRoutes[subRoutes.length - 1];
|
|
|
|
if (nav.routes[0].routes[0].index > 0 &&
|
|
|
|
['About', 'Settings'].indexOf(lastRoute.key) > -1) {
|
|
|
|
dispatch(NavigationActions.back());
|
|
|
|
return true;
|
|
|
|
}
|
2018-03-11 16:32:13 +01:00
|
|
|
}
|
2018-05-10 22:07:18 +02:00
|
|
|
if (nav.routes[0].routeName === 'Main') {
|
2018-06-08 10:13:45 +02:00
|
|
|
if (nav.routes[0].routes[0].routes[0].index > 0) {
|
2018-05-10 22:07:18 +02:00
|
|
|
dispatch(NavigationActions.back());
|
2018-04-23 06:01:33 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2018-03-11 16:32:13 +01:00
|
|
|
}
|
2018-08-16 11:48:34 +02:00
|
|
|
return false;
|
2018-03-11 16:32:13 +01:00
|
|
|
}.bind(this));
|
|
|
|
}
|
2018-06-08 10:13:45 +02:00
|
|
|
|
2018-05-25 09:13:05 +02:00
|
|
|
componentDidMount() {
|
|
|
|
Linking.addEventListener('url', this._handleUrl);
|
|
|
|
}
|
2018-06-08 10:13:45 +02:00
|
|
|
|
2018-03-11 16:32:13 +01:00
|
|
|
componentWillUnmount() {
|
2018-03-23 00:03:05 +01:00
|
|
|
AppState.removeEventListener('change', this._handleAppStateChange);
|
2018-03-11 16:32:13 +01:00
|
|
|
BackHandler.removeEventListener('hardwareBackPress');
|
2018-05-25 09:13:05 +02:00
|
|
|
Linking.removeEventListener('url', this._handleUrl);
|
2018-03-11 16:32:13 +01:00
|
|
|
}
|
2018-06-08 10:13:45 +02:00
|
|
|
|
2018-05-03 20:58:31 +02:00
|
|
|
componentWillUpdate(nextProps) {
|
|
|
|
const { dispatch } = this.props;
|
2018-08-16 11:48:34 +02:00
|
|
|
const {
|
|
|
|
notification,
|
|
|
|
emailToVerify,
|
|
|
|
emailVerifyPending,
|
|
|
|
emailVerifyErrorMessage,
|
|
|
|
user
|
|
|
|
} = nextProps;
|
|
|
|
|
2018-05-03 20:58:31 +02:00
|
|
|
if (notification) {
|
|
|
|
const { displayType, message } = notification;
|
|
|
|
let currentDisplayType;
|
2018-08-28 12:59:14 +02:00
|
|
|
if (displayType && displayType.length) {
|
2018-05-03 20:58:31 +02:00
|
|
|
for (let i = 0; i < displayType.length; i++) {
|
|
|
|
const type = displayType[i];
|
|
|
|
if (AppWithNavigationState.supportedDisplayTypes.indexOf(type) > -1) {
|
|
|
|
currentDisplayType = type;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (AppWithNavigationState.supportedDisplayTypes.indexOf(displayType) > -1) {
|
|
|
|
currentDisplayType = displayType;
|
|
|
|
}
|
2018-06-08 10:13:45 +02:00
|
|
|
|
2018-08-28 12:59:14 +02:00
|
|
|
if (!currentDisplayType && message) {
|
|
|
|
// default to toast if no display type set and there is a message specified
|
|
|
|
currentDisplayType = 'toast';
|
|
|
|
}
|
|
|
|
|
2018-05-03 20:58:31 +02:00
|
|
|
if ('toast' === currentDisplayType) {
|
|
|
|
ToastAndroid.show(message, ToastAndroid.SHORT);
|
|
|
|
}
|
2018-06-08 10:13:45 +02:00
|
|
|
|
2018-05-03 20:58:31 +02:00
|
|
|
dispatch(doHideNotification());
|
|
|
|
}
|
2018-08-16 11:48:34 +02:00
|
|
|
|
|
|
|
if (user &&
|
|
|
|
!emailVerifyPending &&
|
|
|
|
!this.state.emailVerifyDone &&
|
|
|
|
(emailToVerify || emailVerifyErrorMessage)) {
|
|
|
|
AsyncStorage.getItem(Constants.KEY_SHOULD_VERIFY_EMAIL).then(shouldVerify => {
|
|
|
|
if ('true' === shouldVerify) {
|
|
|
|
this.setState({ emailVerifyDone: true });
|
|
|
|
const message = emailVerifyErrorMessage ?
|
|
|
|
String(emailVerifyErrorMessage) : 'Your email address was successfully verified.';
|
2018-08-16 14:21:28 +02:00
|
|
|
if (!emailVerifyErrorMessage) {
|
|
|
|
AsyncStorage.removeItem(Constants.KEY_FIRST_RUN_EMAIL);
|
|
|
|
}
|
|
|
|
AsyncStorage.removeItem(Constants.KEY_SHOULD_VERIFY_EMAIL);
|
2018-08-16 11:48:34 +02:00
|
|
|
dispatch(doNotify({ message, displayType: ['toast'] }));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2018-05-03 20:58:31 +02:00
|
|
|
}
|
2018-06-08 10:13:45 +02:00
|
|
|
|
2018-03-23 00:03:05 +01:00
|
|
|
_handleAppStateChange = (nextAppState) => {
|
2018-08-20 17:00:16 +02:00
|
|
|
const { backgroundPlayEnabled, dispatch } = this.props;
|
2018-04-24 21:32:17 +02:00
|
|
|
// Check if the app was suspended
|
|
|
|
if (AppState.currentState && AppState.currentState.match(/inactive|background/)) {
|
|
|
|
AsyncStorage.getItem('firstLaunchTime').then(start => {
|
|
|
|
if (start !== null && !isNaN(parseInt(start, 10))) {
|
|
|
|
// App suspended during first launch?
|
|
|
|
// If so, this needs to be included as a property when tracking
|
|
|
|
AsyncStorage.setItem('firstLaunchSuspended', 'true');
|
|
|
|
}
|
2018-08-20 17:00:16 +02:00
|
|
|
|
|
|
|
// Background media
|
|
|
|
if (backgroundPlayEnabled && NativeModules.BackgroundMedia && window.currentMediaInfo) {
|
|
|
|
const { title, channel } = window.currentMediaInfo;
|
|
|
|
NativeModules.BackgroundMedia.showPlaybackNotification(title, channel, null, false);
|
|
|
|
}
|
2018-04-24 21:32:17 +02:00
|
|
|
});
|
|
|
|
}
|
2018-08-17 20:11:15 +02:00
|
|
|
|
|
|
|
if (AppState.currentState && AppState.currentState.match(/active/)) {
|
|
|
|
// Cleanup blobs for completed files upon app resume to save space
|
|
|
|
dispatch(doDeleteCompleteBlobs());
|
2018-08-20 17:00:16 +02:00
|
|
|
if (backgroundPlayEnabled || NativeModules.BackgroundMedia) {
|
|
|
|
NativeModules.BackgroundMedia.hidePlaybackNotification();
|
|
|
|
}
|
2018-08-17 20:11:15 +02:00
|
|
|
}
|
2018-03-23 00:03:05 +01:00
|
|
|
}
|
2018-06-08 10:13:45 +02:00
|
|
|
|
2018-05-25 09:13:05 +02:00
|
|
|
_handleUrl = (evt) => {
|
|
|
|
const { dispatch } = this.props;
|
|
|
|
if (evt.url) {
|
2018-08-16 11:48:34 +02:00
|
|
|
if (evt.url.startsWith('lbry://?verify=')) {
|
|
|
|
this.setState({ emailVerifyDone: false });
|
|
|
|
let verification = {};
|
|
|
|
try {
|
|
|
|
verification = JSON.parse(atob(evt.url.substring(15)));
|
|
|
|
} catch (error) {
|
|
|
|
console.log(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verification.token && verification.recaptcha) {
|
|
|
|
AsyncStorage.setItem(Constants.KEY_SHOULD_VERIFY_EMAIL, 'true');
|
|
|
|
try {
|
|
|
|
dispatch(doUserEmailVerify(verification.token, verification.recaptcha));
|
|
|
|
} catch (error) {
|
|
|
|
const message = 'Invalid Verification Token';
|
|
|
|
dispatch(doUserEmailVerifyFailure(message));
|
|
|
|
dispatch(doNotify({ message, displayType: ['toast'] }));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
dispatch(doNotify({
|
|
|
|
message: 'Invalid Verification URI',
|
|
|
|
displayType: ['toast'],
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const navigateAction = NavigationActions.navigate({
|
|
|
|
routeName: 'File',
|
|
|
|
key: evt.url,
|
|
|
|
params: { uri: evt.url }
|
|
|
|
});
|
|
|
|
dispatch(navigateAction);
|
|
|
|
}
|
2018-05-25 09:13:05 +02:00
|
|
|
}
|
|
|
|
}
|
2018-06-08 10:13:45 +02:00
|
|
|
|
2018-03-11 16:32:13 +01:00
|
|
|
render() {
|
|
|
|
const { dispatch, nav } = this.props;
|
|
|
|
return (
|
|
|
|
<AppNavigator
|
|
|
|
navigation={addNavigationHelpers({
|
|
|
|
dispatch,
|
|
|
|
state: nav,
|
|
|
|
addListener,
|
|
|
|
})}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const mapStateToProps = state => ({
|
2018-08-20 17:00:16 +02:00
|
|
|
backgroundPlayEnabled: makeSelectClientSetting(SETTINGS.BACKGROUND_PLAY_ENABLED)(state),
|
2018-08-16 11:48:34 +02:00
|
|
|
keepDaemonRunning: makeSelectClientSetting(SETTINGS.KEEP_DAEMON_RUNNING)(state),
|
2018-03-11 16:32:13 +01:00
|
|
|
nav: state.nav,
|
2018-05-03 20:58:31 +02:00
|
|
|
notification: selectNotification(state),
|
2018-08-16 11:48:34 +02:00
|
|
|
emailToVerify: selectEmailToVerify(state),
|
|
|
|
emailVerifyPending: selectEmailVerifyIsPending(state),
|
|
|
|
emailVerifyErrorMessage: selectEmailVerifyErrorMessage(state),
|
|
|
|
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_NSFW)(state),
|
|
|
|
user: selectUser(state),
|
2018-03-11 16:32:13 +01:00
|
|
|
});
|
2018-06-08 10:13:45 +02:00
|
|
|
|
2018-05-03 20:58:31 +02:00
|
|
|
export default connect(mapStateToProps)(AppWithNavigationState);
|