commit
89c653e613
148 changed files with 4736 additions and 2589 deletions
36
app/.eslintrc.json
Normal file
36
app/.eslintrc.json
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"parser": "babel-eslint",
|
||||||
|
"extends": ["standard", "standard-jsx", "plugin:flowtype/recommended"],
|
||||||
|
"plugins": ["flowtype", "import"],
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"__": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"no-multi-spaces": 0,
|
||||||
|
"new-cap": 0,
|
||||||
|
"prefer-promise-reject-errors": 0,
|
||||||
|
"no-unused-vars": 0,
|
||||||
|
"standard/object-curly-even-spacing": 0,
|
||||||
|
"handle-callback-err": 0,
|
||||||
|
"one-var": 0,
|
||||||
|
"object-curly-spacing": 0,
|
||||||
|
"no-redeclare": 0,
|
||||||
|
"no-return-await": 0,
|
||||||
|
"standard/no-callback-literal": 0,
|
||||||
|
"comma-dangle": ["error", "always-multiline"],
|
||||||
|
"space-before-function-paren": ["error", "never"],
|
||||||
|
"jsx-quotes": ["error", "prefer-double"],
|
||||||
|
"no-use-before-define": 0,
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"always",
|
||||||
|
{
|
||||||
|
"omitLastInOneLineBlock": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
6
app/.lintstagedrc.json
Normal file
6
app/.lintstagedrc.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"linters": {
|
||||||
|
"src/**/*.{js,json}": ["prettier --write", "git add"],
|
||||||
|
"src/**/*.js": ["eslint --fix", "git add"]
|
||||||
|
}
|
||||||
|
}
|
5
app/.prettierrc.json
Normal file
5
app/.prettierrc.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"printWidth": 120,
|
||||||
|
"singleQuote": true
|
||||||
|
}
|
1580
app/package-lock.json
generated
1580
app/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -3,7 +3,9 @@
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node node_modules/react-native/local-cli/cli.js start"
|
"start": "node node_modules/react-native/local-cli/cli.js start",
|
||||||
|
"format": "prettier 'src/**/*.{js,json}' --write",
|
||||||
|
"precommit": "lint-staged"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"base-64": "^0.1.0",
|
"base-64": "^0.1.0",
|
||||||
|
@ -42,6 +44,17 @@
|
||||||
"babel-preset-react-native": "5.0.2",
|
"babel-preset-react-native": "5.0.2",
|
||||||
"babel-preset-stage-2": "^6.18.0",
|
"babel-preset-stage-2": "^6.18.0",
|
||||||
"babel-plugin-module-resolver": "^3.1.1",
|
"babel-plugin-module-resolver": "^3.1.1",
|
||||||
"flow-babel-webpack-plugin": "^1.1.1"
|
"eslint": "^5.16.0",
|
||||||
|
"eslint-config-standard": "^12.0.0",
|
||||||
|
"eslint-config-standard-jsx": "^6.0.2",
|
||||||
|
"eslint-plugin-flowtype": "^2.46.1",
|
||||||
|
"eslint-plugin-import": "^2.17.2",
|
||||||
|
"eslint-plugin-node": "^8.0.1",
|
||||||
|
"eslint-plugin-promise": "^4.1.1",
|
||||||
|
"eslint-plugin-react": "^7.12.4",
|
||||||
|
"eslint-plugin-standard": "^4.0.0",
|
||||||
|
"flow-babel-webpack-plugin": "^1.1.1",
|
||||||
|
"lint-staged": "^7.0.4",
|
||||||
|
"prettier": "^1.11.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,25 +14,14 @@ import SubscriptionsPage from 'page/subscriptions';
|
||||||
import TransactionHistoryPage from 'page/transactionHistory';
|
import TransactionHistoryPage from 'page/transactionHistory';
|
||||||
import VerificationScreen from 'page/verification';
|
import VerificationScreen from 'page/verification';
|
||||||
import WalletPage from 'page/wallet';
|
import WalletPage from 'page/wallet';
|
||||||
import {
|
import { createDrawerNavigator, createStackNavigator, NavigationActions } from 'react-navigation';
|
||||||
createDrawerNavigator,
|
|
||||||
createStackNavigator,
|
|
||||||
NavigationActions
|
|
||||||
} from 'react-navigation';
|
|
||||||
import {
|
import {
|
||||||
createReduxContainer,
|
createReduxContainer,
|
||||||
createReactNavigationReduxMiddleware,
|
createReactNavigationReduxMiddleware,
|
||||||
createNavigationReducer
|
createNavigationReducer,
|
||||||
} from 'react-navigation-redux-helpers';
|
} from 'react-navigation-redux-helpers';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import { AppState, BackHandler, Linking, NativeModules, TextInput, ToastAndroid } from 'react-native';
|
||||||
AppState,
|
|
||||||
BackHandler,
|
|
||||||
Linking,
|
|
||||||
NativeModules,
|
|
||||||
TextInput,
|
|
||||||
ToastAndroid
|
|
||||||
} from 'react-native';
|
|
||||||
import { selectDrawerStack } from 'redux/selectors/drawer';
|
import { selectDrawerStack } from 'redux/selectors/drawer';
|
||||||
import { SETTINGS, doDismissToast, doToast, selectToast } from 'lbry-redux';
|
import { SETTINGS, doDismissToast, doToast, selectToast } from 'lbry-redux';
|
||||||
import {
|
import {
|
||||||
|
@ -43,7 +32,7 @@ import {
|
||||||
selectEmailToVerify,
|
selectEmailToVerify,
|
||||||
selectEmailVerifyIsPending,
|
selectEmailVerifyIsPending,
|
||||||
selectEmailVerifyErrorMessage,
|
selectEmailVerifyErrorMessage,
|
||||||
selectUser
|
selectUser,
|
||||||
} from 'lbryinc';
|
} from 'lbryinc';
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
import { decode as atob } from 'base-64';
|
import { decode as atob } from 'base-64';
|
||||||
|
@ -57,37 +46,43 @@ import discoverStyle from 'styles/discover';
|
||||||
import searchStyle from 'styles/search';
|
import searchStyle from 'styles/search';
|
||||||
import SearchRightHeaderIcon from 'component/searchRightHeaderIcon';
|
import SearchRightHeaderIcon from 'component/searchRightHeaderIcon';
|
||||||
|
|
||||||
const menuNavigationButton = (navigation) => <NavigationButton
|
const menuNavigationButton = navigation => (
|
||||||
name="bars"
|
<NavigationButton
|
||||||
size={24}
|
name="bars"
|
||||||
style={discoverStyle.drawerMenuButton}
|
size={24}
|
||||||
iconStyle={discoverStyle.drawerHamburger}
|
style={discoverStyle.drawerMenuButton}
|
||||||
onPress={() => navigation.openDrawer() } />
|
iconStyle={discoverStyle.drawerHamburger}
|
||||||
|
onPress={() => navigation.openDrawer()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
const discoverStack = createStackNavigator({
|
const discoverStack = createStackNavigator(
|
||||||
Discover: {
|
{
|
||||||
screen: DiscoverPage,
|
Discover: {
|
||||||
navigationOptions: ({ navigation }) => ({
|
screen: DiscoverPage,
|
||||||
title: 'Explore',
|
navigationOptions: ({ navigation }) => ({
|
||||||
header: null
|
title: 'Explore',
|
||||||
}),
|
header: null,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
File: {
|
||||||
|
screen: FilePage,
|
||||||
|
navigationOptions: ({ navigation }) => ({
|
||||||
|
header: null,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Search: {
|
||||||
|
screen: SearchPage,
|
||||||
|
navigationOptions: ({ navigation }) => ({
|
||||||
|
header: null,
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
File: {
|
{
|
||||||
screen: FilePage,
|
headerMode: 'screen',
|
||||||
navigationOptions: ({ navigation }) => ({
|
transitionConfig: () => ({ screenInterpolator: () => null }),
|
||||||
header: null
|
|
||||||
})
|
|
||||||
},
|
|
||||||
Search: {
|
|
||||||
screen: SearchPage,
|
|
||||||
navigationOptions: ({ navigation }) => ({
|
|
||||||
header: null
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}, {
|
);
|
||||||
headerMode: 'screen',
|
|
||||||
transitionConfig: () => ({ screenInterpolator: () => null }),
|
|
||||||
});
|
|
||||||
|
|
||||||
discoverStack.navigationOptions = ({ navigation }) => {
|
discoverStack.navigationOptions = ({ navigation }) => {
|
||||||
let drawerLockMode = 'unlocked';
|
let drawerLockMode = 'unlocked';
|
||||||
|
@ -96,102 +91,137 @@ discoverStack.navigationOptions = ({ navigation }) => {
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
return {
|
return {
|
||||||
drawerLockMode
|
drawerLockMode,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const walletStack = createStackNavigator({
|
const walletStack = createStackNavigator(
|
||||||
Wallet: {
|
{
|
||||||
screen: WalletPage,
|
Wallet: {
|
||||||
navigationOptions: ({ navigation }) => ({
|
screen: WalletPage,
|
||||||
title: 'Wallet',
|
navigationOptions: ({ navigation }) => ({
|
||||||
header: null
|
title: 'Wallet',
|
||||||
})
|
header: null,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
TransactionHistory: {
|
||||||
|
screen: TransactionHistoryPage,
|
||||||
|
navigationOptions: {
|
||||||
|
title: 'Transaction History',
|
||||||
|
header: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
TransactionHistory: {
|
{
|
||||||
screen: TransactionHistoryPage,
|
headerMode: 'screen',
|
||||||
navigationOptions: {
|
transitionConfig: () => ({ screenInterpolator: () => null }),
|
||||||
title: 'Transaction History',
|
|
||||||
header: null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, {
|
);
|
||||||
headerMode: 'screen',
|
|
||||||
transitionConfig: () => ({ screenInterpolator: () => null }),
|
|
||||||
});
|
|
||||||
|
|
||||||
const drawer = createDrawerNavigator({
|
const drawer = createDrawerNavigator(
|
||||||
DiscoverStack: { screen: discoverStack, navigationOptions: {
|
{
|
||||||
title: 'Explore', drawerIcon: ({ tintColor }) => <Icon name="home" size={20} style={{ color: tintColor }} />
|
DiscoverStack: {
|
||||||
}},
|
screen: discoverStack,
|
||||||
TrendingStack: { screen: TrendingPage, navigationOptions: {
|
navigationOptions: {
|
||||||
title: 'Trending', drawerIcon: ({ tintColor }) => <Icon name="fire" size={20} style={{ color: tintColor }} />
|
title: 'Explore',
|
||||||
}},
|
drawerIcon: ({ tintColor }) => <Icon name="home" size={20} style={{ color: tintColor }} />,
|
||||||
MySubscriptionsStack: { screen: SubscriptionsPage, navigationOptions: {
|
},
|
||||||
title: 'Subscriptions', drawerIcon: ({ tintColor }) => <Icon name="heart" solid={true} size={20} style={{ color: tintColor }} />
|
},
|
||||||
}},
|
TrendingStack: {
|
||||||
WalletStack: { screen: walletStack, navigationOptions: {
|
screen: TrendingPage,
|
||||||
title: 'Wallet', drawerIcon: ({ tintColor }) => <Icon name="wallet" size={20} style={{ color: tintColor }} />
|
navigationOptions: {
|
||||||
}},
|
title: 'Trending',
|
||||||
Rewards: { screen: RewardsPage, navigationOptions: {
|
drawerIcon: ({ tintColor }) => <Icon name="fire" size={20} style={{ color: tintColor }} />,
|
||||||
drawerIcon: ({ tintColor }) => <Icon name="award" size={20} style={{ color: tintColor }} />
|
},
|
||||||
}},
|
},
|
||||||
MyLBRYStack: { screen: DownloadsPage, navigationOptions: {
|
MySubscriptionsStack: {
|
||||||
title: 'Library', drawerIcon: ({ tintColor }) => <Icon name="download" size={20} style={{ color: tintColor }} />
|
screen: SubscriptionsPage,
|
||||||
}},
|
navigationOptions: {
|
||||||
Settings: { screen: SettingsPage, navigationOptions: {
|
title: 'Subscriptions',
|
||||||
drawerLockMode: 'locked-closed',
|
drawerIcon: ({ tintColor }) => <Icon name="heart" solid={true} size={20} style={{ color: tintColor }} />,
|
||||||
drawerIcon: ({ tintColor }) => <Icon name="cog" size={20} style={{ color: tintColor }} />
|
},
|
||||||
}},
|
},
|
||||||
About: { screen: AboutPage, navigationOptions: {
|
WalletStack: {
|
||||||
drawerLockMode: 'locked-closed',
|
screen: walletStack,
|
||||||
drawerIcon: ({ tintColor }) => <Icon name="info" size={20} style={{ color: tintColor }} />
|
navigationOptions: {
|
||||||
}}
|
title: 'Wallet',
|
||||||
}, {
|
drawerIcon: ({ tintColor }) => <Icon name="wallet" size={20} style={{ color: tintColor }} />,
|
||||||
drawerWidth: 300,
|
},
|
||||||
headerMode: 'none',
|
},
|
||||||
contentComponent: DrawerContent,
|
Rewards: {
|
||||||
contentOptions: {
|
screen: RewardsPage,
|
||||||
activeTintColor: Colors.LbryGreen,
|
navigationOptions: {
|
||||||
labelStyle: discoverStyle.menuText
|
drawerIcon: ({ tintColor }) => <Icon name="award" size={20} style={{ color: tintColor }} />,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MyLBRYStack: {
|
||||||
|
screen: DownloadsPage,
|
||||||
|
navigationOptions: {
|
||||||
|
title: 'Library',
|
||||||
|
drawerIcon: ({ tintColor }) => <Icon name="download" size={20} style={{ color: tintColor }} />,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Settings: {
|
||||||
|
screen: SettingsPage,
|
||||||
|
navigationOptions: {
|
||||||
|
drawerLockMode: 'locked-closed',
|
||||||
|
drawerIcon: ({ tintColor }) => <Icon name="cog" size={20} style={{ color: tintColor }} />,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
About: {
|
||||||
|
screen: AboutPage,
|
||||||
|
navigationOptions: {
|
||||||
|
drawerLockMode: 'locked-closed',
|
||||||
|
drawerIcon: ({ tintColor }) => <Icon name="info" size={20} style={{ color: tintColor }} />,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
drawerWidth: 300,
|
||||||
|
headerMode: 'none',
|
||||||
|
contentComponent: DrawerContent,
|
||||||
|
contentOptions: {
|
||||||
|
activeTintColor: Colors.LbryGreen,
|
||||||
|
labelStyle: discoverStyle.menuText,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
const mainStackNavigator = new createStackNavigator({
|
const mainStackNavigator = new createStackNavigator(
|
||||||
FirstRun: {
|
{
|
||||||
screen: FirstRunScreen,
|
FirstRun: {
|
||||||
navigationOptions: {
|
screen: FirstRunScreen,
|
||||||
drawerLockMode: 'locked-closed'
|
navigationOptions: {
|
||||||
}
|
drawerLockMode: 'locked-closed',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Splash: {
|
||||||
|
screen: SplashScreen,
|
||||||
|
navigationOptions: {
|
||||||
|
drawerLockMode: 'locked-closed',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Main: {
|
||||||
|
screen: drawer,
|
||||||
|
},
|
||||||
|
Verification: {
|
||||||
|
screen: VerificationScreen,
|
||||||
|
navigationOptions: {
|
||||||
|
drawerLockMode: 'locked-closed',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Splash: {
|
{
|
||||||
screen: SplashScreen,
|
headerMode: 'none',
|
||||||
navigationOptions: {
|
|
||||||
drawerLockMode: 'locked-closed'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Main: {
|
|
||||||
screen: drawer
|
|
||||||
},
|
|
||||||
Verification: {
|
|
||||||
screen: VerificationScreen,
|
|
||||||
navigationOptions: {
|
|
||||||
drawerLockMode: 'locked-closed'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, {
|
);
|
||||||
headerMode: 'none'
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
export const AppNavigator = mainStackNavigator;
|
export const AppNavigator = mainStackNavigator;
|
||||||
export const navigatorReducer = createNavigationReducer(AppNavigator);
|
export const navigatorReducer = createNavigationReducer(AppNavigator);
|
||||||
export const reactNavigationMiddleware = createReactNavigationReduxMiddleware(
|
export const reactNavigationMiddleware = createReactNavigationReduxMiddleware(state => state.nav);
|
||||||
state => state.nav,
|
|
||||||
);
|
|
||||||
|
|
||||||
const App = createReduxContainer(mainStackNavigator);
|
const App = createReduxContainer(mainStackNavigator);
|
||||||
const appMapStateToProps = (state) => ({
|
const appMapStateToProps = state => ({
|
||||||
state: state.nav,
|
state: state.nav,
|
||||||
});
|
});
|
||||||
const ReduxAppNavigator = connect(appMapStateToProps)(App);
|
const ReduxAppNavigator = connect(appMapStateToProps)(App);
|
||||||
|
@ -204,29 +234,34 @@ class AppWithNavigationState extends React.Component {
|
||||||
this.emailVerifyCheckInterval = null;
|
this.emailVerifyCheckInterval = null;
|
||||||
this.state = {
|
this.state = {
|
||||||
emailVerifyDone: false,
|
emailVerifyDone: false,
|
||||||
verifyPending: false
|
verifyPending: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
AppState.addEventListener('change', this._handleAppStateChange);
|
AppState.addEventListener('change', this._handleAppStateChange);
|
||||||
BackHandler.addEventListener('hardwareBackPress', function() {
|
BackHandler.addEventListener(
|
||||||
const { dispatch, nav, drawerStack } = this.props;
|
'hardwareBackPress',
|
||||||
// There should be a better way to check this
|
function() {
|
||||||
if (nav.routes.length > 0) {
|
const { dispatch, nav, drawerStack } = this.props;
|
||||||
if (nav.routes[0].routeName === 'Main') {
|
// There should be a better way to check this
|
||||||
const mainRoute = nav.routes[0];
|
if (nav.routes.length > 0) {
|
||||||
if (mainRoute.index > 0 ||
|
if (nav.routes[0].routeName === 'Main') {
|
||||||
|
const mainRoute = nav.routes[0];
|
||||||
|
if (
|
||||||
|
mainRoute.index > 0 ||
|
||||||
mainRoute.routes[0].index > 0 /* Discover stack index */ ||
|
mainRoute.routes[0].index > 0 /* Discover stack index */ ||
|
||||||
mainRoute.routes[4].index > 0 /* Wallet stack index */ ||
|
mainRoute.routes[4].index > 0 /* Wallet stack index */ ||
|
||||||
mainRoute.index >= 5 /* Settings and About screens */) {
|
mainRoute.index >= 5 /* Settings and About screens */
|
||||||
dispatchNavigateBack(dispatch, nav, drawerStack);
|
) {
|
||||||
return true;
|
dispatchNavigateBack(dispatch, nav, drawerStack);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return false;
|
||||||
return false;
|
}.bind(this)
|
||||||
}.bind(this));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -237,12 +272,12 @@ class AppWithNavigationState extends React.Component {
|
||||||
checkEmailVerification = () => {
|
checkEmailVerification = () => {
|
||||||
const { dispatch } = this.props;
|
const { dispatch } = this.props;
|
||||||
AsyncStorage.getItem(Constants.KEY_EMAIL_VERIFY_PENDING).then(pending => {
|
AsyncStorage.getItem(Constants.KEY_EMAIL_VERIFY_PENDING).then(pending => {
|
||||||
this.setState({ verifyPending: ('true' === pending) });
|
this.setState({ verifyPending: 'true' === pending });
|
||||||
if ('true' === pending) {
|
if ('true' === pending) {
|
||||||
dispatch(doUserCheckEmailVerified());
|
dispatch(doUserCheckEmailVerified());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
AppState.removeEventListener('change', this._handleAppStateChange);
|
AppState.removeEventListener('change', this._handleAppStateChange);
|
||||||
|
@ -270,13 +305,7 @@ class AppWithNavigationState extends React.Component {
|
||||||
|
|
||||||
componentWillUpdate(nextProps) {
|
componentWillUpdate(nextProps) {
|
||||||
const { dispatch } = this.props;
|
const { dispatch } = this.props;
|
||||||
const {
|
const { toast, emailToVerify, emailVerifyPending, emailVerifyErrorMessage, user } = nextProps;
|
||||||
toast,
|
|
||||||
emailToVerify,
|
|
||||||
emailVerifyPending,
|
|
||||||
emailVerifyErrorMessage,
|
|
||||||
user
|
|
||||||
} = nextProps;
|
|
||||||
|
|
||||||
if (toast) {
|
if (toast) {
|
||||||
const { message } = toast;
|
const { message } = toast;
|
||||||
|
@ -293,15 +322,13 @@ class AppWithNavigationState extends React.Component {
|
||||||
dispatch(doDismissToast());
|
dispatch(doDismissToast());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user &&
|
if (user && !emailVerifyPending && !this.state.emailVerifyDone && (emailToVerify || emailVerifyErrorMessage)) {
|
||||||
!emailVerifyPending &&
|
|
||||||
!this.state.emailVerifyDone &&
|
|
||||||
(emailToVerify || emailVerifyErrorMessage)) {
|
|
||||||
AsyncStorage.getItem(Constants.KEY_SHOULD_VERIFY_EMAIL).then(shouldVerify => {
|
AsyncStorage.getItem(Constants.KEY_SHOULD_VERIFY_EMAIL).then(shouldVerify => {
|
||||||
if ('true' === shouldVerify) {
|
if ('true' === shouldVerify) {
|
||||||
this.setState({ emailVerifyDone: true });
|
this.setState({ emailVerifyDone: true });
|
||||||
const message = emailVerifyErrorMessage ?
|
const message = emailVerifyErrorMessage
|
||||||
String(emailVerifyErrorMessage) : 'Your email address was successfully verified.';
|
? String(emailVerifyErrorMessage)
|
||||||
|
: 'Your email address was successfully verified.';
|
||||||
if (!emailVerifyErrorMessage) {
|
if (!emailVerifyErrorMessage) {
|
||||||
AsyncStorage.removeItem(Constants.KEY_FIRST_RUN_EMAIL);
|
AsyncStorage.removeItem(Constants.KEY_FIRST_RUN_EMAIL);
|
||||||
}
|
}
|
||||||
|
@ -313,7 +340,7 @@ class AppWithNavigationState extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleAppStateChange = (nextAppState) => {
|
_handleAppStateChange = nextAppState => {
|
||||||
const { backgroundPlayEnabled, dispatch } = this.props;
|
const { backgroundPlayEnabled, dispatch } = this.props;
|
||||||
// Check if the app was suspended
|
// Check if the app was suspended
|
||||||
if (AppState.currentState && AppState.currentState.match(/inactive|background/)) {
|
if (AppState.currentState && AppState.currentState.match(/inactive|background/)) {
|
||||||
|
@ -337,9 +364,9 @@ class AppWithNavigationState extends React.Component {
|
||||||
NativeModules.BackgroundMedia.hidePlaybackNotification();
|
NativeModules.BackgroundMedia.hidePlaybackNotification();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
_handleUrl = (evt) => {
|
_handleUrl = evt => {
|
||||||
const { dispatch, nav } = this.props;
|
const { dispatch, nav } = this.props;
|
||||||
if (evt.url) {
|
if (evt.url) {
|
||||||
if (evt.url.startsWith('lbry://?verify=')) {
|
if (evt.url.startsWith('lbry://?verify=')) {
|
||||||
|
@ -361,15 +388,17 @@ class AppWithNavigationState extends React.Component {
|
||||||
dispatch(doToast({ message }));
|
dispatch(doToast({ message }));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dispatch(doToast({
|
dispatch(
|
||||||
message: 'Invalid Verification URI',
|
doToast({
|
||||||
}));
|
message: 'Invalid Verification URI',
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dispatchNavigateToUri(dispatch, nav, evt.url);
|
dispatchNavigateToUri(dispatch, nav, evt.url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <ReduxAppNavigator />;
|
return <ReduxAppNavigator />;
|
||||||
|
@ -386,7 +415,7 @@ const mapStateToProps = state => ({
|
||||||
emailVerifyPending: selectEmailVerifyIsPending(state),
|
emailVerifyPending: selectEmailVerifyIsPending(state),
|
||||||
emailVerifyErrorMessage: selectEmailVerifyErrorMessage(state),
|
emailVerifyErrorMessage: selectEmailVerifyErrorMessage(state),
|
||||||
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_NSFW)(state),
|
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_NSFW)(state),
|
||||||
user: selectUser(state)
|
user: selectUser(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(AppWithNavigationState);
|
export default connect(mapStateToProps)(AppWithNavigationState);
|
||||||
|
|
|
@ -2,6 +2,9 @@ import { connect } from 'react-redux';
|
||||||
import { doToast } from 'lbry-redux';
|
import { doToast } from 'lbry-redux';
|
||||||
import Address from './view';
|
import Address from './view';
|
||||||
|
|
||||||
export default connect(null, {
|
export default connect(
|
||||||
doToast,
|
null,
|
||||||
})(Address);
|
{
|
||||||
|
doToast,
|
||||||
|
}
|
||||||
|
)(Address);
|
||||||
|
|
|
@ -15,13 +15,19 @@ export default class Address extends React.PureComponent<Props> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[walletStyle.row, style]}>
|
<View style={[walletStyle.row, style]}>
|
||||||
<Text selectable={true} numberOfLines={1} style={walletStyle.address}>{address || ''}</Text>
|
<Text selectable={true} numberOfLines={1} style={walletStyle.address}>
|
||||||
<Button icon={'clipboard'} style={walletStyle.button} onPress={() => {
|
{address || ''}
|
||||||
Clipboard.setString(address);
|
</Text>
|
||||||
doToast({
|
<Button
|
||||||
message: 'Address copied',
|
icon={'clipboard'}
|
||||||
});
|
style={walletStyle.button}
|
||||||
}} />
|
onPress={() => {
|
||||||
|
Clipboard.setString(address);
|
||||||
|
doToast({
|
||||||
|
message: 'Address copied',
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import Button from './view';
|
import Button from './view';
|
||||||
|
|
||||||
export default connect(null, null)(Button);
|
export default connect(
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)(Button);
|
||||||
|
|
|
@ -6,17 +6,7 @@ import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||||
|
|
||||||
export default class Button extends React.PureComponent {
|
export default class Button extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const { disabled, style, text, icon, iconColor, solid, theme, onPress, onLayout } = this.props;
|
||||||
disabled,
|
|
||||||
style,
|
|
||||||
text,
|
|
||||||
icon,
|
|
||||||
iconColor,
|
|
||||||
solid,
|
|
||||||
theme,
|
|
||||||
onPress,
|
|
||||||
onLayout
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
let styles = [buttonStyle.button, buttonStyle.row];
|
let styles = [buttonStyle.button, buttonStyle.row];
|
||||||
if (style) {
|
if (style) {
|
||||||
|
@ -43,16 +33,25 @@ export default class Button extends React.PureComponent {
|
||||||
textStyles.push(buttonStyle.textLight);
|
textStyles.push(buttonStyle.textLight);
|
||||||
}
|
}
|
||||||
|
|
||||||
let renderIcon = (<Icon name={icon} size={18} color={iconColor ? iconColor : ('light' === theme ? Colors.DarkGrey : Colors.White)} />);
|
let renderIcon = (
|
||||||
|
<Icon name={icon} size={18} color={iconColor ? iconColor : 'light' === theme ? Colors.DarkGrey : Colors.White} />
|
||||||
|
);
|
||||||
if (solid) {
|
if (solid) {
|
||||||
renderIcon = (<Icon name={icon} size={18} color={iconColor ? iconColor : ('light' === theme ? Colors.DarkGrey : Colors.White)} solid />);
|
renderIcon = (
|
||||||
|
<Icon
|
||||||
|
name={icon}
|
||||||
|
size={18}
|
||||||
|
color={iconColor ? iconColor : 'light' === theme ? Colors.DarkGrey : Colors.White}
|
||||||
|
solid
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity disabled={disabled} style={styles} onPress={onPress} onLayout={onLayout}>
|
<TouchableOpacity disabled={disabled} style={styles} onPress={onPress} onLayout={onLayout}>
|
||||||
{icon && renderIcon}
|
{icon && renderIcon}
|
||||||
{text && (text.trim().length > 0) && <Text style={textStyles}>{text}</Text>}
|
{text && text.trim().length > 0 && <Text style={textStyles}>{text}</Text>}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import CategoryList from './view';
|
import CategoryList from './view';
|
||||||
|
|
||||||
export default connect(null, null)(CategoryList);
|
export default connect(
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)(CategoryList);
|
||||||
|
|
|
@ -16,7 +16,7 @@ class CategoryList extends React.PureComponent {
|
||||||
initialNumToRender={3}
|
initialNumToRender={3}
|
||||||
maxToRenderPerBatch={3}
|
maxToRenderPerBatch={3}
|
||||||
removeClippedSubviews={true}
|
removeClippedSubviews={true}
|
||||||
renderItem={ ({item}) => (
|
renderItem={({ item }) => (
|
||||||
<FileItem
|
<FileItem
|
||||||
style={discoverStyle.fileItem}
|
style={discoverStyle.fileItem}
|
||||||
mediaStyle={discoverStyle.fileItemMedia}
|
mediaStyle={discoverStyle.fileItemMedia}
|
||||||
|
@ -24,9 +24,9 @@ class CategoryList extends React.PureComponent {
|
||||||
uri={normalizeURI(item)}
|
uri={normalizeURI(item)}
|
||||||
navigation={navigation}
|
navigation={navigation}
|
||||||
showDetails={true}
|
showDetails={true}
|
||||||
compactView={false} />
|
compactView={false}
|
||||||
)
|
/>
|
||||||
}
|
)}
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
showsHorizontalScrollIndicator={false}
|
showsHorizontalScrollIndicator={false}
|
||||||
data={categoryMap[category]}
|
data={categoryMap[category]}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {
|
||||||
doClaimRewardClearError,
|
doClaimRewardClearError,
|
||||||
makeSelectClaimRewardError,
|
makeSelectClaimRewardError,
|
||||||
makeSelectIsRewardClaimPending,
|
makeSelectIsRewardClaimPending,
|
||||||
rewards as REWARD_TYPES
|
rewards as REWARD_TYPES,
|
||||||
} from 'lbryinc';
|
} from 'lbryinc';
|
||||||
import CustomRewardCard from './view';
|
import CustomRewardCard from './view';
|
||||||
|
|
||||||
|
@ -20,7 +20,10 @@ const perform = dispatch => ({
|
||||||
claimReward: reward => dispatch(doClaimRewardType(reward.reward_type, true)),
|
claimReward: reward => dispatch(doClaimRewardType(reward.reward_type, true)),
|
||||||
clearError: reward => dispatch(doClaimRewardClearError(reward)),
|
clearError: reward => dispatch(doClaimRewardClearError(reward)),
|
||||||
notify: data => dispatch(doToast(data)),
|
notify: data => dispatch(doToast(data)),
|
||||||
submitRewardCode: code => dispatch(doClaimRewardType(REWARD_TYPES.TYPE_REWARD_CODE, { params: { code } }))
|
submitRewardCode: code => dispatch(doClaimRewardType(REWARD_TYPES.TYPE_REWARD_CODE, { params: { code } })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(CustomRewardCard);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(CustomRewardCard);
|
||||||
|
|
|
@ -10,7 +10,7 @@ import rewardStyle from '../../styles/reward';
|
||||||
class CustomRewardCard extends React.PureComponent<Props> {
|
class CustomRewardCard extends React.PureComponent<Props> {
|
||||||
state = {
|
state = {
|
||||||
claimStarted: false,
|
claimStarted: false,
|
||||||
rewardCode: ''
|
rewardCode: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
|
@ -49,31 +49,39 @@ class CustomRewardCard extends React.PureComponent<Props> {
|
||||||
this.setState({ claimStarted: true }, () => {
|
this.setState({ claimStarted: true }, () => {
|
||||||
submitRewardCode(rewardCode);
|
submitRewardCode(rewardCode);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { canClaim, rewardIsPending } = this.props;
|
const { canClaim, rewardIsPending } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[rewardStyle.rewardCard, rewardStyle.row]} >
|
<View style={[rewardStyle.rewardCard, rewardStyle.row]}>
|
||||||
<View style={rewardStyle.leftCol}>
|
<View style={rewardStyle.leftCol}>
|
||||||
{rewardIsPending && <ActivityIndicator size="small" color={Colors.LbryGreen} />}
|
{rewardIsPending && <ActivityIndicator size="small" color={Colors.LbryGreen} />}
|
||||||
</View>
|
</View>
|
||||||
<View style={rewardStyle.midCol}>
|
<View style={rewardStyle.midCol}>
|
||||||
<Text style={rewardStyle.rewardTitle}>Custom Code</Text>
|
<Text style={rewardStyle.rewardTitle}>Custom Code</Text>
|
||||||
<Text style={rewardStyle.rewardDescription}>Are you a supermodel or rockstar that received a custom reward code? Claim it here.</Text>
|
<Text style={rewardStyle.rewardDescription}>
|
||||||
|
Are you a supermodel or rockstar that received a custom reward code? Claim it here.
|
||||||
|
</Text>
|
||||||
|
|
||||||
<View>
|
<View>
|
||||||
<TextInput style={rewardStyle.customCodeInput}
|
<TextInput
|
||||||
placeholder={"0123abc"}
|
style={rewardStyle.customCodeInput}
|
||||||
onChangeText={text => this.setState({ rewardCode: text })}
|
placeholder={'0123abc'}
|
||||||
value={this.state.rewardCode} />
|
onChangeText={text => this.setState({ rewardCode: text })}
|
||||||
<Button style={rewardStyle.redeemButton}
|
value={this.state.rewardCode}
|
||||||
text={"Redeem"}
|
/>
|
||||||
disabled={(!this.state.rewardCode || this.state.rewardCode.trim().length === 0 || rewardIsPending)}
|
<Button
|
||||||
onPress={() => {
|
style={rewardStyle.redeemButton}
|
||||||
if (!rewardIsPending) { this.onClaimPress(); }
|
text={'Redeem'}
|
||||||
}} />
|
disabled={!this.state.rewardCode || this.state.rewardCode.trim().length === 0 || rewardIsPending}
|
||||||
|
onPress={() => {
|
||||||
|
if (!rewardIsPending) {
|
||||||
|
this.onClaimPress();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<View style={rewardStyle.rightCol}>
|
<View style={rewardStyle.rightCol}>
|
||||||
|
@ -83,6 +91,6 @@ class CustomRewardCard extends React.PureComponent<Props> {
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export default CustomRewardCard;
|
export default CustomRewardCard;
|
||||||
|
|
|
@ -29,7 +29,11 @@ class DateTime extends React.PureComponent<Props> {
|
||||||
const locale = 'en-US'; // default to en-US until we get a working i18n module for RN
|
const locale = 'en-US'; // default to en-US until we get a working i18n module for RN
|
||||||
|
|
||||||
if (timeAgo) {
|
if (timeAgo) {
|
||||||
return date ? <View style={style}><Text style={textStyle}>{moment(date).from(moment())}</Text></View> : null;
|
return date ? (
|
||||||
|
<View style={style}>
|
||||||
|
<Text style={textStyle}>{moment(date).from(moment())}</Text>
|
||||||
|
</View>
|
||||||
|
) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: formatOptions not working as expected in RN
|
// TODO: formatOptions not working as expected in RN
|
||||||
|
@ -38,13 +42,9 @@ class DateTime extends React.PureComponent<Props> {
|
||||||
return (
|
return (
|
||||||
<View style={style}>
|
<View style={style}>
|
||||||
<Text style={textStyle}>
|
<Text style={textStyle}>
|
||||||
{date &&
|
{date && (show === DateTime.SHOW_BOTH || show === DateTime.SHOW_DATE) && moment(date).format('MMMM D, YYYY')}
|
||||||
(show === DateTime.SHOW_BOTH || show === DateTime.SHOW_DATE) &&
|
|
||||||
moment(date).format('MMMM D, YYYY')}
|
|
||||||
{show === DateTime.SHOW_BOTH && ' '}
|
{show === DateTime.SHOW_BOTH && ' '}
|
||||||
{date &&
|
{date && (show === DateTime.SHOW_BOTH || show === DateTime.SHOW_TIME) && date.toLocaleTimeString()}
|
||||||
(show === DateTime.SHOW_BOTH || show === DateTime.SHOW_TIME) &&
|
|
||||||
date.toLocaleTimeString()}
|
|
||||||
{!date && '...'}
|
{!date && '...'}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -14,7 +14,7 @@ class DrawerContent extends React.PureComponent {
|
||||||
<SafeAreaView style={discoverStyle.drawerContentContainer} forceInset={{ top: 'always', horizontal: 'never' }}>
|
<SafeAreaView style={discoverStyle.drawerContentContainer} forceInset={{ top: 'always', horizontal: 'never' }}>
|
||||||
<DrawerItems
|
<DrawerItems
|
||||||
{...props}
|
{...props}
|
||||||
onItemPress={(route) => {
|
onItemPress={route => {
|
||||||
const { routeName } = route.route;
|
const { routeName } = route.route;
|
||||||
if (Constants.FULL_ROUTE_NAME_DISCOVER === routeName) {
|
if (Constants.FULL_ROUTE_NAME_DISCOVER === routeName) {
|
||||||
navigation.navigate({ routeName: Constants.DRAWER_ROUTE_DISCOVER });
|
navigation.navigate({ routeName: Constants.DRAWER_ROUTE_DISCOVER });
|
||||||
|
@ -28,7 +28,7 @@ class DrawerContent extends React.PureComponent {
|
||||||
|
|
||||||
onItemPress(route);
|
onItemPress(route);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
|
|
|
@ -22,4 +22,7 @@ const perform = dispatch => ({
|
||||||
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
|
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(FileDownloadButton);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(FileDownloadButton);
|
||||||
|
|
|
@ -49,13 +49,14 @@ class FileDownloadButton extends React.PureComponent {
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if ((fileInfo && !fileInfo.stopped) || loading || downloading) {
|
if ((fileInfo && !fileInfo.stopped) || loading || downloading) {
|
||||||
const progress =
|
const progress = fileInfo && fileInfo.written_bytes ? (fileInfo.written_bytes / fileInfo.total_bytes) * 100 : 0,
|
||||||
fileInfo && fileInfo.written_bytes ? fileInfo.written_bytes / fileInfo.total_bytes * 100 : 0,
|
|
||||||
label = fileInfo ? progress.toFixed(0) + '% complete' : 'Connecting...';
|
label = fileInfo ? progress.toFixed(0) + '% complete' : 'Connecting...';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[style, fileDownloadButtonStyle.container]}>
|
<View style={[style, fileDownloadButtonStyle.container]}>
|
||||||
<View style={{ width: `${progress}%`, backgroundColor: '#ff0000', position: 'absolute', left: 0, top: 0 }}></View>
|
<View
|
||||||
|
style={{ width: `${progress}%`, backgroundColor: '#ff0000', position: 'absolute', left: 0, top: 0 }}
|
||||||
|
></View>
|
||||||
<Text style={fileDownloadButtonStyle.text}>{label}</Text>
|
<Text style={fileDownloadButtonStyle.text}>{label}</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -68,29 +69,35 @@ class FileDownloadButton extends React.PureComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Button icon={isPlayable ? 'play' : null}
|
<Button
|
||||||
text={(isPlayable ? 'Play' : (isViewable ? 'View' : 'Download'))}
|
icon={isPlayable ? 'play' : null}
|
||||||
onLayout={onButtonLayout}
|
text={isPlayable ? 'Play' : isViewable ? 'View' : 'Download'}
|
||||||
style={[style, fileDownloadButtonStyle.container]} onPress={() => {
|
onLayout={onButtonLayout}
|
||||||
if (NativeModules.Firebase) {
|
style={[style, fileDownloadButtonStyle.container]}
|
||||||
NativeModules.Firebase.track('purchase_uri', { uri: uri });
|
onPress={() => {
|
||||||
}
|
if (NativeModules.Firebase) {
|
||||||
purchaseUri(uri, costInfo, !isPlayable);
|
NativeModules.Firebase.track('purchase_uri', { uri: uri });
|
||||||
if (NativeModules.UtilityModule) {
|
}
|
||||||
NativeModules.UtilityModule.checkDownloads();
|
purchaseUri(uri, costInfo, !isPlayable);
|
||||||
}
|
if (NativeModules.UtilityModule) {
|
||||||
if (isPlayable && onPlay) {
|
NativeModules.UtilityModule.checkDownloads();
|
||||||
this.props.onPlay();
|
}
|
||||||
}
|
if (isPlayable && onPlay) {
|
||||||
if (isViewable && onView) {
|
this.props.onPlay();
|
||||||
this.props.onView();
|
}
|
||||||
}
|
if (isViewable && onView) {
|
||||||
}} />
|
this.props.onView();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
} else if (fileInfo && fileInfo.download_path) {
|
} else if (fileInfo && fileInfo.download_path) {
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity onLayout={onButtonLayout}
|
<TouchableOpacity
|
||||||
style={[style, fileDownloadButtonStyle.container]} onPress={openFile}>
|
onLayout={onButtonLayout}
|
||||||
|
style={[style, fileDownloadButtonStyle.container]}
|
||||||
|
onPress={openFile}
|
||||||
|
>
|
||||||
<Text style={fileDownloadButtonStyle.text}>{isViewable ? 'View' : 'Open'}</Text>
|
<Text style={fileDownloadButtonStyle.text}>{isViewable ? 'View' : 'Open'}</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
makeSelectThumbnailForUri,
|
makeSelectThumbnailForUri,
|
||||||
makeSelectTitleForUri,
|
makeSelectTitleForUri,
|
||||||
makeSelectIsUriResolving,
|
makeSelectIsUriResolving,
|
||||||
makeSelectClaimIsNsfw
|
makeSelectClaimIsNsfw,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { selectRewardContentClaimIds } from 'lbryinc';
|
import { selectRewardContentClaimIds } from 'lbryinc';
|
||||||
import { selectShowNsfw } from 'redux/selectors/settings';
|
import { selectShowNsfw } from 'redux/selectors/settings';
|
||||||
|
@ -29,4 +29,7 @@ const perform = dispatch => ({
|
||||||
resolveUri: uri => dispatch(doResolveUri(uri)),
|
resolveUri: uri => dispatch(doResolveUri(uri)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(FileItem);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(FileItem);
|
||||||
|
|
|
@ -40,7 +40,7 @@ class FileItem extends React.PureComponent {
|
||||||
NativeModules.Firebase.track('explore_click', { uri: normalizedUri });
|
NativeModules.Firebase.track('explore_click', { uri: normalizedUri });
|
||||||
}
|
}
|
||||||
navigateToUri(navigation, normalizedUri);
|
navigateToUri(navigation, normalizedUri);
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
|
@ -56,49 +56,76 @@ class FileItem extends React.PureComponent {
|
||||||
navigation,
|
navigation,
|
||||||
showDetails,
|
showDetails,
|
||||||
compactView,
|
compactView,
|
||||||
titleBeforeThumbnail
|
titleBeforeThumbnail,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const uri = normalizeURI(this.props.uri);
|
const uri = normalizeURI(this.props.uri);
|
||||||
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
|
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
|
||||||
const isRewardContent = claim && rewardedContentClaimIds.includes(claim.claim_id);
|
const isRewardContent = claim && rewardedContentClaimIds.includes(claim.claim_id);
|
||||||
const channelName = claim ? claim.channel_name : null;
|
const channelName = claim ? claim.channel_name : null;
|
||||||
const channelClaimId = claim && claim.value && claim.value.publisherSignature && claim.value.publisherSignature.certificateId;
|
const channelClaimId =
|
||||||
|
claim && claim.value && claim.value.publisherSignature && claim.value.publisherSignature.certificateId;
|
||||||
const fullChannelUri = channelClaimId ? `${channelName}#${channelClaimId}` : channelName;
|
const fullChannelUri = channelClaimId ? `${channelName}#${channelClaimId}` : channelName;
|
||||||
const height = claim ? claim.height : null;
|
const height = claim ? claim.height : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={style}>
|
<View style={style}>
|
||||||
<TouchableOpacity style={discoverStyle.container} onPress={this.navigateToFileUri}>
|
<TouchableOpacity style={discoverStyle.container} onPress={this.navigateToFileUri}>
|
||||||
{!compactView && titleBeforeThumbnail && <Text numberOfLines={1} style={[discoverStyle.fileItemName, discoverStyle.rewardTitle]}>{title}</Text>}
|
{!compactView && titleBeforeThumbnail && (
|
||||||
<FileItemMedia title={title}
|
<Text numberOfLines={1} style={[discoverStyle.fileItemName, discoverStyle.rewardTitle]}>
|
||||||
thumbnail={thumbnail}
|
{title}
|
||||||
blurRadius={obscureNsfw ? 15 : 0}
|
</Text>
|
||||||
resizeMode="cover"
|
)}
|
||||||
isResolvingUri={isResolvingUri}
|
<FileItemMedia
|
||||||
style={mediaStyle} />
|
title={title}
|
||||||
|
thumbnail={thumbnail}
|
||||||
|
blurRadius={obscureNsfw ? 15 : 0}
|
||||||
|
resizeMode="cover"
|
||||||
|
isResolvingUri={isResolvingUri}
|
||||||
|
style={mediaStyle}
|
||||||
|
/>
|
||||||
|
|
||||||
{(!compactView && fileInfo && fileInfo.completed && fileInfo.download_path) &&
|
{!compactView && fileInfo && fileInfo.completed && fileInfo.download_path && (
|
||||||
<Icon style={discoverStyle.downloadedIcon} solid={true} color={Colors.NextLbryGreen} name={"folder"} size={16} />}
|
<Icon
|
||||||
{(!compactView && (!fileInfo || !fileInfo.completed || !fileInfo.download_path)) &&
|
style={discoverStyle.downloadedIcon}
|
||||||
<FilePrice uri={uri} style={discoverStyle.filePriceContainer} textStyle={discoverStyle.filePriceText} />}
|
solid={true}
|
||||||
{!compactView && <View style={isRewardContent ? discoverStyle.rewardTitleContainer : null}>
|
color={Colors.NextLbryGreen}
|
||||||
<Text numberOfLines={1} style={[discoverStyle.fileItemName, discoverStyle.rewardTitle]}>{title}</Text>
|
name={'folder'}
|
||||||
{isRewardContent && <Icon style={discoverStyle.rewardIcon} name="award" size={14} />}
|
size={16}
|
||||||
</View>}
|
/>
|
||||||
{(!compactView && showDetails) &&
|
)}
|
||||||
<View style={discoverStyle.detailsRow}>
|
{!compactView && (!fileInfo || !fileInfo.completed || !fileInfo.download_path) && (
|
||||||
{channelName &&
|
<FilePrice uri={uri} style={discoverStyle.filePriceContainer} textStyle={discoverStyle.filePriceText} />
|
||||||
<Link style={discoverStyle.channelName} text={channelName} onPress={() => {
|
)}
|
||||||
navigateToUri(navigation, normalizeURI(fullChannelUri));
|
{!compactView && (
|
||||||
}} />}
|
<View style={isRewardContent ? discoverStyle.rewardTitleContainer : null}>
|
||||||
<DateTime style={discoverStyle.dateTime} textStyle={discoverStyle.dateTimeText} timeAgo uri={uri} />
|
<Text numberOfLines={1} style={[discoverStyle.fileItemName, discoverStyle.rewardTitle]}>
|
||||||
</View>}
|
{title}
|
||||||
|
</Text>
|
||||||
|
{isRewardContent && <Icon style={discoverStyle.rewardIcon} name="award" size={14} />}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{!compactView && showDetails && (
|
||||||
|
<View style={discoverStyle.detailsRow}>
|
||||||
|
{channelName && (
|
||||||
|
<Link
|
||||||
|
style={discoverStyle.channelName}
|
||||||
|
text={channelName}
|
||||||
|
onPress={() => {
|
||||||
|
navigateToUri(navigation, normalizeURI(fullChannelUri));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<DateTime style={discoverStyle.dateTime} textStyle={discoverStyle.dateTimeText} timeAgo uri={uri} />
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
{obscureNsfw && <NsfwOverlay onPress={() => navigation.navigate({ routeName: 'Settings', key: 'settingsPage' })} />}
|
{obscureNsfw && (
|
||||||
|
<NsfwOverlay onPress={() => navigation.navigate({ routeName: 'Settings', key: 'settingsPage' })} />
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FileItem;
|
export default FileItem;
|
||||||
|
|
|
@ -4,4 +4,7 @@ import FileItemMedia from './view';
|
||||||
const select = state => ({});
|
const select = state => ({});
|
||||||
const perform = dispatch => ({});
|
const perform = dispatch => ({});
|
||||||
|
|
||||||
export default connect(select, perform)(FileItemMedia);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(FileItemMedia);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ActivityIndicator, Image, Text, View } from 'react-native';
|
import { ActivityIndicator, Image, Text, View } from 'react-native';
|
||||||
import Colors from 'styles/colors';
|
import Colors from 'styles/colors';
|
||||||
import FastImage from 'react-native-fast-image'
|
import FastImage from 'react-native-fast-image';
|
||||||
import fileItemMediaStyle from 'styles/fileItemMedia';
|
import fileItemMediaStyle from 'styles/fileItemMedia';
|
||||||
|
|
||||||
class FileItemMedia extends React.PureComponent {
|
class FileItemMedia extends React.PureComponent {
|
||||||
|
@ -20,33 +20,31 @@ class FileItemMedia extends React.PureComponent {
|
||||||
];
|
];
|
||||||
|
|
||||||
state: {
|
state: {
|
||||||
imageLoadFailed: false
|
imageLoadFailed: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.setState({
|
this.setState({
|
||||||
autoThumbStyle:
|
autoThumbStyle:
|
||||||
FileItemMedia.AUTO_THUMB_STYLES[
|
FileItemMedia.AUTO_THUMB_STYLES[Math.floor(Math.random() * FileItemMedia.AUTO_THUMB_STYLES.length)],
|
||||||
Math.floor(Math.random() * FileItemMedia.AUTO_THUMB_STYLES.length)
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getFastImageResizeMode(resizeMode) {
|
getFastImageResizeMode(resizeMode) {
|
||||||
switch (resizeMode) {
|
switch (resizeMode) {
|
||||||
case "contain":
|
case 'contain':
|
||||||
return FastImage.resizeMode.contain;
|
return FastImage.resizeMode.contain;
|
||||||
case "stretch":
|
case 'stretch':
|
||||||
return FastImage.resizeMode.stretch;
|
return FastImage.resizeMode.stretch;
|
||||||
case "center":
|
case 'center':
|
||||||
return FastImage.resizeMode.center;
|
return FastImage.resizeMode.center;
|
||||||
default:
|
default:
|
||||||
return FastImage.resizeMode.cover;
|
return FastImage.resizeMode.cover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isThumbnailValid = (thumbnail) => {
|
isThumbnailValid = thumbnail => {
|
||||||
if (!thumbnail || ((typeof thumbnail) !== 'string')) {
|
if (!thumbnail || typeof thumbnail !== 'string') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +53,7 @@ class FileItemMedia extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let style = this.props.style;
|
let style = this.props.style;
|
||||||
|
@ -70,16 +68,17 @@ class FileItemMedia extends React.PureComponent {
|
||||||
// No blur radius support in FastImage yet
|
// No blur radius support in FastImage yet
|
||||||
return (
|
return (
|
||||||
<Image
|
<Image
|
||||||
source={{uri: thumbnail}}
|
source={{ uri: thumbnail }}
|
||||||
blurRadius={blurRadius}
|
blurRadius={blurRadius}
|
||||||
resizeMode={resizeMode ? resizeMode : "cover"}
|
resizeMode={resizeMode ? resizeMode : 'cover'}
|
||||||
style={style}
|
style={style}
|
||||||
/>);
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FastImage
|
<FastImage
|
||||||
source={{uri: thumbnail}}
|
source={{ uri: thumbnail }}
|
||||||
onError={() => this.setState({ imageLoadFailed: true })}
|
onError={() => this.setState({ imageLoadFailed: true })}
|
||||||
resizeMode={this.getFastImageResizeMode(resizeMode)}
|
resizeMode={this.getFastImageResizeMode(resizeMode)}
|
||||||
style={style}
|
style={style}
|
||||||
|
@ -91,15 +90,19 @@ class FileItemMedia extends React.PureComponent {
|
||||||
<View style={[style ? style : fileItemMediaStyle.autothumb, atStyle]}>
|
<View style={[style ? style : fileItemMediaStyle.autothumb, atStyle]}>
|
||||||
{isResolvingUri && (
|
{isResolvingUri && (
|
||||||
<View style={fileItemMediaStyle.resolving}>
|
<View style={fileItemMediaStyle.resolving}>
|
||||||
<ActivityIndicator color={Colors.White} size={"large"} />
|
<ActivityIndicator color={Colors.White} size={'large'} />
|
||||||
<Text style={fileItemMediaStyle.text}>Resolving...</Text>
|
<Text style={fileItemMediaStyle.text}>Resolving...</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{!isResolvingUri && <Text style={fileItemMediaStyle.autothumbText}>{title &&
|
{!isResolvingUri && (
|
||||||
title
|
<Text style={fileItemMediaStyle.autothumbText}>
|
||||||
.replace(/\s+/g, '')
|
{title &&
|
||||||
.substring(0, Math.min(title.replace(' ', '').length, 5))
|
title
|
||||||
.toUpperCase()}</Text>}
|
.replace(/\s+/g, '')
|
||||||
|
.substring(0, Math.min(title.replace(' ', '').length, 5))
|
||||||
|
.toUpperCase()}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,4 +8,7 @@ const select = state => ({
|
||||||
|
|
||||||
const perform = dispatch => ({});
|
const perform = dispatch => ({});
|
||||||
|
|
||||||
export default connect(select, perform)(FileList);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(FileList);
|
||||||
|
|
|
@ -145,15 +145,7 @@ class FileList extends React.PureComponent<Props, State> {
|
||||||
sortFunctions: {};
|
sortFunctions: {};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const { contentContainerStyle, fileInfos, hideFilter, checkPending, navigation, onEndReached, style } = this.props;
|
||||||
contentContainerStyle,
|
|
||||||
fileInfos,
|
|
||||||
hideFilter,
|
|
||||||
checkPending,
|
|
||||||
navigation,
|
|
||||||
onEndReached,
|
|
||||||
style
|
|
||||||
} = this.props;
|
|
||||||
const { sortBy } = this.state;
|
const { sortBy } = this.state;
|
||||||
const items = [];
|
const items = [];
|
||||||
|
|
||||||
|
@ -182,13 +174,16 @@ class FileList extends React.PureComponent<Props, State> {
|
||||||
data={items}
|
data={items}
|
||||||
onEndReached={onEndReached}
|
onEndReached={onEndReached}
|
||||||
keyExtractor={(item, index) => item}
|
keyExtractor={(item, index) => item}
|
||||||
renderItem={({item}) => (
|
renderItem={({ item }) => (
|
||||||
<FileItem style={fileListStyle.fileItem}
|
<FileItem
|
||||||
uri={item}
|
style={fileListStyle.fileItem}
|
||||||
navigation={navigation}
|
uri={item}
|
||||||
showDetails={true}
|
navigation={navigation}
|
||||||
compactView={false} />
|
showDetails={true}
|
||||||
)} />
|
compactView={false}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,10 @@ const select = (state, props) => ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
resolveUri: uri => dispatch(doResolveUri(uri))
|
resolveUri: uri => dispatch(doResolveUri(uri)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(FileListItem);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(FileListItem);
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { normalizeURI, parseURI } from 'lbry-redux';
|
import { normalizeURI, parseURI } from 'lbry-redux';
|
||||||
import {
|
import { ActivityIndicator, Platform, Text, TouchableOpacity, View } from 'react-native';
|
||||||
ActivityIndicator,
|
|
||||||
Platform,
|
|
||||||
Text,
|
|
||||||
TouchableOpacity,
|
|
||||||
View
|
|
||||||
} from 'react-native';
|
|
||||||
import { navigateToUri, formatBytes } from 'utils/helper';
|
import { navigateToUri, formatBytes } from 'utils/helper';
|
||||||
import Colors from 'styles/colors';
|
import Colors from 'styles/colors';
|
||||||
import DateTime from 'component/dateTime';
|
import DateTime from 'component/dateTime';
|
||||||
|
@ -18,7 +12,7 @@ import ProgressBar from 'component/progressBar';
|
||||||
import fileListStyle from 'styles/fileList';
|
import fileListStyle from 'styles/fileList';
|
||||||
|
|
||||||
class FileListItem extends React.PureComponent {
|
class FileListItem extends React.PureComponent {
|
||||||
getStorageForFileInfo = (fileInfo) => {
|
getStorageForFileInfo = fileInfo => {
|
||||||
if (!fileInfo.completed) {
|
if (!fileInfo.completed) {
|
||||||
const written = formatBytes(fileInfo.written_bytes);
|
const written = formatBytes(fileInfo.written_bytes);
|
||||||
const total = formatBytes(fileInfo.total_bytes);
|
const total = formatBytes(fileInfo.total_bytes);
|
||||||
|
@ -26,19 +20,19 @@ class FileListItem extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return formatBytes(fileInfo.written_bytes);
|
return formatBytes(fileInfo.written_bytes);
|
||||||
}
|
};
|
||||||
|
|
||||||
formatTitle = (title) => {
|
formatTitle = title => {
|
||||||
if (!title) {
|
if (!title) {
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (title.length > 80) ? title.substring(0, 77).trim() + '...' : title;
|
return title.length > 80 ? title.substring(0, 77).trim() + '...' : title;
|
||||||
}
|
};
|
||||||
|
|
||||||
getDownloadProgress = (fileInfo) => {
|
getDownloadProgress = fileInfo => {
|
||||||
return Math.ceil((fileInfo.written_bytes / fileInfo.total_bytes) * 100);
|
return Math.ceil((fileInfo.written_bytes / fileInfo.total_bytes) * 100);
|
||||||
}
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { claim, resolveUri, uri } = this.props;
|
const { claim, resolveUri, uri } = this.props;
|
||||||
|
@ -59,7 +53,7 @@ class FileListItem extends React.PureComponent {
|
||||||
onPress,
|
onPress,
|
||||||
navigation,
|
navigation,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
title
|
title,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const uri = normalizeURI(this.props.uri);
|
const uri = normalizeURI(this.props.uri);
|
||||||
|
@ -82,50 +76,80 @@ class FileListItem extends React.PureComponent {
|
||||||
return (
|
return (
|
||||||
<View style={style}>
|
<View style={style}>
|
||||||
<TouchableOpacity style={style} onPress={onPress}>
|
<TouchableOpacity style={style} onPress={onPress}>
|
||||||
<FileItemMedia style={fileListStyle.thumbnail}
|
<FileItemMedia
|
||||||
blurRadius={obscureNsfw ? 15 : 0}
|
style={fileListStyle.thumbnail}
|
||||||
resizeMode="cover"
|
blurRadius={obscureNsfw ? 15 : 0}
|
||||||
title={(title || name)}
|
resizeMode="cover"
|
||||||
thumbnail={thumbnail} />
|
title={title || name}
|
||||||
{(fileInfo && fileInfo.completed && fileInfo.download_path) &&
|
thumbnail={thumbnail}
|
||||||
<Icon style={fileListStyle.downloadedIcon} solid={true} color={Colors.NextLbryGreen} name={"folder"} size={16} />}
|
/>
|
||||||
|
{fileInfo && fileInfo.completed && fileInfo.download_path && (
|
||||||
|
<Icon
|
||||||
|
style={fileListStyle.downloadedIcon}
|
||||||
|
solid={true}
|
||||||
|
color={Colors.NextLbryGreen}
|
||||||
|
name={'folder'}
|
||||||
|
size={16}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<View style={fileListStyle.detailsContainer}>
|
<View style={fileListStyle.detailsContainer}>
|
||||||
{featuredResult && <Text style={fileListStyle.featuredUri} numberOfLines={1}>{uri}</Text>}
|
{featuredResult && (
|
||||||
|
<Text style={fileListStyle.featuredUri} numberOfLines={1}>
|
||||||
|
{uri}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
|
||||||
{!title && !name && !channel && isResolving && (
|
{!title && !name && !channel && isResolving && (
|
||||||
<View>
|
<View>
|
||||||
{(!title && !name) && <Text style={fileListStyle.uri}>{uri}</Text>}
|
{!title && !name && <Text style={fileListStyle.uri}>{uri}</Text>}
|
||||||
{(!title && !name) && <View style={fileListStyle.row}>
|
{!title && !name && (
|
||||||
<ActivityIndicator size={"small"} color={featuredResult ? Colors.White : Colors.LbryGreen} />
|
<View style={fileListStyle.row}>
|
||||||
</View>}
|
<ActivityIndicator size={'small'} color={featuredResult ? Colors.White : Colors.LbryGreen} />
|
||||||
</View>)}
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
{(title || name) && <Text style={featuredResult ? fileListStyle.featuredTitle : fileListStyle.title}>{this.formatTitle(title) || this.formatTitle(name)}</Text>}
|
{(title || name) && (
|
||||||
{channel &&
|
<Text style={featuredResult ? fileListStyle.featuredTitle : fileListStyle.title}>
|
||||||
<Link style={fileListStyle.publisher} text={channel} onPress={() => {
|
{this.formatTitle(title) || this.formatTitle(name)}
|
||||||
navigateToUri(navigation, normalizeURI(fullChannelUri));
|
</Text>
|
||||||
}} />}
|
)}
|
||||||
|
{channel && (
|
||||||
|
<Link
|
||||||
|
style={fileListStyle.publisher}
|
||||||
|
text={channel}
|
||||||
|
onPress={() => {
|
||||||
|
navigateToUri(navigation, normalizeURI(fullChannelUri));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<View style={fileListStyle.info}>
|
<View style={fileListStyle.info}>
|
||||||
{(fileInfo && !isNaN(fileInfo.written_bytes) && fileInfo.written_bytes > 0) &&
|
{fileInfo && !isNaN(fileInfo.written_bytes) && fileInfo.written_bytes > 0 && (
|
||||||
<Text style={fileListStyle.infoText}>{this.getStorageForFileInfo(fileInfo)}</Text>}
|
<Text style={fileListStyle.infoText}>{this.getStorageForFileInfo(fileInfo)}</Text>
|
||||||
|
)}
|
||||||
<DateTime style={fileListStyle.publishInfo} textStyle={fileListStyle.infoText} timeAgo uri={uri} />
|
<DateTime style={fileListStyle.publishInfo} textStyle={fileListStyle.infoText} timeAgo uri={uri} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{(fileInfo && fileInfo.download_path) &&
|
{fileInfo && fileInfo.download_path && (
|
||||||
<View style={fileListStyle.downloadInfo}>
|
<View style={fileListStyle.downloadInfo}>
|
||||||
{!fileInfo.completed &&
|
{!fileInfo.completed && (
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
borderRadius={3}
|
borderRadius={3}
|
||||||
color={Colors.NextLbryGreen}
|
color={Colors.NextLbryGreen}
|
||||||
height={3}
|
height={3}
|
||||||
style={fileListStyle.progress}
|
style={fileListStyle.progress}
|
||||||
progress={this.getDownloadProgress(fileInfo)} />}
|
progress={this.getDownloadProgress(fileInfo)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
{obscureNsfw && <NsfwOverlay onPress={() => navigation.navigate({ routeName: 'Settings', key: 'settingsPage' })} />}
|
{obscureNsfw && (
|
||||||
|
<NsfwOverlay onPress={() => navigation.navigate({ routeName: 'Settings', key: 'settingsPage' })} />
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,4 +13,7 @@ const perform = dispatch => ({
|
||||||
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
|
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(FilePrice);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(FilePrice);
|
||||||
|
|
|
@ -46,9 +46,7 @@ class CreditAmount extends React.PureComponent {
|
||||||
} else {
|
} else {
|
||||||
if (this.props.label) {
|
if (this.props.label) {
|
||||||
const label =
|
const label =
|
||||||
typeof this.props.label === 'string'
|
typeof this.props.label === 'string' ? this.props.label : parseFloat(amount) == 1 ? 'credit' : 'credits';
|
||||||
? this.props.label
|
|
||||||
: parseFloat(amount) == 1 ? 'credit' : 'credits';
|
|
||||||
|
|
||||||
amountText = `${formattedAmount} ${label}`;
|
amountText = `${formattedAmount} ${label}`;
|
||||||
} else {
|
} else {
|
||||||
|
@ -67,9 +65,7 @@ class CreditAmount extends React.PureComponent {
|
||||||
*
|
*
|
||||||
</span>
|
</span>
|
||||||
) : null}*/
|
) : null}*/
|
||||||
return (
|
return <Text style={style}>{amountText}</Text>;
|
||||||
<Text style={style}>{amountText}</Text>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +96,7 @@ class FilePrice extends React.PureComponent {
|
||||||
<View style={style}>
|
<View style={style}>
|
||||||
<Text style={textStyle}>???</Text>
|
<Text style={textStyle}>???</Text>
|
||||||
</View>
|
</View>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -111,10 +107,13 @@ class FilePrice extends React.PureComponent {
|
||||||
amount={parseFloat(costInfo.cost)}
|
amount={parseFloat(costInfo.cost)}
|
||||||
isEstimate={isEstimate}
|
isEstimate={isEstimate}
|
||||||
showFree
|
showFree
|
||||||
showFullPrice={showFullPrice}>???</CreditAmount>
|
showFullPrice={showFullPrice}
|
||||||
|
>
|
||||||
|
???
|
||||||
|
</CreditAmount>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FilePrice;
|
export default FilePrice;
|
||||||
|
|
|
@ -11,4 +11,7 @@ const select = state => ({
|
||||||
rewardsNotInterested: makeSelectClientSetting(Constants.SETTING_REWARDS_NOT_INTERESTED)(state),
|
rewardsNotInterested: makeSelectClientSetting(Constants.SETTING_REWARDS_NOT_INTERESTED)(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, null)(FloatingWalletBalance);
|
export default connect(
|
||||||
|
select,
|
||||||
|
null
|
||||||
|
)(FloatingWalletBalance);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ActivityIndicator, Text, TouchableOpacity, View } from 'react-native';
|
import { ActivityIndicator, Text, TouchableOpacity, View } from 'react-native';
|
||||||
import { formatCredits } from 'lbry-redux'
|
import { formatCredits } from 'lbry-redux';
|
||||||
import Address from 'component/address';
|
import Address from 'component/address';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import Colors from 'styles/colors';
|
import Colors from 'styles/colors';
|
||||||
|
@ -18,17 +18,23 @@ class FloatingWalletBalance extends React.PureComponent<Props> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[floatingButtonStyle.view, floatingButtonStyle.bottomRight]}>
|
<View style={[floatingButtonStyle.view, floatingButtonStyle.bottomRight]}>
|
||||||
{(!rewardsNotInterested && unclaimedRewardAmount > 0) &&
|
{!rewardsNotInterested && unclaimedRewardAmount > 0 && (
|
||||||
<TouchableOpacity style={floatingButtonStyle.pendingContainer}
|
<TouchableOpacity
|
||||||
onPress={() => navigation && navigation.navigate({ routeName: 'Rewards' })} >
|
style={floatingButtonStyle.pendingContainer}
|
||||||
<Icon name="award" size={18} style={floatingButtonStyle.rewardIcon} />
|
onPress={() => navigation && navigation.navigate({ routeName: 'Rewards' })}
|
||||||
<Text style={floatingButtonStyle.text}>{unclaimedRewardAmount}</Text>
|
>
|
||||||
</TouchableOpacity>}
|
<Icon name="award" size={18} style={floatingButtonStyle.rewardIcon} />
|
||||||
<TouchableOpacity style={floatingButtonStyle.container}
|
<Text style={floatingButtonStyle.text}>{unclaimedRewardAmount}</Text>
|
||||||
onPress={() => navigation && navigation.navigate({ routeName: 'WalletStack' })}>
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
<TouchableOpacity
|
||||||
|
style={floatingButtonStyle.container}
|
||||||
|
onPress={() => navigation && navigation.navigate({ routeName: 'WalletStack' })}
|
||||||
|
>
|
||||||
{isNaN(balance) && <ActivityIndicator size="small" color={Colors.White} />}
|
{isNaN(balance) && <ActivityIndicator size="small" color={Colors.White} />}
|
||||||
{(!isNaN(balance) || balance === 0) && (
|
{(!isNaN(balance) || balance === 0) && (
|
||||||
<Text style={floatingButtonStyle.text}>{(formatCredits(parseFloat(balance), 2) + ' LBC')}</Text>)}
|
<Text style={floatingButtonStyle.text}>{formatCredits(parseFloat(balance), 2) + ' LBC'}</Text>
|
||||||
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,7 +3,10 @@ import { doToast } from 'lbry-redux';
|
||||||
import Link from './view';
|
import Link from './view';
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
notify: (data) => dispatch(doToast(data))
|
notify: data => dispatch(doToast(data)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(null, perform)(Link);
|
export default connect(
|
||||||
|
null,
|
||||||
|
perform
|
||||||
|
)(Link);
|
||||||
|
|
|
@ -2,13 +2,12 @@ import React from 'react';
|
||||||
import { Linking, Text, TouchableOpacity } from 'react-native';
|
import { Linking, Text, TouchableOpacity } from 'react-native';
|
||||||
|
|
||||||
export default class Link extends React.PureComponent {
|
export default class Link extends React.PureComponent {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
tappedStyle: false,
|
tappedStyle: false,
|
||||||
}
|
};
|
||||||
this.addTappedStyle = this.addTappedStyle.bind(this)
|
this.addTappedStyle = this.addTappedStyle.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePress = () => {
|
handlePress = () => {
|
||||||
|
@ -19,28 +18,27 @@ export default class Link extends React.PureComponent {
|
||||||
} else {
|
} else {
|
||||||
if (this.props.effectOnTap) this.addTappedStyle();
|
if (this.props.effectOnTap) this.addTappedStyle();
|
||||||
Linking.openURL(href)
|
Linking.openURL(href)
|
||||||
.then(() => setTimeout(() => { this.setState({ tappedStyle: false }); }, 2000))
|
.then(() =>
|
||||||
.catch(err => {
|
setTimeout(() => {
|
||||||
notify({ message: error, isError: true })
|
this.setState({ tappedStyle: false });
|
||||||
this.setState({tappedStyle: false})
|
}, 2000)
|
||||||
}
|
)
|
||||||
);
|
.catch(err => {
|
||||||
|
notify({ message: error, isError: true });
|
||||||
|
this.setState({ tappedStyle: false });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
addTappedStyle() {
|
addTappedStyle() {
|
||||||
this.setState({ tappedStyle: true });
|
this.setState({ tappedStyle: true });
|
||||||
setTimeout(() => { this.setState({ tappedStyle: false }); }, 2000);
|
setTimeout(() => {
|
||||||
|
this.setState({ tappedStyle: false });
|
||||||
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const { ellipsizeMode, numberOfLines, onPress, style, text } = this.props;
|
||||||
ellipsizeMode,
|
|
||||||
numberOfLines,
|
|
||||||
onPress,
|
|
||||||
style,
|
|
||||||
text
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
let styles = [];
|
let styles = [];
|
||||||
if (style) {
|
if (style) {
|
||||||
|
@ -60,9 +58,10 @@ export default class Link extends React.PureComponent {
|
||||||
style={styles}
|
style={styles}
|
||||||
numberOfLines={numberOfLines}
|
numberOfLines={numberOfLines}
|
||||||
ellipsizeMode={ellipsizeMode}
|
ellipsizeMode={ellipsizeMode}
|
||||||
onPress={onPress ? onPress : this.handlePress}>
|
onPress={onPress ? onPress : this.handlePress}
|
||||||
|
>
|
||||||
{text}
|
{text}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -15,4 +15,7 @@ const perform = dispatch => ({
|
||||||
setPlayerVisible: () => dispatch(doSetPlayerVisible(true)),
|
setPlayerVisible: () => dispatch(doSetPlayerVisible(true)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(MediaPlayer);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(MediaPlayer);
|
||||||
|
|
|
@ -9,16 +9,16 @@ import {
|
||||||
Text,
|
Text,
|
||||||
View,
|
View,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
TouchableOpacity
|
TouchableOpacity,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import Colors from 'styles/colors';
|
import Colors from 'styles/colors';
|
||||||
import FastImage from 'react-native-fast-image'
|
import FastImage from 'react-native-fast-image';
|
||||||
import Video from 'react-native-video';
|
import Video from 'react-native-video';
|
||||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||||
import FileItemMedia from 'component/fileItemMedia';
|
import FileItemMedia from 'component/fileItemMedia';
|
||||||
import mediaPlayerStyle from 'styles/mediaPlayer';
|
import mediaPlayerStyle from 'styles/mediaPlayer';
|
||||||
|
|
||||||
const positionSaveInterval = 10
|
const positionSaveInterval = 10;
|
||||||
|
|
||||||
class MediaPlayer extends React.PureComponent {
|
class MediaPlayer extends React.PureComponent {
|
||||||
static ControlsTimeout = 3000;
|
static ControlsTimeout = 3000;
|
||||||
|
@ -52,13 +52,15 @@ class MediaPlayer extends React.PureComponent {
|
||||||
seekerOffset: 0,
|
seekerOffset: 0,
|
||||||
seekerPosition: 0,
|
seekerPosition: 0,
|
||||||
firstPlay: true,
|
firstPlay: true,
|
||||||
seekTimeout: -1
|
seekTimeout: -1,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
formatTime(time) {
|
formatTime(time) {
|
||||||
let str = '';
|
let str = '';
|
||||||
let minutes = 0, hours = 0, seconds = parseInt(time, 10);
|
let minutes = 0,
|
||||||
|
hours = 0,
|
||||||
|
seconds = parseInt(time, 10);
|
||||||
if (seconds > 60) {
|
if (seconds > 60) {
|
||||||
minutes = parseInt(seconds / 60, 10);
|
minutes = parseInt(seconds / 60, 10);
|
||||||
seconds = seconds % 60;
|
seconds = seconds % 60;
|
||||||
|
@ -84,9 +86,9 @@ class MediaPlayer extends React.PureComponent {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoad = (data) => {
|
onLoad = data => {
|
||||||
this.setState({
|
this.setState({
|
||||||
duration: data.duration
|
duration: data.duration,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { position } = this.props;
|
const { position } = this.props;
|
||||||
|
@ -98,9 +100,9 @@ class MediaPlayer extends React.PureComponent {
|
||||||
if (this.props.onMediaLoaded) {
|
if (this.props.onMediaLoaded) {
|
||||||
this.props.onMediaLoaded();
|
this.props.onMediaLoaded();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
onProgress = (data) => {
|
onProgress = data => {
|
||||||
const { savePosition, claim } = this.props;
|
const { savePosition, claim } = this.props;
|
||||||
|
|
||||||
this.setState({ buffering: false, currentTime: data.currentTime });
|
this.setState({ buffering: false, currentTime: data.currentTime });
|
||||||
|
@ -121,13 +123,13 @@ class MediaPlayer extends React.PureComponent {
|
||||||
|
|
||||||
this.hidePlayerControls();
|
this.hidePlayerControls();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
clearControlsTimeout = () => {
|
clearControlsTimeout = () => {
|
||||||
if (this.state.controlsTimeout > -1) {
|
if (this.state.controlsTimeout > -1) {
|
||||||
clearTimeout(this.state.controlsTimeout)
|
clearTimeout(this.state.controlsTimeout);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
showPlayerControls = () => {
|
showPlayerControls = () => {
|
||||||
this.clearControlsTimeout();
|
this.clearControlsTimeout();
|
||||||
|
@ -135,12 +137,12 @@ class MediaPlayer extends React.PureComponent {
|
||||||
this.setState({ areControlsVisible: true });
|
this.setState({ areControlsVisible: true });
|
||||||
}
|
}
|
||||||
this.hidePlayerControls();
|
this.hidePlayerControls();
|
||||||
}
|
};
|
||||||
|
|
||||||
manualHidePlayerControls = () => {
|
manualHidePlayerControls = () => {
|
||||||
this.clearControlsTimeout();
|
this.clearControlsTimeout();
|
||||||
this.setState({ areControlsVisible: false });
|
this.setState({ areControlsVisible: false });
|
||||||
}
|
};
|
||||||
|
|
||||||
hidePlayerControls() {
|
hidePlayerControls() {
|
||||||
const player = this;
|
const player = this;
|
||||||
|
@ -161,19 +163,19 @@ class MediaPlayer extends React.PureComponent {
|
||||||
} else {
|
} else {
|
||||||
this.showPlayerControls();
|
this.showPlayerControls();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
togglePlay = () => {
|
togglePlay = () => {
|
||||||
this.showPlayerControls();
|
this.showPlayerControls();
|
||||||
this.setState({ paused: !this.state.paused }, this.handlePausedState);
|
this.setState({ paused: !this.state.paused }, this.handlePausedState);
|
||||||
}
|
};
|
||||||
|
|
||||||
handlePausedState = () => {
|
handlePausedState = () => {
|
||||||
if (!this.state.paused) {
|
if (!this.state.paused) {
|
||||||
// onProgress will automatically clear this, so it's fine
|
// onProgress will automatically clear this, so it's fine
|
||||||
this.setState({ buffering: true });
|
this.setState({ buffering: true });
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
toggleFullscreenMode = () => {
|
toggleFullscreenMode = () => {
|
||||||
this.showPlayerControls();
|
this.showPlayerControls();
|
||||||
|
@ -183,7 +185,7 @@ class MediaPlayer extends React.PureComponent {
|
||||||
onFullscreenToggled(this.state.fullscreenMode);
|
onFullscreenToggled(this.state.fullscreenMode);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
onEnd = () => {
|
onEnd = () => {
|
||||||
this.setState({ paused: true });
|
this.setState({ paused: true });
|
||||||
|
@ -191,7 +193,7 @@ class MediaPlayer extends React.PureComponent {
|
||||||
this.props.onPlaybackFinished();
|
this.props.onPlaybackFinished();
|
||||||
}
|
}
|
||||||
this.video.seek(0);
|
this.video.seek(0);
|
||||||
}
|
};
|
||||||
|
|
||||||
setSeekerPosition(position = 0) {
|
setSeekerPosition(position = 0) {
|
||||||
position = this.checkSeekerPosition(position);
|
position = this.checkSeekerPosition(position);
|
||||||
|
@ -244,10 +246,14 @@ class MediaPlayer extends React.PureComponent {
|
||||||
this.onEnd();
|
this.onEnd();
|
||||||
} else {
|
} else {
|
||||||
this.seekTo(time);
|
this.seekTo(time);
|
||||||
this.setState({ seekTimeout: setTimeout(() => { this.setState({ seeking: false }); }, 100) });
|
this.setState({
|
||||||
|
seekTimeout: setTimeout(() => {
|
||||||
|
this.setState({ seeking: false });
|
||||||
|
}, 100),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
this.hidePlayerControls();
|
this.hidePlayerControls();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,7 +274,7 @@ class MediaPlayer extends React.PureComponent {
|
||||||
return parseFloat(this.state.currentTime) / parseFloat(this.state.duration);
|
return parseFloat(this.state.currentTime) / parseFloat(this.state.duration);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
};
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.initSeeker();
|
this.initSeeker();
|
||||||
|
@ -283,7 +289,7 @@ class MediaPlayer extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { assignPlayer, backgroundPlayEnabled } = this.props;
|
const { assignPlayer, backgroundPlayEnabled } = this.props;
|
||||||
if (assignPlayer) {
|
if (assignPlayer) {
|
||||||
assignPlayer(this);
|
assignPlayer(this);
|
||||||
}
|
}
|
||||||
|
@ -319,21 +325,21 @@ class MediaPlayer extends React.PureComponent {
|
||||||
this.setState({ paused: false, autoPaused: false });
|
this.setState({ paused: false, autoPaused: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
onBuffer = () => {
|
onBuffer = () => {
|
||||||
if (!this.state.paused) {
|
if (!this.state.paused) {
|
||||||
this.setState({ buffering: true }, () => this.manualHidePlayerControls());
|
this.setState({ buffering: true }, () => this.manualHidePlayerControls());
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
play = () => {
|
play = () => {
|
||||||
this.setState({ paused: false }, this.updateBackgroundMediaNotification);
|
this.setState({ paused: false }, this.updateBackgroundMediaNotification);
|
||||||
}
|
};
|
||||||
|
|
||||||
pause = () => {
|
pause = () => {
|
||||||
this.setState({ paused: true }, this.updateBackgroundMediaNotification);
|
this.setState({ paused: true }, this.updateBackgroundMediaNotification);
|
||||||
}
|
};
|
||||||
|
|
||||||
updateBackgroundMediaNotification = () => {
|
updateBackgroundMediaNotification = () => {
|
||||||
this.handlePausedState();
|
this.handlePausedState();
|
||||||
|
@ -344,7 +350,7 @@ class MediaPlayer extends React.PureComponent {
|
||||||
NativeModules.BackgroundMedia.showPlaybackNotification(title, channel, uri, this.state.paused);
|
NativeModules.BackgroundMedia.showPlaybackNotification(title, channel, uri, this.state.paused);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
renderPlayerControls() {
|
renderPlayerControls() {
|
||||||
const { onBackButtonPressed } = this.props;
|
const { onBackButtonPressed } = this.props;
|
||||||
|
@ -353,11 +359,10 @@ class MediaPlayer extends React.PureComponent {
|
||||||
return (
|
return (
|
||||||
<View style={mediaPlayerStyle.playerControlsContainer}>
|
<View style={mediaPlayerStyle.playerControlsContainer}>
|
||||||
<TouchableOpacity style={mediaPlayerStyle.backButton} onPress={onBackButtonPressed}>
|
<TouchableOpacity style={mediaPlayerStyle.backButton} onPress={onBackButtonPressed}>
|
||||||
<Icon name={"arrow-left"} size={18} style={mediaPlayerStyle.backButtonIcon} />
|
<Icon name={'arrow-left'} size={18} style={mediaPlayerStyle.backButtonIcon} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
<TouchableOpacity style={mediaPlayerStyle.playPauseButton}
|
<TouchableOpacity style={mediaPlayerStyle.playPauseButton} onPress={this.togglePlay}>
|
||||||
onPress={this.togglePlay}>
|
|
||||||
{this.state.paused && <Icon name="play" size={40} color="#ffffff" />}
|
{this.state.paused && <Icon name="play" size={40} color="#ffffff" />}
|
||||||
{!this.state.paused && <Icon name="pause" size={40} color="#ffffff" />}
|
{!this.state.paused && <Icon name="pause" size={40} color="#ffffff" />}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
@ -376,7 +381,7 @@ class MediaPlayer extends React.PureComponent {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
onSeekerTouchAreaPressed = (evt) => {
|
onSeekerTouchAreaPressed = evt => {
|
||||||
if (evt && evt.nativeEvent) {
|
if (evt && evt.nativeEvent) {
|
||||||
const newSeekerPosition = evt.nativeEvent.locationX;
|
const newSeekerPosition = evt.nativeEvent.locationX;
|
||||||
if (!isNaN(newSeekerPosition)) {
|
if (!isNaN(newSeekerPosition)) {
|
||||||
|
@ -385,13 +390,13 @@ class MediaPlayer extends React.PureComponent {
|
||||||
this.seekTo(time);
|
this.seekTo(time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
onTrackingLayout = (evt) => {
|
onTrackingLayout = evt => {
|
||||||
this.trackingOffset = evt.nativeEvent.layout.x;
|
this.trackingOffset = evt.nativeEvent.layout.x;
|
||||||
this.seekerWidth = evt.nativeEvent.layout.width;
|
this.seekerWidth = evt.nativeEvent.layout.width;
|
||||||
this.setSeekerPosition(this.calculateSeekerPosition());
|
this.setSeekerPosition(this.calculateSeekerPosition());
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { onLayout, source, style, thumbnail } = this.props;
|
const { onLayout, source, style, thumbnail } = this.props;
|
||||||
|
@ -406,64 +411,90 @@ class MediaPlayer extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const trackingStyle = [mediaPlayerStyle.trackingControls, this.state.fullscreenMode ?
|
const trackingStyle = [
|
||||||
mediaPlayerStyle.fullscreenTrackingControls : mediaPlayerStyle.containedTrackingControls];
|
mediaPlayerStyle.trackingControls,
|
||||||
|
this.state.fullscreenMode
|
||||||
|
? mediaPlayerStyle.fullscreenTrackingControls
|
||||||
|
: mediaPlayerStyle.containedTrackingControls,
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles} onLayout={onLayout}>
|
<View style={styles} onLayout={onLayout}>
|
||||||
<Video source={{ uri: source }}
|
<Video
|
||||||
bufferConfig={{ minBufferMs: 15000, maxBufferMs: 60000, bufferForPlaybackMs: 5000, bufferForPlaybackAfterRebufferMs: 5000 }}
|
source={{ uri: source }}
|
||||||
ref={(ref: Video) => { this.video = ref; }}
|
bufferConfig={{
|
||||||
resizeMode={this.state.resizeMode}
|
minBufferMs: 15000,
|
||||||
playInBackground={this.state.backgroundPlayEnabled}
|
maxBufferMs: 60000,
|
||||||
style={mediaPlayerStyle.player}
|
bufferForPlaybackMs: 5000,
|
||||||
rate={this.state.rate}
|
bufferForPlaybackAfterRebufferMs: 5000,
|
||||||
volume={this.state.volume}
|
}}
|
||||||
paused={this.state.paused}
|
ref={(ref: Video) => {
|
||||||
onLoad={this.onLoad}
|
this.video = ref;
|
||||||
onBuffer={this.onBuffer}
|
}}
|
||||||
onProgress={this.onProgress}
|
resizeMode={this.state.resizeMode}
|
||||||
onEnd={this.onEnd}
|
playInBackground={this.state.backgroundPlayEnabled}
|
||||||
onError={this.onError}
|
style={mediaPlayerStyle.player}
|
||||||
minLoadRetryCount={999}
|
rate={this.state.rate}
|
||||||
/>
|
volume={this.state.volume}
|
||||||
|
paused={this.state.paused}
|
||||||
|
onLoad={this.onLoad}
|
||||||
|
onBuffer={this.onBuffer}
|
||||||
|
onProgress={this.onProgress}
|
||||||
|
onEnd={this.onEnd}
|
||||||
|
onError={this.onError}
|
||||||
|
minLoadRetryCount={999}
|
||||||
|
/>
|
||||||
|
|
||||||
{this.state.firstPlay && thumbnail && thumbnail.trim().length > 0 &&
|
{this.state.firstPlay && thumbnail && thumbnail.trim().length > 0 && (
|
||||||
<FastImage
|
<FastImage
|
||||||
source={{uri: thumbnail}}
|
source={{ uri: thumbnail }}
|
||||||
resizeMode={FastImage.resizeMode.cover}
|
resizeMode={FastImage.resizeMode.cover}
|
||||||
style={mediaPlayerStyle.playerThumbnail}
|
style={mediaPlayerStyle.playerThumbnail}
|
||||||
/>}
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<TouchableOpacity style={mediaPlayerStyle.playerControls} onPress={this.togglePlayerControls}>
|
<TouchableOpacity style={mediaPlayerStyle.playerControls} onPress={this.togglePlayerControls}>
|
||||||
{this.renderPlayerControls()}
|
{this.renderPlayerControls()}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
{(!this.state.fullscreenMode || (this.state.fullscreenMode && this.state.areControlsVisible)) &&
|
{(!this.state.fullscreenMode || (this.state.fullscreenMode && this.state.areControlsVisible)) && (
|
||||||
<View style={trackingStyle} onLayout={this.onTrackingLayout}>
|
<View style={trackingStyle} onLayout={this.onTrackingLayout}>
|
||||||
<View style={mediaPlayerStyle.progress}>
|
<View style={mediaPlayerStyle.progress}>
|
||||||
<View style={[mediaPlayerStyle.innerProgressCompleted, { width: completedWidth }]} />
|
<View style={[mediaPlayerStyle.innerProgressCompleted, { width: completedWidth }]} />
|
||||||
<View style={[mediaPlayerStyle.innerProgressRemaining, { width: remainingWidth }]} />
|
<View style={[mediaPlayerStyle.innerProgressRemaining, { width: remainingWidth }]} />
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>}
|
)}
|
||||||
|
|
||||||
{this.state.buffering &&
|
{this.state.buffering && (
|
||||||
<View style={mediaPlayerStyle.loadingContainer}>
|
<View style={mediaPlayerStyle.loadingContainer}>
|
||||||
<ActivityIndicator color={Colors.LbryGreen} size="large" />
|
<ActivityIndicator color={Colors.LbryGreen} size="large" />
|
||||||
</View>}
|
|
||||||
|
|
||||||
{this.state.areControlsVisible &&
|
|
||||||
<View style={{ left: this.getTrackingOffset(), width: this.seekerWidth }}>
|
|
||||||
<View style={[mediaPlayerStyle.seekerHandle,
|
|
||||||
(this.state.fullscreenMode ? mediaPlayerStyle.seekerHandleFs : mediaPlayerStyle.seekerHandleContained),
|
|
||||||
{ left: this.state.seekerPosition }]} { ...this.seekResponder.panHandlers }>
|
|
||||||
<View style={this.state.seeking ? mediaPlayerStyle.bigSeekerCircle : mediaPlayerStyle.seekerCircle} />
|
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity
|
)}
|
||||||
style={[mediaPlayerStyle.seekerTouchArea,
|
|
||||||
(this.state.fullscreenMode ? mediaPlayerStyle.seekerTouchAreaFs : mediaPlayerStyle.seekerTouchAreaContained)]}
|
{this.state.areControlsVisible && (
|
||||||
onPress={this.onSeekerTouchAreaPressed} />
|
<View style={{ left: this.getTrackingOffset(), width: this.seekerWidth }}>
|
||||||
</View>}
|
<View
|
||||||
|
style={[
|
||||||
|
mediaPlayerStyle.seekerHandle,
|
||||||
|
this.state.fullscreenMode ? mediaPlayerStyle.seekerHandleFs : mediaPlayerStyle.seekerHandleContained,
|
||||||
|
{ left: this.state.seekerPosition },
|
||||||
|
]}
|
||||||
|
{...this.seekResponder.panHandlers}
|
||||||
|
>
|
||||||
|
<View style={this.state.seeking ? mediaPlayerStyle.bigSeekerCircle : mediaPlayerStyle.seekerCircle} />
|
||||||
|
</View>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[
|
||||||
|
mediaPlayerStyle.seekerTouchArea,
|
||||||
|
this.state.fullscreenMode
|
||||||
|
? mediaPlayerStyle.seekerTouchAreaFs
|
||||||
|
: mediaPlayerStyle.seekerTouchAreaContained,
|
||||||
|
]}
|
||||||
|
onPress={this.onSeekerTouchAreaPressed}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
||||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||||
import { TouchableOpacity } from 'react-native';
|
import { TouchableOpacity } from 'react-native';
|
||||||
|
|
||||||
|
|
||||||
class NavigationButton extends React.PureComponent {
|
class NavigationButton extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
const { iconStyle, name, onPress, size, style } = this.props;
|
const { iconStyle, name, onPress, size, style } = this.props;
|
||||||
|
@ -13,6 +12,6 @@ class NavigationButton extends React.PureComponent {
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export default NavigationButton;
|
export default NavigationButton;
|
||||||
|
|
|
@ -3,4 +3,7 @@ import NsfwOverlay from './view';
|
||||||
|
|
||||||
const perform = dispatch => ({});
|
const perform = dispatch => ({});
|
||||||
|
|
||||||
export default connect(null, perform)(NsfwOverlay);
|
export default connect(
|
||||||
|
null,
|
||||||
|
perform
|
||||||
|
)(NsfwOverlay);
|
||||||
|
|
|
@ -6,9 +6,11 @@ class NsfwOverlay extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity style={discoverStyle.overlay} activeOpacity={0.95} onPress={this.props.onPress}>
|
<TouchableOpacity style={discoverStyle.overlay} activeOpacity={0.95} onPress={this.props.onPress}>
|
||||||
<Text style={discoverStyle.overlayText}>This content is Not Safe For Work. To view adult content, please change your Settings.</Text>
|
<Text style={discoverStyle.overlayText}>
|
||||||
|
This content is Not Safe For Work. To view adult content, please change your Settings.
|
||||||
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,4 +3,7 @@ import PageHeader from './view';
|
||||||
|
|
||||||
const perform = dispatch => ({});
|
const perform = dispatch => ({});
|
||||||
|
|
||||||
export default connect(null, perform)(PageHeader);
|
export default connect(
|
||||||
|
null,
|
||||||
|
perform
|
||||||
|
)(PageHeader);
|
||||||
|
|
|
@ -1,13 +1,6 @@
|
||||||
// Based on https://github.com/react-navigation/react-navigation/blob/master/src/views/Header/Header.js
|
// Based on https://github.com/react-navigation/react-navigation/blob/master/src/views/Header/Header.js
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import { Animated, Platform, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||||
Animated,
|
|
||||||
Platform,
|
|
||||||
StyleSheet,
|
|
||||||
Text,
|
|
||||||
TouchableOpacity,
|
|
||||||
View
|
|
||||||
} from 'react-native';
|
|
||||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||||
import NavigationButton from 'component/navigationButton';
|
import NavigationButton from 'component/navigationButton';
|
||||||
import pageHeaderStyle from 'styles/pageHeader';
|
import pageHeaderStyle from 'styles/pageHeader';
|
||||||
|
@ -18,20 +11,14 @@ const AnimatedText = Animated.Text;
|
||||||
class PageHeader extends React.PureComponent {
|
class PageHeader extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
const { title, onBackPressed } = this.props;
|
const { title, onBackPressed } = this.props;
|
||||||
const containerStyles = [
|
const containerStyles = [pageHeaderStyle.container, { height: APPBAR_HEIGHT }];
|
||||||
pageHeaderStyle.container,
|
|
||||||
{ height: APPBAR_HEIGHT }
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={containerStyles}>
|
<View style={containerStyles}>
|
||||||
<View style={pageHeaderStyle.flexOne}>
|
<View style={pageHeaderStyle.flexOne}>
|
||||||
<View style={pageHeaderStyle.header}>
|
<View style={pageHeaderStyle.header}>
|
||||||
<View style={pageHeaderStyle.title}>
|
<View style={pageHeaderStyle.title}>
|
||||||
<AnimatedText
|
<AnimatedText numberOfLines={1} style={pageHeaderStyle.titleText} accessibilityTraits="header">
|
||||||
numberOfLines={1}
|
|
||||||
style={pageHeaderStyle.titleText}
|
|
||||||
accessibilityTraits="header">
|
|
||||||
{title}
|
{title}
|
||||||
</AnimatedText>
|
</AnimatedText>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -19,7 +19,7 @@ class ProgressBar extends React.PureComponent {
|
||||||
return new Error('progress should be between 0 and 100');
|
return new Error('progress should be between 0 and 100');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
style: PropTypes.any
|
style: PropTypes.any,
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -39,13 +39,15 @@ class ProgressBar extends React.PureComponent {
|
||||||
borderRadius: borderRadius || defaultBorderRadius,
|
borderRadius: borderRadius || defaultBorderRadius,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
height: height || defaultHeight,
|
height: height || defaultHeight,
|
||||||
overflow: 'hidden'
|
overflow: 'hidden',
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles}>
|
<View style={styles}>
|
||||||
<View style={{ backgroundColor: color, borderRadius: borderRadius || defaultBorderRadius, flex: currentProgress }} />
|
<View
|
||||||
<View style={{ backgroundColor: color, opacity: 0.2, flex: (100 - currentProgress) }} />
|
style={{ backgroundColor: color, borderRadius: borderRadius || defaultBorderRadius, flex: currentProgress }}
|
||||||
|
/>
|
||||||
|
<View style={{ backgroundColor: color, opacity: 0.2, flex: 100 - currentProgress }} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,4 +22,4 @@ const perform = dispatch => ({
|
||||||
export default connect(
|
export default connect(
|
||||||
select,
|
select,
|
||||||
perform
|
perform
|
||||||
)(RelatedContent);
|
)(RelatedContent);
|
||||||
|
|
|
@ -50,16 +50,18 @@ export default class RelatedContent extends React.PureComponent<Props> {
|
||||||
return (
|
return (
|
||||||
<View style={relatedContentStyle.container}>
|
<View style={relatedContentStyle.container}>
|
||||||
<Text style={relatedContentStyle.title}>Related Content</Text>
|
<Text style={relatedContentStyle.title}>Related Content</Text>
|
||||||
{recommendedContent && recommendedContent.map(recommendedUri => (
|
{recommendedContent &&
|
||||||
<FileListItem
|
recommendedContent.map(recommendedUri => (
|
||||||
style={fileListStyle.item}
|
<FileListItem
|
||||||
key={recommendedUri}
|
style={fileListStyle.item}
|
||||||
uri={recommendedUri}
|
key={recommendedUri}
|
||||||
navigation={navigation}
|
uri={recommendedUri}
|
||||||
onPress={() => navigateToUri(navigation, recommendedUri, { autoplay: true })} />
|
navigation={navigation}
|
||||||
))}
|
onPress={() => navigateToUri(navigation, recommendedUri, { autoplay: true })}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
{isSearching && <ActivityIndicator size="small" color={Colors.LbryGreen} style={relatedContentStyle.loading} />}
|
{isSearching && <ActivityIndicator size="small" color={Colors.LbryGreen} style={relatedContentStyle.loading} />}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,10 @@ const makeSelect = () => {
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
claimReward: reward => dispatch(doClaimRewardType(reward.reward_type, true)),
|
claimReward: reward => dispatch(doClaimRewardType(reward.reward_type, true)),
|
||||||
clearError: reward => dispatch(doClaimRewardClearError(reward)),
|
clearError: reward => dispatch(doClaimRewardClearError(reward)),
|
||||||
notify: data => dispatch(doToast(data))
|
notify: data => dispatch(doToast(data)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(makeSelect, perform)(RewardCard);
|
export default connect(
|
||||||
|
makeSelect,
|
||||||
|
perform
|
||||||
|
)(RewardCard);
|
||||||
|
|
|
@ -7,7 +7,7 @@ import Link from '../link';
|
||||||
import rewardStyle from '../../styles/reward';
|
import rewardStyle from '../../styles/reward';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
canClaim: bool,
|
canClaim: boolean,
|
||||||
onClaimPress: object,
|
onClaimPress: object,
|
||||||
reward: {
|
reward: {
|
||||||
id: string,
|
id: string,
|
||||||
|
@ -22,7 +22,7 @@ type Props = {
|
||||||
|
|
||||||
class RewardCard extends React.PureComponent<Props> {
|
class RewardCard extends React.PureComponent<Props> {
|
||||||
state = {
|
state = {
|
||||||
claimStarted: false
|
claimStarted: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
|
@ -40,13 +40,7 @@ class RewardCard extends React.PureComponent<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
onClaimPress = () => {
|
onClaimPress = () => {
|
||||||
const {
|
const { canClaim, claimReward, notify, reward, showVerification } = this.props;
|
||||||
canClaim,
|
|
||||||
claimReward,
|
|
||||||
notify,
|
|
||||||
reward,
|
|
||||||
showVerification
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (!canClaim) {
|
if (!canClaim) {
|
||||||
if (showVerification) {
|
if (showVerification) {
|
||||||
|
@ -59,37 +53,52 @@ class RewardCard extends React.PureComponent<Props> {
|
||||||
this.setState({ claimStarted: true }, () => {
|
this.setState({ claimStarted: true }, () => {
|
||||||
claimReward(reward);
|
claimReward(reward);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { canClaim, isPending, onClaimPress, reward } = this.props;
|
const { canClaim, isPending, onClaimPress, reward } = this.props;
|
||||||
const claimed = !!reward.transaction_id;
|
const claimed = !!reward.transaction_id;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity style={[rewardStyle.rewardCard, rewardStyle.row]} onPress={() => {
|
<TouchableOpacity
|
||||||
if (!isPending && !claimed) {
|
style={[rewardStyle.rewardCard, rewardStyle.row]}
|
||||||
this.onClaimPress();
|
onPress={() => {
|
||||||
}
|
if (!isPending && !claimed) {
|
||||||
}}>
|
this.onClaimPress();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
<View style={rewardStyle.leftCol}>
|
<View style={rewardStyle.leftCol}>
|
||||||
{!isPending && <TouchableOpacity onPress={() => {
|
{!isPending && (
|
||||||
if (!claimed) {
|
<TouchableOpacity
|
||||||
this.onClaimPress();
|
onPress={() => {
|
||||||
}
|
if (!claimed) {
|
||||||
}}>
|
this.onClaimPress();
|
||||||
{claimed && <Icon name={claimed ? "check-circle" : "circle"}
|
}
|
||||||
style={claimed ? rewardStyle.claimed : (canClaim ? rewardStyle.unclaimed : rewardStyle.disabled)}
|
}}
|
||||||
size={20} />}
|
>
|
||||||
</TouchableOpacity>}
|
{claimed && (
|
||||||
|
<Icon
|
||||||
|
name={claimed ? 'check-circle' : 'circle'}
|
||||||
|
style={claimed ? rewardStyle.claimed : canClaim ? rewardStyle.unclaimed : rewardStyle.disabled}
|
||||||
|
size={20}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
{isPending && <ActivityIndicator size="small" color={Colors.LbryGreen} />}
|
{isPending && <ActivityIndicator size="small" color={Colors.LbryGreen} />}
|
||||||
</View>
|
</View>
|
||||||
<View style={rewardStyle.midCol}>
|
<View style={rewardStyle.midCol}>
|
||||||
<Text style={rewardStyle.rewardTitle}>{reward.reward_title}</Text>
|
<Text style={rewardStyle.rewardTitle}>{reward.reward_title}</Text>
|
||||||
<Text style={rewardStyle.rewardDescription}>{reward.reward_description}</Text>
|
<Text style={rewardStyle.rewardDescription}>{reward.reward_description}</Text>
|
||||||
{claimed && <Link style={rewardStyle.link}
|
{claimed && (
|
||||||
href={`https://explorer.lbry.com/tx/${reward.transaction_id}`}
|
<Link
|
||||||
text={reward.transaction_id.substring(0, 7)}
|
style={rewardStyle.link}
|
||||||
error={'The transaction URL could not be opened'} />}
|
href={`https://explorer.lbry.com/tx/${reward.transaction_id}`}
|
||||||
|
text={reward.transaction_id.substring(0, 7)}
|
||||||
|
error={'The transaction URL could not be opened'}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
<View style={rewardStyle.rightCol}>
|
<View style={rewardStyle.rightCol}>
|
||||||
<Text style={rewardStyle.rewardAmount}>{reward.reward_amount}</Text>
|
<Text style={rewardStyle.rewardAmount}>{reward.reward_amount}</Text>
|
||||||
|
@ -98,6 +107,6 @@ class RewardCard extends React.PureComponent<Props> {
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export default RewardCard;
|
export default RewardCard;
|
||||||
|
|
|
@ -7,7 +7,7 @@ import RewardEnrolment from './view';
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
unclaimedRewardAmount: selectUnclaimedRewardValue(state),
|
unclaimedRewardAmount: selectUnclaimedRewardValue(state),
|
||||||
fetching: selectFetchingRewards(state),
|
fetching: selectFetchingRewards(state),
|
||||||
user: selectUser(state)
|
user: selectUser(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
|
@ -16,4 +16,7 @@ const perform = dispatch => ({
|
||||||
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(RewardEnrolment);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(RewardEnrolment);
|
||||||
|
|
|
@ -17,12 +17,12 @@ class RewardEnrolment extends React.Component {
|
||||||
const { navigation, setClientSetting } = this.props;
|
const { navigation, setClientSetting } = this.props;
|
||||||
setClientSetting(Constants.SETTING_REWARDS_NOT_INTERESTED, true);
|
setClientSetting(Constants.SETTING_REWARDS_NOT_INTERESTED, true);
|
||||||
navigation.navigate({ routeName: 'DiscoverStack' });
|
navigation.navigate({ routeName: 'DiscoverStack' });
|
||||||
}
|
};
|
||||||
|
|
||||||
onEnrollPressed = () => {
|
onEnrollPressed = () => {
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.navigate({ routeName: 'Verification', key: 'verification', params: { syncFlow: false }});
|
navigation.navigate({ routeName: 'Verification', key: 'verification', params: { syncFlow: false } });
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { fetching, navigation, unclaimedRewardAmount, user } = this.props;
|
const { fetching, navigation, unclaimedRewardAmount, user } = this.props;
|
||||||
|
@ -31,20 +31,20 @@ class RewardEnrolment extends React.Component {
|
||||||
<View style={rewardStyle.enrollContainer} onPress>
|
<View style={rewardStyle.enrollContainer} onPress>
|
||||||
<View style={rewardStyle.summaryRow}>
|
<View style={rewardStyle.summaryRow}>
|
||||||
<Icon name="award" size={36} color={Colors.White} />
|
<Icon name="award" size={36} color={Colors.White} />
|
||||||
<Text style={rewardStyle.summaryText}>
|
<Text style={rewardStyle.summaryText}>{unclaimedRewardAmount} unclaimed credits</Text>
|
||||||
{unclaimedRewardAmount} unclaimed credits
|
|
||||||
</Text>
|
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={rewardStyle.onboarding}>
|
<View style={rewardStyle.onboarding}>
|
||||||
<Text style={rewardStyle.enrollDescText}>LBRY credits allow you to purchase content, publish content, and influence the network. You can start earning credits by watching videos on LBRY.</Text>
|
<Text style={rewardStyle.enrollDescText}>
|
||||||
|
LBRY credits allow you to purchase content, publish content, and influence the network. You can start
|
||||||
|
earning credits by watching videos on LBRY.
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={rewardStyle.buttonRow}>
|
<View style={rewardStyle.buttonRow}>
|
||||||
<Link style={rewardStyle.notInterestedLink} text={"Not interested"} onPress={this.onNotInterestedPressed} />
|
<Link style={rewardStyle.notInterestedLink} text={'Not interested'} onPress={this.onNotInterestedPressed} />
|
||||||
<Button style={rewardStyle.enrollButton} theme={"light"} text={"Enroll"} onPress={this.onEnrollPressed} />
|
<Button style={rewardStyle.enrollButton} theme={'light'} text={'Enroll'} onPress={this.onEnrollPressed} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,15 @@ import RewardSummary from './view';
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
unclaimedRewardAmount: selectUnclaimedRewardValue(state),
|
unclaimedRewardAmount: selectUnclaimedRewardValue(state),
|
||||||
fetching: selectFetchingRewards(state),
|
fetching: selectFetchingRewards(state),
|
||||||
user: selectUser(state)
|
user: selectUser(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
fetchRewards: () => dispatch(doRewardList()),
|
fetchRewards: () => dispatch(doRewardList()),
|
||||||
notify: data => dispatch(doToast(data))
|
notify: data => dispatch(doToast(data)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(RewardSummary);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(RewardSummary);
|
||||||
|
|
|
@ -11,7 +11,7 @@ class RewardSummary extends React.Component {
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
actionsLeft: 0,
|
actionsLeft: 0,
|
||||||
dismissed: false
|
dismissed: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -42,14 +42,14 @@ class RewardSummary extends React.Component {
|
||||||
this.props.notify({
|
this.props.notify({
|
||||||
message: 'You can always claim your rewards from the Rewards page.',
|
message: 'You can always claim your rewards from the Rewards page.',
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
handleSummaryPressed = () => {
|
handleSummaryPressed = () => {
|
||||||
const { showVerification } = this.props;
|
const { showVerification } = this.props;
|
||||||
if (showVerification) {
|
if (showVerification) {
|
||||||
showVerification();
|
showVerification();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { fetching, navigation, unclaimedRewardAmount, user } = this.props;
|
const { fetching, navigation, unclaimedRewardAmount, user } = this.props;
|
||||||
|
@ -58,10 +58,12 @@ class RewardSummary extends React.Component {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.dismissed ||
|
if (
|
||||||
(user && user.is_reward_approved) ||
|
this.state.dismissed ||
|
||||||
this.state.actionsLeft === 0 ||
|
(user && user.is_reward_approved) ||
|
||||||
unclaimedRewardAmount === 0) {
|
this.state.actionsLeft === 0 ||
|
||||||
|
unclaimedRewardAmount === 0
|
||||||
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,11 +71,9 @@ class RewardSummary extends React.Component {
|
||||||
<TouchableOpacity style={rewardStyle.summaryContainer} onPress={this.handleSummaryPressed}>
|
<TouchableOpacity style={rewardStyle.summaryContainer} onPress={this.handleSummaryPressed}>
|
||||||
<View style={rewardStyle.summaryRow}>
|
<View style={rewardStyle.summaryRow}>
|
||||||
<Icon name="award" size={36} color={Colors.White} />
|
<Icon name="award" size={36} color={Colors.White} />
|
||||||
<Text style={rewardStyle.summaryText}>
|
<Text style={rewardStyle.summaryText}>{unclaimedRewardAmount} unclaimed credits</Text>
|
||||||
{unclaimedRewardAmount} unclaimed credits
|
|
||||||
</Text>
|
|
||||||
</View>
|
</View>
|
||||||
<Button style={rewardStyle.dismissButton} theme={"light"} text={"Dismiss"} onPress={this.onDismissPressed} />
|
<Button style={rewardStyle.dismissButton} theme={'light'} text={'Dismiss'} onPress={this.onDismissPressed} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { NativeModules } from 'react-native';
|
import { NativeModules } from 'react-native';
|
||||||
import { doSearch, doUpdateSearchQuery } from 'lbry-redux';
|
import { doSearch, doUpdateSearchQuery } from 'lbry-redux';
|
||||||
import SearchInput from './view';
|
import SearchInput from './view';
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
|
@ -10,7 +10,10 @@ const perform = dispatch => ({
|
||||||
}
|
}
|
||||||
return dispatch(doSearch(search));
|
return dispatch(doSearch(search));
|
||||||
},
|
},
|
||||||
updateSearchQuery: query => dispatch(doUpdateSearchQuery(query, false))
|
updateSearchQuery: query => dispatch(doUpdateSearchQuery(query, false)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(null, perform)(SearchInput);
|
export default connect(
|
||||||
|
null,
|
||||||
|
perform
|
||||||
|
)(SearchInput);
|
||||||
|
|
|
@ -3,11 +3,11 @@ import { TextInput } from 'react-native';
|
||||||
|
|
||||||
class SearchInput extends React.PureComponent {
|
class SearchInput extends React.PureComponent {
|
||||||
static INPUT_TIMEOUT = 500;
|
static INPUT_TIMEOUT = 500;
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
changeTextTimeout: -1
|
changeTextTimeout: -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleChangeText = text => {
|
handleChangeText = text => {
|
||||||
clearTimeout(this.state.changeTextTimeout);
|
clearTimeout(this.state.changeTextTimeout);
|
||||||
if (!text || text.trim().length < 2) {
|
if (!text || text.trim().length < 2) {
|
||||||
|
@ -16,23 +16,24 @@ class SearchInput extends React.PureComponent {
|
||||||
}
|
}
|
||||||
const { search, updateSearchQuery } = this.props;
|
const { search, updateSearchQuery } = this.props;
|
||||||
updateSearchQuery(text);
|
updateSearchQuery(text);
|
||||||
|
|
||||||
let timeout = setTimeout(() => {
|
let timeout = setTimeout(() => {
|
||||||
search(text);
|
search(text);
|
||||||
}, SearchInput.INPUT_TIMEOUT);
|
}, SearchInput.INPUT_TIMEOUT);
|
||||||
this.setState({ changeTextTimeout: timeout });
|
this.setState({ changeTextTimeout: timeout });
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { style, value } = this.props;
|
const { style, value } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TextInput
|
<TextInput
|
||||||
style={style}
|
style={style}
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
underlineColorAndroid="transparent"
|
underlineColorAndroid="transparent"
|
||||||
value={value}
|
value={value}
|
||||||
onChangeText={text => this.handleChangeText(text)} />
|
onChangeText={text => this.handleChangeText(text)}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,13 @@ import { connect } from 'react-redux';
|
||||||
import SearchRightHeaderIcon from './view';
|
import SearchRightHeaderIcon from './view';
|
||||||
import { ACTIONS } from 'lbry-redux';
|
import { ACTIONS } from 'lbry-redux';
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
clearQuery: () => dispatch({
|
clearQuery: () =>
|
||||||
type: ACTIONS.HISTORY_NAVIGATE
|
dispatch({
|
||||||
})
|
type: ACTIONS.HISTORY_NAVIGATE,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(null, perform)(SearchRightHeaderIcon);
|
export default connect(
|
||||||
|
null,
|
||||||
|
perform
|
||||||
|
)(SearchRightHeaderIcon);
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { NavigationActions } from 'react-navigation';
|
import { NavigationActions } from 'react-navigation';
|
||||||
import Feather from "react-native-vector-icons/Feather";
|
import Feather from 'react-native-vector-icons/Feather';
|
||||||
|
|
||||||
class SearchRightHeaderIcon extends React.PureComponent {
|
class SearchRightHeaderIcon extends React.PureComponent {
|
||||||
|
|
||||||
clearAndGoBack() {
|
clearAndGoBack() {
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
this.props.clearQuery();
|
this.props.clearQuery();
|
||||||
navigation.dispatch(NavigationActions.back())
|
navigation.dispatch(NavigationActions.back());
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -1,13 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { normalizeURI, parseURI } from 'lbry-redux';
|
import { normalizeURI, parseURI } from 'lbry-redux';
|
||||||
import {
|
import { ActivityIndicator, Platform, Switch, Text, TouchableOpacity, View } from 'react-native';
|
||||||
ActivityIndicator,
|
|
||||||
Platform,
|
|
||||||
Switch,
|
|
||||||
Text,
|
|
||||||
TouchableOpacity,
|
|
||||||
View
|
|
||||||
} from 'react-native';
|
|
||||||
import { formatBytes } from '../../utils/helper';
|
import { formatBytes } from '../../utils/helper';
|
||||||
import Colors from '../../styles/colors';
|
import Colors from '../../styles/colors';
|
||||||
import storageStatsStyle from '../../styles/storageStats';
|
import storageStatsStyle from '../../styles/storageStats';
|
||||||
|
@ -23,15 +16,20 @@ class StorageStatsCard extends React.PureComponent {
|
||||||
totalVideoPercent: 0,
|
totalVideoPercent: 0,
|
||||||
totalOtherBytes: 0,
|
totalOtherBytes: 0,
|
||||||
totalOtherPercent: 0,
|
totalOtherPercent: 0,
|
||||||
showStats: false
|
showStats: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
// calculate total bytes
|
// calculate total bytes
|
||||||
const { fileInfos } = this.props;
|
const { fileInfos } = this.props;
|
||||||
|
|
||||||
let totalBytes = 0, totalAudioBytes = 0, totalImageBytes = 0, totalVideoBytes = 0;
|
let totalBytes = 0,
|
||||||
let totalAudioPercent = 0, totalImagePercent = 0, totalVideoPercent = 0;
|
totalAudioBytes = 0,
|
||||||
|
totalImageBytes = 0,
|
||||||
|
totalVideoBytes = 0;
|
||||||
|
let totalAudioPercent = 0,
|
||||||
|
totalImagePercent = 0,
|
||||||
|
totalVideoPercent = 0;
|
||||||
|
|
||||||
fileInfos.forEach(fileInfo => {
|
fileInfos.forEach(fileInfo => {
|
||||||
if (fileInfo.completed) {
|
if (fileInfo.completed) {
|
||||||
|
@ -59,9 +57,10 @@ class StorageStatsCard extends React.PureComponent {
|
||||||
totalVideoBytes,
|
totalVideoBytes,
|
||||||
totalVideoPercent,
|
totalVideoPercent,
|
||||||
totalOtherBytes: totalBytes - (totalAudioBytes + totalImageBytes + totalVideoBytes),
|
totalOtherBytes: totalBytes - (totalAudioBytes + totalImageBytes + totalVideoBytes),
|
||||||
totalOtherPercent: (100 - (parseFloat(totalAudioPercent) +
|
totalOtherPercent: (
|
||||||
parseFloat(totalImagePercent) +
|
100 -
|
||||||
parseFloat(totalVideoPercent))).toFixed(2)
|
(parseFloat(totalAudioPercent) + parseFloat(totalImagePercent) + parseFloat(totalVideoPercent))
|
||||||
|
).toFixed(2),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,50 +81,52 @@ class StorageStatsCard extends React.PureComponent {
|
||||||
<Switch
|
<Switch
|
||||||
style={storageStatsStyle.statsToggle}
|
style={storageStatsStyle.statsToggle}
|
||||||
value={this.state.showStats}
|
value={this.state.showStats}
|
||||||
onValueChange={(value) => this.setState({ showStats: value })} />
|
onValueChange={value => this.setState({ showStats: value })}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{this.state.showStats &&
|
{this.state.showStats && (
|
||||||
<View>
|
<View>
|
||||||
<View style={storageStatsStyle.distributionBar}>
|
<View style={storageStatsStyle.distributionBar}>
|
||||||
<View style={[storageStatsStyle.audioDistribution, { flex: parseFloat(this.state.totalAudioPercent) }]} />
|
<View style={[storageStatsStyle.audioDistribution, { flex: parseFloat(this.state.totalAudioPercent) }]} />
|
||||||
<View style={[storageStatsStyle.imageDistribution, { flex: parseFloat(this.state.totalImagePercent) }]} />
|
<View style={[storageStatsStyle.imageDistribution, { flex: parseFloat(this.state.totalImagePercent) }]} />
|
||||||
<View style={[storageStatsStyle.videoDistribution, { flex: parseFloat(this.state.totalVideoPercent) }]} />
|
<View style={[storageStatsStyle.videoDistribution, { flex: parseFloat(this.state.totalVideoPercent) }]} />
|
||||||
<View style={[storageStatsStyle.otherDistribution, { flex: parseFloat(this.state.totalOtherPercent) }]} />
|
<View style={[storageStatsStyle.otherDistribution, { flex: parseFloat(this.state.totalOtherPercent) }]} />
|
||||||
|
</View>
|
||||||
|
<View style={storageStatsStyle.legend}>
|
||||||
|
{this.state.totalAudioBytes > 0 && (
|
||||||
|
<View style={[storageStatsStyle.row, storageStatsStyle.legendItem]}>
|
||||||
|
<View style={[storageStatsStyle.legendBox, storageStatsStyle.audioDistribution]} />
|
||||||
|
<Text style={storageStatsStyle.legendText}>Audio</Text>
|
||||||
|
<Text style={storageStatsStyle.legendSize}>{formatBytes(this.state.totalAudioBytes, 2)}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{this.state.totalImageBytes > 0 && (
|
||||||
|
<View style={[storageStatsStyle.row, storageStatsStyle.legendItem]}>
|
||||||
|
<View style={[storageStatsStyle.legendBox, storageStatsStyle.imageDistribution]} />
|
||||||
|
<Text style={storageStatsStyle.legendText}>Images</Text>
|
||||||
|
<Text style={storageStatsStyle.legendSize}>{formatBytes(this.state.totalImageBytes, 2)}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{this.state.totalVideoBytes > 0 && (
|
||||||
|
<View style={[storageStatsStyle.row, storageStatsStyle.legendItem]}>
|
||||||
|
<View style={[storageStatsStyle.legendBox, storageStatsStyle.videoDistribution]} />
|
||||||
|
<Text style={storageStatsStyle.legendText}>Videos</Text>
|
||||||
|
<Text style={storageStatsStyle.legendSize}>{formatBytes(this.state.totalVideoBytes, 2)}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{this.state.totalOtherBytes > 0 && (
|
||||||
|
<View style={[storageStatsStyle.row, storageStatsStyle.legendItem]}>
|
||||||
|
<View style={[storageStatsStyle.legendBox, storageStatsStyle.otherDistribution]} />
|
||||||
|
<Text style={storageStatsStyle.legendText}>Other</Text>
|
||||||
|
<Text style={storageStatsStyle.legendSize}>{formatBytes(this.state.totalOtherBytes, 2)}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<View style={storageStatsStyle.legend}>
|
)}
|
||||||
{this.state.totalAudioBytes > 0 &&
|
|
||||||
<View style={[storageStatsStyle.row, storageStatsStyle.legendItem]}>
|
|
||||||
<View style={[storageStatsStyle.legendBox, storageStatsStyle.audioDistribution]} />
|
|
||||||
<Text style={storageStatsStyle.legendText}>Audio</Text>
|
|
||||||
<Text style={storageStatsStyle.legendSize}>{formatBytes(this.state.totalAudioBytes, 2)}</Text>
|
|
||||||
</View>
|
|
||||||
}
|
|
||||||
{this.state.totalImageBytes > 0 &&
|
|
||||||
<View style={[storageStatsStyle.row, storageStatsStyle.legendItem]}>
|
|
||||||
<View style={[storageStatsStyle.legendBox, storageStatsStyle.imageDistribution]} />
|
|
||||||
<Text style={storageStatsStyle.legendText}>Images</Text>
|
|
||||||
<Text style={storageStatsStyle.legendSize}>{formatBytes(this.state.totalImageBytes, 2)}</Text>
|
|
||||||
</View>
|
|
||||||
}
|
|
||||||
{this.state.totalVideoBytes > 0 &&
|
|
||||||
<View style={[storageStatsStyle.row, storageStatsStyle.legendItem]}>
|
|
||||||
<View style={[storageStatsStyle.legendBox, storageStatsStyle.videoDistribution]} />
|
|
||||||
<Text style={storageStatsStyle.legendText}>Videos</Text>
|
|
||||||
<Text style={storageStatsStyle.legendSize}>{formatBytes(this.state.totalVideoBytes, 2)}</Text>
|
|
||||||
</View>
|
|
||||||
}
|
|
||||||
{this.state.totalOtherBytes > 0 &&
|
|
||||||
<View style={[storageStatsStyle.row, storageStatsStyle.legendItem]}>
|
|
||||||
<View style={[storageStatsStyle.legendBox, storageStatsStyle.otherDistribution]} />
|
|
||||||
<Text style={storageStatsStyle.legendText}>Other</Text>
|
|
||||||
<Text style={storageStatsStyle.legendSize}>{formatBytes(this.state.totalOtherBytes, 2)}</Text>
|
|
||||||
</View>
|
|
||||||
}
|
|
||||||
</View>
|
|
||||||
</View>}
|
|
||||||
</View>
|
</View>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import { doChannelSubscribe, doChannelUnsubscribe, selectSubscriptions, makeSelectIsSubscribed } from 'lbryinc';
|
||||||
doChannelSubscribe,
|
|
||||||
doChannelUnsubscribe,
|
|
||||||
selectSubscriptions,
|
|
||||||
makeSelectIsSubscribed,
|
|
||||||
} from 'lbryinc';
|
|
||||||
import { doToast } from 'lbry-redux';
|
import { doToast } from 'lbry-redux';
|
||||||
import SubscribeButton from './view';
|
import SubscribeButton from './view';
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,7 @@ import Colors from 'styles/colors';
|
||||||
|
|
||||||
class SubscribeButton extends React.PureComponent {
|
class SubscribeButton extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const { uri, isSubscribed, doChannelSubscribe, doChannelUnsubscribe, style, hideText } = this.props;
|
||||||
uri,
|
|
||||||
isSubscribed,
|
|
||||||
doChannelSubscribe,
|
|
||||||
doChannelUnsubscribe,
|
|
||||||
style,
|
|
||||||
hideText
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
let styles = [];
|
let styles = [];
|
||||||
if (style) {
|
if (style) {
|
||||||
|
@ -32,8 +25,8 @@ class SubscribeButton extends React.PureComponent {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
style={styles}
|
style={styles}
|
||||||
theme={"light"}
|
theme={'light'}
|
||||||
icon={isSubscribed ? "heart-broken" : "heart"}
|
icon={isSubscribed ? 'heart-broken' : 'heart'}
|
||||||
iconColor={iconColor}
|
iconColor={iconColor}
|
||||||
solid={isSubscribed ? false : true}
|
solid={isSubscribed ? false : true}
|
||||||
text={hideText ? null : subscriptionLabel}
|
text={hideText ? null : subscriptionLabel}
|
||||||
|
@ -42,7 +35,8 @@ class SubscribeButton extends React.PureComponent {
|
||||||
channelName: claimName,
|
channelName: claimName,
|
||||||
uri: normalizeURI(uri),
|
uri: normalizeURI(uri),
|
||||||
});
|
});
|
||||||
}} />
|
}}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,11 @@ class SubscribeNotificationButton extends React.PureComponent {
|
||||||
doToast,
|
doToast,
|
||||||
enabledChannelNotifications,
|
enabledChannelNotifications,
|
||||||
isSubscribed,
|
isSubscribed,
|
||||||
style
|
style,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (!isSubscribed) {
|
if (!isSubscribed) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let styles = [];
|
let styles = [];
|
||||||
|
@ -36,8 +36,8 @@ class SubscribeNotificationButton extends React.PureComponent {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
style={styles}
|
style={styles}
|
||||||
theme={"light"}
|
theme={'light'}
|
||||||
icon={shouldNotify ? "bell-slash" : "bell"}
|
icon={shouldNotify ? 'bell-slash' : 'bell'}
|
||||||
solid={true}
|
solid={true}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (shouldNotify) {
|
if (shouldNotify) {
|
||||||
|
@ -47,7 +47,8 @@ class SubscribeNotificationButton extends React.PureComponent {
|
||||||
doChannelSubscriptionEnableNotifications(name);
|
doChannelSubscriptionEnableNotifications(name);
|
||||||
doToast({ message: 'You will receive all notifications for new content.' });
|
doToast({ message: 'You will receive all notifications for new content.' });
|
||||||
}
|
}
|
||||||
}} />
|
}}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,19 +14,19 @@ class SuggestedSubscriptionItem extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uriForClaim = (claim) => {
|
uriForClaim = claim => {
|
||||||
const { name: claimName, claim_name: claimNameDownloaded, claim_id: claimId } = claim;
|
const { name: claimName, claim_name: claimNameDownloaded, claim_id: claimId } = claim;
|
||||||
const uriParams = {};
|
const uriParams = {};
|
||||||
|
|
||||||
// This is unfortunate
|
// This is unfortunate
|
||||||
// https://github.com/lbryio/lbry/issues/1159
|
// https://github.com/lbryio/lbry/issues/1159
|
||||||
const name = claimName || claimNameDownloaded;
|
const name = claimName || claimNameDownloaded;
|
||||||
uriParams.contentName = name;
|
uriParams.contentName = name;
|
||||||
uriParams.claimId = claimId;
|
uriParams.claimId = claimId;
|
||||||
const uri = buildURI(uriParams);
|
const uri = buildURI(uriParams);
|
||||||
|
|
||||||
return uri;
|
return uri;
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { categoryLink, fetching, obscureNsfw, claims, navigation } = this.props;
|
const { categoryLink, fetching, obscureNsfw, claims, navigation } = this.props;
|
||||||
|
@ -46,23 +46,26 @@ class SuggestedSubscriptionItem extends React.PureComponent {
|
||||||
style={subscriptionsStyle.compactMainFileItem}
|
style={subscriptionsStyle.compactMainFileItem}
|
||||||
mediaStyle={subscriptionsStyle.fileItemMedia}
|
mediaStyle={subscriptionsStyle.fileItemMedia}
|
||||||
uri={this.uriForClaim(claims[0])}
|
uri={this.uriForClaim(claims[0])}
|
||||||
navigation={navigation} />
|
navigation={navigation}
|
||||||
{(claims.length > 1) &&
|
/>
|
||||||
<FlatList style={subscriptionsStyle.compactItems}
|
{claims.length > 1 && (
|
||||||
horizontal={true}
|
<FlatList
|
||||||
renderItem={ ({item}) => (
|
style={subscriptionsStyle.compactItems}
|
||||||
|
horizontal={true}
|
||||||
|
renderItem={({ item }) => (
|
||||||
<FileItem
|
<FileItem
|
||||||
style={subscriptionsStyle.compactFileItem}
|
style={subscriptionsStyle.compactFileItem}
|
||||||
mediaStyle={subscriptionsStyle.compactFileItemMedia}
|
mediaStyle={subscriptionsStyle.compactFileItemMedia}
|
||||||
key={item}
|
key={item}
|
||||||
uri={normalizeURI(item)}
|
uri={normalizeURI(item)}
|
||||||
navigation={navigation}
|
navigation={navigation}
|
||||||
compactView={true} />
|
compactView={true}
|
||||||
)
|
/>
|
||||||
}
|
)}
|
||||||
data={claims.slice(1, 4).map(claim => this.uriForClaim(claim))}
|
data={claims.slice(1, 4).map(claim => this.uriForClaim(claim))}
|
||||||
keyExtractor={(item, index) => item}
|
keyExtractor={(item, index) => item}
|
||||||
/>}
|
/>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,4 +10,4 @@ const select = state => ({
|
||||||
export default connect(
|
export default connect(
|
||||||
select,
|
select,
|
||||||
null
|
null
|
||||||
)(SuggestedSubscriptions);
|
)(SuggestedSubscriptions);
|
||||||
|
|
|
@ -22,34 +22,33 @@ class SuggestedSubscriptions extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return suggested ? (
|
return suggested ? (
|
||||||
<SectionList style={subscriptionsStyle.scrollContainer}
|
<SectionList
|
||||||
renderItem={ ({item, index, section}) => (
|
style={subscriptionsStyle.scrollContainer}
|
||||||
<SuggestedSubscriptionItem
|
renderItem={({ item, index, section }) => (
|
||||||
key={item}
|
<SuggestedSubscriptionItem key={item} categoryLink={normalizeURI(item)} navigation={navigation} />
|
||||||
categoryLink={normalizeURI(item)}
|
)}
|
||||||
navigation={navigation} />
|
renderSectionHeader={({ section: { title } }) => {
|
||||||
)
|
const titleParts = title.split(';');
|
||||||
}
|
const channelName = titleParts[0];
|
||||||
renderSectionHeader={
|
const channelUri = normalizeURI(titleParts[1]);
|
||||||
({section: {title}}) => {
|
return (
|
||||||
const titleParts = title.split(';');
|
<View style={subscriptionsStyle.titleRow}>
|
||||||
const channelName = titleParts[0];
|
<Link
|
||||||
const channelUri = normalizeURI(titleParts[1]);
|
style={subscriptionsStyle.channelTitle}
|
||||||
return (
|
text={channelName}
|
||||||
<View style={subscriptionsStyle.titleRow}>
|
onPress={() => {
|
||||||
<Link style={subscriptionsStyle.channelTitle} text={channelName} onPress={() => {
|
|
||||||
navigateToUri(navigation, normalizeURI(channelUri));
|
navigateToUri(navigation, normalizeURI(channelUri));
|
||||||
}} />
|
}}
|
||||||
<SubscribeButton style={subscriptionsStyle.subscribeButton} uri={channelUri} name={channelName} />
|
/>
|
||||||
</View>
|
<SubscribeButton style={subscriptionsStyle.subscribeButton} uri={channelUri} name={channelName} />
|
||||||
)
|
</View>
|
||||||
}
|
);
|
||||||
}
|
}}
|
||||||
sections={suggested.map(({ uri, label }) => ({ title: (label + ';' + uri), data: [uri] }))}
|
sections={suggested.map(({ uri, label }) => ({ title: label + ';' + uri, data: [uri] }))}
|
||||||
keyExtractor={(item, index) => item}
|
keyExtractor={(item, index) => item}
|
||||||
/>
|
/>
|
||||||
) : null;
|
) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SuggestedSubscriptions;
|
export default SuggestedSubscriptions;
|
||||||
|
|
|
@ -8,4 +8,7 @@ const select = state => ({
|
||||||
myClaims: selectAllMyClaimsByOutpoint(state),
|
myClaims: selectAllMyClaimsByOutpoint(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, null)(TransactionList);
|
export default connect(
|
||||||
|
select,
|
||||||
|
null
|
||||||
|
)(TransactionList);
|
||||||
|
|
|
@ -25,20 +25,25 @@ class TransactionListItem extends React.PureComponent {
|
||||||
<Link
|
<Link
|
||||||
style={transactionListStyle.link}
|
style={transactionListStyle.link}
|
||||||
onPress={() => navigateToUri(navigation, buildURI({ claimName: name, claimId }))}
|
onPress={() => navigateToUri(navigation, buildURI({ claimName: name, claimId }))}
|
||||||
text={name} />
|
text={name}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
<View style={transactionListStyle.col}>
|
<View style={transactionListStyle.col}>
|
||||||
<Text style={[transactionListStyle.amount, transactionListStyle.text]}>{formatCredits(amount, 8)}</Text>
|
<Text style={[transactionListStyle.amount, transactionListStyle.text]}>{formatCredits(amount, 8)}</Text>
|
||||||
{ fee !== 0 && (<Text style={[transactionListStyle.amount, transactionListStyle.text]}>fee {formatCredits(fee, 8)}</Text>) }
|
{fee !== 0 && (
|
||||||
|
<Text style={[transactionListStyle.amount, transactionListStyle.text]}>fee {formatCredits(fee, 8)}</Text>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<View style={transactionListStyle.row}>
|
<View style={transactionListStyle.row}>
|
||||||
<View style={transactionListStyle.col}>
|
<View style={transactionListStyle.col}>
|
||||||
<Link style={transactionListStyle.smallLink}
|
<Link
|
||||||
text={txid.substring(0, 8)}
|
style={transactionListStyle.smallLink}
|
||||||
href={`https://explorer.lbry.com/tx/${txid}`}
|
text={txid.substring(0, 8)}
|
||||||
error={'The transaction URL could not be opened'} />
|
href={`https://explorer.lbry.com/tx/${txid}`}
|
||||||
|
error={'The transaction URL could not be opened'}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={transactionListStyle.col}>
|
<View style={transactionListStyle.col}>
|
||||||
{date ? (
|
{date ? (
|
||||||
|
|
|
@ -38,18 +38,18 @@ class TransactionList extends React.PureComponent {
|
||||||
|
|
||||||
return filter === 'all' || filter === transaction.type;
|
return filter === 'all' || filter === transaction.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { emptyMessage, rewards, transactions, navigation } = this.props;
|
const { emptyMessage, rewards, transactions, navigation } = this.props;
|
||||||
const { filter } = this.state;
|
const { filter } = this.state;
|
||||||
const transactionList = transactions.filter(this.filterTransaction);
|
const transactionList = transactions.filter(this.filterTransaction);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
{!transactionList.length && (
|
{!transactionList.length && (
|
||||||
<Text style={transactionListStyle.noTransactions}>{emptyMessage || 'No transactions to list.'}</Text>
|
<Text style={transactionListStyle.noTransactions}>{emptyMessage || 'No transactions to list.'}</Text>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!!transactionList.length && (
|
{!!transactionList.length && (
|
||||||
<View>
|
<View>
|
||||||
{transactionList.map(t => (
|
{transactionList.map(t => (
|
||||||
|
|
|
@ -17,4 +17,7 @@ const perform = dispatch => ({
|
||||||
fetchTransactions: () => dispatch(doFetchTransactions()),
|
fetchTransactions: () => dispatch(doFetchTransactions()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(TransactionListRecent);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(TransactionListRecent);
|
||||||
|
|
|
@ -27,14 +27,9 @@ class TransactionListRecent extends React.PureComponent<Props> {
|
||||||
<View style={walletStyle.transactionsCard}>
|
<View style={walletStyle.transactionsCard}>
|
||||||
<View style={[walletStyle.row, walletStyle.transactionsHeader]}>
|
<View style={[walletStyle.row, walletStyle.transactionsHeader]}>
|
||||||
<Text style={walletStyle.transactionsTitle}>Recent Transactions</Text>
|
<Text style={walletStyle.transactionsTitle}>Recent Transactions</Text>
|
||||||
<Link style={walletStyle.link}
|
<Link style={walletStyle.link} navigation={navigation} text={'View All'} href={'#TransactionHistory'} />
|
||||||
navigation={navigation}
|
|
||||||
text={'View All'}
|
|
||||||
href={'#TransactionHistory'} />
|
|
||||||
</View>
|
</View>
|
||||||
{fetchingTransactions && (
|
{fetchingTransactions && <Text style={walletStyle.infoText}>Fetching transactions...</Text>}
|
||||||
<Text style={walletStyle.infoText}>Fetching transactions...</Text>
|
|
||||||
)}
|
|
||||||
{!fetchingTransactions && (
|
{!fetchingTransactions && (
|
||||||
<TransactionList
|
<TransactionList
|
||||||
navigation={navigation}
|
navigation={navigation}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {
|
||||||
doUpdateSearchQuery,
|
doUpdateSearchQuery,
|
||||||
selectSearchState as selectSearch,
|
selectSearchState as selectSearch,
|
||||||
selectSearchValue,
|
selectSearchValue,
|
||||||
selectSearchSuggestions
|
selectSearchSuggestions,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { selectCurrentRoute } from 'redux/selectors/drawer';
|
import { selectCurrentRoute } from 'redux/selectors/drawer';
|
||||||
import UriBar from './view';
|
import UriBar from './view';
|
||||||
|
@ -15,7 +15,7 @@ const select = state => {
|
||||||
...searchState,
|
...searchState,
|
||||||
query: selectSearchValue(state),
|
query: selectSearchValue(state),
|
||||||
currentRoute: selectCurrentRoute(state),
|
currentRoute: selectCurrentRoute(state),
|
||||||
suggestions: selectSearchSuggestions(state)
|
suggestions: selectSearchSuggestions(state),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -23,4 +23,7 @@ const perform = dispatch => ({
|
||||||
updateSearchQuery: query => dispatch(doUpdateSearchQuery(query)),
|
updateSearchQuery: query => dispatch(doUpdateSearchQuery(query)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(UriBar);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(UriBar);
|
||||||
|
|
|
@ -13,16 +13,16 @@ class UriBarItem extends React.PureComponent {
|
||||||
let icon;
|
let icon;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SEARCH_TYPES.CHANNEL:
|
case SEARCH_TYPES.CHANNEL:
|
||||||
icon = <Icon name="at" size={18} />
|
icon = <Icon name="at" size={18} />;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SEARCH_TYPES.SEARCH:
|
case SEARCH_TYPES.SEARCH:
|
||||||
icon = <Icon name="search" size={18} />
|
icon = <Icon name="search" size={18} />;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SEARCH_TYPES.FILE:
|
case SEARCH_TYPES.FILE:
|
||||||
default:
|
default:
|
||||||
icon = <Icon name="file" size={18} />
|
icon = <Icon name="file" size={18} />;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,9 @@ class UriBarItem extends React.PureComponent {
|
||||||
<TouchableOpacity style={uriBarStyle.item} onPress={onPress}>
|
<TouchableOpacity style={uriBarStyle.item} onPress={onPress}>
|
||||||
{icon}
|
{icon}
|
||||||
<View style={uriBarStyle.itemContent}>
|
<View style={uriBarStyle.itemContent}>
|
||||||
<Text style={uriBarStyle.itemText} numberOfLines={1}>{shorthand || value} - {type === SEARCH_TYPES.SEARCH ? 'Search' : value}</Text>
|
<Text style={uriBarStyle.itemText} numberOfLines={1}>
|
||||||
|
{shorthand || value} - {type === SEARCH_TYPES.SEARCH ? 'Search' : value}
|
||||||
|
</Text>
|
||||||
<Text style={uriBarStyle.itemDesc} numberOfLines={1}>
|
<Text style={uriBarStyle.itemDesc} numberOfLines={1}>
|
||||||
{type === SEARCH_TYPES.SEARCH && `Search for '${value}'`}
|
{type === SEARCH_TYPES.SEARCH && `Search for '${value}'`}
|
||||||
{type === SEARCH_TYPES.CHANNEL && `View the @${shorthand} channel`}
|
{type === SEARCH_TYPES.CHANNEL && `View the @${shorthand} channel`}
|
||||||
|
@ -38,7 +40,7 @@ class UriBarItem extends React.PureComponent {
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ class UriBar extends React.PureComponent {
|
||||||
|
|
||||||
keyboardDidHideListener = null;
|
keyboardDidHideListener = null;
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount() {
|
||||||
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
|
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
|
||||||
this.setSelection();
|
this.setSelection();
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ class UriBar extends React.PureComponent {
|
||||||
inputText: null,
|
inputText: null,
|
||||||
focused: false,
|
focused: false,
|
||||||
// TODO: Add a setting to enable / disable direct search?
|
// TODO: Add a setting to enable / disable direct search?
|
||||||
directSearch: true
|
directSearch: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,15 +66,14 @@ class UriBar extends React.PureComponent {
|
||||||
if (onSearchSubmitted) {
|
if (onSearchSubmitted) {
|
||||||
onSearchSubmitted(text);
|
onSearchSubmitted(text);
|
||||||
} else {
|
} else {
|
||||||
navigation.navigate({ routeName: 'Search', key: 'searchPage', params: { searchQuery: text }});
|
navigation.navigate({ routeName: 'Search', key: 'searchPage', params: { searchQuery: text } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}, UriBar.INPUT_TIMEOUT);
|
}, UriBar.INPUT_TIMEOUT);
|
||||||
this.setState({ inputText: newValue, currentValue: newValue, changeTextTimeout: timeout });
|
this.setState({ inputText: newValue, currentValue: newValue, changeTextTimeout: timeout });
|
||||||
}
|
};
|
||||||
|
|
||||||
handleItemPress = (item) => {
|
handleItemPress = item => {
|
||||||
const { navigation, onSearchSubmitted, updateSearchQuery } = this.props;
|
const { navigation, onSearchSubmitted, updateSearchQuery } = this.props;
|
||||||
const { type, value } = item;
|
const { type, value } = item;
|
||||||
|
|
||||||
|
@ -89,23 +88,23 @@ class UriBar extends React.PureComponent {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
navigation.navigate({ routeName: 'Search', key: 'searchPage', params: { searchQuery: value }});
|
navigation.navigate({ routeName: 'Search', key: 'searchPage', params: { searchQuery: value } });
|
||||||
} else {
|
} else {
|
||||||
const uri = normalizeURI(value);
|
const uri = normalizeURI(value);
|
||||||
navigateToUri(navigation, uri);
|
navigateToUri(navigation, uri);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
_keyboardDidHide = () => {
|
_keyboardDidHide = () => {
|
||||||
if (this.textInput) {
|
if (this.textInput) {
|
||||||
this.textInput.blur();
|
this.textInput.blur();
|
||||||
}
|
}
|
||||||
this.setState({ focused: false });
|
this.setState({ focused: false });
|
||||||
}
|
};
|
||||||
|
|
||||||
setSelection() {
|
setSelection() {
|
||||||
if (this.textInput) {
|
if (this.textInput) {
|
||||||
this.textInput.setNativeProps({ selection: { start: 0, end: 0 }});
|
this.textInput.setNativeProps({ selection: { start: 0, end: 0 } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,17 +126,17 @@ class UriBar extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the search page with the query populated
|
// Open the search page with the query populated
|
||||||
navigation.navigate({ routeName: 'Search', key: 'searchPage', params: { searchQuery: inputText }});
|
navigation.navigate({ routeName: 'Search', key: 'searchPage', params: { searchQuery: inputText } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
onSearchPageBlurred() {
|
onSearchPageBlurred() {
|
||||||
this.setState({ currenValueSet: false });
|
this.setState({ currenValueSet: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { navigation, suggestions, query, value } = this.props;
|
const { navigation, suggestions, query, value } = this.props;
|
||||||
if (this.state.currentValue === null) {
|
if (this.state.currentValue === null) {
|
||||||
this.setState({ currentValue: value });
|
this.setState({ currentValue: value });
|
||||||
}
|
}
|
||||||
|
@ -155,39 +154,46 @@ class UriBar extends React.PureComponent {
|
||||||
size={24}
|
size={24}
|
||||||
style={uriBarStyle.drawerMenuButton}
|
style={uriBarStyle.drawerMenuButton}
|
||||||
iconStyle={discoverStyle.drawerHamburger}
|
iconStyle={discoverStyle.drawerHamburger}
|
||||||
onPress={() => navigation.openDrawer() } />
|
onPress={() => navigation.openDrawer()}
|
||||||
<TextInput ref={(ref) => { this.textInput = ref }}
|
/>
|
||||||
style={uriBarStyle.uriText}
|
<TextInput
|
||||||
onLayout={() => { this.setSelection(); }}
|
ref={ref => {
|
||||||
selectTextOnFocus={true}
|
this.textInput = ref;
|
||||||
placeholder={'Search movies, music, and more'}
|
}}
|
||||||
underlineColorAndroid={'transparent'}
|
style={uriBarStyle.uriText}
|
||||||
numberOfLines={1}
|
onLayout={() => {
|
||||||
clearButtonMode={'while-editing'}
|
this.setSelection();
|
||||||
value={this.state.currentValue}
|
}}
|
||||||
returnKeyType={'go'}
|
selectTextOnFocus={true}
|
||||||
inlineImageLeft={'baseline_search_black_24'}
|
placeholder={'Search movies, music, and more'}
|
||||||
inlineImagePadding={16}
|
underlineColorAndroid={'transparent'}
|
||||||
onFocus={() => this.setState({ focused: true })}
|
numberOfLines={1}
|
||||||
onBlur={() => {
|
clearButtonMode={'while-editing'}
|
||||||
this.setState({ focused: false });
|
value={this.state.currentValue}
|
||||||
this.setSelection();
|
returnKeyType={'go'}
|
||||||
}}
|
inlineImageLeft={'baseline_search_black_24'}
|
||||||
onChangeText={this.handleChangeText}
|
inlineImagePadding={16}
|
||||||
onSubmitEditing={this.handleSubmitEditing}/>
|
onFocus={() => this.setState({ focused: true })}
|
||||||
{(this.state.focused && !this.state.directSearch) && (
|
onBlur={() => {
|
||||||
<View style={uriBarStyle.suggestions}>
|
this.setState({ focused: false });
|
||||||
<FlatList style={uriBarStyle.suggestionList}
|
this.setSelection();
|
||||||
data={suggestions}
|
}}
|
||||||
keyboardShouldPersistTaps={'handled'}
|
onChangeText={this.handleChangeText}
|
||||||
keyExtractor={(item, value) => item.value}
|
onSubmitEditing={this.handleSubmitEditing}
|
||||||
renderItem={({item}) => (
|
/>
|
||||||
<UriBarItem
|
{this.state.focused && !this.state.directSearch && (
|
||||||
item={item}
|
<View style={uriBarStyle.suggestions}>
|
||||||
navigation={navigation}
|
<FlatList
|
||||||
onPress={() => this.handleItemPress(item)}
|
style={uriBarStyle.suggestionList}
|
||||||
/>)} />
|
data={suggestions}
|
||||||
</View>)}
|
keyboardShouldPersistTaps={'handled'}
|
||||||
|
keyExtractor={(item, value) => item.value}
|
||||||
|
renderItem={({ item }) => (
|
||||||
|
<UriBarItem item={item} navigation={navigation} onPress={() => this.handleItemPress(item)} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import { doCheckAddressIsMine, doGetNewAddress, selectReceiveAddress, selectGettingNewAddress } from 'lbry-redux';
|
||||||
doCheckAddressIsMine,
|
|
||||||
doGetNewAddress,
|
|
||||||
selectReceiveAddress,
|
|
||||||
selectGettingNewAddress,
|
|
||||||
} from 'lbry-redux';
|
|
||||||
import WalletAddress from './view';
|
import WalletAddress from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
|
@ -17,4 +12,7 @@ const perform = dispatch => ({
|
||||||
getNewAddress: () => dispatch(doGetNewAddress()),
|
getNewAddress: () => dispatch(doGetNewAddress()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(WalletAddress);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(WalletAddress);
|
||||||
|
|
|
@ -28,16 +28,20 @@ class WalletAddress extends React.PureComponent<Props> {
|
||||||
return (
|
return (
|
||||||
<View style={walletStyle.card}>
|
<View style={walletStyle.card}>
|
||||||
<Text style={walletStyle.title}>Receive Credits</Text>
|
<Text style={walletStyle.title}>Receive Credits</Text>
|
||||||
<Text style={[walletStyle.text, walletStyle.bottomMarginMedium]}>Use this wallet address to receive credits sent by another user (or yourself).</Text>
|
<Text style={[walletStyle.text, walletStyle.bottomMarginMedium]}>
|
||||||
|
Use this wallet address to receive credits sent by another user (or yourself).
|
||||||
|
</Text>
|
||||||
<Address address={receiveAddress} style={walletStyle.bottomMarginSmall} />
|
<Address address={receiveAddress} style={walletStyle.bottomMarginSmall} />
|
||||||
<Button style={[walletStyle.button, walletStyle.bottomMarginLarge]}
|
<Button
|
||||||
icon={'sync'}
|
style={[walletStyle.button, walletStyle.bottomMarginLarge]}
|
||||||
text={'Get new address'}
|
icon={'sync'}
|
||||||
onPress={getNewAddress}
|
text={'Get new address'}
|
||||||
disabled={gettingNewAddress}
|
onPress={getNewAddress}
|
||||||
/>
|
disabled={gettingNewAddress}
|
||||||
|
/>
|
||||||
<Text style={walletStyle.smallText}>
|
<Text style={walletStyle.smallText}>
|
||||||
You can generate a new address at any time, and any previous addresses will continue to work. Using multiple addresses can be helpful for keeping track of incoming payments from multiple sources.
|
You can generate a new address at any time, and any previous addresses will continue to work. Using multiple
|
||||||
|
addresses can be helpful for keeping track of incoming payments from multiple sources.
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,4 +6,7 @@ const select = state => ({
|
||||||
balance: selectBalance(state),
|
balance: selectBalance(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, null)(WalletBalance);
|
export default connect(
|
||||||
|
select,
|
||||||
|
null
|
||||||
|
)(WalletBalance);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Image, Text, View } from 'react-native';
|
import { Image, Text, View } from 'react-native';
|
||||||
import { Lbry, formatCredits } from 'lbry-redux'
|
import { Lbry, formatCredits } from 'lbry-redux';
|
||||||
import Address from 'component/address';
|
import Address from 'component/address';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import walletStyle from 'styles/wallet';
|
import walletStyle from 'styles/wallet';
|
||||||
|
@ -19,7 +19,7 @@ class WalletBalance extends React.PureComponent<Props> {
|
||||||
<Text style={walletStyle.balanceTitle}>Balance</Text>
|
<Text style={walletStyle.balanceTitle}>Balance</Text>
|
||||||
<Text style={walletStyle.balanceCaption}>You currently have</Text>
|
<Text style={walletStyle.balanceCaption}>You currently have</Text>
|
||||||
<Text style={walletStyle.balance}>
|
<Text style={walletStyle.balance}>
|
||||||
{(balance || balance === 0) && (formatCredits(parseFloat(balance), 2) + ' LBC')}
|
{(balance || balance === 0) && formatCredits(parseFloat(balance), 2) + ' LBC'}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {
|
||||||
doSendDraftTransaction,
|
doSendDraftTransaction,
|
||||||
selectDraftTransaction,
|
selectDraftTransaction,
|
||||||
selectDraftTransactionError,
|
selectDraftTransactionError,
|
||||||
selectBalance
|
selectBalance,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import WalletSend from './view';
|
import WalletSend from './view';
|
||||||
|
|
||||||
|
@ -16,7 +16,10 @@ const select = state => ({
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
sendToAddress: (address, amount) => dispatch(doSendDraftTransaction(address, amount)),
|
sendToAddress: (address, amount) => dispatch(doSendDraftTransaction(address, amount)),
|
||||||
notify: (data) => dispatch(doToast(data))
|
notify: data => dispatch(doToast(data)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(WalletSend);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(WalletSend);
|
||||||
|
|
|
@ -22,7 +22,7 @@ class WalletSend extends React.PureComponent<Props> {
|
||||||
amount: null,
|
amount: null,
|
||||||
address: null,
|
address: null,
|
||||||
addressChanged: false,
|
addressChanged: false,
|
||||||
addressValid: false
|
addressValid: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillUpdate(nextProps) {
|
componentWillUpdate(nextProps) {
|
||||||
|
@ -51,18 +51,18 @@ class WalletSend extends React.PureComponent<Props> {
|
||||||
|
|
||||||
if (amount && address) {
|
if (amount && address) {
|
||||||
// Show confirmation before send
|
// Show confirmation before send
|
||||||
Alert.alert(
|
Alert.alert('Send LBC', `Are you sure you want to send ${amount} LBC to ${address}?`, [
|
||||||
'Send LBC',
|
|
||||||
`Are you sure you want to send ${amount} LBC to ${address}?`,
|
|
||||||
[
|
|
||||||
{ text: 'No' },
|
{ text: 'No' },
|
||||||
{ text: 'Yes', onPress: () => {
|
{
|
||||||
sendToAddress(address, parseFloat(amount));
|
text: 'Yes',
|
||||||
this.setState({ address: null, amount: null });
|
onPress: () => {
|
||||||
}}
|
sendToAddress(address, parseFloat(amount));
|
||||||
|
this.setState({ address: null, amount: null });
|
||||||
|
},
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleAddressInputBlur = () => {
|
handleAddressInputBlur = () => {
|
||||||
if (this.state.addressChanged && !this.state.addressValid) {
|
if (this.state.addressChanged && !this.state.addressValid) {
|
||||||
|
@ -71,52 +71,58 @@ class WalletSend extends React.PureComponent<Props> {
|
||||||
message: 'The recipient address is not a valid LBRY address.',
|
message: 'The recipient address is not a valid LBRY address.',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleAddressInputSubmit = () => {
|
handleAddressInputSubmit = () => {
|
||||||
if (this.amountInput) {
|
if (this.amountInput) {
|
||||||
this.amountInput.focus();
|
this.amountInput.focus();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { balance } = this.props;
|
const { balance } = this.props;
|
||||||
const canSend = this.state.address &&
|
const canSend = this.state.address && this.state.amount > 0 && this.state.address.trim().length > 0;
|
||||||
this.state.amount > 0 &&
|
|
||||||
this.state.address.trim().length > 0;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={walletStyle.card}>
|
<View style={walletStyle.card}>
|
||||||
<Text style={walletStyle.title}>Send Credits</Text>
|
<Text style={walletStyle.title}>Send Credits</Text>
|
||||||
<Text style={walletStyle.text}>Recipient address</Text>
|
<Text style={walletStyle.text}>Recipient address</Text>
|
||||||
<View style={[walletStyle.row, walletStyle.bottomMarginMedium]}>
|
<View style={[walletStyle.row, walletStyle.bottomMarginMedium]}>
|
||||||
<TextInput onChangeText={value => this.setState({
|
<TextInput
|
||||||
address: value,
|
onChangeText={value =>
|
||||||
addressChanged: true,
|
this.setState({
|
||||||
addressValid: (value.trim().length == 0 || regexAddress.test(value))
|
address: value,
|
||||||
})}
|
addressChanged: true,
|
||||||
onBlur={this.handleAddressInputBlur}
|
addressValid: value.trim().length == 0 || regexAddress.test(value),
|
||||||
onSubmitEditing={this.handleAddressInputSubmit}
|
})
|
||||||
placeholder={'bbFxRyXXXXXXXXXXXZD8nE7XTLUxYnddTs'}
|
}
|
||||||
value={this.state.address}
|
onBlur={this.handleAddressInputBlur}
|
||||||
returnKeyType={'next'}
|
onSubmitEditing={this.handleAddressInputSubmit}
|
||||||
style={[walletStyle.input, walletStyle.addressInput, walletStyle.bottomMarginMedium]} />
|
placeholder={'bbFxRyXXXXXXXXXXXZD8nE7XTLUxYnddTs'}
|
||||||
|
value={this.state.address}
|
||||||
|
returnKeyType={'next'}
|
||||||
|
style={[walletStyle.input, walletStyle.addressInput, walletStyle.bottomMarginMedium]}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
<Text style={walletStyle.text}>Amount</Text>
|
<Text style={walletStyle.text}>Amount</Text>
|
||||||
<View style={walletStyle.row}>
|
<View style={walletStyle.row}>
|
||||||
<View style={walletStyle.amountRow}>
|
<View style={walletStyle.amountRow}>
|
||||||
<TextInput ref={ref => this.amountInput = ref}
|
<TextInput
|
||||||
onChangeText={value => this.setState({amount: value})}
|
ref={ref => (this.amountInput = ref)}
|
||||||
keyboardType={'numeric'}
|
onChangeText={value => this.setState({ amount: value })}
|
||||||
placeholder={'0'}
|
keyboardType={'numeric'}
|
||||||
value={this.state.amount}
|
placeholder={'0'}
|
||||||
style={[walletStyle.input, walletStyle.amountInput]} />
|
value={this.state.amount}
|
||||||
|
style={[walletStyle.input, walletStyle.amountInput]}
|
||||||
|
/>
|
||||||
<Text style={[walletStyle.text, walletStyle.currency]}>LBC</Text>
|
<Text style={[walletStyle.text, walletStyle.currency]}>LBC</Text>
|
||||||
</View>
|
</View>
|
||||||
<Button text={'Send'}
|
<Button
|
||||||
style={[walletStyle.button, walletStyle.sendButton]}
|
text={'Send'}
|
||||||
disabled={!canSend}
|
style={[walletStyle.button, walletStyle.sendButton]}
|
||||||
onPress={this.handleSend} />
|
disabled={!canSend}
|
||||||
|
onPress={this.handleSend}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,4 +7,7 @@ const select = state => ({
|
||||||
deviceWalletSynced: makeSelectClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED)(state),
|
deviceWalletSynced: makeSelectClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED)(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, null)(WalletSyncDriver);
|
export default connect(
|
||||||
|
select,
|
||||||
|
null
|
||||||
|
)(WalletSyncDriver);
|
||||||
|
|
|
@ -8,7 +8,7 @@ class WalletSyncDriver extends React.PureComponent<Props> {
|
||||||
onEnableSyncPressed = () => {
|
onEnableSyncPressed = () => {
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.navigate({ routeName: 'Verification', key: 'verification', params: { syncFlow: true } });
|
navigation.navigate({ routeName: 'Verification', key: 'verification', params: { syncFlow: true } });
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { deviceWalletSynced } = this.props;
|
const { deviceWalletSynced } = this.props;
|
||||||
|
@ -17,14 +17,29 @@ class WalletSyncDriver extends React.PureComponent<Props> {
|
||||||
<View style={walletStyle.syncDriverCard}>
|
<View style={walletStyle.syncDriverCard}>
|
||||||
<View style={walletStyle.syncDriverRow}>
|
<View style={walletStyle.syncDriverRow}>
|
||||||
<Text style={walletStyle.syncDriverTitle}>Wallet sync is {deviceWalletSynced ? 'on' : 'off'}.</Text>
|
<Text style={walletStyle.syncDriverTitle}>Wallet sync is {deviceWalletSynced ? 'on' : 'off'}.</Text>
|
||||||
{!deviceWalletSynced &&
|
{!deviceWalletSynced && (
|
||||||
<Link text="Sync FAQ" href="https://lbry.com/faq/how-to-backup-wallet#sync" style={walletStyle.syncDriverText} />}
|
<Link
|
||||||
|
text="Sync FAQ"
|
||||||
|
href="https://lbry.com/faq/how-to-backup-wallet#sync"
|
||||||
|
style={walletStyle.syncDriverText}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
{!deviceWalletSynced &&
|
{!deviceWalletSynced && (
|
||||||
<View style={walletStyle.actionRow}>
|
<View style={walletStyle.actionRow}>
|
||||||
<Button style={walletStyle.enrollButton} theme={"light"} text={"Enable"} onPress={this.onEnableSyncPressed} />
|
<Button
|
||||||
<Link text="Manual backup" href="https://lbry.com/faq/how-to-backup-wallet#android" style={walletStyle.syncDriverText} />
|
style={walletStyle.enrollButton}
|
||||||
</View>}
|
theme={'light'}
|
||||||
|
text={'Enable'}
|
||||||
|
onPress={this.onEnableSyncPressed}
|
||||||
|
/>
|
||||||
|
<Link
|
||||||
|
text="Manual backup"
|
||||||
|
href="https://lbry.com/faq/how-to-backup-wallet#android"
|
||||||
|
style={walletStyle.syncDriverText}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,72 +1,72 @@
|
||||||
const Constants = {
|
const Constants = {
|
||||||
FIRST_RUN_PAGE_WELCOME: "welcome",
|
FIRST_RUN_PAGE_WELCOME: 'welcome',
|
||||||
FIRST_RUN_PAGE_EMAIL_COLLECT: "email-collect",
|
FIRST_RUN_PAGE_EMAIL_COLLECT: 'email-collect',
|
||||||
FIRST_RUN_PAGE_EMAIL_VERIFY:"email-verify",
|
FIRST_RUN_PAGE_EMAIL_VERIFY: 'email-verify',
|
||||||
FIRST_RUN_PAGE_WALLET: "wallet",
|
FIRST_RUN_PAGE_WALLET: 'wallet',
|
||||||
FIRST_RUN_PAGE_SKIP_ACCOUNT: "skip-account",
|
FIRST_RUN_PAGE_SKIP_ACCOUNT: 'skip-account',
|
||||||
|
|
||||||
VERIFY_PAGE_EMAIL: "email-verify",
|
VERIFY_PAGE_EMAIL: 'email-verify',
|
||||||
VERIFY_PAGE_PHONE_NUMBER: "phone-number-verify",
|
VERIFY_PAGE_PHONE_NUMBER: 'phone-number-verify',
|
||||||
|
|
||||||
PHASE_COLLECTION: "collection",
|
PHASE_COLLECTION: 'collection',
|
||||||
PHASE_VERIFICATION: "verification",
|
PHASE_VERIFICATION: 'verification',
|
||||||
|
|
||||||
CONTENT_TAB: "content",
|
CONTENT_TAB: 'content',
|
||||||
ABOUT_TAB: "about",
|
ABOUT_TAB: 'about',
|
||||||
|
|
||||||
KEY_FIRST_RUN_EMAIL: "firstRunEmail",
|
KEY_FIRST_RUN_EMAIL: 'firstRunEmail',
|
||||||
KEY_FIRST_RUN_PASSWORD: "firstRunPassword",
|
KEY_FIRST_RUN_PASSWORD: 'firstRunPassword',
|
||||||
KEY_FIRST_USER_AUTH: "firstUserAuth",
|
KEY_FIRST_USER_AUTH: 'firstUserAuth',
|
||||||
KEY_SHOULD_VERIFY_EMAIL: "shouldVerifyEmail",
|
KEY_SHOULD_VERIFY_EMAIL: 'shouldVerifyEmail',
|
||||||
KEY_EMAIL_VERIFY_PENDING: "emailVerifyPending",
|
KEY_EMAIL_VERIFY_PENDING: 'emailVerifyPending',
|
||||||
|
|
||||||
SETTING_ALPHA_UNDERSTANDS_RISKS: "alphaUnderstandRisks",
|
SETTING_ALPHA_UNDERSTANDS_RISKS: 'alphaUnderstandRisks',
|
||||||
SETTING_SUBSCRIPTIONS_VIEW_MODE: "subscriptionsViewMode",
|
SETTING_SUBSCRIPTIONS_VIEW_MODE: 'subscriptionsViewMode',
|
||||||
SETTING_RATING_REMINDER_LAST_SHOWN: "ratingReminderLastShown",
|
SETTING_RATING_REMINDER_LAST_SHOWN: 'ratingReminderLastShown',
|
||||||
SETTING_RATING_REMINDER_DISABLED: "ratingReminderDisabled",
|
SETTING_RATING_REMINDER_DISABLED: 'ratingReminderDisabled',
|
||||||
SETTING_BACKUP_DISMISSED: "backupDismissed",
|
SETTING_BACKUP_DISMISSED: 'backupDismissed',
|
||||||
SETTING_REWARDS_NOT_INTERESTED: "rewardsNotInterested",
|
SETTING_REWARDS_NOT_INTERESTED: 'rewardsNotInterested',
|
||||||
SETTING_DEVICE_WALLET_SYNCED: "deviceWalletSynced",
|
SETTING_DEVICE_WALLET_SYNCED: 'deviceWalletSynced',
|
||||||
|
|
||||||
ACTION_DELETE_COMPLETED_BLOBS: "DELETE_COMPLETED_BLOBS",
|
ACTION_DELETE_COMPLETED_BLOBS: 'DELETE_COMPLETED_BLOBS',
|
||||||
ACTION_FIRST_RUN_PAGE_CHANGED: "FIRST_RUN_PAGE_CHANGED",
|
ACTION_FIRST_RUN_PAGE_CHANGED: 'FIRST_RUN_PAGE_CHANGED',
|
||||||
|
|
||||||
ACTION_PUSH_DRAWER_STACK: "PUSH_DRAWER_STACK",
|
ACTION_PUSH_DRAWER_STACK: 'PUSH_DRAWER_STACK',
|
||||||
ACTION_POP_DRAWER_STACK: "POP_DRAWER_STACK",
|
ACTION_POP_DRAWER_STACK: 'POP_DRAWER_STACK',
|
||||||
ACTION_SET_PLAYER_VISIBLE: "SET_PLAYER_VISIBLE",
|
ACTION_SET_PLAYER_VISIBLE: 'SET_PLAYER_VISIBLE',
|
||||||
|
|
||||||
ACTION_REACT_NAVIGATION_RESET: "Navigation/RESET",
|
ACTION_REACT_NAVIGATION_RESET: 'Navigation/RESET',
|
||||||
ACTION_REACT_NAVIGATION_NAVIGATE: "Navigation/NAVIGATE",
|
ACTION_REACT_NAVIGATION_NAVIGATE: 'Navigation/NAVIGATE',
|
||||||
ACTION_REACT_NAVIGATION_REPLACE: "Navigation/REPLACE",
|
ACTION_REACT_NAVIGATION_REPLACE: 'Navigation/REPLACE',
|
||||||
|
|
||||||
PAGE_REWARDS: "rewards",
|
PAGE_REWARDS: 'rewards',
|
||||||
PAGE_SETTINGS: "settings",
|
PAGE_SETTINGS: 'settings',
|
||||||
PAGE_TRENDING: "trending",
|
PAGE_TRENDING: 'trending',
|
||||||
PAGE_WALLET: "wallet",
|
PAGE_WALLET: 'wallet',
|
||||||
|
|
||||||
DRAWER_ROUTE_DISCOVER: "Discover",
|
DRAWER_ROUTE_DISCOVER: 'Discover',
|
||||||
DRAWER_ROUTE_TRENDING: "Trending",
|
DRAWER_ROUTE_TRENDING: 'Trending',
|
||||||
DRAWER_ROUTE_SUBSCRIPTIONS: "Subscriptions",
|
DRAWER_ROUTE_SUBSCRIPTIONS: 'Subscriptions',
|
||||||
DRAWER_ROUTE_MY_LBRY: "Downloads",
|
DRAWER_ROUTE_MY_LBRY: 'Downloads',
|
||||||
DRAWER_ROUTE_REWARDS: "Rewards",
|
DRAWER_ROUTE_REWARDS: 'Rewards',
|
||||||
DRAWER_ROUTE_WALLET: "Wallet",
|
DRAWER_ROUTE_WALLET: 'Wallet',
|
||||||
DRAWER_ROUTE_SETTINGS: "Settings",
|
DRAWER_ROUTE_SETTINGS: 'Settings',
|
||||||
DRAWER_ROUTE_ABOUT: "About",
|
DRAWER_ROUTE_ABOUT: 'About',
|
||||||
DRAWER_ROUTE_SEARCH: "Search",
|
DRAWER_ROUTE_SEARCH: 'Search',
|
||||||
DRAWER_ROUTE_TRANSACTION_HISTORY: "TransactionHistory",
|
DRAWER_ROUTE_TRANSACTION_HISTORY: 'TransactionHistory',
|
||||||
|
|
||||||
FULL_ROUTE_NAME_DISCOVER: "DiscoverStack",
|
FULL_ROUTE_NAME_DISCOVER: 'DiscoverStack',
|
||||||
FULL_ROUTE_NAME_TRENDING: "TrendingStack",
|
FULL_ROUTE_NAME_TRENDING: 'TrendingStack',
|
||||||
FULL_ROUTE_NAME_MY_SUBSCRIPTIONS: "MySubscriptionsStack",
|
FULL_ROUTE_NAME_MY_SUBSCRIPTIONS: 'MySubscriptionsStack',
|
||||||
FULL_ROUTE_NAME_WALLET: "WalletStack",
|
FULL_ROUTE_NAME_WALLET: 'WalletStack',
|
||||||
FULL_ROUTE_NAME_MY_LBRY: "MyLBRYStack",
|
FULL_ROUTE_NAME_MY_LBRY: 'MyLBRYStack',
|
||||||
|
|
||||||
ROUTE_FILE: "File",
|
ROUTE_FILE: 'File',
|
||||||
|
|
||||||
SUBSCRIPTIONS_VIEW_ALL: "view_all",
|
SUBSCRIPTIONS_VIEW_ALL: 'view_all',
|
||||||
SUBSCRIPTIONS_VIEW_LATEST_FIRST: "view_latest_first",
|
SUBSCRIPTIONS_VIEW_LATEST_FIRST: 'view_latest_first',
|
||||||
|
|
||||||
PLAY_STORE_URL: "https://play.google.com/store/apps/details?id=io.lbry.browser",
|
PLAY_STORE_URL: 'https://play.google.com/store/apps/details?id=io.lbry.browser',
|
||||||
RATING_REMINDER_INTERVAL: 604800, // 7 days (7 * 24 * 3600s)
|
RATING_REMINDER_INTERVAL: 604800, // 7 days (7 * 24 * 3600s)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -82,5 +82,5 @@ export const DrawerRoutes = [
|
||||||
Constants.DRAWER_ROUTE_SETTINGS,
|
Constants.DRAWER_ROUTE_SETTINGS,
|
||||||
Constants.DRAWER_ROUTE_ABOUT,
|
Constants.DRAWER_ROUTE_ABOUT,
|
||||||
Constants.DRAWER_ROUTE_SEARCH,
|
Constants.DRAWER_ROUTE_SEARCH,
|
||||||
Constants.DRAWER_ROUTE_TRANSACTION_HISTORY
|
Constants.DRAWER_ROUTE_TRANSACTION_HISTORY,
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { setJSExceptionHandler } from 'react-native-exception-handler';
|
import { setJSExceptionHandler } from 'react-native-exception-handler';
|
||||||
import { Provider, connect } from 'react-redux';
|
import { Provider, connect } from 'react-redux';
|
||||||
import {
|
import { AppRegistry, Text, View, NativeModules } from 'react-native';
|
||||||
AppRegistry,
|
|
||||||
Text,
|
|
||||||
View,
|
|
||||||
NativeModules
|
|
||||||
} from 'react-native';
|
|
||||||
import {
|
import {
|
||||||
Lbry,
|
Lbry,
|
||||||
claimsReducer,
|
claimsReducer,
|
||||||
|
@ -15,7 +10,7 @@ import {
|
||||||
fileInfoReducer,
|
fileInfoReducer,
|
||||||
notificationsReducer,
|
notificationsReducer,
|
||||||
searchReducer,
|
searchReducer,
|
||||||
walletReducer
|
walletReducer,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import {
|
import {
|
||||||
authReducer,
|
authReducer,
|
||||||
|
@ -25,10 +20,14 @@ import {
|
||||||
rewardsReducer,
|
rewardsReducer,
|
||||||
subscriptionsReducer,
|
subscriptionsReducer,
|
||||||
syncReducer,
|
syncReducer,
|
||||||
userReducer
|
userReducer,
|
||||||
} from 'lbryinc';
|
} from 'lbryinc';
|
||||||
import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
|
import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
|
||||||
import AppWithNavigationState, { AppNavigator, navigatorReducer, reactNavigationMiddleware } from 'component/AppNavigator';
|
import AppWithNavigationState, {
|
||||||
|
AppNavigator,
|
||||||
|
navigatorReducer,
|
||||||
|
reactNavigationMiddleware,
|
||||||
|
} from 'component/AppNavigator';
|
||||||
import { persistStore, autoRehydrate } from 'redux-persist';
|
import { persistStore, autoRehydrate } from 'redux-persist';
|
||||||
import AsyncStorage from '@react-native-community/async-storage';
|
import AsyncStorage from '@react-native-community/async-storage';
|
||||||
import FilesystemStorage from 'redux-persist-filesystem-storage';
|
import FilesystemStorage from 'redux-persist-filesystem-storage';
|
||||||
|
@ -39,10 +38,9 @@ import drawerReducer from 'redux/reducers/drawer';
|
||||||
import settingsReducer from 'redux/reducers/settings';
|
import settingsReducer from 'redux/reducers/settings';
|
||||||
import thunk from 'redux-thunk';
|
import thunk from 'redux-thunk';
|
||||||
|
|
||||||
|
|
||||||
const globalExceptionHandler = (error, isFatal) => {
|
const globalExceptionHandler = (error, isFatal) => {
|
||||||
if (error && NativeModules.Firebase) {
|
if (error && NativeModules.Firebase) {
|
||||||
NativeModules.Firebase.logException(isFatal, error.message ? error.message : "No message", JSON.stringify(error));
|
NativeModules.Firebase.logException(isFatal, error.message ? error.message : 'No message', JSON.stringify(error));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
setJSExceptionHandler(globalExceptionHandler, true);
|
setJSExceptionHandler(globalExceptionHandler, true);
|
||||||
|
@ -97,7 +95,7 @@ const reducers = combineReducers({
|
||||||
subscriptions: subscriptionsReducer,
|
subscriptions: subscriptionsReducer,
|
||||||
sync: syncReducer,
|
sync: syncReducer,
|
||||||
user: userReducer,
|
user: userReducer,
|
||||||
wallet: walletReducer
|
wallet: walletReducer,
|
||||||
});
|
});
|
||||||
|
|
||||||
const bulkThunk = createBulkThunkMiddleware();
|
const bulkThunk = createBulkThunkMiddleware();
|
||||||
|
@ -109,10 +107,7 @@ const composeEnhancers = compose;
|
||||||
const store = createStore(
|
const store = createStore(
|
||||||
enableBatching(reducers),
|
enableBatching(reducers),
|
||||||
{}, // initial state,
|
{}, // initial state,
|
||||||
composeEnhancers(
|
composeEnhancers(autoRehydrate(), applyMiddleware(...middleware))
|
||||||
autoRehydrate(),
|
|
||||||
applyMiddleware(...middleware)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
window.store = store;
|
window.store = store;
|
||||||
|
|
||||||
|
@ -130,7 +125,7 @@ const persistOptions = {
|
||||||
// read the data
|
// read the data
|
||||||
transforms: [authFilter, saveClaimsFilter, subscriptionsFilter, settingsFilter, walletFilter, compressor],
|
transforms: [authFilter, saveClaimsFilter, subscriptionsFilter, settingsFilter, walletFilter, compressor],
|
||||||
debounce: 10000,
|
debounce: 10000,
|
||||||
storage: FilesystemStorage
|
storage: FilesystemStorage,
|
||||||
};
|
};
|
||||||
|
|
||||||
persistStore(store, persistOptions, err => {
|
persistStore(store, persistOptions, err => {
|
||||||
|
@ -140,7 +135,7 @@ persistStore(store, persistOptions, err => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Find i18n module that is compatible with react-native
|
// TODO: Find i18n module that is compatible with react-native
|
||||||
global.__ = (str) => str;
|
global.__ = str => str;
|
||||||
|
|
||||||
class LBRYApp extends React.Component {
|
class LBRYApp extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -21,4 +21,7 @@ const perform = dispatch => ({
|
||||||
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
|
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(AboutPage);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(AboutPage);
|
||||||
|
|
|
@ -11,7 +11,7 @@ class AboutPage extends React.PureComponent {
|
||||||
state = {
|
state = {
|
||||||
appVersion: null,
|
appVersion: null,
|
||||||
lbryId: null,
|
lbryId: null,
|
||||||
versionInfo: null
|
versionInfo: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
didFocusListener;
|
didFocusListener;
|
||||||
|
@ -46,7 +46,7 @@ class AboutPage extends React.PureComponent {
|
||||||
|
|
||||||
if (NativeModules.VersionInfo) {
|
if (NativeModules.VersionInfo) {
|
||||||
NativeModules.VersionInfo.getAppVersion().then(version => {
|
NativeModules.VersionInfo.getAppVersion().then(version => {
|
||||||
this.setState({appVersion: version});
|
this.setState({ appVersion: version });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Lbry.version().then(info => {
|
Lbry.version().then(info => {
|
||||||
|
@ -61,7 +61,7 @@ class AboutPage extends React.PureComponent {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!this.props.accessToken) this.props.fetchAccessToken();
|
if (!this.props.accessToken) this.props.fetchAccessToken();
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { accessToken, drawerStack, navigation, notify, popDrawerStack, userEmail } = this.props;
|
const { accessToken, drawerStack, navigation, notify, popDrawerStack, userEmail } = this.props;
|
||||||
|
@ -70,15 +70,14 @@ class AboutPage extends React.PureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={aboutStyle.container}>
|
<View style={aboutStyle.container}>
|
||||||
<PageHeader title={"About LBRY"}
|
<PageHeader title={'About LBRY'} onBackPressed={() => navigateBack(navigation, drawerStack, popDrawerStack)} />
|
||||||
onBackPressed={() => navigateBack(navigation, drawerStack, popDrawerStack)} />
|
|
||||||
<ScrollView style={aboutStyle.scrollContainer}>
|
<ScrollView style={aboutStyle.scrollContainer}>
|
||||||
<Text style={aboutStyle.title}>Content Freedom</Text>
|
<Text style={aboutStyle.title}>Content Freedom</Text>
|
||||||
<Text style={aboutStyle.paragraph}>
|
<Text style={aboutStyle.paragraph}>
|
||||||
LBRY is a free, open, and community-run digital marketplace. It is a decentralized peer-to-peer
|
LBRY is a free, open, and community-run digital marketplace. It is a decentralized peer-to-peer content
|
||||||
content distribution platform for creators to upload and share content, and earn LBRY credits
|
distribution platform for creators to upload and share content, and earn LBRY credits for their effort.
|
||||||
for their effort. Users will be able to find a wide selection of videos, music, ebooks and other
|
Users will be able to find a wide selection of videos, music, ebooks and other digital content they are
|
||||||
digital content they are interested in.
|
interested in.
|
||||||
</Text>
|
</Text>
|
||||||
<View style={aboutStyle.links}>
|
<View style={aboutStyle.links}>
|
||||||
<Link style={aboutStyle.link} href="https://lbry.com/faq/what-is-lbry" text="What is LBRY?" />
|
<Link style={aboutStyle.link} href="https://lbry.com/faq/what-is-lbry" text="What is LBRY?" />
|
||||||
|
@ -87,7 +86,8 @@ class AboutPage extends React.PureComponent {
|
||||||
</View>
|
</View>
|
||||||
<Text style={aboutStyle.socialTitle}>Get Social</Text>
|
<Text style={aboutStyle.socialTitle}>Get Social</Text>
|
||||||
<Text style={aboutStyle.paragraph}>
|
<Text style={aboutStyle.paragraph}>
|
||||||
You can interact with the LBRY team and members of the community on Discord, Facebook, Instagram, Twitter or Reddit.
|
You can interact with the LBRY team and members of the community on Discord, Facebook, Instagram, Twitter or
|
||||||
|
Reddit.
|
||||||
</Text>
|
</Text>
|
||||||
<View style={aboutStyle.links}>
|
<View style={aboutStyle.links}>
|
||||||
<Link style={aboutStyle.link} href="https://discordapp.com/invite/Z3bERWA" text="Discord" />
|
<Link style={aboutStyle.link} href="https://discordapp.com/invite/Z3bERWA" text="Discord" />
|
||||||
|
@ -98,54 +98,88 @@ class AboutPage extends React.PureComponent {
|
||||||
<Link style={aboutStyle.link} href="https://t.me/lbryofficial" text="Telegram" />
|
<Link style={aboutStyle.link} href="https://t.me/lbryofficial" text="Telegram" />
|
||||||
</View>
|
</View>
|
||||||
<Text style={aboutStyle.releaseInfoTitle}>App info</Text>
|
<Text style={aboutStyle.releaseInfoTitle}>App info</Text>
|
||||||
{userEmail && userEmail.trim().length > 0 &&
|
{userEmail && userEmail.trim().length > 0 && (
|
||||||
<View style={aboutStyle.verticalRow}>
|
<View style={aboutStyle.verticalRow}>
|
||||||
<View style={aboutStyle.innerRow}>
|
<View style={aboutStyle.innerRow}>
|
||||||
<View style={aboutStyle.col}><Text style={aboutStyle.text}>Connected Email</Text></View>
|
<View style={aboutStyle.col}>
|
||||||
<View style={aboutStyle.col}><Text selectable={true} style={aboutStyle.valueText}>{userEmail}</Text></View>
|
<Text style={aboutStyle.text}>Connected Email</Text>
|
||||||
|
</View>
|
||||||
|
<View style={aboutStyle.col}>
|
||||||
|
<Text selectable={true} style={aboutStyle.valueText}>
|
||||||
|
{userEmail}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<Link
|
||||||
|
style={aboutStyle.listLink}
|
||||||
|
href={`http://lbry.com/list/edit/${accessToken}`}
|
||||||
|
text="Update mailing preferences"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<View>
|
)}
|
||||||
<Link
|
|
||||||
style={aboutStyle.listLink}
|
|
||||||
href={`http://lbry.com/list/edit/${accessToken}`}
|
|
||||||
text="Update mailing preferences" />
|
|
||||||
</View>
|
|
||||||
</View>}
|
|
||||||
|
|
||||||
<View style={aboutStyle.row}>
|
<View style={aboutStyle.row}>
|
||||||
<View style={aboutStyle.col}><Text style={aboutStyle.text}>App version</Text></View>
|
<View style={aboutStyle.col}>
|
||||||
<View style={aboutStyle.col}><Text selectable={true} style={aboutStyle.valueText}>{this.state.appVersion}</Text></View>
|
<Text style={aboutStyle.text}>App version</Text>
|
||||||
|
</View>
|
||||||
|
<View style={aboutStyle.col}>
|
||||||
|
<Text selectable={true} style={aboutStyle.valueText}>
|
||||||
|
{this.state.appVersion}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={aboutStyle.row}>
|
<View style={aboutStyle.row}>
|
||||||
<View style={aboutStyle.col}><Text style={aboutStyle.text}>Daemon (lbrynet)</Text></View>
|
<View style={aboutStyle.col}>
|
||||||
<View style={aboutStyle.col}><Text selectable={true} style={aboutStyle.valueText}>{ver ? ver.lbrynet_version : loading }</Text></View>
|
<Text style={aboutStyle.text}>Daemon (lbrynet)</Text>
|
||||||
|
</View>
|
||||||
|
<View style={aboutStyle.col}>
|
||||||
|
<Text selectable={true} style={aboutStyle.valueText}>
|
||||||
|
{ver ? ver.lbrynet_version : loading}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={aboutStyle.row}>
|
<View style={aboutStyle.row}>
|
||||||
<View style={aboutStyle.col}><Text style={aboutStyle.text}>Platform</Text></View>
|
<View style={aboutStyle.col}>
|
||||||
<View style={aboutStyle.col}><Text selectable={true} style={aboutStyle.valueText}>{ver ? ver.platform : loading }</Text></View>
|
<Text style={aboutStyle.text}>Platform</Text>
|
||||||
|
</View>
|
||||||
|
<View style={aboutStyle.col}>
|
||||||
|
<Text selectable={true} style={aboutStyle.valueText}>
|
||||||
|
{ver ? ver.platform : loading}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={aboutStyle.row}>
|
<View style={aboutStyle.row}>
|
||||||
<View style={aboutStyle.col}>
|
<View style={aboutStyle.col}>
|
||||||
<Text style={aboutStyle.text}>Installation ID</Text>
|
<Text style={aboutStyle.text}>Installation ID</Text>
|
||||||
<Text selectable={true} style={aboutStyle.lineValueText}>{this.state.lbryId ? this.state.lbryId : loading}</Text>
|
<Text selectable={true} style={aboutStyle.lineValueText}>
|
||||||
|
{this.state.lbryId ? this.state.lbryId : loading}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={aboutStyle.row}>
|
<View style={aboutStyle.row}>
|
||||||
<View style={aboutStyle.col}><Text style={aboutStyle.text}>Logs</Text></View>
|
|
||||||
<View style={aboutStyle.col}>
|
<View style={aboutStyle.col}>
|
||||||
<Link style={aboutStyle.listLink} text="Send log" onPress={() => {
|
<Text style={aboutStyle.text}>Logs</Text>
|
||||||
if (NativeModules.UtilityModule) {
|
</View>
|
||||||
NativeModules.UtilityModule.shareLogFile((error) => {
|
<View style={aboutStyle.col}>
|
||||||
if (error) {
|
<Link
|
||||||
notify(error);
|
style={aboutStyle.listLink}
|
||||||
}
|
text="Send log"
|
||||||
});
|
onPress={() => {
|
||||||
}
|
if (NativeModules.UtilityModule) {
|
||||||
}} />
|
NativeModules.UtilityModule.shareLogFile(error => {
|
||||||
|
if (error) {
|
||||||
|
notify(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {
|
||||||
makeSelectClaimForUri,
|
makeSelectClaimForUri,
|
||||||
makeSelectClaimsInChannelForCurrentPageState,
|
makeSelectClaimsInChannelForCurrentPageState,
|
||||||
makeSelectFetchingChannelClaims,
|
makeSelectFetchingChannelClaims,
|
||||||
makeSelectTotalPagesForChannel
|
makeSelectTotalPagesForChannel,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { doPopDrawerStack } from 'redux/actions/drawer';
|
import { doPopDrawerStack } from 'redux/actions/drawer';
|
||||||
import { selectDrawerStack } from 'redux/selectors/drawer';
|
import { selectDrawerStack } from 'redux/selectors/drawer';
|
||||||
|
@ -20,7 +20,10 @@ const select = (state, props) => ({
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
fetchClaims: (uri, page) => dispatch(doFetchClaimsByChannel(uri, page)),
|
fetchClaims: (uri, page) => dispatch(doFetchClaimsByChannel(uri, page)),
|
||||||
popDrawerStack: () => dispatch(doPopDrawerStack())
|
popDrawerStack: () => dispatch(doPopDrawerStack()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(ChannelPage);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(ChannelPage);
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import { ActivityIndicator, Dimensions, Image, ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
||||||
ActivityIndicator,
|
|
||||||
Dimensions,
|
|
||||||
Image,
|
|
||||||
ScrollView,
|
|
||||||
Text,
|
|
||||||
TouchableOpacity,
|
|
||||||
View
|
|
||||||
} from 'react-native';
|
|
||||||
import { TabView, SceneMap } from 'react-native-tab-view';
|
import { TabView, SceneMap } from 'react-native-tab-view';
|
||||||
import { navigateBack } from 'utils/helper';
|
import { navigateBack } from 'utils/helper';
|
||||||
import Colors from 'styles/colors';
|
import Colors from 'styles/colors';
|
||||||
|
@ -25,7 +17,7 @@ class ChannelPage extends React.PureComponent {
|
||||||
state = {
|
state = {
|
||||||
page: 1,
|
page: 1,
|
||||||
showPageButtons: false,
|
showPageButtons: false,
|
||||||
activeTab: Constants.CONTENT_TAB
|
activeTab: Constants.CONTENT_TAB,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -43,7 +35,7 @@ class ChannelPage extends React.PureComponent {
|
||||||
fetchClaims(uri, this.state.page);
|
fetchClaims(uri, this.state.page);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleNextPage = () => {
|
handleNextPage = () => {
|
||||||
const { uri, fetchClaims, totalPages } = this.props;
|
const { uri, fetchClaims, totalPages } = this.props;
|
||||||
|
@ -52,7 +44,7 @@ class ChannelPage extends React.PureComponent {
|
||||||
fetchClaims(uri, this.state.page);
|
fetchClaims(uri, this.state.page);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
renderContent = () => {
|
renderContent = () => {
|
||||||
const { fetching, claimsInChannel, totalPages, navigation } = this.props;
|
const { fetching, claimsInChannel, totalPages, navigation } = this.props;
|
||||||
|
@ -68,13 +60,15 @@ class ChannelPage extends React.PureComponent {
|
||||||
} else {
|
} else {
|
||||||
contentList =
|
contentList =
|
||||||
claimsInChannel && claimsInChannel.length ? (
|
claimsInChannel && claimsInChannel.length ? (
|
||||||
<FileList sortByHeight
|
<FileList
|
||||||
hideFilter
|
sortByHeight
|
||||||
fileInfos={claimsInChannel}
|
hideFilter
|
||||||
navigation={navigation}
|
fileInfos={claimsInChannel}
|
||||||
style={channelPageStyle.fileList}
|
navigation={navigation}
|
||||||
contentContainerStyle={channelPageStyle.fileListContent}
|
style={channelPageStyle.fileList}
|
||||||
onEndReached={() => this.setState({ showPageButtons: true })} />
|
contentContainerStyle={channelPageStyle.fileListContent}
|
||||||
|
onEndReached={() => this.setState({ showPageButtons: true })}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<View style={channelPageStyle.busyContainer}>
|
<View style={channelPageStyle.busyContainer}>
|
||||||
<Text style={channelPageStyle.infoText}>No content found.</Text>
|
<Text style={channelPageStyle.infoText}>No content found.</Text>
|
||||||
|
@ -87,17 +81,23 @@ class ChannelPage extends React.PureComponent {
|
||||||
pageButtons = (
|
pageButtons = (
|
||||||
<View style={channelPageStyle.pageButtons}>
|
<View style={channelPageStyle.pageButtons}>
|
||||||
<View>
|
<View>
|
||||||
{(this.state.page > 1) && <Button
|
{this.state.page > 1 && (
|
||||||
style={channelPageStyle.button}
|
<Button
|
||||||
text={"Previous"}
|
style={channelPageStyle.button}
|
||||||
disabled={!!fetching}
|
text={'Previous'}
|
||||||
onPress={this.handlePreviousPage} />}
|
disabled={!!fetching}
|
||||||
|
onPress={this.handlePreviousPage}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
{(this.state.page < totalPages) && <Button
|
{this.state.page < totalPages && (
|
||||||
style={[channelPageStyle.button, channelPageStyle.nextButton]}
|
<Button
|
||||||
text={"Next"}
|
style={[channelPageStyle.button, channelPageStyle.nextButton]}
|
||||||
disabled={!!fetching}
|
text={'Next'}
|
||||||
onPress={this.handleNextPage} />}
|
disabled={!!fetching}
|
||||||
|
onPress={this.handleNextPage}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ class ChannelPage extends React.PureComponent {
|
||||||
{pageButtons}
|
{pageButtons}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
renderAbout = () => {
|
renderAbout = () => {
|
||||||
const { claim } = this.props;
|
const { claim } = this.props;
|
||||||
|
@ -126,45 +126,41 @@ class ChannelPage extends React.PureComponent {
|
||||||
const { cover, description, thumbnail, email, website_url, title } = claim.value;
|
const { cover, description, thumbnail, email, website_url, title } = claim.value;
|
||||||
return (
|
return (
|
||||||
<View style={channelPageStyle.aboutTab}>
|
<View style={channelPageStyle.aboutTab}>
|
||||||
{(!website_url && !email && !description) &&
|
{!website_url && !email && !description && (
|
||||||
<View style={channelPageStyle.busyContainer}>
|
<View style={channelPageStyle.busyContainer}>
|
||||||
<Text style={channelPageStyle.infoText}>Nothing here yet. Please check back later.</Text>
|
<Text style={channelPageStyle.infoText}>Nothing here yet. Please check back later.</Text>
|
||||||
</View>}
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
{(website_url || email || description) &&
|
{(website_url || email || description) && (
|
||||||
<ScrollView style={channelPageStyle.aboutScroll} contentContainerStyle={channelPageStyle.aboutScrollContent}>
|
<ScrollView style={channelPageStyle.aboutScroll} contentContainerStyle={channelPageStyle.aboutScrollContent}>
|
||||||
{(website_url && website_url.trim().length > 0) &&
|
{website_url && website_url.trim().length > 0 && (
|
||||||
<View style={channelPageStyle.aboutItem}>
|
<View style={channelPageStyle.aboutItem}>
|
||||||
<Text style={channelPageStyle.aboutTitle}>Website</Text>
|
<Text style={channelPageStyle.aboutTitle}>Website</Text>
|
||||||
<Link style={channelPageStyle.aboutText} text={website_url} href={website_url} />
|
<Link style={channelPageStyle.aboutText} text={website_url} href={website_url} />
|
||||||
</View>}
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
{(email && email.trim().length > 0) &&
|
{email && email.trim().length > 0 && (
|
||||||
<View style={channelPageStyle.aboutItem}>
|
<View style={channelPageStyle.aboutItem}>
|
||||||
<Text style={channelPageStyle.aboutTitle}>Email</Text>
|
<Text style={channelPageStyle.aboutTitle}>Email</Text>
|
||||||
<Link style={channelPageStyle.aboutText} text={email} href={`mailto:${email}`} />
|
<Link style={channelPageStyle.aboutText} text={email} href={`mailto:${email}`} />
|
||||||
</View>}
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
{(description && description.trim().length > 0) &&
|
{description && description.trim().length > 0 && (
|
||||||
<View style={channelPageStyle.aboutItem}>
|
<View style={channelPageStyle.aboutItem}>
|
||||||
<Text style={channelPageStyle.aboutText}>{description}</Text>
|
<Text style={channelPageStyle.aboutText}>{description}</Text>
|
||||||
</View>}
|
</View>
|
||||||
</ScrollView>}
|
)}
|
||||||
|
</ScrollView>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const { fetching, claimsInChannel, claim, navigation, totalPages, uri, drawerStack, popDrawerStack } = this.props;
|
||||||
fetching,
|
|
||||||
claimsInChannel,
|
|
||||||
claim,
|
|
||||||
navigation,
|
|
||||||
totalPages,
|
|
||||||
uri,
|
|
||||||
drawerStack,
|
|
||||||
popDrawerStack
|
|
||||||
} = this.props;
|
|
||||||
const { name, permanent_url: permanentUrl } = claim;
|
const { name, permanent_url: permanentUrl } = claim;
|
||||||
|
|
||||||
let thumbnailUrl, coverUrl, title;
|
let thumbnailUrl, coverUrl, title;
|
||||||
|
@ -187,28 +183,44 @@ class ChannelPage extends React.PureComponent {
|
||||||
<Image
|
<Image
|
||||||
style={channelPageStyle.coverImage}
|
style={channelPageStyle.coverImage}
|
||||||
resizeMode={'cover'}
|
resizeMode={'cover'}
|
||||||
source={(coverUrl && coverUrl.trim().length > 0) ? { uri: coverUrl } : require('../../assets/default_channel_cover.png')} />
|
source={
|
||||||
|
coverUrl && coverUrl.trim().length > 0
|
||||||
|
? { uri: coverUrl }
|
||||||
|
: require('../../assets/default_channel_cover.png')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<View style={channelPageStyle.channelHeader}>
|
<View style={channelPageStyle.channelHeader}>
|
||||||
<Text style={channelPageStyle.channelName}>{(title && title.trim().length > 0) ? title : name}</Text>
|
<Text style={channelPageStyle.channelName}>{title && title.trim().length > 0 ? title : name}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={channelPageStyle.avatarImageContainer}>
|
<View style={channelPageStyle.avatarImageContainer}>
|
||||||
<Image
|
<Image
|
||||||
style={channelPageStyle.avatarImage}
|
style={channelPageStyle.avatarImage}
|
||||||
resizeMode={'cover'}
|
resizeMode={'cover'}
|
||||||
source={(thumbnailUrl && thumbnailUrl.trim().length > 0) ? { uri: thumbnailUrl } : require('../../assets/default_avatar.jpg')} />
|
source={
|
||||||
|
thumbnailUrl && thumbnailUrl.trim().length > 0
|
||||||
|
? { uri: thumbnailUrl }
|
||||||
|
: require('../../assets/default_avatar.jpg')
|
||||||
|
}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<SubscribeButton style={channelPageStyle.subscribeButton} uri={uri} name={name} />
|
<SubscribeButton style={channelPageStyle.subscribeButton} uri={uri} name={name} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={channelPageStyle.tabBar}>
|
<View style={channelPageStyle.tabBar}>
|
||||||
<TouchableOpacity style={channelPageStyle.tab} onPress={() => this.setState({ activeTab: Constants.CONTENT_TAB })}>
|
<TouchableOpacity
|
||||||
|
style={channelPageStyle.tab}
|
||||||
|
onPress={() => this.setState({ activeTab: Constants.CONTENT_TAB })}
|
||||||
|
>
|
||||||
<Text style={channelPageStyle.tabTitle}>CONTENT</Text>
|
<Text style={channelPageStyle.tabTitle}>CONTENT</Text>
|
||||||
{Constants.CONTENT_TAB === this.state.activeTab && <View style={channelPageStyle.activeTabHint} />}
|
{Constants.CONTENT_TAB === this.state.activeTab && <View style={channelPageStyle.activeTabHint} />}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity style={channelPageStyle.tab} onPress={() => this.setState({ activeTab: Constants.ABOUT_TAB })}>
|
<TouchableOpacity
|
||||||
|
style={channelPageStyle.tab}
|
||||||
|
onPress={() => this.setState({ activeTab: Constants.ABOUT_TAB })}
|
||||||
|
>
|
||||||
<Text style={channelPageStyle.tabTitle}>ABOUT</Text>
|
<Text style={channelPageStyle.tabTitle}>ABOUT</Text>
|
||||||
{Constants.ABOUT_TAB === this.state.activeTab && <View style={channelPageStyle.activeTabHint} />}
|
{Constants.ABOUT_TAB === this.state.activeTab && <View style={channelPageStyle.activeTabHint} />}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
@ -218,7 +230,7 @@ class ChannelPage extends React.PureComponent {
|
||||||
{Constants.ABOUT_TAB === this.state.activeTab && this.renderAbout()}
|
{Constants.ABOUT_TAB === this.state.activeTab && this.renderAbout()}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import { doFileList, selectBalance, selectFileInfosDownloaded } from 'lbry-redux';
|
||||||
doFileList,
|
|
||||||
selectBalance,
|
|
||||||
selectFileInfosDownloaded,
|
|
||||||
} from 'lbry-redux';
|
|
||||||
import {
|
import {
|
||||||
doFetchFeaturedUris,
|
doFetchFeaturedUris,
|
||||||
doFetchRewardedContent,
|
doFetchRewardedContent,
|
||||||
|
@ -38,7 +34,10 @@ const perform = dispatch => ({
|
||||||
fetchSubscriptions: () => dispatch(doFetchMySubscriptions()),
|
fetchSubscriptions: () => dispatch(doFetchMySubscriptions()),
|
||||||
fileList: () => dispatch(doFileList()),
|
fileList: () => dispatch(doFileList()),
|
||||||
removeUnreadSubscriptions: () => dispatch(doRemoveUnreadSubscriptions()),
|
removeUnreadSubscriptions: () => dispatch(doRemoveUnreadSubscriptions()),
|
||||||
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value))
|
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(DiscoverPage);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(DiscoverPage);
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import NavigationActions from 'react-navigation';
|
import NavigationActions from 'react-navigation';
|
||||||
import {
|
import { Alert, ActivityIndicator, Linking, NativeModules, SectionList, Text, View } from 'react-native';
|
||||||
Alert,
|
|
||||||
ActivityIndicator,
|
|
||||||
Linking,
|
|
||||||
NativeModules,
|
|
||||||
SectionList,
|
|
||||||
Text,
|
|
||||||
View
|
|
||||||
} from 'react-native';
|
|
||||||
import { Lbry, normalizeURI, parseURI } from 'lbry-redux';
|
import { Lbry, normalizeURI, parseURI } from 'lbry-redux';
|
||||||
import AsyncStorage from '@react-native-community/async-storage';
|
import AsyncStorage from '@react-native-community/async-storage';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
@ -33,22 +25,18 @@ class DiscoverPage extends React.PureComponent {
|
||||||
const delta = now - start;
|
const delta = now - start;
|
||||||
AsyncStorage.getItem('firstLaunchSuspended').then(suspended => {
|
AsyncStorage.getItem('firstLaunchSuspended').then(suspended => {
|
||||||
AsyncStorage.removeItem('firstLaunchSuspended');
|
AsyncStorage.removeItem('firstLaunchSuspended');
|
||||||
const appSuspended = (suspended === 'true');
|
const appSuspended = suspended === 'true';
|
||||||
if (NativeModules.Firebase) {
|
if (NativeModules.Firebase) {
|
||||||
NativeModules.Firebase.track('first_run_time', {
|
NativeModules.Firebase.track('first_run_time', {
|
||||||
'total_seconds': delta, 'app_suspended': appSuspended
|
total_seconds: delta,
|
||||||
|
app_suspended: appSuspended,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const { fetchFeaturedUris, fetchRewardedContent, fetchSubscriptions, fileList } = this.props;
|
||||||
fetchFeaturedUris,
|
|
||||||
fetchRewardedContent,
|
|
||||||
fetchSubscriptions,
|
|
||||||
fileList
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
fetchFeaturedUris();
|
fetchFeaturedUris();
|
||||||
fetchRewardedContent();
|
fetchRewardedContent();
|
||||||
|
@ -73,14 +61,15 @@ class DiscoverPage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
};
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
const { unreadSubscriptions, enabledChannelNotifications } = this.props;
|
const { unreadSubscriptions, enabledChannelNotifications } = this.props;
|
||||||
|
|
||||||
const utility = NativeModules.UtilityModule;
|
const utility = NativeModules.UtilityModule;
|
||||||
if (utility) {
|
if (utility) {
|
||||||
const hasUnread = prevProps.unreadSubscriptions &&
|
const hasUnread =
|
||||||
|
prevProps.unreadSubscriptions &&
|
||||||
prevProps.unreadSubscriptions.length !== unreadSubscriptions.length &&
|
prevProps.unreadSubscriptions.length !== unreadSubscriptions.length &&
|
||||||
unreadSubscriptions.length > 0;
|
unreadSubscriptions.length > 0;
|
||||||
|
|
||||||
|
@ -98,10 +87,17 @@ class DiscoverPage extends React.PureComponent {
|
||||||
const source = sub.value.stream.source;
|
const source = sub.value.stream.source;
|
||||||
const metadata = sub.value.stream.metadata;
|
const metadata = sub.value.stream.metadata;
|
||||||
if (source) {
|
if (source) {
|
||||||
isPlayable = source.contentType && ['audio', 'video'].indexOf(source.contentType.substring(0, 5)) > -1;
|
isPlayable =
|
||||||
|
source.contentType && ['audio', 'video'].indexOf(source.contentType.substring(0, 5)) > -1;
|
||||||
}
|
}
|
||||||
if (metadata) {
|
if (metadata) {
|
||||||
utility.showNotificationForContent(uri, metadata.title, channelName, metadata.thumbnail, isPlayable);
|
utility.showNotificationForContent(
|
||||||
|
uri,
|
||||||
|
metadata.title,
|
||||||
|
channelName,
|
||||||
|
metadata.thumbnail,
|
||||||
|
isPlayable
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -122,17 +118,23 @@ class DiscoverPage extends React.PureComponent {
|
||||||
const lastShownTime = parseInt(lastShownParts[0], 10);
|
const lastShownTime = parseInt(lastShownParts[0], 10);
|
||||||
const lastShownCount = parseInt(lastShownParts[1], 10);
|
const lastShownCount = parseInt(lastShownParts[1], 10);
|
||||||
if (!isNaN(lastShownTime) && !isNaN(lastShownCount)) {
|
if (!isNaN(lastShownTime) && !isNaN(lastShownCount)) {
|
||||||
if (now > (lastShownTime + (Constants.RATING_REMINDER_INTERVAL * lastShownCount))) {
|
if (now > lastShownTime + Constants.RATING_REMINDER_INTERVAL * lastShownCount) {
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
'Enjoying LBRY?',
|
'Enjoying LBRY?',
|
||||||
'Are you enjoying your experience with the LBRY app? You can leave a review for us on the Play Store.',
|
'Are you enjoying your experience with the LBRY app? You can leave a review for us on the Play Store.',
|
||||||
[
|
[
|
||||||
{ text: 'Never ask again', onPress: () => setClientSetting(Constants.SETTING_RATING_REMINDER_DISABLED, 'true')},
|
{
|
||||||
{ text: 'Maybe later', onPress: () => this.updateRatingReminderShown(lastShownCount)},
|
text: 'Never ask again',
|
||||||
{ text: 'Rate app', onPress: () => {
|
onPress: () => setClientSetting(Constants.SETTING_RATING_REMINDER_DISABLED, 'true'),
|
||||||
setClientSetting(Constants.SETTING_RATING_REMINDER_DISABLED, 'true');
|
},
|
||||||
Linking.openURL(Constants.PLAY_STORE_URL);
|
{ text: 'Maybe later', onPress: () => this.updateRatingReminderShown(lastShownCount) },
|
||||||
}}
|
{
|
||||||
|
text: 'Rate app',
|
||||||
|
onPress: () => {
|
||||||
|
setClientSetting(Constants.SETTING_RATING_REMINDER_DISABLED, 'true');
|
||||||
|
Linking.openURL(Constants.PLAY_STORE_URL);
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
{ cancelable: false }
|
{ cancelable: false }
|
||||||
);
|
);
|
||||||
|
@ -144,13 +146,13 @@ class DiscoverPage extends React.PureComponent {
|
||||||
// first time, so set a value for the next interval multiplier
|
// first time, so set a value for the next interval multiplier
|
||||||
this.updateRatingReminderShown(0);
|
this.updateRatingReminderShown(0);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
updateRatingReminderShown = (lastShownCount) => {
|
updateRatingReminderShown = lastShownCount => {
|
||||||
const { setClientSetting } = this.props;
|
const { setClientSetting } = this.props;
|
||||||
const settingString = (moment().unix() + '|' + (lastShownCount + 1));
|
const settingString = moment().unix() + '|' + (lastShownCount + 1);
|
||||||
setClientSetting(Constants.SETTING_RATING_REMINDER_LAST_SHOWN, settingString);
|
setClientSetting(Constants.SETTING_RATING_REMINDER_LAST_SHOWN, settingString);
|
||||||
}
|
};
|
||||||
|
|
||||||
trimClaimIdFromCategory(category) {
|
trimClaimIdFromCategory(category) {
|
||||||
return category.split('#')[0];
|
return category.split('#')[0];
|
||||||
|
@ -170,27 +172,24 @@ class DiscoverPage extends React.PureComponent {
|
||||||
<Text style={discoverStyle.title}>Fetching content...</Text>
|
<Text style={discoverStyle.title}>Fetching content...</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{(!!hasContent) &&
|
{!!hasContent && (
|
||||||
(<SectionList
|
<SectionList
|
||||||
style={discoverStyle.scrollContainer}
|
style={discoverStyle.scrollContainer}
|
||||||
contentContainerStyle={discoverStyle.scrollPadding}
|
contentContainerStyle={discoverStyle.scrollPadding}
|
||||||
initialNumToRender={4}
|
initialNumToRender={4}
|
||||||
maxToRenderPerBatch={4}
|
maxToRenderPerBatch={4}
|
||||||
removeClippedSubviews={true}
|
removeClippedSubviews={true}
|
||||||
renderItem={ ({item, index, section}) => (
|
renderItem={({ item, index, section }) => (
|
||||||
<CategoryList
|
<CategoryList key={item} category={item} categoryMap={featuredUris} navigation={navigation} />
|
||||||
key={item}
|
|
||||||
category={item}
|
|
||||||
categoryMap={featuredUris}
|
|
||||||
navigation={navigation} />
|
|
||||||
)}
|
)}
|
||||||
renderSectionHeader={
|
renderSectionHeader={({ section: { title } }) => <Text style={discoverStyle.categoryName}>{title}</Text>}
|
||||||
({section: {title}}) => (<Text style={discoverStyle.categoryName}>{title}</Text>)
|
sections={Object.keys(featuredUris).map(category => ({
|
||||||
}
|
title: this.trimClaimIdFromCategory(category),
|
||||||
sections={Object.keys(featuredUris).map(category => ({ title: this.trimClaimIdFromCategory(category), data: [category] }))}
|
data: [category],
|
||||||
|
}))}
|
||||||
keyExtractor={(item, index) => item}
|
keyExtractor={(item, index) => item}
|
||||||
/>)
|
/>
|
||||||
}
|
)}
|
||||||
<FloatingWalletBalance navigation={navigation} />
|
<FloatingWalletBalance navigation={navigation} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { selectCurrentRoute } from 'redux/selectors/drawer';
|
||||||
import Constants from 'constants';
|
import Constants from 'constants';
|
||||||
import DownloadsPage from './view';
|
import DownloadsPage from './view';
|
||||||
|
|
||||||
const select = (state) => ({
|
const select = state => ({
|
||||||
claims: selectMyClaimsWithoutChannels(state),
|
claims: selectMyClaimsWithoutChannels(state),
|
||||||
currentRoute: selectCurrentRoute(state),
|
currentRoute: selectCurrentRoute(state),
|
||||||
fileInfos: selectFileInfosDownloaded(state),
|
fileInfos: selectFileInfosDownloaded(state),
|
||||||
|
@ -20,7 +20,10 @@ const select = (state) => ({
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
fileList: () => dispatch(doFileList()),
|
fileList: () => dispatch(doFileList()),
|
||||||
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_MY_LBRY)),
|
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_MY_LBRY)),
|
||||||
setPlayerVisible: () => dispatch(doSetPlayerVisible(false))
|
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(DownloadsPage);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(DownloadsPage);
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Lbry, buildURI } from 'lbry-redux';
|
import { Lbry, buildURI } from 'lbry-redux';
|
||||||
import {
|
import { ActivityIndicator, Button, FlatList, Text, TextInput, View, ScrollView } from 'react-native';
|
||||||
ActivityIndicator,
|
|
||||||
Button,
|
|
||||||
FlatList,
|
|
||||||
Text,
|
|
||||||
TextInput,
|
|
||||||
View,
|
|
||||||
ScrollView
|
|
||||||
} from 'react-native';
|
|
||||||
import { navigateToUri, uriFromFileInfo } from 'utils/helper';
|
import { navigateToUri, uriFromFileInfo } from 'utils/helper';
|
||||||
import Colors from 'styles/colors';
|
import Colors from 'styles/colors';
|
||||||
import Constants from 'constants';
|
import Constants from 'constants';
|
||||||
|
@ -22,7 +14,7 @@ import fileListStyle from 'styles/fileList';
|
||||||
|
|
||||||
class DownloadsPage extends React.PureComponent {
|
class DownloadsPage extends React.PureComponent {
|
||||||
static navigationOptions = {
|
static navigationOptions = {
|
||||||
title: 'Downloads'
|
title: 'Downloads',
|
||||||
};
|
};
|
||||||
|
|
||||||
didFocusListener;
|
didFocusListener;
|
||||||
|
@ -43,7 +35,7 @@ class DownloadsPage extends React.PureComponent {
|
||||||
pushDrawerStack();
|
pushDrawerStack();
|
||||||
setPlayerVisible();
|
setPlayerVisible();
|
||||||
fileList();
|
fileList();
|
||||||
}
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.onComponentFocused();
|
this.onComponentFocused();
|
||||||
|
@ -64,32 +56,37 @@ class DownloadsPage extends React.PureComponent {
|
||||||
return (
|
return (
|
||||||
<View style={downloadsStyle.container}>
|
<View style={downloadsStyle.container}>
|
||||||
<UriBar navigation={navigation} />
|
<UriBar navigation={navigation} />
|
||||||
{!fetching && !hasDownloads &&
|
{!fetching && !hasDownloads && (
|
||||||
<View style={downloadsStyle.busyContainer}>
|
<View style={downloadsStyle.busyContainer}>
|
||||||
<Text style={downloadsStyle.noDownloadsText}>You have not watched or downloaded any content from LBRY yet.</Text>
|
<Text style={downloadsStyle.noDownloadsText}>
|
||||||
</View>}
|
You have not watched or downloaded any content from LBRY yet.
|
||||||
{fetching && !hasDownloads &&
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{fetching && !hasDownloads && (
|
||||||
<View style={downloadsStyle.busyContainer}>
|
<View style={downloadsStyle.busyContainer}>
|
||||||
<ActivityIndicator size="large" color={Colors.LbryGreen} style={downloadsStyle.loading} />
|
<ActivityIndicator size="large" color={Colors.LbryGreen} style={downloadsStyle.loading} />
|
||||||
</View>}
|
</View>
|
||||||
{hasDownloads &&
|
)}
|
||||||
|
{hasDownloads && (
|
||||||
<View style={downloadsStyle.subContainer}>
|
<View style={downloadsStyle.subContainer}>
|
||||||
<StorageStatsCard fileInfos={fileInfos} />
|
<StorageStatsCard fileInfos={fileInfos} />
|
||||||
<FlatList
|
<FlatList
|
||||||
style={downloadsStyle.scrollContainer}
|
style={downloadsStyle.scrollContainer}
|
||||||
contentContainerStyle={downloadsStyle.scrollPadding}
|
contentContainerStyle={downloadsStyle.scrollPadding}
|
||||||
renderItem={ ({item}) => (
|
renderItem={({ item }) => (
|
||||||
<FileListItem
|
<FileListItem
|
||||||
style={fileListStyle.item}
|
style={fileListStyle.item}
|
||||||
uri={uriFromFileInfo(item)}
|
uri={uriFromFileInfo(item)}
|
||||||
navigation={navigation}
|
navigation={navigation}
|
||||||
onPress={() => navigateToUri(navigation, uriFromFileInfo(item), { autoplay: true })} />
|
onPress={() => navigateToUri(navigation, uriFromFileInfo(item), { autoplay: true })}
|
||||||
)
|
/>
|
||||||
}
|
)}
|
||||||
data={fileInfos}
|
data={fileInfos}
|
||||||
keyExtractor={(item, index) => item.outpoint}
|
keyExtractor={(item, index) => item.outpoint}
|
||||||
/>
|
/>
|
||||||
</View>}
|
</View>
|
||||||
|
)}
|
||||||
<FloatingWalletBalance navigation={navigation} />
|
<FloatingWalletBalance navigation={navigation} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -26,14 +26,14 @@ import {
|
||||||
doFetchCostInfoForUri,
|
doFetchCostInfoForUri,
|
||||||
makeSelectCostInfoForUri,
|
makeSelectCostInfoForUri,
|
||||||
selectRewardContentClaimIds,
|
selectRewardContentClaimIds,
|
||||||
selectBlackListedOutpoints
|
selectBlackListedOutpoints,
|
||||||
} from 'lbryinc';
|
} from 'lbryinc';
|
||||||
import {
|
import {
|
||||||
doStartDownload,
|
doStartDownload,
|
||||||
doUpdateDownload,
|
doUpdateDownload,
|
||||||
doCompleteDownload,
|
doCompleteDownload,
|
||||||
doDeleteFile,
|
doDeleteFile,
|
||||||
doStopDownloadingFile
|
doStopDownloadingFile,
|
||||||
} from 'redux/actions/file';
|
} from 'redux/actions/file';
|
||||||
import { doPopDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
|
import { doPopDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
|
||||||
import { selectDrawerStack } from 'redux/selectors/drawer';
|
import { selectDrawerStack } from 'redux/selectors/drawer';
|
||||||
|
@ -77,7 +77,8 @@ const perform = dispatch => ({
|
||||||
purchaseUri: (uri, costInfo, saveFile) => dispatch(doPurchaseUri(uri, costInfo, saveFile)),
|
purchaseUri: (uri, costInfo, saveFile) => dispatch(doPurchaseUri(uri, costInfo, saveFile)),
|
||||||
deletePurchasedUri: uri => dispatch(doDeletePurchasedUri(uri)),
|
deletePurchasedUri: uri => dispatch(doDeletePurchasedUri(uri)),
|
||||||
resolveUri: uri => dispatch(doResolveUri(uri)),
|
resolveUri: uri => dispatch(doResolveUri(uri)),
|
||||||
sendTip: (amount, claimId, uri, successCallback, errorCallback) => dispatch(doSendTip(amount, claimId, uri, successCallback, errorCallback)),
|
sendTip: (amount, claimId, uri, successCallback, errorCallback) =>
|
||||||
|
dispatch(doSendTip(amount, claimId, uri, successCallback, errorCallback)),
|
||||||
setPlayerVisible: () => dispatch(doSetPlayerVisible(true)),
|
setPlayerVisible: () => dispatch(doSetPlayerVisible(true)),
|
||||||
stopDownload: (uri, fileInfo) => dispatch(doStopDownloadingFile(uri, fileInfo)),
|
stopDownload: (uri, fileInfo) => dispatch(doStopDownloadingFile(uri, fileInfo)),
|
||||||
startDownload: (uri, outpoint, fileInfo) => dispatch(doStartDownload(uri, outpoint, fileInfo)),
|
startDownload: (uri, outpoint, fileInfo) => dispatch(doStartDownload(uri, outpoint, fileInfo)),
|
||||||
|
@ -85,4 +86,7 @@ const perform = dispatch => ({
|
||||||
completeDownload: (uri, outpoint, fileInfo) => dispatch(doCompleteDownload(uri, outpoint, fileInfo)),
|
completeDownload: (uri, outpoint, fileInfo) => dispatch(doCompleteDownload(uri, outpoint, fileInfo)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(FilePage);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(FilePage);
|
||||||
|
|
|
@ -15,7 +15,7 @@ import {
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
TouchableWithoutFeedback,
|
TouchableWithoutFeedback,
|
||||||
View,
|
View,
|
||||||
WebView
|
WebView,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { NavigationEvents } from 'react-navigation';
|
import { NavigationEvents } from 'react-navigation';
|
||||||
import { navigateBack, navigateToUri } from 'utils/helper';
|
import { navigateBack, navigateToUri } from 'utils/helper';
|
||||||
|
@ -43,7 +43,7 @@ import uriBarStyle from 'styles/uriBar';
|
||||||
|
|
||||||
class FilePage extends React.PureComponent {
|
class FilePage extends React.PureComponent {
|
||||||
static navigationOptions = {
|
static navigationOptions = {
|
||||||
title: ''
|
title: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
tipAmountInput = null;
|
tipAmountInput = null;
|
||||||
|
@ -79,7 +79,7 @@ class FilePage extends React.PureComponent {
|
||||||
uri: null,
|
uri: null,
|
||||||
uriVars: null,
|
uriVars: null,
|
||||||
stopDownloadConfirmed: false,
|
stopDownloadConfirmed: false,
|
||||||
streamingMode: false
|
streamingMode: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ class FilePage extends React.PureComponent {
|
||||||
if (NativeModules.UtilityModule) {
|
if (NativeModules.UtilityModule) {
|
||||||
NativeModules.UtilityModule.keepAwakeOn();
|
NativeModules.UtilityModule.keepAwakeOn();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.onComponentFocused();
|
this.onComponentFocused();
|
||||||
|
@ -126,7 +126,7 @@ class FilePage extends React.PureComponent {
|
||||||
purchasedUris: prevPurchasedUris,
|
purchasedUris: prevPurchasedUris,
|
||||||
navigation,
|
navigation,
|
||||||
contentType,
|
contentType,
|
||||||
notify
|
notify,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { uri } = navigation.state.params;
|
const { uri } = navigation.state.params;
|
||||||
const {
|
const {
|
||||||
|
@ -135,7 +135,7 @@ class FilePage extends React.PureComponent {
|
||||||
fileInfo,
|
fileInfo,
|
||||||
purchasedUris,
|
purchasedUris,
|
||||||
purchaseUriErrorMessage,
|
purchaseUriErrorMessage,
|
||||||
streamingUrl
|
streamingUrl,
|
||||||
} = nextProps;
|
} = nextProps;
|
||||||
|
|
||||||
if (Constants.ROUTE_FILE === currentRoute && currentRoute !== prevRoute) {
|
if (Constants.ROUTE_FILE === currentRoute && currentRoute !== prevRoute) {
|
||||||
|
@ -151,7 +151,10 @@ class FilePage extends React.PureComponent {
|
||||||
|
|
||||||
const mediaType = Lbry.getMediaType(contentType);
|
const mediaType = Lbry.getMediaType(contentType);
|
||||||
const isPlayable = mediaType === 'video' || mediaType === 'audio';
|
const isPlayable = mediaType === 'video' || mediaType === 'audio';
|
||||||
if ((this.state.fileGetStarted || prevPurchasedUris.length !== purchasedUris.length) && NativeModules.UtilityModule) {
|
if (
|
||||||
|
(this.state.fileGetStarted || prevPurchasedUris.length !== purchasedUris.length) &&
|
||||||
|
NativeModules.UtilityModule
|
||||||
|
) {
|
||||||
if (purchasedUris.includes(uri)) {
|
if (purchasedUris.includes(uri)) {
|
||||||
const { nout, txid } = claim;
|
const { nout, txid } = claim;
|
||||||
const outpoint = `${txid}:${nout}`;
|
const outpoint = `${txid}:${nout}`;
|
||||||
|
@ -187,7 +190,7 @@ class FilePage extends React.PureComponent {
|
||||||
window.currentMediaInfo = {
|
window.currentMediaInfo = {
|
||||||
channel: claim ? claim.channel_name : null,
|
channel: claim ? claim.channel_name : null,
|
||||||
title: metadata ? metadata.title : claim.name,
|
title: metadata ? metadata.title : claim.name,
|
||||||
uri: this.state.uri
|
uri: this.state.uri,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,7 +207,7 @@ class FilePage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleFullscreenToggle = (mode) => {
|
handleFullscreenToggle = mode => {
|
||||||
this.setState({ fullscreenMode: mode });
|
this.setState({ fullscreenMode: mode });
|
||||||
StatusBar.setHidden(mode);
|
StatusBar.setHidden(mode);
|
||||||
if (NativeModules.ScreenOrientation) {
|
if (NativeModules.ScreenOrientation) {
|
||||||
|
@ -224,7 +227,7 @@ class FilePage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
onDeletePressed = () => {
|
onDeletePressed = () => {
|
||||||
const { claim, deleteFile, deletePurchasedUri, fileInfo, navigation } = this.props;
|
const { claim, deleteFile, deletePurchasedUri, fileInfo, navigation } = this.props;
|
||||||
|
@ -234,24 +237,27 @@ class FilePage extends React.PureComponent {
|
||||||
'Are you sure you want to remove this file from your device?',
|
'Are you sure you want to remove this file from your device?',
|
||||||
[
|
[
|
||||||
{ text: 'No' },
|
{ text: 'No' },
|
||||||
{ text: 'Yes', onPress: () => {
|
{
|
||||||
const { uri } = navigation.state.params;
|
text: 'Yes',
|
||||||
deleteFile(`${claim.txid}:${claim.nout}`, true);
|
onPress: () => {
|
||||||
deletePurchasedUri(uri);
|
const { uri } = navigation.state.params;
|
||||||
if (NativeModules.UtilityModule) {
|
deleteFile(`${claim.txid}:${claim.nout}`, true);
|
||||||
NativeModules.UtilityModule.deleteDownload(uri);
|
deletePurchasedUri(uri);
|
||||||
}
|
if (NativeModules.UtilityModule) {
|
||||||
this.setState({
|
NativeModules.UtilityModule.deleteDownload(uri);
|
||||||
downloadPressed: false,
|
}
|
||||||
fileViewLogged: false,
|
this.setState({
|
||||||
mediaLoaded: false,
|
downloadPressed: false,
|
||||||
stopDownloadConfirmed: false
|
fileViewLogged: false,
|
||||||
});
|
mediaLoaded: false,
|
||||||
}}
|
stopDownloadConfirmed: false,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
{ cancelable: true }
|
{ cancelable: true }
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
onStopDownloadPressed = () => {
|
onStopDownloadPressed = () => {
|
||||||
const { deletePurchasedUri, fileInfo, navigation, notify, stopDownload } = this.props;
|
const { deletePurchasedUri, fileInfo, navigation, notify, stopDownload } = this.props;
|
||||||
|
@ -261,30 +267,33 @@ class FilePage extends React.PureComponent {
|
||||||
'Are you sure you want to stop downloading this file?',
|
'Are you sure you want to stop downloading this file?',
|
||||||
[
|
[
|
||||||
{ text: 'No' },
|
{ text: 'No' },
|
||||||
{ text: 'Yes', onPress: () => {
|
{
|
||||||
const { uri } = navigation.state.params;
|
text: 'Yes',
|
||||||
stopDownload(uri, fileInfo);
|
onPress: () => {
|
||||||
deletePurchasedUri(uri);
|
const { uri } = navigation.state.params;
|
||||||
if (NativeModules.UtilityModule) {
|
stopDownload(uri, fileInfo);
|
||||||
NativeModules.UtilityModule.deleteDownload(uri);
|
deletePurchasedUri(uri);
|
||||||
}
|
if (NativeModules.UtilityModule) {
|
||||||
this.setState({
|
NativeModules.UtilityModule.deleteDownload(uri);
|
||||||
downloadPressed: false,
|
}
|
||||||
fileViewLogged: false,
|
this.setState({
|
||||||
mediaLoaded: false,
|
downloadPressed: false,
|
||||||
stopDownloadConfirmed: true
|
fileViewLogged: false,
|
||||||
});
|
mediaLoaded: false,
|
||||||
|
stopDownloadConfirmed: true,
|
||||||
|
});
|
||||||
|
|
||||||
// there can be a bit of lag between the user pressing Yes and the UI being updated
|
// there can be a bit of lag between the user pressing Yes and the UI being updated
|
||||||
// after the file_set_status and file_delete operations, so let the user know
|
// after the file_set_status and file_delete operations, so let the user know
|
||||||
notify({
|
notify({
|
||||||
message: 'The download will stop momentarily. You do not need to wait to discover something else.',
|
message: 'The download will stop momentarily. You do not need to wait to discover something else.',
|
||||||
});
|
});
|
||||||
}}
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
{ cancelable: true }
|
{ cancelable: true }
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
StatusBar.setHidden(false);
|
StatusBar.setHidden(false);
|
||||||
|
@ -309,32 +318,32 @@ class FilePage extends React.PureComponent {
|
||||||
DeviceEventEmitter.removeListener('onDownloadCompleted', this.handleDownloadCompleted);
|
DeviceEventEmitter.removeListener('onDownloadCompleted', this.handleDownloadCompleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDownloadStarted = (evt) => {
|
handleDownloadStarted = evt => {
|
||||||
const { startDownload } = this.props;
|
const { startDownload } = this.props;
|
||||||
const { uri, outpoint, fileInfo } = evt;
|
const { uri, outpoint, fileInfo } = evt;
|
||||||
startDownload(uri, outpoint, fileInfo);
|
startDownload(uri, outpoint, fileInfo);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleDownloadUpdated = (evt) => {
|
handleDownloadUpdated = evt => {
|
||||||
const { updateDownload } = this.props;
|
const { updateDownload } = this.props;
|
||||||
const { uri, outpoint, fileInfo, progress } = evt;
|
const { uri, outpoint, fileInfo, progress } = evt;
|
||||||
updateDownload(uri, outpoint, fileInfo, progress);
|
updateDownload(uri, outpoint, fileInfo, progress);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleDownloadCompleted = (evt) => {
|
handleDownloadCompleted = evt => {
|
||||||
const { completeDownload } = this.props;
|
const { completeDownload } = this.props;
|
||||||
const { uri, outpoint, fileInfo } = evt;
|
const { uri, outpoint, fileInfo } = evt;
|
||||||
completeDownload(uri, outpoint, fileInfo);
|
completeDownload(uri, outpoint, fileInfo);
|
||||||
}
|
};
|
||||||
|
|
||||||
localUriForFileInfo = (fileInfo) => {
|
localUriForFileInfo = fileInfo => {
|
||||||
if (!fileInfo) {
|
if (!fileInfo) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return 'file:///' + fileInfo.download_path;
|
return 'file:///' + fileInfo.download_path;
|
||||||
}
|
};
|
||||||
|
|
||||||
playerUriForFileInfo = (fileInfo) => {
|
playerUriForFileInfo = fileInfo => {
|
||||||
const { streamingUrl } = this.props;
|
const { streamingUrl } = this.props;
|
||||||
if (fileInfo && fileInfo.download_path) {
|
if (fileInfo && fileInfo.download_path) {
|
||||||
return this.getEncodedDownloadPath(fileInfo);
|
return this.getEncodedDownloadPath(fileInfo);
|
||||||
|
@ -347,9 +356,9 @@ class FilePage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
};
|
||||||
|
|
||||||
getEncodedDownloadPath = (fileInfo) => {
|
getEncodedDownloadPath = fileInfo => {
|
||||||
if (this.state.encodedFilePath) {
|
if (this.state.encodedFilePath) {
|
||||||
return this.state.encodedFilePath;
|
return this.state.encodedFilePath;
|
||||||
}
|
}
|
||||||
|
@ -358,36 +367,41 @@ class FilePage extends React.PureComponent {
|
||||||
const encodedFileName = encodeURIComponent(fileName).replace(/!/g, '%21');
|
const encodedFileName = encodeURIComponent(fileName).replace(/!/g, '%21');
|
||||||
const encodedFilePath = fileInfo.download_path.replace(fileName, encodedFileName);
|
const encodedFilePath = fileInfo.download_path.replace(fileName, encodedFileName);
|
||||||
return encodedFilePath;
|
return encodedFilePath;
|
||||||
}
|
};
|
||||||
|
|
||||||
linkify = (text) => {
|
linkify = text => {
|
||||||
let linkifiedContent = [];
|
let linkifiedContent = [];
|
||||||
let lines = text.split(/\n/g);
|
let lines = text.split(/\n/g);
|
||||||
linkifiedContent = lines.map((line, i) => {
|
linkifiedContent = lines.map((line, i) => {
|
||||||
let tokens = line.split(/\s/g);
|
let tokens = line.split(/\s/g);
|
||||||
let lineContent = tokens.length === 0 ? '' : tokens.map((token, j) => {
|
let lineContent =
|
||||||
let hasSpace = j !== (tokens.length - 1);
|
tokens.length === 0
|
||||||
let space = hasSpace ? ' ' : '';
|
? ''
|
||||||
|
: tokens.map((token, j) => {
|
||||||
|
let hasSpace = j !== tokens.length - 1;
|
||||||
|
let space = hasSpace ? ' ' : '';
|
||||||
|
|
||||||
if (token.match(/^(lbry|https?):\/\//g)) {
|
if (token.match(/^(lbry|https?):\/\//g)) {
|
||||||
return (
|
return (
|
||||||
<Link key={j}
|
<Link
|
||||||
style={filePageStyle.link}
|
key={j}
|
||||||
href={token}
|
style={filePageStyle.link}
|
||||||
text={token}
|
href={token}
|
||||||
effectOnTap={filePageStyle.linkTapped} />
|
text={token}
|
||||||
);
|
effectOnTap={filePageStyle.linkTapped}
|
||||||
} else {
|
/>
|
||||||
return token + space;
|
);
|
||||||
}
|
} else {
|
||||||
});
|
return token + space;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
lineContent.push("\n");
|
lineContent.push('\n');
|
||||||
return (<Text key={i}>{lineContent}</Text>);
|
return <Text key={i}>{lineContent}</Text>;
|
||||||
});
|
});
|
||||||
|
|
||||||
return linkifiedContent;
|
return linkifiedContent;
|
||||||
}
|
};
|
||||||
|
|
||||||
checkOrientation = () => {
|
checkOrientation = () => {
|
||||||
if (this.state.fullscreenMode) {
|
if (this.state.fullscreenMode) {
|
||||||
|
@ -405,16 +419,18 @@ class FilePage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLandscape) {
|
if (isLandscape) {
|
||||||
this.playerBackground.setNativeProps({ height: screenHeight - StyleSheet.flatten(uriBarStyle.uriContainer).height });
|
this.playerBackground.setNativeProps({
|
||||||
|
height: screenHeight - StyleSheet.flatten(uriBarStyle.uriContainer).height,
|
||||||
|
});
|
||||||
} else if (this.state.playerBgHeight > 0) {
|
} else if (this.state.playerBgHeight > 0) {
|
||||||
this.playerBackground.setNativeProps({ height: this.state.playerBgHeight });
|
this.playerBackground.setNativeProps({ height: this.state.playerBgHeight });
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
onMediaLoaded = (channelName, title, uri) => {
|
onMediaLoaded = (channelName, title, uri) => {
|
||||||
this.setState({ mediaLoaded: true });
|
this.setState({ mediaLoaded: true });
|
||||||
window.currentMediaInfo = { channel: channelName, title, uri };
|
window.currentMediaInfo = { channel: channelName, title, uri };
|
||||||
}
|
};
|
||||||
|
|
||||||
onPlaybackStarted = () => {
|
onPlaybackStarted = () => {
|
||||||
let timeToStartMillis, timeToStart;
|
let timeToStartMillis, timeToStart;
|
||||||
|
@ -428,25 +444,25 @@ class FilePage extends React.PureComponent {
|
||||||
const { uri } = navigation.state.params;
|
const { uri } = navigation.state.params;
|
||||||
this.logFileView(uri, claim, timeToStartMillis);
|
this.logFileView(uri, claim, timeToStartMillis);
|
||||||
|
|
||||||
let payload = { 'uri': uri };
|
let payload = { uri: uri };
|
||||||
if (!isNaN(timeToStart)) {
|
if (!isNaN(timeToStart)) {
|
||||||
payload['time_to_start_seconds'] = timeToStart;
|
payload['time_to_start_seconds'] = timeToStart;
|
||||||
payload['time_to_start_ms'] = timeToStartMillis;
|
payload['time_to_start_ms'] = timeToStartMillis;
|
||||||
}
|
}
|
||||||
NativeModules.Firebase.track('play', payload);
|
NativeModules.Firebase.track('play', payload);
|
||||||
}
|
};
|
||||||
|
|
||||||
onPlaybackFinished = () => {
|
onPlaybackFinished = () => {
|
||||||
if (this.scrollView && this.state.relatedContentY) {
|
if (this.scrollView && this.state.relatedContentY) {
|
||||||
this.scrollView.scrollTo({ x: 0, y: this.state.relatedContentY, animated: true});
|
this.scrollView.scrollTo({ x: 0, y: this.state.relatedContentY, animated: true });
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
setRelatedContentPosition = (evt) => {
|
setRelatedContentPosition = evt => {
|
||||||
if (!this.state.relatedContentY) {
|
if (!this.state.relatedContentY) {
|
||||||
this.setState({ relatedContentY: evt.nativeEvent.layout.y });
|
this.setState({ relatedContentY: evt.nativeEvent.layout.y });
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
logFileView = (uri, claim, timeToStart) => {
|
logFileView = (uri, claim, timeToStart) => {
|
||||||
if (!claim) {
|
if (!claim) {
|
||||||
|
@ -458,7 +474,7 @@ class FilePage extends React.PureComponent {
|
||||||
const params = {
|
const params = {
|
||||||
uri,
|
uri,
|
||||||
outpoint,
|
outpoint,
|
||||||
claim_id: claimId
|
claim_id: claimId,
|
||||||
};
|
};
|
||||||
if (!isNaN(timeToStart)) {
|
if (!isNaN(timeToStart)) {
|
||||||
params.time_to_start = timeToStart;
|
params.time_to_start = timeToStart;
|
||||||
|
@ -466,7 +482,7 @@ class FilePage extends React.PureComponent {
|
||||||
|
|
||||||
Lbryio.call('file', 'view', params).catch(() => {});
|
Lbryio.call('file', 'view', params).catch(() => {});
|
||||||
this.setState({ fileViewLogged: true });
|
this.setState({ fileViewLogged: true });
|
||||||
}
|
};
|
||||||
|
|
||||||
handleSendTip = () => {
|
handleSendTip = () => {
|
||||||
const { claim, balance, navigation, notify, sendTip } = this.props;
|
const { claim, balance, navigation, notify, sendTip } = this.props;
|
||||||
|
@ -480,26 +496,30 @@ class FilePage extends React.PureComponent {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendTip(tipAmount, claim.claim_id, uri, () => { this.setState({ tipAmount: 0, showTipView: false }) });
|
sendTip(tipAmount, claim.claim_id, uri, () => {
|
||||||
}
|
this.setState({ tipAmount: 0, showTipView: false });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
renderTags = (tags) => {
|
renderTags = tags => {
|
||||||
return tags.map((tag, i) => (
|
return tags.map((tag, i) => (
|
||||||
<Text style={filePageStyle.tagItem} key={`${tag}-${i}`}>{tag}</Text>
|
<Text style={filePageStyle.tagItem} key={`${tag}-${i}`}>
|
||||||
|
{tag}
|
||||||
|
</Text>
|
||||||
));
|
));
|
||||||
}
|
};
|
||||||
|
|
||||||
onFileDownloadButtonPlayed = () => {
|
onFileDownloadButtonPlayed = () => {
|
||||||
const { setPlayerVisible } = this.props;
|
const { setPlayerVisible } = this.props;
|
||||||
this.startTime = Date.now();
|
this.startTime = Date.now();
|
||||||
this.setState({ downloadPressed: true, autoPlayMedia: true, stopDownloadConfirmed: false });
|
this.setState({ downloadPressed: true, autoPlayMedia: true, stopDownloadConfirmed: false });
|
||||||
setPlayerVisible();
|
setPlayerVisible();
|
||||||
}
|
};
|
||||||
|
|
||||||
onBackButtonPressed = () => {
|
onBackButtonPressed = () => {
|
||||||
const { navigation, drawerStack, popDrawerStack } = this.props;
|
const { navigation, drawerStack, popDrawerStack } = this.props;
|
||||||
navigateBack(navigation, drawerStack, popDrawerStack);
|
navigateBack(navigation, drawerStack, popDrawerStack);
|
||||||
}
|
};
|
||||||
|
|
||||||
onSaveFilePressed = () => {
|
onSaveFilePressed = () => {
|
||||||
const { costInfo, fileGet, fileInfo, navigation, purchasedUris, purchaseUri } = this.props;
|
const { costInfo, fileGet, fileInfo, navigation, purchasedUris, purchaseUri } = this.props;
|
||||||
|
@ -509,13 +529,16 @@ class FilePage extends React.PureComponent {
|
||||||
// file already in library or URI already purchased, use fileGet directly
|
// file already in library or URI already purchased, use fileGet directly
|
||||||
this.setState({ fileGetStarted: true }, () => fileGet(uri, true));
|
this.setState({ fileGetStarted: true }, () => fileGet(uri, true));
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState(
|
||||||
downloadPressed: true,
|
{
|
||||||
autoPlayMedia: false,
|
downloadPressed: true,
|
||||||
stopDownloadConfirmed: false
|
autoPlayMedia: false,
|
||||||
}, () => purchaseUri(uri, costInfo, true));
|
stopDownloadConfirmed: false,
|
||||||
|
},
|
||||||
|
() => purchaseUri(uri, costInfo, true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
|
@ -534,7 +557,7 @@ class FilePage extends React.PureComponent {
|
||||||
position,
|
position,
|
||||||
purchaseUri,
|
purchaseUri,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
title
|
title,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { uri, autoplay } = navigation.state.params;
|
const { uri, autoplay } = navigation.state.params;
|
||||||
|
|
||||||
|
@ -542,23 +565,22 @@ class FilePage extends React.PureComponent {
|
||||||
if ((isResolvingUri && !claim) || !claim) {
|
if ((isResolvingUri && !claim) || !claim) {
|
||||||
innerContent = (
|
innerContent = (
|
||||||
<View style={filePageStyle.container}>
|
<View style={filePageStyle.container}>
|
||||||
{isResolvingUri &&
|
{isResolvingUri && (
|
||||||
<View style={filePageStyle.busyContainer}>
|
<View style={filePageStyle.busyContainer}>
|
||||||
<ActivityIndicator size="large" color={Colors.LbryGreen} />
|
<ActivityIndicator size="large" color={Colors.LbryGreen} />
|
||||||
<Text style={filePageStyle.infoText}>Loading decentralized data...</Text>
|
<Text style={filePageStyle.infoText}>Loading decentralized data...</Text>
|
||||||
</View>}
|
</View>
|
||||||
{claim === null && !isResolvingUri &&
|
)}
|
||||||
|
{claim === null && !isResolvingUri && (
|
||||||
<View style={filePageStyle.container}>
|
<View style={filePageStyle.container}>
|
||||||
<Text style={filePageStyle.emptyClaimText}>There's nothing at this location.</Text>
|
<Text style={filePageStyle.emptyClaimText}>There's nothing at this location.</Text>
|
||||||
</View>
|
</View>
|
||||||
}
|
)}
|
||||||
<UriBar value={uri} navigation={navigation} />
|
<UriBar value={uri} navigation={navigation} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
} else if (claim && claim.name.length && claim.name[0] === '@') {
|
} else if (claim && claim.name.length && claim.name[0] === '@') {
|
||||||
innerContent = (
|
innerContent = <ChannelPage uri={uri} navigation={navigation} />;
|
||||||
<ChannelPage uri={uri} navigation={navigation} />
|
|
||||||
);
|
|
||||||
} else if (claim) {
|
} else if (claim) {
|
||||||
let isClaimBlackListed = false;
|
let isClaimBlackListed = false;
|
||||||
|
|
||||||
|
@ -577,7 +599,8 @@ class FilePage extends React.PureComponent {
|
||||||
<View style={filePageStyle.pageContainer}>
|
<View style={filePageStyle.pageContainer}>
|
||||||
<View style={filePageStyle.dmcaContainer}>
|
<View style={filePageStyle.dmcaContainer}>
|
||||||
<Text style={filePageStyle.dmcaText}>
|
<Text style={filePageStyle.dmcaText}>
|
||||||
In response to a complaint we received under the US Digital Millennium Copyright Act, we have blocked access to this content from our applications.
|
In response to a complaint we received under the US Digital Millennium Copyright Act, we have blocked
|
||||||
|
access to this content from our applications.
|
||||||
</Text>
|
</Text>
|
||||||
<Link style={filePageStyle.dmcaLink} href="https://lbry.com/faq/dmca" text="Read More" />
|
<Link style={filePageStyle.dmcaLink} href="https://lbry.com/faq/dmca" text="Read More" />
|
||||||
</View>
|
</View>
|
||||||
|
@ -585,7 +608,6 @@ class FilePage extends React.PureComponent {
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
let tags = [];
|
let tags = [];
|
||||||
if (claim && claim.value && claim.value.tags) {
|
if (claim && claim.value && claim.value.tags) {
|
||||||
tags = claim.value.tags;
|
tags = claim.value.tags;
|
||||||
|
@ -597,27 +619,38 @@ class FilePage extends React.PureComponent {
|
||||||
const mediaType = Lbry.getMediaType(contentType);
|
const mediaType = Lbry.getMediaType(contentType);
|
||||||
const isPlayable = mediaType === 'video' || mediaType === 'audio';
|
const isPlayable = mediaType === 'video' || mediaType === 'audio';
|
||||||
const { height, channel_name: channelName, value } = claim;
|
const { height, channel_name: channelName, value } = claim;
|
||||||
const showActions = (fileInfo && fileInfo.download_path) &&
|
const showActions =
|
||||||
|
fileInfo &&
|
||||||
|
fileInfo.download_path &&
|
||||||
!this.state.fullscreenMode &&
|
!this.state.fullscreenMode &&
|
||||||
!this.state.showImageViewer &&
|
!this.state.showImageViewer &&
|
||||||
!this.state.showWebView;
|
!this.state.showWebView;
|
||||||
const showFileActions = (fileInfo && fileInfo.download_path) &&
|
const showFileActions =
|
||||||
|
fileInfo &&
|
||||||
|
fileInfo.download_path &&
|
||||||
(completed || (fileInfo && !fileInfo.stopped && fileInfo.written_bytes < fileInfo.total_bytes));
|
(completed || (fileInfo && !fileInfo.stopped && fileInfo.written_bytes < fileInfo.total_bytes));
|
||||||
const channelClaimId = claim && claim.signing_channel && claim.signing_channel.claim_id;
|
const channelClaimId = claim && claim.signing_channel && claim.signing_channel.claim_id;
|
||||||
const canSendTip = this.state.tipAmount > 0;
|
const canSendTip = this.state.tipAmount > 0;
|
||||||
const fullChannelUri = channelClaimId && channelClaimId.trim().length > 0 ? `${channelName}#${channelClaimId}` : channelName;
|
const fullChannelUri =
|
||||||
|
channelClaimId && channelClaimId.trim().length > 0 ? `${channelName}#${channelClaimId}` : channelName;
|
||||||
|
|
||||||
const playerStyle = [filePageStyle.player,
|
const playerStyle = [
|
||||||
this.state.isLandscape ? filePageStyle.containedPlayerLandscape :
|
filePageStyle.player,
|
||||||
(this.state.fullscreenMode ? filePageStyle.fullscreenPlayer : filePageStyle.containedPlayer)];
|
this.state.isLandscape
|
||||||
|
? filePageStyle.containedPlayerLandscape
|
||||||
|
: this.state.fullscreenMode
|
||||||
|
? filePageStyle.fullscreenPlayer
|
||||||
|
: filePageStyle.containedPlayer,
|
||||||
|
];
|
||||||
const playerBgStyle = [filePageStyle.playerBackground, filePageStyle.containedPlayerBackground];
|
const playerBgStyle = [filePageStyle.playerBackground, filePageStyle.containedPlayerBackground];
|
||||||
const fsPlayerBgStyle = [filePageStyle.playerBackground, filePageStyle.fullscreenPlayerBackground];
|
const fsPlayerBgStyle = [filePageStyle.playerBackground, filePageStyle.fullscreenPlayerBackground];
|
||||||
// at least 2MB (or the full download) before media can be loaded
|
// at least 2MB (or the full download) before media can be loaded
|
||||||
const canLoadMedia = (this.state.streamingMode) || (fileInfo &&
|
const canLoadMedia =
|
||||||
(fileInfo.written_bytes >= 2097152 || fileInfo.written_bytes == fileInfo.total_bytes)); // 2MB = 1024*1024*2
|
this.state.streamingMode ||
|
||||||
const isViewable = (mediaType === 'image' || mediaType === 'text');
|
(fileInfo && (fileInfo.written_bytes >= 2097152 || fileInfo.written_bytes == fileInfo.total_bytes)); // 2MB = 1024*1024*2
|
||||||
|
const isViewable = mediaType === 'image' || mediaType === 'text';
|
||||||
const isWebViewable = mediaType === 'text';
|
const isWebViewable = mediaType === 'text';
|
||||||
const canOpen = isViewable && completed;
|
const canOpen = isViewable && completed;
|
||||||
const localFileUri = this.localUriForFileInfo(fileInfo);
|
const localFileUri = this.localUriForFileInfo(fileInfo);
|
||||||
|
|
||||||
const openFile = () => {
|
const openFile = () => {
|
||||||
|
@ -625,10 +658,12 @@ class FilePage extends React.PureComponent {
|
||||||
// use image viewer
|
// use image viewer
|
||||||
if (!this.state.showImageViewer) {
|
if (!this.state.showImageViewer) {
|
||||||
this.setState({
|
this.setState({
|
||||||
imageUrls: [{
|
imageUrls: [
|
||||||
url: localFileUri
|
{
|
||||||
}],
|
url: localFileUri,
|
||||||
showImageViewer: true
|
},
|
||||||
|
],
|
||||||
|
showImageViewer: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -636,13 +671,18 @@ class FilePage extends React.PureComponent {
|
||||||
// show webview
|
// show webview
|
||||||
if (!this.state.showWebView) {
|
if (!this.state.showWebView) {
|
||||||
this.setState({
|
this.setState({
|
||||||
showWebView: true
|
showWebView: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
if (fileInfo && !this.state.autoDownloadStarted && this.state.uriVars && 'true' === this.state.uriVars.download) {
|
if (
|
||||||
|
fileInfo &&
|
||||||
|
!this.state.autoDownloadStarted &&
|
||||||
|
this.state.uriVars &&
|
||||||
|
'true' === this.state.uriVars.download
|
||||||
|
) {
|
||||||
this.setState({ autoDownloadStarted: true }, () => {
|
this.setState({ autoDownloadStarted: true }, () => {
|
||||||
purchaseUri(uri, costInfo, !isPlayable);
|
purchaseUri(uri, costInfo, !isPlayable);
|
||||||
if (NativeModules.UtilityModule) {
|
if (NativeModules.UtilityModule) {
|
||||||
|
@ -659,57 +699,86 @@ class FilePage extends React.PureComponent {
|
||||||
innerContent = (
|
innerContent = (
|
||||||
<View style={filePageStyle.pageContainer}>
|
<View style={filePageStyle.pageContainer}>
|
||||||
{!this.state.fullscreenMode && <UriBar value={uri} navigation={navigation} />}
|
{!this.state.fullscreenMode && <UriBar value={uri} navigation={navigation} />}
|
||||||
{this.state.showWebView && isWebViewable && <WebView source={{ uri: localFileUri }}
|
{this.state.showWebView && isWebViewable && (
|
||||||
style={filePageStyle.viewer} />}
|
<WebView source={{ uri: localFileUri }} style={filePageStyle.viewer} />
|
||||||
|
)}
|
||||||
|
|
||||||
{this.state.showImageViewer && <ImageViewer style={StyleSheet.flatten(filePageStyle.viewer)}
|
{this.state.showImageViewer && (
|
||||||
imageUrls={this.state.imageUrls}
|
<ImageViewer
|
||||||
renderIndicator={() => null} />}
|
style={StyleSheet.flatten(filePageStyle.viewer)}
|
||||||
|
imageUrls={this.state.imageUrls}
|
||||||
|
renderIndicator={() => null}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{!this.state.showWebView && (
|
{!this.state.showWebView && (
|
||||||
<View style={this.state.fullscreenMode ? filePageStyle.innerPageContainerFsMode : filePageStyle.innerPageContainer}
|
<View
|
||||||
onLayout={this.checkOrientation}>
|
style={
|
||||||
|
this.state.fullscreenMode ? filePageStyle.innerPageContainerFsMode : filePageStyle.innerPageContainer
|
||||||
|
}
|
||||||
|
onLayout={this.checkOrientation}
|
||||||
|
>
|
||||||
<View style={filePageStyle.mediaContainer}>
|
<View style={filePageStyle.mediaContainer}>
|
||||||
{((canOpen || (!fileInfo || (isPlayable && !canLoadMedia))) || (!canOpen && fileInfo)) &&
|
{(canOpen || (!fileInfo || (isPlayable && !canLoadMedia)) || (!canOpen && fileInfo)) && (
|
||||||
<FileItemMedia style={filePageStyle.thumbnail} title={title} thumbnail={thumbnail} />}
|
<FileItemMedia style={filePageStyle.thumbnail} title={title} thumbnail={thumbnail} />
|
||||||
{((!this.state.downloadButtonShown || this.state.downloadPressed) && !this.state.mediaLoaded) &&
|
)}
|
||||||
<ActivityIndicator size="large" color={Colors.LbryGreen} style={filePageStyle.loading} />}
|
{(!this.state.downloadButtonShown || this.state.downloadPressed) && !this.state.mediaLoaded && (
|
||||||
{((isPlayable && !completed && !canLoadMedia) || canOpen || (!completed && !this.state.streamingMode)) &&
|
<ActivityIndicator size="large" color={Colors.LbryGreen} style={filePageStyle.loading} />
|
||||||
(!this.state.downloadPressed) &&
|
)}
|
||||||
<FileDownloadButton uri={uri}
|
{((isPlayable && !completed && !canLoadMedia) ||
|
||||||
style={filePageStyle.downloadButton}
|
canOpen ||
|
||||||
openFile={openFile}
|
(!completed && !this.state.streamingMode)) &&
|
||||||
isPlayable={isPlayable}
|
!this.state.downloadPressed && (
|
||||||
isViewable={isViewable}
|
<FileDownloadButton
|
||||||
onPlay={this.onFileDownloadButtonPlayed}
|
uri={uri}
|
||||||
onView={() => this.setState({ downloadPressed: true })}
|
style={filePageStyle.downloadButton}
|
||||||
onButtonLayout={() => this.setState({ downloadButtonShown: true })} />}
|
openFile={openFile}
|
||||||
{!fileInfo && <FilePrice uri={uri} style={filePageStyle.filePriceContainer} textStyle={filePageStyle.filePriceText} />}
|
isPlayable={isPlayable}
|
||||||
|
isViewable={isViewable}
|
||||||
|
onPlay={this.onFileDownloadButtonPlayed}
|
||||||
|
onView={() => this.setState({ downloadPressed: true })}
|
||||||
|
onButtonLayout={() => this.setState({ downloadButtonShown: true })}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{!fileInfo && (
|
||||||
|
<FilePrice
|
||||||
|
uri={uri}
|
||||||
|
style={filePageStyle.filePriceContainer}
|
||||||
|
textStyle={filePageStyle.filePriceText}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<TouchableOpacity style={filePageStyle.backButton} onPress={this.onBackButtonPressed}>
|
<TouchableOpacity style={filePageStyle.backButton} onPress={this.onBackButtonPressed}>
|
||||||
<Icon name={"arrow-left"} size={18} style={filePageStyle.backButtonIcon} />
|
<Icon name={'arrow-left'} size={18} style={filePageStyle.backButtonIcon} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
{(this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) && (
|
||||||
|
<View
|
||||||
|
style={playerBgStyle}
|
||||||
|
ref={ref => {
|
||||||
|
this.playerBackground = ref;
|
||||||
|
}}
|
||||||
|
onLayout={evt => {
|
||||||
|
if (!this.state.playerBgHeight) {
|
||||||
|
this.setState({ playerBgHeight: evt.nativeEvent.layout.height });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{(this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) &&
|
{(this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) &&
|
||||||
<View style={playerBgStyle}
|
this.state.fullscreenMode && <View style={fsPlayerBgStyle} />}
|
||||||
ref={(ref) => { this.playerBackground = ref; }}
|
{(this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) && (
|
||||||
onLayout={(evt) => {
|
|
||||||
if (!this.state.playerBgHeight) {
|
|
||||||
this.setState({ playerBgHeight: evt.nativeEvent.layout.height });
|
|
||||||
}
|
|
||||||
}} />}
|
|
||||||
{((this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) && this.state.fullscreenMode) &&
|
|
||||||
<View style={fsPlayerBgStyle} />}
|
|
||||||
{(this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) &&
|
|
||||||
<MediaPlayer
|
<MediaPlayer
|
||||||
claim={claim}
|
claim={claim}
|
||||||
assignPlayer={(ref) => { this.player = ref; }}
|
assignPlayer={ref => {
|
||||||
|
this.player = ref;
|
||||||
|
}}
|
||||||
uri={uri}
|
uri={uri}
|
||||||
source={this.playerUriForFileInfo(fileInfo)}
|
source={this.playerUriForFileInfo(fileInfo)}
|
||||||
style={playerStyle}
|
style={playerStyle}
|
||||||
autoPlay={autoplay || this.state.autoPlayMedia}
|
autoPlay={autoplay || this.state.autoPlayMedia}
|
||||||
onFullscreenToggled={this.handleFullscreenToggle}
|
onFullscreenToggled={this.handleFullscreenToggle}
|
||||||
onLayout={(evt) => {
|
onLayout={evt => {
|
||||||
if (!this.state.playerHeight) {
|
if (!this.state.playerHeight) {
|
||||||
this.setState({ playerHeight: evt.nativeEvent.layout.height });
|
this.setState({ playerHeight: evt.nativeEvent.layout.height });
|
||||||
}
|
}
|
||||||
|
@ -720,125 +789,168 @@ class FilePage extends React.PureComponent {
|
||||||
onPlaybackFinished={this.onPlaybackFinished}
|
onPlaybackFinished={this.onPlaybackFinished}
|
||||||
thumbnail={thumbnail}
|
thumbnail={thumbnail}
|
||||||
position={position}
|
position={position}
|
||||||
/>}
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{(showActions && showFileActions) &&
|
{showActions && showFileActions && (
|
||||||
<View style={filePageStyle.actions}>
|
<View style={filePageStyle.actions}>
|
||||||
{showFileActions &&
|
{showFileActions && (
|
||||||
<View style={filePageStyle.fileActions}>
|
<View style={filePageStyle.fileActions}>
|
||||||
{completed && <Button style={filePageStyle.actionButton}
|
{completed && (
|
||||||
theme={"light"}
|
<Button
|
||||||
icon={"trash"}
|
style={filePageStyle.actionButton}
|
||||||
text={"Delete"}
|
theme={'light'}
|
||||||
onPress={this.onDeletePressed} />}
|
icon={'trash'}
|
||||||
{!completed && fileInfo && !fileInfo.stopped &&
|
text={'Delete'}
|
||||||
fileInfo.written_bytes < fileInfo.total_bytes &&
|
onPress={this.onDeletePressed}
|
||||||
!this.state.stopDownloadConfirmed &&
|
/>
|
||||||
<Button style={filePageStyle.actionButton}
|
)}
|
||||||
icon={"stop"}
|
{!completed &&
|
||||||
theme={"light"}
|
fileInfo &&
|
||||||
text={"Stop Download"}
|
!fileInfo.stopped &&
|
||||||
onPress={this.onStopDownloadPressed} />
|
fileInfo.written_bytes < fileInfo.total_bytes &&
|
||||||
}
|
!this.state.stopDownloadConfirmed && (
|
||||||
</View>}
|
<Button
|
||||||
</View>}
|
style={filePageStyle.actionButton}
|
||||||
|
icon={'stop'}
|
||||||
|
theme={'light'}
|
||||||
|
text={'Stop Download'}
|
||||||
|
onPress={this.onStopDownloadPressed}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
<ScrollView
|
<ScrollView
|
||||||
style={showActions ? filePageStyle.scrollContainerActions : filePageStyle.scrollContainer}
|
style={showActions ? filePageStyle.scrollContainerActions : filePageStyle.scrollContainer}
|
||||||
contentContainerstyle={showActions ? null : filePageStyle.scrollContent}
|
contentContainerstyle={showActions ? null : filePageStyle.scrollContent}
|
||||||
keyboardShouldPersistTaps={'handled'}
|
keyboardShouldPersistTaps={'handled'}
|
||||||
ref={(ref) => { this.scrollView = ref; }}>
|
ref={ref => {
|
||||||
<TouchableWithoutFeedback style={filePageStyle.titleTouch}
|
this.scrollView = ref;
|
||||||
onPress={() => this.setState({ showDescription: !this.state.showDescription })}>
|
}}
|
||||||
|
>
|
||||||
|
<TouchableWithoutFeedback
|
||||||
|
style={filePageStyle.titleTouch}
|
||||||
|
onPress={() => this.setState({ showDescription: !this.state.showDescription })}
|
||||||
|
>
|
||||||
<View style={filePageStyle.titleRow}>
|
<View style={filePageStyle.titleRow}>
|
||||||
<Text style={filePageStyle.title} selectable={true}>{title}</Text>
|
<Text style={filePageStyle.title} selectable={true}>
|
||||||
|
{title}
|
||||||
|
</Text>
|
||||||
<View style={filePageStyle.descriptionToggle}>
|
<View style={filePageStyle.descriptionToggle}>
|
||||||
<Icon name={this.state.showDescription ? "caret-up" : "caret-down"} size={24} />
|
<Icon name={this.state.showDescription ? 'caret-up' : 'caret-down'} size={24} />
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</TouchableWithoutFeedback>
|
</TouchableWithoutFeedback>
|
||||||
{channelName &&
|
{channelName && (
|
||||||
<View style={filePageStyle.channelRow}>
|
<View style={filePageStyle.channelRow}>
|
||||||
<View style={filePageStyle.publishInfo}>
|
<View style={filePageStyle.publishInfo}>
|
||||||
<Link style={filePageStyle.channelName}
|
<Link
|
||||||
selectable={true}
|
style={filePageStyle.channelName}
|
||||||
text={channelName}
|
selectable={true}
|
||||||
numberOfLines={1}
|
text={channelName}
|
||||||
ellipsizeMode={"tail"}
|
numberOfLines={1}
|
||||||
onPress={() => {
|
ellipsizeMode={'tail'}
|
||||||
navigateToUri(navigation, normalizeURI(fullChannelUri));
|
onPress={() => {
|
||||||
}} />
|
navigateToUri(navigation, normalizeURI(fullChannelUri));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<DateTime
|
<DateTime
|
||||||
style={filePageStyle.publishDate}
|
style={filePageStyle.publishDate}
|
||||||
textStyle={filePageStyle.publishDateText}
|
textStyle={filePageStyle.publishDateText}
|
||||||
uri={uri}
|
uri={uri}
|
||||||
formatOptions={{ day: 'numeric', month: 'long', year: 'numeric' }}
|
formatOptions={{ day: 'numeric', month: 'long', year: 'numeric' }}
|
||||||
show={DateTime.SHOW_DATE} />
|
show={DateTime.SHOW_DATE}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={filePageStyle.subscriptionRow}>
|
<View style={filePageStyle.subscriptionRow}>
|
||||||
{((isPlayable && !fileInfo) || (isPlayable && fileInfo && !fileInfo.download_path)) &&
|
{((isPlayable && !fileInfo) || (isPlayable && fileInfo && !fileInfo.download_path)) && (
|
||||||
<Button style={[filePageStyle.actionButton, filePageStyle.saveFileButton]}
|
<Button
|
||||||
theme={"light"}
|
style={[filePageStyle.actionButton, filePageStyle.saveFileButton]}
|
||||||
icon={"download"}
|
theme={'light'}
|
||||||
onPress={this.onSaveFilePressed} />}
|
icon={'download'}
|
||||||
<Button style={[filePageStyle.actionButton, filePageStyle.tipButton]}
|
onPress={this.onSaveFilePressed}
|
||||||
theme={"light"}
|
/>
|
||||||
icon={"gift"}
|
)}
|
||||||
onPress={() => this.setState({ showTipView: true })} />
|
<Button
|
||||||
|
style={[filePageStyle.actionButton, filePageStyle.tipButton]}
|
||||||
|
theme={'light'}
|
||||||
|
icon={'gift'}
|
||||||
|
onPress={() => this.setState({ showTipView: true })}
|
||||||
|
/>
|
||||||
<SubscribeButton
|
<SubscribeButton
|
||||||
style={filePageStyle.actionButton}
|
style={filePageStyle.actionButton}
|
||||||
uri={fullChannelUri}
|
uri={fullChannelUri}
|
||||||
name={channelName}
|
name={channelName}
|
||||||
hideText={false} />
|
hideText={false}
|
||||||
|
/>
|
||||||
<SubscribeNotificationButton
|
<SubscribeNotificationButton
|
||||||
style={[filePageStyle.actionButton, filePageStyle.bellButton]}
|
style={[filePageStyle.actionButton, filePageStyle.bellButton]}
|
||||||
uri={fullChannelUri}
|
uri={fullChannelUri}
|
||||||
name={channelName} />
|
name={channelName}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
}
|
)}
|
||||||
|
|
||||||
{this.state.showTipView && <View style={filePageStyle.divider} />}
|
{this.state.showTipView && <View style={filePageStyle.divider} />}
|
||||||
{this.state.showTipView &&
|
{this.state.showTipView && (
|
||||||
<View style={filePageStyle.tipCard}>
|
<View style={filePageStyle.tipCard}>
|
||||||
<View style={filePageStyle.row}>
|
<View style={filePageStyle.row}>
|
||||||
<View style={filePageStyle.amountRow}>
|
<View style={filePageStyle.amountRow}>
|
||||||
<TextInput ref={ref => this.tipAmountInput = ref}
|
<TextInput
|
||||||
onChangeText={value => this.setState({tipAmount: value})}
|
ref={ref => (this.tipAmountInput = ref)}
|
||||||
keyboardType={'numeric'}
|
onChangeText={value => this.setState({ tipAmount: value })}
|
||||||
placeholder={'0'}
|
keyboardType={'numeric'}
|
||||||
value={this.state.tipAmount}
|
placeholder={'0'}
|
||||||
style={[filePageStyle.input, filePageStyle.tipAmountInput]} />
|
value={this.state.tipAmount}
|
||||||
<Text style={[filePageStyle.text, filePageStyle.currency]}>LBC</Text>
|
style={[filePageStyle.input, filePageStyle.tipAmountInput]}
|
||||||
|
/>
|
||||||
|
<Text style={[filePageStyle.text, filePageStyle.currency]}>LBC</Text>
|
||||||
|
</View>
|
||||||
|
<Link
|
||||||
|
style={[filePageStyle.link, filePageStyle.cancelTipLink]}
|
||||||
|
text={'Cancel'}
|
||||||
|
onPress={() => this.setState({ showTipView: false })}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
text={'Send a tip'}
|
||||||
|
style={[filePageStyle.button, filePageStyle.sendButton]}
|
||||||
|
disabled={!canSendTip}
|
||||||
|
onPress={this.handleSendTip}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
<Link style={[filePageStyle.link, filePageStyle.cancelTipLink]} text={'Cancel'} onPress={() => this.setState({ showTipView: false })} />
|
|
||||||
<Button text={'Send a tip'}
|
|
||||||
style={[filePageStyle.button, filePageStyle.sendButton]}
|
|
||||||
disabled={!canSendTip}
|
|
||||||
onPress={this.handleSendTip} />
|
|
||||||
</View>
|
</View>
|
||||||
</View>}
|
)}
|
||||||
|
|
||||||
{(this.state.showDescription && description && description.length > 0) && <View style={filePageStyle.divider} />}
|
{this.state.showDescription && description && description.length > 0 && (
|
||||||
{(this.state.showDescription && description) && (
|
<View style={filePageStyle.divider} />
|
||||||
|
)}
|
||||||
|
{this.state.showDescription && description && (
|
||||||
<View>
|
<View>
|
||||||
<Text style={filePageStyle.description} selectable={true}>{this.linkify(description)}</Text>
|
<Text style={filePageStyle.description} selectable={true}>
|
||||||
|
{this.linkify(description)}
|
||||||
|
</Text>
|
||||||
{tags && tags.length > 0 && (
|
{tags && tags.length > 0 && (
|
||||||
<View style={filePageStyle.tagContainer}>
|
<View style={filePageStyle.tagContainer}>
|
||||||
<Text style={filePageStyle.tagTitle}>Tags</Text>
|
<Text style={filePageStyle.tagTitle}>Tags</Text>
|
||||||
<View style={filePageStyle.tagList}>{this.renderTags(tags)}</View>
|
<View style={filePageStyle.tagList}>{this.renderTags(tags)}</View>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>)}
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
{(costInfo && parseFloat(costInfo.cost) > balance) && <FileRewardsDriver navigation={navigation} />}
|
{costInfo && parseFloat(costInfo.cost) > balance && <FileRewardsDriver navigation={navigation} />}
|
||||||
|
|
||||||
<View onLayout={this.setRelatedContentPosition} />
|
<View onLayout={this.setRelatedContentPosition} />
|
||||||
<RelatedContent navigation={navigation} uri={uri} />
|
<RelatedContent navigation={navigation} uri={uri} />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{(!this.state.fullscreenMode && !this.state.showImageViewer && !this.state.showWebView) &&
|
{!this.state.fullscreenMode && !this.state.showImageViewer && !this.state.showWebView && (
|
||||||
<FloatingWalletBalance navigation={navigation} />}
|
<FloatingWalletBalance navigation={navigation} />
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ import {
|
||||||
import { doSetClientSetting } from 'redux/actions/settings';
|
import { doSetClientSetting } from 'redux/actions/settings';
|
||||||
import FirstRun from './view';
|
import FirstRun from './view';
|
||||||
|
|
||||||
const select = (state) => ({
|
const select = state => ({
|
||||||
authenticating: selectAuthenticationIsPending(state),
|
authenticating: selectAuthenticationIsPending(state),
|
||||||
authToken: selectAuthToken(state),
|
authToken: selectAuthToken(state),
|
||||||
emailToVerify: selectEmailToVerify(state),
|
emailToVerify: selectEmailToVerify(state),
|
||||||
|
@ -48,7 +48,10 @@ const perform = dispatch => ({
|
||||||
checkSync: () => dispatch(doCheckSync()),
|
checkSync: () => dispatch(doCheckSync()),
|
||||||
setDefaultAccount: () => dispatch(doSetDefaultAccount()),
|
setDefaultAccount: () => dispatch(doSetDefaultAccount()),
|
||||||
notify: data => dispatch(doToast(data)),
|
notify: data => dispatch(doToast(data)),
|
||||||
resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email))
|
resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(FirstRun);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(FirstRun);
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Lbry } from 'lbry-redux';
|
import { Lbry } from 'lbry-redux';
|
||||||
import {
|
import { NativeModules, Platform, Text, TextInput, View } from 'react-native';
|
||||||
NativeModules,
|
|
||||||
Platform,
|
|
||||||
Text,
|
|
||||||
TextInput,
|
|
||||||
View
|
|
||||||
} from 'react-native';
|
|
||||||
import AsyncStorage from '@react-native-community/async-storage';
|
import AsyncStorage from '@react-native-community/async-storage';
|
||||||
import Colors from 'styles/colors';
|
import Colors from 'styles/colors';
|
||||||
import Constants from 'constants';
|
import Constants from 'constants';
|
||||||
|
@ -16,7 +10,7 @@ class EmailCollectPage extends React.PureComponent {
|
||||||
state = {
|
state = {
|
||||||
email: null,
|
email: null,
|
||||||
placeholder: 'you@example.com',
|
placeholder: 'you@example.com',
|
||||||
verifying: true
|
verifying: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
|
@ -34,7 +28,7 @@ class EmailCollectPage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChangeText = (text) => {
|
handleChangeText = text => {
|
||||||
// save the value to the state email
|
// save the value to the state email
|
||||||
const { onEmailChanged } = this.props;
|
const { onEmailChanged } = this.props;
|
||||||
this.setState({ email: text });
|
this.setState({ email: text });
|
||||||
|
@ -43,7 +37,7 @@ class EmailCollectPage extends React.PureComponent {
|
||||||
if (onEmailChanged) {
|
if (onEmailChanged) {
|
||||||
onEmailChanged(text);
|
onEmailChanged(text);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { onEmailViewLayout } = this.props;
|
const { onEmailViewLayout } = this.props;
|
||||||
|
@ -51,33 +45,34 @@ class EmailCollectPage extends React.PureComponent {
|
||||||
const content = (
|
const content = (
|
||||||
<View onLayout={onEmailViewLayout}>
|
<View onLayout={onEmailViewLayout}>
|
||||||
<Text style={firstRunStyle.title}>Setup account</Text>
|
<Text style={firstRunStyle.title}>Setup account</Text>
|
||||||
<TextInput style={firstRunStyle.emailInput}
|
<TextInput
|
||||||
placeholder={this.state.placeholder}
|
style={firstRunStyle.emailInput}
|
||||||
underlineColorAndroid="transparent"
|
placeholder={this.state.placeholder}
|
||||||
selectionColor={Colors.NextLbryGreen}
|
underlineColorAndroid="transparent"
|
||||||
value={this.state.email}
|
selectionColor={Colors.NextLbryGreen}
|
||||||
onChangeText={text => this.handleChangeText(text)}
|
value={this.state.email}
|
||||||
onFocus={() => {
|
onChangeText={text => this.handleChangeText(text)}
|
||||||
if (!this.state.email || this.state.email.length === 0) {
|
onFocus={() => {
|
||||||
this.setState({ placeholder: '' });
|
if (!this.state.email || this.state.email.length === 0) {
|
||||||
}
|
this.setState({ placeholder: '' });
|
||||||
}}
|
}
|
||||||
onBlur={() => {
|
}}
|
||||||
if (!this.state.email || this.state.email.length === 0) {
|
onBlur={() => {
|
||||||
this.setState({ placeholder: 'you@example.com' });
|
if (!this.state.email || this.state.email.length === 0) {
|
||||||
}
|
this.setState({ placeholder: 'you@example.com' });
|
||||||
}}
|
}
|
||||||
/>
|
}}
|
||||||
<Text style={firstRunStyle.paragraph}>An account will allow you to earn rewards and keep your account and settings synced.</Text>
|
/>
|
||||||
<Text style={firstRunStyle.infoParagraph}>This information is disclosed only to LBRY, Inc. and not to the LBRY network.</Text>
|
<Text style={firstRunStyle.paragraph}>
|
||||||
|
An account will allow you to earn rewards and keep your account and settings synced.
|
||||||
|
</Text>
|
||||||
|
<Text style={firstRunStyle.infoParagraph}>
|
||||||
|
This information is disclosed only to LBRY, Inc. and not to the LBRY network.
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return <View style={firstRunStyle.container}>{content}</View>;
|
||||||
<View style={firstRunStyle.container}>
|
|
||||||
{content}
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Lbry } from 'lbry-redux';
|
import { Lbry } from 'lbry-redux';
|
||||||
import {
|
import { ActivityIndicator, Linking, NativeModules, Platform, Switch, Text, TextInput, View } from 'react-native';
|
||||||
ActivityIndicator,
|
|
||||||
Linking,
|
|
||||||
NativeModules,
|
|
||||||
Platform,
|
|
||||||
Switch,
|
|
||||||
Text,
|
|
||||||
TextInput,
|
|
||||||
View
|
|
||||||
} from 'react-native';
|
|
||||||
import AsyncStorage from '@react-native-community/async-storage';
|
import AsyncStorage from '@react-native-community/async-storage';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import Colors from 'styles/colors';
|
import Colors from 'styles/colors';
|
||||||
|
@ -23,7 +14,7 @@ class EmailVerifyPage extends React.PureComponent {
|
||||||
resendVerificationEmail(email);
|
resendVerificationEmail(email);
|
||||||
AsyncStorage.setItem(Constants.KEY_EMAIL_VERIFY_PENDING, 'true');
|
AsyncStorage.setItem(Constants.KEY_EMAIL_VERIFY_PENDING, 'true');
|
||||||
notify({ message: 'Please follow the instructions in the email sent to your address to continue.' });
|
notify({ message: 'Please follow the instructions in the email sent to your address to continue.' });
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { onEmailViewLayout, email } = this.props;
|
const { onEmailViewLayout, email } = this.props;
|
||||||
|
@ -31,19 +22,26 @@ class EmailVerifyPage extends React.PureComponent {
|
||||||
const content = (
|
const content = (
|
||||||
<View onLayout={onEmailViewLayout}>
|
<View onLayout={onEmailViewLayout}>
|
||||||
<Text style={firstRunStyle.title}>Verify Email</Text>
|
<Text style={firstRunStyle.title}>Verify Email</Text>
|
||||||
<Text style={firstRunStyle.paragraph}>An email has been sent to <Text style={firstRunStyle.nowrap} numberOfLines={1}>{email}</Text>. Please follow the instructions in the message to verify your email address.</Text>
|
<Text style={firstRunStyle.paragraph}>
|
||||||
|
An email has been sent to{' '}
|
||||||
|
<Text style={firstRunStyle.nowrap} numberOfLines={1}>
|
||||||
|
{email}
|
||||||
|
</Text>
|
||||||
|
. Please follow the instructions in the message to verify your email address.
|
||||||
|
</Text>
|
||||||
|
|
||||||
<View style={firstRunStyle.buttonContainer}>
|
<View style={firstRunStyle.buttonContainer}>
|
||||||
<Button style={firstRunStyle.verificationButton} theme={"light"} text={"Resend"} onPress={this.onResendPressed} />
|
<Button
|
||||||
|
style={firstRunStyle.verificationButton}
|
||||||
|
theme={'light'}
|
||||||
|
text={'Resend'}
|
||||||
|
onPress={this.onResendPressed}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return <View style={firstRunStyle.container}>{content}</View>;
|
||||||
<View style={firstRunStyle.container}>
|
|
||||||
{content}
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
Switch,
|
Switch,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
View
|
View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import Colors from 'styles/colors';
|
import Colors from 'styles/colors';
|
||||||
import Constants from 'constants';
|
import Constants from 'constants';
|
||||||
|
@ -18,7 +18,7 @@ import firstRunStyle from 'styles/firstRun';
|
||||||
|
|
||||||
class SkipAccountPage extends React.PureComponent {
|
class SkipAccountPage extends React.PureComponent {
|
||||||
state = {
|
state = {
|
||||||
confirmed: false
|
confirmed: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -30,22 +30,29 @@ class SkipAccountPage extends React.PureComponent {
|
||||||
<Icon name="exclamation-triangle" style={firstRunStyle.titleIcon} size={32} color={Colors.White} />
|
<Icon name="exclamation-triangle" style={firstRunStyle.titleIcon} size={32} color={Colors.White} />
|
||||||
<Text style={firstRunStyle.title}>Are you sure?</Text>
|
<Text style={firstRunStyle.title}>Are you sure?</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text style={firstRunStyle.paragraph}>Without an account, you will not receive rewards, sync and backup services, or security updates.</Text>
|
<Text style={firstRunStyle.paragraph}>
|
||||||
|
Without an account, you will not receive rewards, sync and backup services, or security updates.
|
||||||
|
</Text>
|
||||||
|
|
||||||
<View style={[firstRunStyle.row, firstRunStyle.confirmContainer]}>
|
<View style={[firstRunStyle.row, firstRunStyle.confirmContainer]}>
|
||||||
<View style={firstRunStyle.rowSwitch}>
|
<View style={firstRunStyle.rowSwitch}>
|
||||||
<Switch value={this.state.confirmed} onValueChange={value => { this.setState({ confirmed: value }); onSkipSwitchChanged(value); }} />
|
<Switch
|
||||||
|
value={this.state.confirmed}
|
||||||
|
onValueChange={value => {
|
||||||
|
this.setState({ confirmed: value });
|
||||||
|
onSkipSwitchChanged(value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
<Text style={firstRunStyle.rowParagraph}>I understand that by uninstalling LBRY I will lose any balances or published content with no recovery option.</Text>
|
<Text style={firstRunStyle.rowParagraph}>
|
||||||
|
I understand that by uninstalling LBRY I will lose any balances or published content with no recovery
|
||||||
|
option.
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return <View style={firstRunStyle.container}>{content}</View>;
|
||||||
<View style={firstRunStyle.container}>
|
|
||||||
{content}
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View
|
View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { BarPasswordStrengthDisplay } from 'react-native-password-strength-meter';
|
import { BarPasswordStrengthDisplay } from 'react-native-password-strength-meter';
|
||||||
import AsyncStorage from '@react-native-community/async-storage';
|
import AsyncStorage from '@react-native-community/async-storage';
|
||||||
|
@ -27,7 +27,7 @@ class WalletPage extends React.PureComponent {
|
||||||
statusTries: 0,
|
statusTries: 0,
|
||||||
walletReady: false,
|
walletReady: false,
|
||||||
hasCheckedSync: false,
|
hasCheckedSync: false,
|
||||||
revealPassword: false
|
revealPassword: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -36,28 +36,30 @@ class WalletPage extends React.PureComponent {
|
||||||
|
|
||||||
checkWalletReady = () => {
|
checkWalletReady = () => {
|
||||||
// make sure the sdk wallet component is ready
|
// make sure the sdk wallet component is ready
|
||||||
Lbry.status().then(status => {
|
Lbry.status()
|
||||||
if (status.startup_status && status.startup_status.wallet) {
|
.then(status => {
|
||||||
this.setState({ walletReady: true }, () => {
|
if (status.startup_status && status.startup_status.wallet) {
|
||||||
this.props.checkSync();
|
this.setState({ walletReady: true }, () => {
|
||||||
setTimeout(() => this.setState({ hasCheckedSync: true}), 1000);
|
this.props.checkSync();
|
||||||
});
|
setTimeout(() => this.setState({ hasCheckedSync: true }), 1000);
|
||||||
return;
|
});
|
||||||
}
|
return;
|
||||||
setTimeout(this.checkWalletReady, 1000);
|
}
|
||||||
}).catch((e) => {
|
setTimeout(this.checkWalletReady, 1000);
|
||||||
setTimeout(this.checkWalletReady, 1000);
|
})
|
||||||
});
|
.catch(e => {
|
||||||
}
|
setTimeout(this.checkWalletReady, 1000);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
handleChangeText = (text) => {
|
handleChangeText = text => {
|
||||||
// save the value to the state email
|
// save the value to the state email
|
||||||
const { onPasswordChanged } = this.props;
|
const { onPasswordChanged } = this.props;
|
||||||
this.setState({ password: text });
|
this.setState({ password: text });
|
||||||
if (onPasswordChanged) {
|
if (onPasswordChanged) {
|
||||||
onPasswordChanged(text);
|
onPasswordChanged(text);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { onPasswordChanged, onWalletViewLayout, getSyncIsPending, hasSyncedWallet, syncApplyIsPending } = this.props;
|
const { onPasswordChanged, onWalletViewLayout, getSyncIsPending, hasSyncedWallet, syncApplyIsPending } = this.props;
|
||||||
|
@ -74,7 +76,7 @@ class WalletPage extends React.PureComponent {
|
||||||
content = (
|
content = (
|
||||||
<View style={firstRunStyle.centered}>
|
<View style={firstRunStyle.centered}>
|
||||||
<ActivityIndicator size="large" color={Colors.White} style={firstRunStyle.waiting} />
|
<ActivityIndicator size="large" color={Colors.White} style={firstRunStyle.waiting} />
|
||||||
<Text style={firstRunStyle.paragraph}>Validating password...</Text>
|
<Text style={firstRunStyle.paragraph}>Validating password...</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -82,11 +84,13 @@ class WalletPage extends React.PureComponent {
|
||||||
<View onLayout={onWalletViewLayout}>
|
<View onLayout={onWalletViewLayout}>
|
||||||
<Text style={firstRunStyle.title}>Password</Text>
|
<Text style={firstRunStyle.title}>Password</Text>
|
||||||
<Text style={firstRunStyle.paragraph}>
|
<Text style={firstRunStyle.paragraph}>
|
||||||
{hasSyncedWallet ? "Please enter the password you used to secure your wallet." :
|
{hasSyncedWallet
|
||||||
"Please enter a password to secure your account and wallet."}
|
? 'Please enter the password you used to secure your wallet.'
|
||||||
|
: 'Please enter a password to secure your account and wallet.'}
|
||||||
</Text>
|
</Text>
|
||||||
<View style={firstRunStyle.passwordInputContainer}>
|
<View style={firstRunStyle.passwordInputContainer}>
|
||||||
<TextInput style={firstRunStyle.passwordInput}
|
<TextInput
|
||||||
|
style={firstRunStyle.passwordInput}
|
||||||
placeholder={this.state.placeholder}
|
placeholder={this.state.placeholder}
|
||||||
underlineColorAndroid="transparent"
|
underlineColorAndroid="transparent"
|
||||||
selectionColor={Colors.NextLbryGreen}
|
selectionColor={Colors.NextLbryGreen}
|
||||||
|
@ -103,31 +107,32 @@ class WalletPage extends React.PureComponent {
|
||||||
this.setState({ placeholder: 'password' });
|
this.setState({ placeholder: 'password' });
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={firstRunStyle.revealPasswordIcon}
|
style={firstRunStyle.revealPasswordIcon}
|
||||||
onPress={() => this.setState({ revealPassword: !this.state.revealPassword })}>
|
onPress={() => this.setState({ revealPassword: !this.state.revealPassword })}
|
||||||
<Icon name={this.state.revealPassword ? "eye-slash" : "eye"} size={16} style={firstRunStyle.revealIcon} />
|
>
|
||||||
|
<Icon name={this.state.revealPassword ? 'eye-slash' : 'eye'} size={16} style={firstRunStyle.revealIcon} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{(!hasSyncedWallet && this.state.password && this.state.password.trim().length) > 0 &&
|
{(!hasSyncedWallet && this.state.password && this.state.password.trim().length) > 0 && (
|
||||||
<View style={firstRunStyle.passwordStrength}>
|
<View style={firstRunStyle.passwordStrength}>
|
||||||
<BarPasswordStrengthDisplay
|
<BarPasswordStrengthDisplay
|
||||||
width={Dimensions.get('window').width - firstRunMargins}
|
width={Dimensions.get('window').width - firstRunMargins}
|
||||||
minLength={1}
|
minLength={1}
|
||||||
password={this.state.password} />
|
password={this.state.password}
|
||||||
</View>}
|
/>
|
||||||
<Text style={firstRunStyle.infoParagraph}>Note: for wallet security purposes, LBRY is unable to reset your password.</Text>
|
</View>
|
||||||
|
)}
|
||||||
|
<Text style={firstRunStyle.infoParagraph}>
|
||||||
|
Note: for wallet security purposes, LBRY is unable to reset your password.
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <View style={firstRunStyle.container}>{content}</View>;
|
||||||
<View style={firstRunStyle.container}>
|
|
||||||
{content}
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Lbry } from 'lbry-redux';
|
import { Lbry } from 'lbry-redux';
|
||||||
import {
|
import { ActivityIndicator, NativeModules, Platform, Text, View } from 'react-native';
|
||||||
ActivityIndicator,
|
|
||||||
NativeModules,
|
|
||||||
Platform,
|
|
||||||
Text,
|
|
||||||
View
|
|
||||||
} from 'react-native';
|
|
||||||
import AsyncStorage from '@react-native-community/async-storage';
|
import AsyncStorage from '@react-native-community/async-storage';
|
||||||
import Colors from 'styles/colors';
|
import Colors from 'styles/colors';
|
||||||
import Constants from 'constants';
|
import Constants from 'constants';
|
||||||
|
@ -53,25 +47,27 @@ class WelcomePage extends React.PureComponent {
|
||||||
const { authenticate } = this.props;
|
const { authenticate } = this.props;
|
||||||
this.setState({ authenticationStarted: true, authenticationFailed: false });
|
this.setState({ authenticationStarted: true, authenticationFailed: false });
|
||||||
NativeModules.VersionInfo.getAppVersion().then(appVersion => {
|
NativeModules.VersionInfo.getAppVersion().then(appVersion => {
|
||||||
Lbry.status().then(info => {
|
Lbry.status()
|
||||||
this.setState({ sdkStarted: true });
|
.then(info => {
|
||||||
|
this.setState({ sdkStarted: true });
|
||||||
|
|
||||||
authenticate(appVersion, Platform.OS);
|
authenticate(appVersion, Platform.OS);
|
||||||
}).catch(error => {
|
})
|
||||||
if (this.state.statusTries >= WelcomePage.MAX_STATUS_TRIES) {
|
.catch(error => {
|
||||||
this.setState({ authenticationFailed: true });
|
if (this.state.statusTries >= WelcomePage.MAX_STATUS_TRIES) {
|
||||||
|
this.setState({ authenticationFailed: true });
|
||||||
|
|
||||||
// sdk_start_failed
|
// sdk_start_failed
|
||||||
NativeModules.Firebase.track('sdk_start_failed', null);
|
NativeModules.Firebase.track('sdk_start_failed', null);
|
||||||
} else {
|
} else {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.startAuthenticating();
|
this.startAuthenticating();
|
||||||
this.setState({ statusTries: this.state.statusTries + 1 });
|
this.setState({ statusTries: this.state.statusTries + 1 });
|
||||||
}, 1000); // Retry every second for a maximum of MAX_STATUS_TRIES tries (60 seconds)
|
}, 1000); // Retry every second for a maximum of MAX_STATUS_TRIES tries (60 seconds)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { authenticating, authToken, onWelcomePageLayout } = this.props;
|
const { authenticating, authToken, onWelcomePageLayout } = this.props;
|
||||||
|
@ -81,7 +77,10 @@ class WelcomePage extends React.PureComponent {
|
||||||
// Ask the user to try again
|
// Ask the user to try again
|
||||||
content = (
|
content = (
|
||||||
<View>
|
<View>
|
||||||
<Text style={firstRunStyle.paragraph}>The LBRY servers were unreachable at this time. Please check your Internet connection and then restart the app to try again.</Text>
|
<Text style={firstRunStyle.paragraph}>
|
||||||
|
The LBRY servers were unreachable at this time. Please check your Internet connection and then restart the
|
||||||
|
app to try again.
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
} else if (!authToken || authenticating) {
|
} else if (!authToken || authenticating) {
|
||||||
|
@ -95,16 +94,15 @@ class WelcomePage extends React.PureComponent {
|
||||||
content = (
|
content = (
|
||||||
<View onLayout={onWelcomePageLayout}>
|
<View onLayout={onWelcomePageLayout}>
|
||||||
<Text style={firstRunStyle.title}>Welcome to LBRY.</Text>
|
<Text style={firstRunStyle.title}>Welcome to LBRY.</Text>
|
||||||
<Text style={firstRunStyle.paragraph}>LBRY is a community-controlled content platform where you can find and publish videos, music, books, and more.</Text>
|
<Text style={firstRunStyle.paragraph}>
|
||||||
|
LBRY is a community-controlled content platform where you can find and publish videos, music, books, and
|
||||||
|
more.
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <View style={firstRunStyle.container}>{content}</View>;
|
||||||
<View style={firstRunStyle.container}>
|
|
||||||
{content}
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Lbry } from 'lbry-redux';
|
import { Lbry } from 'lbry-redux';
|
||||||
import {
|
import { ActivityIndicator, Linking, NativeModules, Text, TouchableOpacity, View } from 'react-native';
|
||||||
ActivityIndicator,
|
|
||||||
Linking,
|
|
||||||
NativeModules,
|
|
||||||
Text,
|
|
||||||
TouchableOpacity,
|
|
||||||
View
|
|
||||||
} from 'react-native';
|
|
||||||
import { NavigationActions, StackActions } from 'react-navigation';
|
import { NavigationActions, StackActions } from 'react-navigation';
|
||||||
import AsyncStorage from '@react-native-community/async-storage';
|
import AsyncStorage from '@react-native-community/async-storage';
|
||||||
import Colors from 'styles/colors';
|
import Colors from 'styles/colors';
|
||||||
|
@ -39,11 +32,11 @@ class FirstRunScreen extends React.PureComponent {
|
||||||
skipAccountConfirmed: false,
|
skipAccountConfirmed: false,
|
||||||
showBottomContainer: false,
|
showBottomContainer: false,
|
||||||
walletPassword: null,
|
walletPassword: null,
|
||||||
syncApplyStarted: false
|
syncApplyStarted: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
Linking.getInitialURL().then((url) => {
|
Linking.getInitialURL().then(url => {
|
||||||
if (url) {
|
if (url) {
|
||||||
this.setState({ launchUrl: url });
|
this.setState({ launchUrl: url });
|
||||||
}
|
}
|
||||||
|
@ -72,10 +65,10 @@ class FirstRunScreen extends React.PureComponent {
|
||||||
if (this.state.emailSubmitted && !emailNewPending) {
|
if (this.state.emailSubmitted && !emailNewPending) {
|
||||||
this.setState({ emailSubmitted: false });
|
this.setState({ emailSubmitted: false });
|
||||||
if (emailNewErrorMessage && emailNewErrorMessage.trim().length > 0) {
|
if (emailNewErrorMessage && emailNewErrorMessage.trim().length > 0) {
|
||||||
notify ({ message: String(emailNewErrorMessage), isError: true });
|
notify({ message: String(emailNewErrorMessage), isError: true });
|
||||||
} else {
|
} else {
|
||||||
// Request successful. Navigate to email verify page.
|
// Request successful. Navigate to email verify page.
|
||||||
this.showPage(Constants.FIRST_RUN_PAGE_EMAIL_VERIFY)
|
this.showPage(Constants.FIRST_RUN_PAGE_EMAIL_VERIFY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,25 +91,26 @@ class FirstRunScreen extends React.PureComponent {
|
||||||
this.checkVerificationStatus(user);
|
this.checkVerificationStatus(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkVerificationStatus = (user) => {
|
checkVerificationStatus = user => {
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
|
|
||||||
this.setState({
|
this.setState(
|
||||||
isEmailVerified: (user && user.primary_email && user.has_verified_email),
|
{
|
||||||
}, () => {
|
isEmailVerified: user && user.primary_email && user.has_verified_email,
|
||||||
if (this.state.isEmailVerified) {
|
},
|
||||||
this.showPage(Constants.FIRST_RUN_PAGE_WALLET);
|
() => {
|
||||||
|
if (this.state.isEmailVerified) {
|
||||||
|
this.showPage(Constants.FIRST_RUN_PAGE_WALLET);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
launchSplashScreen() {
|
launchSplashScreen() {
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
const resetAction = StackActions.reset({
|
const resetAction = StackActions.reset({
|
||||||
index: 0,
|
index: 0,
|
||||||
actions: [
|
actions: [NavigationActions.navigate({ routeName: 'Splash', params: { launchUri: this.state.launchUri } })],
|
||||||
NavigationActions.navigate({ routeName: 'Splash', params: { launchUri: this.state.launchUri } })
|
|
||||||
]
|
|
||||||
});
|
});
|
||||||
navigation.dispatch(resetAction);
|
navigation.dispatch(resetAction);
|
||||||
}
|
}
|
||||||
|
@ -136,14 +130,14 @@ class FirstRunScreen extends React.PureComponent {
|
||||||
if (Constants.FIRST_RUN_PAGE_EMAIL_VERIFY === this.state.currentPage) {
|
if (Constants.FIRST_RUN_PAGE_EMAIL_VERIFY === this.state.currentPage) {
|
||||||
this.showPage(Constants.FIRST_RUN_PAGE_EMAIL_COLLECT);
|
this.showPage(Constants.FIRST_RUN_PAGE_EMAIL_COLLECT);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
checkWalletPassword = () => {
|
checkWalletPassword = () => {
|
||||||
const { syncApply, syncHash, syncData } = this.props;
|
const { syncApply, syncHash, syncData } = this.props;
|
||||||
this.setState({ syncApplyStarted: true, showBottomContainer: false }, () => {
|
this.setState({ syncApplyStarted: true, showBottomContainer: false }, () => {
|
||||||
syncApply(syncHash, syncData, this.state.walletPassword);
|
syncApply(syncHash, syncData, this.state.walletPassword);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
handleContinuePressed = () => {
|
handleContinuePressed = () => {
|
||||||
const { notify, user, hasSyncedWallet } = this.props;
|
const { notify, user, hasSyncedWallet } = this.props;
|
||||||
|
@ -163,7 +157,10 @@ class FirstRunScreen extends React.PureComponent {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Constants.FIRST_RUN_PAGE_EMAIL_COLLECT !== this.state.currentPage && pageIndex === (FirstRunScreen.pages.length - 1)) {
|
if (
|
||||||
|
Constants.FIRST_RUN_PAGE_EMAIL_COLLECT !== this.state.currentPage &&
|
||||||
|
pageIndex === FirstRunScreen.pages.length - 1
|
||||||
|
) {
|
||||||
this.closeFinalPage();
|
this.closeFinalPage();
|
||||||
} else {
|
} else {
|
||||||
// TODO: Actions and page verification for specific pages
|
// TODO: Actions and page verification for specific pages
|
||||||
|
@ -174,7 +171,7 @@ class FirstRunScreen extends React.PureComponent {
|
||||||
this.showNextPage();
|
this.showNextPage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleEmailCollectPageContinue() {
|
handleEmailCollectPageContinue() {
|
||||||
const { notify, addUserEmail } = this.props;
|
const { notify, addUserEmail } = this.props;
|
||||||
|
@ -190,19 +187,19 @@ class FirstRunScreen extends React.PureComponent {
|
||||||
this.setState({ emailSubmitted: true });
|
this.setState({ emailSubmitted: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
checkBottomContainer = (pageName) => {
|
checkBottomContainer = pageName => {
|
||||||
if (Constants.FIRST_RUN_PAGE_EMAIL_COLLECT === pageName || Constants.FIRST_RUN_PAGE_WALLET === pageName) {
|
if (Constants.FIRST_RUN_PAGE_EMAIL_COLLECT === pageName || Constants.FIRST_RUN_PAGE_WALLET === pageName) {
|
||||||
// do not show the buttons (because we're waiting to get things ready)
|
// do not show the buttons (because we're waiting to get things ready)
|
||||||
this.setState({ showBottomContainer: false });
|
this.setState({ showBottomContainer: false });
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
showNextPage = () => {
|
showNextPage = () => {
|
||||||
const pageIndex = FirstRunScreen.pages.indexOf(this.state.currentPage);
|
const pageIndex = FirstRunScreen.pages.indexOf(this.state.currentPage);
|
||||||
const nextPage = FirstRunScreen.pages[pageIndex + 1];
|
const nextPage = FirstRunScreen.pages[pageIndex + 1];
|
||||||
this.setState({ currentPage: nextPage });
|
this.setState({ currentPage: nextPage });
|
||||||
this.checkBottomContainer(nextPage);
|
this.checkBottomContainer(nextPage);
|
||||||
}
|
};
|
||||||
|
|
||||||
showPage(pageName) {
|
showPage(pageName) {
|
||||||
const pageIndex = FirstRunScreen.pages.indexOf(pageName);
|
const pageIndex = FirstRunScreen.pages.indexOf(pageName);
|
||||||
|
@ -222,36 +219,36 @@ class FirstRunScreen extends React.PureComponent {
|
||||||
this.launchSplashScreen();
|
this.launchSplashScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
onEmailChanged = (email) => {
|
onEmailChanged = email => {
|
||||||
this.setState({ email });
|
this.setState({ email });
|
||||||
if (Constants.FIRST_RUN_PAGE_EMAIL_COLLECT == this.state.currentPage) {
|
if (Constants.FIRST_RUN_PAGE_EMAIL_COLLECT == this.state.currentPage) {
|
||||||
this.setState({ showSkip: (!email || email.trim().length === 0) });
|
this.setState({ showSkip: !email || email.trim().length === 0 });
|
||||||
} else {
|
} else {
|
||||||
this.setState({ showSkip: false });
|
this.setState({ showSkip: false });
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
onEmailViewLayout = () => {
|
onEmailViewLayout = () => {
|
||||||
this.setState({ showBottomContainer: true, showSkip: true });
|
this.setState({ showBottomContainer: true, showSkip: true });
|
||||||
AsyncStorage.removeItem(Constants.KEY_FIRST_RUN_EMAIL);
|
AsyncStorage.removeItem(Constants.KEY_FIRST_RUN_EMAIL);
|
||||||
AsyncStorage.removeItem(Constants.KEY_EMAIL_VERIFY_PENDING);
|
AsyncStorage.removeItem(Constants.KEY_EMAIL_VERIFY_PENDING);
|
||||||
}
|
};
|
||||||
|
|
||||||
onWalletPasswordChanged = (password) => {
|
onWalletPasswordChanged = password => {
|
||||||
this.setState({ walletPassword: password });
|
this.setState({ walletPassword: password });
|
||||||
}
|
};
|
||||||
|
|
||||||
onWalletViewLayout = () => {
|
onWalletViewLayout = () => {
|
||||||
this.setState({ showBottomContainer: true });
|
this.setState({ showBottomContainer: true });
|
||||||
}
|
};
|
||||||
|
|
||||||
onWelcomePageLayout = () => {
|
onWelcomePageLayout = () => {
|
||||||
this.setState({ showBottomContainer: true });
|
this.setState({ showBottomContainer: true });
|
||||||
}
|
};
|
||||||
|
|
||||||
onSkipSwitchChanged = (checked) => {
|
onSkipSwitchChanged = checked => {
|
||||||
this.setState({ skipAccountConfirmed: checked });
|
this.setState({ skipAccountConfirmed: checked });
|
||||||
}
|
};
|
||||||
|
|
||||||
setFreshPassword = () => {
|
setFreshPassword = () => {
|
||||||
const { getSync, setClientSetting } = this.props;
|
const { getSync, setClientSetting } = this.props;
|
||||||
|
@ -265,7 +262,7 @@ class FirstRunScreen extends React.PureComponent {
|
||||||
this.closeFinalPage();
|
this.closeFinalPage();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
|
@ -281,84 +278,113 @@ class FirstRunScreen extends React.PureComponent {
|
||||||
getSyncIsPending,
|
getSyncIsPending,
|
||||||
syncApplyIsPending,
|
syncApplyIsPending,
|
||||||
resendVerificationEmail,
|
resendVerificationEmail,
|
||||||
user
|
user,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
let page = null;
|
let page = null;
|
||||||
switch (this.state.currentPage) {
|
switch (this.state.currentPage) {
|
||||||
case Constants.FIRST_RUN_PAGE_WELCOME:
|
case Constants.FIRST_RUN_PAGE_WELCOME:
|
||||||
page = (<WelcomePage
|
page = (
|
||||||
authenticating={authenticating}
|
<WelcomePage
|
||||||
authToken={authToken}
|
authenticating={authenticating}
|
||||||
authenticate={authenticate}
|
authToken={authToken}
|
||||||
onWelcomePageLayout={this.onWelcomePageLayout} />);
|
authenticate={authenticate}
|
||||||
|
onWelcomePageLayout={this.onWelcomePageLayout}
|
||||||
|
/>
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Constants.FIRST_RUN_PAGE_EMAIL_COLLECT:
|
case Constants.FIRST_RUN_PAGE_EMAIL_COLLECT:
|
||||||
page = (<EmailCollectPage
|
page = (
|
||||||
user={user}
|
<EmailCollectPage
|
||||||
showNextPage={this.showNextPage}
|
user={user}
|
||||||
onEmailChanged={this.onEmailChanged}
|
showNextPage={this.showNextPage}
|
||||||
onEmailViewLayout={this.onEmailViewLayout} />);
|
onEmailChanged={this.onEmailChanged}
|
||||||
|
onEmailViewLayout={this.onEmailViewLayout}
|
||||||
|
/>
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Constants.FIRST_RUN_PAGE_EMAIL_VERIFY:
|
case Constants.FIRST_RUN_PAGE_EMAIL_VERIFY:
|
||||||
page = (<EmailVerifyPage
|
page = (
|
||||||
onEmailViewLayout={this.onEmailViewLayout}
|
<EmailVerifyPage
|
||||||
email={this.state.email}
|
onEmailViewLayout={this.onEmailViewLayout}
|
||||||
notify={notify}
|
email={this.state.email}
|
||||||
resendVerificationEmail={resendVerificationEmail} />);
|
notify={notify}
|
||||||
|
resendVerificationEmail={resendVerificationEmail}
|
||||||
|
/>
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Constants.FIRST_RUN_PAGE_WALLET:
|
case Constants.FIRST_RUN_PAGE_WALLET:
|
||||||
page = (<WalletPage
|
page = (
|
||||||
checkSync={checkSync}
|
<WalletPage
|
||||||
hasSyncedWallet={hasSyncedWallet}
|
checkSync={checkSync}
|
||||||
getSyncIsPending={getSyncIsPending}
|
hasSyncedWallet={hasSyncedWallet}
|
||||||
syncApplyIsPending={syncApplyIsPending}
|
getSyncIsPending={getSyncIsPending}
|
||||||
onWalletViewLayout={this.onWalletViewLayout}
|
syncApplyIsPending={syncApplyIsPending}
|
||||||
onPasswordChanged={this.onWalletPasswordChanged} />);
|
onWalletViewLayout={this.onWalletViewLayout}
|
||||||
|
onPasswordChanged={this.onWalletPasswordChanged}
|
||||||
|
/>
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT:
|
case Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT:
|
||||||
page = (<SkipAccountPage
|
page = (
|
||||||
onSkipAccountViewLayout={this.onSkipAccountViewLayout}
|
<SkipAccountPage
|
||||||
onSkipSwitchChanged={this.onSkipSwitchChanged} />);
|
onSkipAccountViewLayout={this.onSkipAccountViewLayout}
|
||||||
|
onSkipSwitchChanged={this.onSkipSwitchChanged}
|
||||||
|
/>
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={firstRunStyle.screenContainer}>
|
<View style={firstRunStyle.screenContainer}>
|
||||||
{page}
|
{page}
|
||||||
{this.state.currentPage && this.state.showBottomContainer &&
|
{this.state.currentPage && this.state.showBottomContainer && (
|
||||||
<View style={firstRunStyle.bottomContainer}>
|
<View style={firstRunStyle.bottomContainer}>
|
||||||
{emailNewPending &&
|
{emailNewPending && (
|
||||||
<ActivityIndicator size="small" color={Colors.White} style={firstRunStyle.pageWaiting} />}
|
<ActivityIndicator size="small" color={Colors.White} style={firstRunStyle.pageWaiting} />
|
||||||
|
)}
|
||||||
|
|
||||||
<View style={firstRunStyle.buttonRow}>
|
<View style={firstRunStyle.buttonRow}>
|
||||||
{([Constants.FIRST_RUN_PAGE_WELCOME, Constants.FIRST_RUN_PAGE_WALLET].indexOf(this.state.currentPage) > -1) && <View />}
|
{[Constants.FIRST_RUN_PAGE_WELCOME, Constants.FIRST_RUN_PAGE_WALLET].indexOf(this.state.currentPage) >
|
||||||
{(Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT === this.state.currentPage ||
|
-1 && <View />}
|
||||||
Constants.FIRST_RUN_PAGE_EMAIL_VERIFY === this.state.currentPage) &&
|
{(Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT === this.state.currentPage ||
|
||||||
<TouchableOpacity style={firstRunStyle.leftButton} onPress={this.handleLeftButtonPressed}>
|
Constants.FIRST_RUN_PAGE_EMAIL_VERIFY === this.state.currentPage) && (
|
||||||
<Text style={firstRunStyle.buttonText}>
|
<TouchableOpacity style={firstRunStyle.leftButton} onPress={this.handleLeftButtonPressed}>
|
||||||
« {Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT === this.state.currentPage ? 'Setup account' : 'Change email'}</Text>
|
<Text style={firstRunStyle.buttonText}>
|
||||||
</TouchableOpacity>}
|
«{' '}
|
||||||
{!emailNewPending && (Constants.FIRST_RUN_PAGE_EMAIL_COLLECT === this.state.currentPage) &&
|
{Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT === this.state.currentPage
|
||||||
<TouchableOpacity style={firstRunStyle.leftButton} onPress={this.handleLeftButtonPressed}>
|
? 'Setup account'
|
||||||
<Text style={firstRunStyle.smallLeftButtonText}>No, thanks »</Text>
|
: 'Change email'}
|
||||||
</TouchableOpacity>}
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
{!emailNewPending && Constants.FIRST_RUN_PAGE_EMAIL_COLLECT === this.state.currentPage && (
|
||||||
|
<TouchableOpacity style={firstRunStyle.leftButton} onPress={this.handleLeftButtonPressed}>
|
||||||
|
<Text style={firstRunStyle.smallLeftButtonText}>No, thanks »</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
|
||||||
{!emailNewPending &&
|
{!emailNewPending && (
|
||||||
<TouchableOpacity style={firstRunStyle.button} onPress={this.handleContinuePressed}>
|
<TouchableOpacity style={firstRunStyle.button} onPress={this.handleContinuePressed}>
|
||||||
{Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT === this.state.currentPage &&
|
{Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT === this.state.currentPage && (
|
||||||
<Text style={firstRunStyle.smallButtonText}>Use LBRY »</Text>}
|
<Text style={firstRunStyle.smallButtonText}>Use LBRY »</Text>
|
||||||
|
)}
|
||||||
|
|
||||||
{Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT !== this.state.currentPage &&
|
{Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT !== this.state.currentPage &&
|
||||||
Constants.FIRST_RUN_PAGE_EMAIL_VERIFY !== this.state.currentPage &&
|
Constants.FIRST_RUN_PAGE_EMAIL_VERIFY !== this.state.currentPage && (
|
||||||
<Text style={firstRunStyle.buttonText}>{Constants.FIRST_RUN_PAGE_WALLET === this.state.currentPage ? 'Use LBRY' : 'Continue'} »</Text>}
|
<Text style={firstRunStyle.buttonText}>
|
||||||
</TouchableOpacity>}
|
{Constants.FIRST_RUN_PAGE_WALLET === this.state.currentPage ? 'Use LBRY' : 'Continue'} »
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>}
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,10 @@ const perform = dispatch => ({
|
||||||
fetchRewards: () => dispatch(doRewardList()),
|
fetchRewards: () => dispatch(doRewardList()),
|
||||||
notify: data => dispatch(doToast(data)),
|
notify: data => dispatch(doToast(data)),
|
||||||
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_REWARDS)),
|
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_REWARDS)),
|
||||||
setPlayerVisible: () => dispatch(doSetPlayerVisible(false))
|
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(RewardsPage);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(RewardsPage);
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Lbry } from 'lbry-redux';
|
import { Lbry } from 'lbry-redux';
|
||||||
import {
|
import { ActivityIndicator, NativeModules, ScrollView, Text, View } from 'react-native';
|
||||||
ActivityIndicator,
|
|
||||||
NativeModules,
|
|
||||||
ScrollView,
|
|
||||||
Text,
|
|
||||||
View
|
|
||||||
} from 'react-native';
|
|
||||||
import Colors from 'styles/colors';
|
import Colors from 'styles/colors';
|
||||||
import Constants from 'constants';
|
import Constants from 'constants';
|
||||||
import Link from 'component/link';
|
import Link from 'component/link';
|
||||||
|
@ -25,7 +19,7 @@ class RewardsPage extends React.PureComponent {
|
||||||
isRewardApproved: false,
|
isRewardApproved: false,
|
||||||
verifyRequestStarted: false,
|
verifyRequestStarted: false,
|
||||||
revealVerification: true,
|
revealVerification: true,
|
||||||
firstRewardClaimed: false
|
firstRewardClaimed: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
scrollView = null;
|
scrollView = null;
|
||||||
|
@ -51,11 +45,11 @@ class RewardsPage extends React.PureComponent {
|
||||||
fetchRewards();
|
fetchRewards();
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
isEmailVerified: (user && user.primary_email && user.has_verified_email),
|
isEmailVerified: user && user.primary_email && user.has_verified_email,
|
||||||
isIdentityVerified: (user && user.is_identity_verified),
|
isIdentityVerified: user && user.is_identity_verified,
|
||||||
isRewardApproved: (user && user.is_reward_approved)
|
isRewardApproved: user && user.is_reward_approved,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.onComponentFocused();
|
this.onComponentFocused();
|
||||||
|
@ -83,9 +77,9 @@ class RewardsPage extends React.PureComponent {
|
||||||
if (user) {
|
if (user) {
|
||||||
// update other checks (if new user data has been retrieved)
|
// update other checks (if new user data has been retrieved)
|
||||||
this.setState({
|
this.setState({
|
||||||
isEmailVerified: (user && user.primary_email && user.has_verified_email),
|
isEmailVerified: user && user.primary_email && user.has_verified_email,
|
||||||
isIdentityVerified: (user && user.is_identity_verified),
|
isIdentityVerified: user && user.is_identity_verified,
|
||||||
isRewardApproved: (user && user.is_reward_approved)
|
isRewardApproved: user && user.is_reward_approved,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +105,13 @@ class RewardsPage extends React.PureComponent {
|
||||||
<View style={[rewardStyle.card, rewardStyle.verification]}>
|
<View style={[rewardStyle.card, rewardStyle.verification]}>
|
||||||
<Text style={rewardStyle.title}>Manual Reward Verification</Text>
|
<Text style={rewardStyle.title}>Manual Reward Verification</Text>
|
||||||
<Text style={rewardStyle.text}>
|
<Text style={rewardStyle.text}>
|
||||||
You need to be manually verified before you can start claiming rewards. Please request to be verified on the <Link style={rewardStyle.greenLink} href="https://discordapp.com/invite/Z3bERWA" text="LBRY Discord server" />.
|
You need to be manually verified before you can start claiming rewards. Please request to be verified on the{' '}
|
||||||
|
<Link
|
||||||
|
style={rewardStyle.greenLink}
|
||||||
|
href="https://discordapp.com/invite/Z3bERWA"
|
||||||
|
text="LBRY Discord server"
|
||||||
|
/>
|
||||||
|
.
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -122,7 +122,7 @@ class RewardsPage extends React.PureComponent {
|
||||||
|
|
||||||
renderUnclaimedRewards() {
|
renderUnclaimedRewards() {
|
||||||
const { claimed, fetching, rewards, user } = this.props;
|
const { claimed, fetching, rewards, user } = this.props;
|
||||||
const unclaimedRewards = (rewards && rewards.length) ? rewards : [];
|
const unclaimedRewards = rewards && rewards.length ? rewards : [];
|
||||||
|
|
||||||
if (fetching) {
|
if (fetching) {
|
||||||
return (
|
return (
|
||||||
|
@ -142,11 +142,15 @@ class RewardsPage extends React.PureComponent {
|
||||||
const isNotEligible = !user || !user.primary_email || !user.has_verified_email || !user.is_reward_approved;
|
const isNotEligible = !user || !user.primary_email || !user.has_verified_email || !user.is_reward_approved;
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
{unclaimedRewards.map(reward => <RewardCard key={reward.reward_type}
|
{unclaimedRewards.map(reward => (
|
||||||
showVerification={this.showVerification}
|
<RewardCard
|
||||||
canClaim={!isNotEligible}
|
key={reward.reward_type}
|
||||||
reward={reward}
|
showVerification={this.showVerification}
|
||||||
reward_type={reward.reward_type} />)}
|
canClaim={!isNotEligible}
|
||||||
|
reward={reward}
|
||||||
|
reward_type={reward.reward_type}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
<CustomRewardCard canClaim={!isNotEligible} showVerification={this.showVerification} />
|
<CustomRewardCard canClaim={!isNotEligible} showVerification={this.showVerification} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -158,7 +162,9 @@ class RewardsPage extends React.PureComponent {
|
||||||
const reversed = claimed.reverse();
|
const reversed = claimed.reverse();
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
{reversed.map(reward => <RewardCard key={reward.transaction_id} reward={reward} />)}
|
{reversed.map(reward => (
|
||||||
|
<RewardCard key={reward.transaction_id} reward={reward} />
|
||||||
|
))}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -170,7 +176,7 @@ class RewardsPage extends React.PureComponent {
|
||||||
this.scrollView.scrollTo({ x: 0, y: 0, animated: true });
|
this.scrollView.scrollTo({ x: 0, y: 0, animated: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { user, navigation } = this.props;
|
const { user, navigation } = this.props;
|
||||||
|
@ -178,18 +184,19 @@ class RewardsPage extends React.PureComponent {
|
||||||
return (
|
return (
|
||||||
<View style={rewardStyle.container}>
|
<View style={rewardStyle.container}>
|
||||||
<UriBar navigation={navigation} />
|
<UriBar navigation={navigation} />
|
||||||
{(!this.state.isEmailVerified || !this.state.isRewardApproved) &&
|
{(!this.state.isEmailVerified || !this.state.isRewardApproved) && <RewardEnrolment navigation={navigation} />}
|
||||||
<RewardEnrolment navigation={navigation} />}
|
|
||||||
|
|
||||||
{(this.state.isEmailVerified && this.state.isRewardApproved) &&
|
{this.state.isEmailVerified && this.state.isRewardApproved && (
|
||||||
<ScrollView
|
<ScrollView
|
||||||
ref={ref => this.scrollView = ref}
|
ref={ref => (this.scrollView = ref)}
|
||||||
keyboardShouldPersistTaps={'handled'}
|
keyboardShouldPersistTaps={'handled'}
|
||||||
style={rewardStyle.scrollContainer}
|
style={rewardStyle.scrollContainer}
|
||||||
contentContainerStyle={rewardStyle.scrollContentContainer}>
|
contentContainerStyle={rewardStyle.scrollContentContainer}
|
||||||
|
>
|
||||||
{this.renderUnclaimedRewards()}
|
{this.renderUnclaimedRewards()}
|
||||||
{this.renderClaimedRewards()}
|
{this.renderClaimedRewards()}
|
||||||
</ScrollView>}
|
</ScrollView>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,14 @@ import {
|
||||||
selectIsSearching,
|
selectIsSearching,
|
||||||
selectSearchValue,
|
selectSearchValue,
|
||||||
makeSelectQueryWithOptions,
|
makeSelectQueryWithOptions,
|
||||||
selectSearchUrisByQuery
|
selectSearchUrisByQuery,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
|
import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
|
||||||
import { selectCurrentRoute } from 'redux/selectors/drawer';
|
import { selectCurrentRoute } from 'redux/selectors/drawer';
|
||||||
import Constants from 'constants';
|
import Constants from 'constants';
|
||||||
import SearchPage from './view';
|
import SearchPage from './view';
|
||||||
|
|
||||||
const select = (state) => ({
|
const select = state => ({
|
||||||
currentRoute: selectCurrentRoute(state),
|
currentRoute: selectCurrentRoute(state),
|
||||||
isSearching: selectIsSearching(state),
|
isSearching: selectIsSearching(state),
|
||||||
query: selectSearchValue(state),
|
query: selectSearchValue(state),
|
||||||
|
@ -22,10 +22,13 @@ const select = (state) => ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
search: (query) => dispatch(doSearch(query, 25)),
|
search: query => dispatch(doSearch(query, 25)),
|
||||||
updateSearchQuery: query => dispatch(doUpdateSearchQuery(query)),
|
updateSearchQuery: query => dispatch(doUpdateSearchQuery(query)),
|
||||||
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_SEARCH)),
|
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_SEARCH)),
|
||||||
setPlayerVisible: () => dispatch(doSetPlayerVisible(false))
|
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(SearchPage);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(SearchPage);
|
||||||
|
|
|
@ -1,13 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Lbry, parseURI, normalizeURI, isURIValid } from 'lbry-redux';
|
import { Lbry, parseURI, normalizeURI, isURIValid } from 'lbry-redux';
|
||||||
import {
|
import { ActivityIndicator, Button, Text, TextInput, View, ScrollView } from 'react-native';
|
||||||
ActivityIndicator,
|
|
||||||
Button,
|
|
||||||
Text,
|
|
||||||
TextInput,
|
|
||||||
View,
|
|
||||||
ScrollView
|
|
||||||
} from 'react-native';
|
|
||||||
import { navigateToUri } from 'utils/helper';
|
import { navigateToUri } from 'utils/helper';
|
||||||
import Colors from 'styles/colors';
|
import Colors from 'styles/colors';
|
||||||
import Constants from 'constants';
|
import Constants from 'constants';
|
||||||
|
@ -21,10 +14,10 @@ class SearchPage extends React.PureComponent {
|
||||||
state = {
|
state = {
|
||||||
currentQuery: null,
|
currentQuery: null,
|
||||||
currentUri: null,
|
currentUri: null,
|
||||||
}
|
};
|
||||||
|
|
||||||
static navigationOptions = {
|
static navigationOptions = {
|
||||||
title: 'Search Results'
|
title: 'Search Results',
|
||||||
};
|
};
|
||||||
|
|
||||||
didFocusListener;
|
didFocusListener;
|
||||||
|
@ -49,11 +42,11 @@ class SearchPage extends React.PureComponent {
|
||||||
if (searchQuery && searchQuery.trim().length > 0) {
|
if (searchQuery && searchQuery.trim().length > 0) {
|
||||||
this.setState({
|
this.setState({
|
||||||
currentQuery: searchQuery,
|
currentQuery: searchQuery,
|
||||||
currentUri: (isURIValid(searchQuery)) ? normalizeURI(searchQuery) : null
|
currentUri: isURIValid(searchQuery) ? normalizeURI(searchQuery) : null,
|
||||||
});
|
});
|
||||||
search(searchQuery);
|
search(searchQuery);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.onComponentFocused();
|
this.onComponentFocused();
|
||||||
|
@ -70,7 +63,7 @@ class SearchPage extends React.PureComponent {
|
||||||
if (query && query.trim().length > 0 && query !== this.state.currentQuery) {
|
if (query && query.trim().length > 0 && query !== this.state.currentQuery) {
|
||||||
this.setState({
|
this.setState({
|
||||||
currentQuery: query,
|
currentQuery: query,
|
||||||
currentUri: (isURIValid(query)) ? normalizeURI(query) : null
|
currentUri: isURIValid(query) ? normalizeURI(query) : null,
|
||||||
});
|
});
|
||||||
search(query);
|
search(query);
|
||||||
}
|
}
|
||||||
|
@ -84,52 +77,61 @@ class SearchPage extends React.PureComponent {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSearchSubmitted = (keywords) => {
|
handleSearchSubmitted = keywords => {
|
||||||
const { search } = this.props;
|
const { search } = this.props;
|
||||||
this.setState({ currentUri: (isURIValid(keywords)) ? normalizeURI(keywords) : null });
|
this.setState({ currentUri: isURIValid(keywords) ? normalizeURI(keywords) : null });
|
||||||
search(keywords);
|
search(keywords);
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { isSearching, navigation, query, uris, urisByQuery } = this.props;
|
const { isSearching, navigation, query, uris, urisByQuery } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={searchStyle.container}>
|
<View style={searchStyle.container}>
|
||||||
<UriBar value={query}
|
<UriBar value={query} navigation={navigation} onSearchSubmitted={this.handleSearchSubmitted} />
|
||||||
navigation={navigation}
|
{isSearching && (
|
||||||
onSearchSubmitted={this.handleSearchSubmitted} />
|
|
||||||
{isSearching &&
|
|
||||||
<View style={searchStyle.busyContainer}>
|
<View style={searchStyle.busyContainer}>
|
||||||
<ActivityIndicator size="large" color={Colors.LbryGreen} style={searchStyle.loading} />
|
<ActivityIndicator size="large" color={Colors.LbryGreen} style={searchStyle.loading} />
|
||||||
</View>}
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isSearching && (
|
||||||
{!isSearching &&
|
<ScrollView
|
||||||
<ScrollView
|
style={searchStyle.scrollContainer}
|
||||||
style={searchStyle.scrollContainer}
|
contentContainerStyle={searchStyle.scrollPadding}
|
||||||
contentContainerStyle={searchStyle.scrollPadding}
|
keyboardShouldPersistTaps={'handled'}
|
||||||
keyboardShouldPersistTaps={'handled'}>
|
>
|
||||||
{this.state.currentUri &&
|
{this.state.currentUri && (
|
||||||
<FileListItem
|
<FileListItem
|
||||||
key={this.state.currentUri}
|
key={this.state.currentUri}
|
||||||
uri={this.state.currentUri}
|
uri={this.state.currentUri}
|
||||||
featuredResult={true}
|
featuredResult={true}
|
||||||
style={searchStyle.featuredResultItem}
|
style={searchStyle.featuredResultItem}
|
||||||
navigation={navigation}
|
navigation={navigation}
|
||||||
onPress={() => navigateToUri(navigation, this.state.currentUri)}
|
onPress={() => navigateToUri(navigation, this.state.currentUri)}
|
||||||
/>}
|
/>
|
||||||
{(uris && uris.length) ? (
|
)}
|
||||||
uris.map(uri => <FileListItem key={uri}
|
{uris && uris.length
|
||||||
uri={uri}
|
? uris.map(uri => (
|
||||||
style={searchStyle.resultItem}
|
<FileListItem
|
||||||
navigation={navigation}
|
key={uri}
|
||||||
onPress={() => navigateToUri(navigation, uri)}/>)
|
uri={uri}
|
||||||
) : null }
|
style={searchStyle.resultItem}
|
||||||
{(!uris || uris.length === 0) &&
|
navigation={navigation}
|
||||||
<View style={searchStyle.noResults}>
|
onPress={() => navigateToUri(navigation, uri)}
|
||||||
<Text style={searchStyle.noResultsText}>There are no results to display for <Text style={searchStyle.boldText}>{query}</Text>. Please try a different search term.</Text>
|
/>
|
||||||
</View>}
|
))
|
||||||
</ScrollView>}
|
: null}
|
||||||
|
{(!uris || uris.length === 0) && (
|
||||||
|
<View style={searchStyle.noResults}>
|
||||||
|
<Text style={searchStyle.noResultsText}>
|
||||||
|
There are no results to display for <Text style={searchStyle.boldText}>{query}</Text>. Please try a
|
||||||
|
different search term.
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</ScrollView>
|
||||||
|
)}
|
||||||
<FloatingWalletBalance navigation={navigation} />
|
<FloatingWalletBalance navigation={navigation} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -22,4 +22,7 @@ const perform = dispatch => ({
|
||||||
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
|
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(SettingsPage);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(SettingsPage);
|
||||||
|
|
|
@ -8,8 +8,8 @@ import settingsStyle from 'styles/settings';
|
||||||
|
|
||||||
class SettingsPage extends React.PureComponent {
|
class SettingsPage extends React.PureComponent {
|
||||||
static navigationOptions = {
|
static navigationOptions = {
|
||||||
title: 'Settings'
|
title: 'Settings',
|
||||||
}
|
};
|
||||||
|
|
||||||
didFocusListener;
|
didFocusListener;
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ class SettingsPage extends React.PureComponent {
|
||||||
const { pushDrawerStack, setPlayerVisible } = this.props;
|
const { pushDrawerStack, setPlayerVisible } = this.props;
|
||||||
pushDrawerStack();
|
pushDrawerStack();
|
||||||
setPlayerVisible();
|
setPlayerVisible();
|
||||||
}
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.onComponentFocused();
|
this.onComponentFocused();
|
||||||
|
@ -50,24 +50,29 @@ class SettingsPage extends React.PureComponent {
|
||||||
navigation,
|
navigation,
|
||||||
popDrawerStack,
|
popDrawerStack,
|
||||||
showNsfw,
|
showNsfw,
|
||||||
setClientSetting
|
setClientSetting,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
// default to true if the setting is null or undefined
|
// default to true if the setting is null or undefined
|
||||||
const actualKeepDaemonRunning = (keepDaemonRunning === null || keepDaemonRunning === undefined) ? true : keepDaemonRunning;
|
const actualKeepDaemonRunning =
|
||||||
|
keepDaemonRunning === null || keepDaemonRunning === undefined ? true : keepDaemonRunning;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={settingsStyle.container}>
|
<View style={settingsStyle.container}>
|
||||||
<PageHeader title={"Settings"}
|
<PageHeader title={'Settings'} onBackPressed={() => navigateBack(navigation, drawerStack, popDrawerStack)} />
|
||||||
onBackPressed={() => navigateBack(navigation, drawerStack, popDrawerStack)} />
|
|
||||||
<ScrollView style={settingsStyle.scrollContainer}>
|
<ScrollView style={settingsStyle.scrollContainer}>
|
||||||
<View style={settingsStyle.row}>
|
<View style={settingsStyle.row}>
|
||||||
<View style={settingsStyle.switchText}>
|
<View style={settingsStyle.switchText}>
|
||||||
<Text style={settingsStyle.label}>Enable background media playback</Text>
|
<Text style={settingsStyle.label}>Enable background media playback</Text>
|
||||||
<Text style={settingsStyle.description}>Enable this option to play audio or video in the background when the app is suspended.</Text>
|
<Text style={settingsStyle.description}>
|
||||||
|
Enable this option to play audio or video in the background when the app is suspended.
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={settingsStyle.switchContainer}>
|
<View style={settingsStyle.switchContainer}>
|
||||||
<Switch value={backgroundPlayEnabled} onValueChange={(value) => setClientSetting(SETTINGS.BACKGROUND_PLAY_ENABLED, value)} />
|
<Switch
|
||||||
|
value={backgroundPlayEnabled}
|
||||||
|
onValueChange={value => setClientSetting(SETTINGS.BACKGROUND_PLAY_ENABLED, value)}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
@ -76,22 +81,28 @@ class SettingsPage extends React.PureComponent {
|
||||||
<Text style={settingsStyle.label}>Show NSFW content</Text>
|
<Text style={settingsStyle.label}>Show NSFW content</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={settingsStyle.switchContainer}>
|
<View style={settingsStyle.switchContainer}>
|
||||||
<Switch value={showNsfw} onValueChange={(value) => setClientSetting(SETTINGS.SHOW_NSFW, value)} />
|
<Switch value={showNsfw} onValueChange={value => setClientSetting(SETTINGS.SHOW_NSFW, value)} />
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={settingsStyle.row}>
|
<View style={settingsStyle.row}>
|
||||||
<View style={settingsStyle.switchText}>
|
<View style={settingsStyle.switchText}>
|
||||||
<Text style={settingsStyle.label}>Keep the daemon background service running after closing the app</Text>
|
<Text style={settingsStyle.label}>Keep the daemon background service running after closing the app</Text>
|
||||||
<Text style={settingsStyle.description}>Enable this option for quicker app launch and to keep the synchronisation with the blockchain up to date.</Text>
|
<Text style={settingsStyle.description}>
|
||||||
|
Enable this option for quicker app launch and to keep the synchronisation with the blockchain up to
|
||||||
|
date.
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={settingsStyle.switchContainer}>
|
<View style={settingsStyle.switchContainer}>
|
||||||
<Switch value={actualKeepDaemonRunning} onValueChange={(value) => {
|
<Switch
|
||||||
setClientSetting(SETTINGS.KEEP_DAEMON_RUNNING, value);
|
value={actualKeepDaemonRunning}
|
||||||
if (NativeModules.DaemonServiceControl) {
|
onValueChange={value => {
|
||||||
NativeModules.DaemonServiceControl.setKeepDaemonRunning(value);
|
setClientSetting(SETTINGS.KEEP_DAEMON_RUNNING, value);
|
||||||
}
|
if (NativeModules.DaemonServiceControl) {
|
||||||
}} />
|
NativeModules.DaemonServiceControl.setKeepDaemonRunning(value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue