lbry.tv hybrid mode #132

Merged
akinwale merged 8 commits from lbry-tv-experiment into master 2020-03-20 08:25:39 +01:00
49 changed files with 479 additions and 12485 deletions

@ -1 +1 @@
Subproject commit bfd3c711abc4f693dbb68b06fdfce67036b1e4a3 Subproject commit e9f25546979eee36751353c187dc9944ab1e508e

12178
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -15,8 +15,8 @@
"base-64": "^0.1.0", "base-64": "^0.1.0",
"@expo/vector-icons": "^8.1.0", "@expo/vector-icons": "^8.1.0",
"gfycat-style-urls": "^1.0.3", "gfycat-style-urls": "^1.0.3",
"lbry-redux": "lbryio/lbry-redux#84e697079968364fe526020086c8a44f4d2ef391", "lbry-redux": "lbryio/lbry-redux#69ffd110dbf3633e5847f61f008751edec033017",
"lbryinc": "lbryio/lbryinc#28afb9b06c3d142bad8347939c043a21b6cb1ae1", "lbryinc": "lbryio/lbryinc#021ac75d9aa2db488cfff8e9be320402f038f955",
"lodash": ">=4.17.11", "lodash": ">=4.17.11",
"merge": ">=1.2.1", "merge": ">=1.2.1",
"moment": "^2.22.1", "moment": "^2.22.1",

View file

@ -42,8 +42,10 @@ import {
} from 'react-native'; } from 'react-native';
import { selectDrawerStack } from 'redux/selectors/drawer'; import { selectDrawerStack } from 'redux/selectors/drawer';
import { import {
Lbry,
ACTIONS, ACTIONS,
SETTINGS, SETTINGS,
doBalanceSubscribe,
doDismissToast, doDismissToast,
doPopulateSharedUserState, doPopulateSharedUserState,
doPreferenceGet, doPreferenceGet,
@ -52,6 +54,8 @@ import {
} from 'lbry-redux'; } from 'lbry-redux';
import { import {
Lbryio, Lbryio,
doBlackListedOutpointsSubscribe,
doFilteredOutpointsSubscribe,
doGetSync, doGetSync,
doUserCheckEmailVerified, doUserCheckEmailVerified,
doUserEmailVerify, doUserEmailVerify,
@ -75,6 +79,7 @@ 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';
import Snackbar from 'react-native-snackbar'; import Snackbar from 'react-native-snackbar';
import { doSetSdkReady } from 'redux/actions/settings';
const SYNC_GET_INTERVAL = 1000 * 60 * 5; // every 5 minutes const SYNC_GET_INTERVAL = 1000 * 60 * 5; // every 5 minutes
@ -327,6 +332,7 @@ class AppWithNavigationState extends React.Component {
this.emailVerifyCheckInterval = setInterval(() => this.checkEmailVerification(), 5000); this.emailVerifyCheckInterval = setInterval(() => this.checkEmailVerification(), 5000);
Linking.addEventListener('url', this._handleUrl); Linking.addEventListener('url', this._handleUrl);
DeviceEventEmitter.addListener('onSdkReady', this.handleSdkReady);
DeviceEventEmitter.addListener('onDownloadAborted', this.handleDownloadAborted); DeviceEventEmitter.addListener('onDownloadAborted', this.handleDownloadAborted);
DeviceEventEmitter.addListener('onDownloadStarted', this.handleDownloadStarted); DeviceEventEmitter.addListener('onDownloadStarted', this.handleDownloadStarted);
DeviceEventEmitter.addListener('onDownloadUpdated', this.handleDownloadUpdated); DeviceEventEmitter.addListener('onDownloadUpdated', this.handleDownloadUpdated);
@ -366,6 +372,46 @@ class AppWithNavigationState extends React.Component {
); );
}; };
handleSdkReady = () => {
const { dispatch } = this.props;
dispatch(doSetSdkReady());
dispatch(doBalanceSubscribe());
dispatch(doBlackListedOutpointsSubscribe());
dispatch(doFilteredOutpointsSubscribe());
Lbry.wallet_status().then(secureWalletStatus => {
// For now, automatically unlock the wallet if a password is set so that downloads work
NativeModules.UtilityModule.getSecureValue(Constants.KEY_WALLET_PASSWORD).then(password => {
if ((secureWalletStatus.is_encrypted && !secureWalletStatus.is_locked) || secureWalletStatus.is_locked) {
this.setState({
message: __('Unlocking account'),
details: __('Decrypting wallet'),
});
// unlock the wallet and then finish the splash screen
Lbry.wallet_unlock({ password: password || '' }).then(unlocked => {
if (unlocked) {
} else {
// this.handleAccountUnlockFailed();
}
});
}
});
});
};
handleAccountUnlockFailed() {
const { dispatch } = this.props;
dispatch(
doToast({
message: __(
'Your wallet failed to unlock, which means you may not be able to play any videos or access your funds. Restart the app to fix this.',
),
isError: true,
}),
);
}
handleDownloadStarted = evt => { handleDownloadStarted = evt => {
const { dispatch } = this.props; const { dispatch } = this.props;
const { uri, outpoint, fileInfo } = evt; const { uri, outpoint, fileInfo } = evt;
@ -401,6 +447,7 @@ class AppWithNavigationState extends React.Component {
}; };
componentWillUnmount() { componentWillUnmount() {
DeviceEventEmitter.removeListener('onSdkReady', this.handleSdkReady);
DeviceEventEmitter.removeListener('onDownloadAborted', this.handleDownloadAborted); DeviceEventEmitter.removeListener('onDownloadAborted', this.handleDownloadAborted);
DeviceEventEmitter.removeListener('onDownloadStarted', this.handleDownloadStarted); DeviceEventEmitter.removeListener('onDownloadStarted', this.handleDownloadStarted);
DeviceEventEmitter.removeListener('onDownloadUpdated', this.handleDownloadUpdated); DeviceEventEmitter.removeListener('onDownloadUpdated', this.handleDownloadUpdated);

View file

@ -19,7 +19,4 @@ const perform = dispatch => ({
resolveUri: uri => dispatch(doResolveUri(uri)), resolveUri: uri => dispatch(doResolveUri(uri)),
}); });
export default connect( export default connect(select, perform)(ChannelIconItem);
select,
perform
)(ChannelIconItem);

View file

@ -23,7 +23,4 @@ const perform = dispatch => ({
claimSearch: options => dispatch(doClaimSearch(options)), claimSearch: options => dispatch(doClaimSearch(options)),
}); });
export default connect( export default connect(select, perform)(ClaimList);
select,
perform,
)(ClaimList);

View file

@ -36,7 +36,4 @@ const perform = dispatch => ({
setPlayerVisible: (visible, uri) => dispatch(doSetPlayerVisible(visible, uri)), setPlayerVisible: (visible, uri) => dispatch(doSetPlayerVisible(visible, uri)),
}); });
export default connect( export default connect(select, perform)(ClaimResultItem);
select,
perform,
)(ClaimResultItem);

View file

@ -1,18 +1,17 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { doFetchChannelListMine, selectMyChannelClaims } from 'lbry-redux'; import { doToast, selectMyChannelClaims } from 'lbry-redux';
import { selectUser } from 'lbryinc'; import { selectUser } from 'lbryinc';
import { selectSdkReady } from 'redux/selectors/settings';
import DrawerContent from './view'; import DrawerContent from './view';
const select = state => ({ const select = state => ({
channels: selectMyChannelClaims(state), channels: selectMyChannelClaims(state),
sdkReady: selectSdkReady(state),
user: selectUser(state), user: selectUser(state),
}); });
const perform = dispatch => ({ const perform = dispatch => ({
fetchChannelListMine: () => dispatch(doFetchChannelListMine(1, 99999, true)), notify: data => dispatch(doToast(data)),
}); });
export default connect( export default connect(select, perform)(DrawerContent);
select,
perform,
)(DrawerContent);

View file

@ -31,13 +31,17 @@ const groupedMenuItems = {
}; };
const groupNames = Object.keys(groupedMenuItems); const groupNames = Object.keys(groupedMenuItems);
const routesRequiringSdkReady = [
Constants.DRAWER_ROUTE_CHANNEL_CREATOR,
Constants.DRAWER_ROUTE_MY_LBRY,
Constants.DRAWER_ROUTE_PUBLISHES,
Constants.DRAWER_ROUTE_PUBLISH,
Constants.DRAWER_ROUTE_WALLET,
Constants.DRAWER_ROUTE_REWARDS,
Constants.DRAWER_ROUTE_INVITES,
];
class DrawerContent extends React.PureComponent { class DrawerContent extends React.PureComponent {
componentDidMount() {
const { fetchChannelListMine } = this.props;
fetchChannelListMine();
}
getAvatarImageUrl = () => { getAvatarImageUrl = () => {
const { channels = [] } = this.props; const { channels = [] } = this.props;
if (channels) { if (channels) {
@ -62,6 +66,21 @@ class DrawerContent extends React.PureComponent {
}); });
}; };
handleItemPress = routeName => {
const { navigation, notify, sdkReady } = this.props;
if (!sdkReady && routesRequiringSdkReady.includes(routeName)) {
if (navigation.closeDrawer) {
navigation.closeDrawer();
}
notify({
message: __('The background service is still initializing. Please try again when initialization is complete.'),
});
return;
}
navigation.navigate({ routeName: routeName });
};
render() { render() {
const { activeTintColor, navigation, user, onItemPress } = this.props; const { activeTintColor, navigation, user, onItemPress } = this.props;
const { state } = navigation; const { state } = navigation;
@ -157,7 +176,7 @@ class DrawerContent extends React.PureComponent {
focused ? discoverStyle.menuItemTouchAreaFocused : null, focused ? discoverStyle.menuItemTouchAreaFocused : null,
]} ]}
key={item.label} key={item.label}
onPress={() => navigation.navigate({ routeName: item.route })} onPress={() => this.handleItemPress(item.route)}
delayPressIn={0} delayPressIn={0}
> >
<View style={discoverStyle.menuItemIcon}> <View style={discoverStyle.menuItemIcon}>

View file

@ -35,7 +35,4 @@ const perform = dispatch => ({
setPlayerVisible: (visible, uri) => dispatch(doSetPlayerVisible(visible, uri)), setPlayerVisible: (visible, uri) => dispatch(doSetPlayerVisible(visible, uri)),
}); });
export default connect( export default connect(select, perform)(FileItem);
select,
perform,
)(FileItem);

View file

@ -36,7 +36,4 @@ const perform = dispatch => ({
setPlayerVisible: (visible, uri) => dispatch(doSetPlayerVisible(visible, uri)), setPlayerVisible: (visible, uri) => dispatch(doSetPlayerVisible(visible, uri)),
}); });
export default connect( export default connect(select, perform)(FileListItem);
select,
perform,
)(FileListItem);

View file

@ -125,7 +125,9 @@ class FileListItem extends React.PureComponent {
const outpointsToHide = !blackListedOutpoints const outpointsToHide = !blackListedOutpoints
? filteredOutpoints ? filteredOutpoints
: blackListedOutpoints.concat(filteredOutpoints); : blackListedOutpoints.concat(filteredOutpoints);
shouldHide = outpointsToHide.some(outpoint => outpoint.txid === claim.txid && outpoint.nout === claim.nout); shouldHide = outpointsToHide.some(
outpoint => outpoint && outpoint.txid === claim.txid && outpoint.nout === claim.nout,
);
} }
// TODO: hide channels on tag pages? // TODO: hide channels on tag pages?
@ -143,10 +145,14 @@ class FileListItem extends React.PureComponent {
<View> <View>
{isRepost && ( {isRepost && (
<View style={fileListStyle.repostInfo}> <View style={fileListStyle.repostInfo}>
<Icon name={"retweet"} size={14} style={fileListStyle.repostIcon} /> <Icon name={'retweet'} size={14} style={fileListStyle.repostIcon} />
<Text style={fileListStyle.repostChannelName}> <Text style={fileListStyle.repostChannelName}>
<Link text={repostChannel} <Link
onPress={() => navigateToUri(navigation, normalizeURI(repostChannelUrl), null, false, null, false)} /> reposted</Text> text={repostChannel}
onPress={() => navigateToUri(navigation, normalizeURI(repostChannelUrl), null, false, null, false)}
/>{' '}
reposted
</Text>
</View> </View>
)} )}

View file

@ -0,0 +1,4 @@
import { connect } from 'react-redux';
import SdkLoadingStatus from './view';
export default connect()(SdkLoadingStatus);

View file

@ -0,0 +1,15 @@
import { ActivityIndicator, Text, View } from 'react-native';
import React from 'react';
import discoverStyle from 'styles/discover';
import Colors from 'styles/colors';
export default class SdkLoadingStatus extends React.PureComponent {
render() {
return (
<View style={discoverStyle.sdkLoading}>
<ActivityIndicator color={Colors.White} size={'small'} />
<Text style={discoverStyle.sdkLoadingText}>{__('The LBRY background service is initializing...')}</Text>
</View>
);
}
}

View file

@ -23,7 +23,4 @@ const perform = dispatch => ({
unsubscribe: subscription => doChannelUnsubscribe(subscription), unsubscribe: subscription => doChannelUnsubscribe(subscription),
}); });
export default connect( export default connect(select, perform)(SuggestedSubscriptionItem);
select,
perform,
)(SuggestedSubscriptionItem);

View file

@ -16,7 +16,4 @@ const perform = dispatch => ({
claimSearch: options => dispatch(doClaimSearch(options)), claimSearch: options => dispatch(doClaimSearch(options)),
}); });
export default connect( export default connect(select, perform)(SuggestedSubscriptions);
select,
perform
)(SuggestedSubscriptions);

View file

@ -24,7 +24,4 @@ const perform = dispatch => ({
claimSearch: options => dispatch(doClaimSearch(options)), claimSearch: options => dispatch(doClaimSearch(options)),
}); });
export default connect( export default connect(select, perform)(SuggestedSubscriptionsGrid);
select,
perform,
)(SuggestedSubscriptionsGrid);

View file

@ -46,6 +46,8 @@ const Constants = {
SETTING_DEVICE_WALLET_SYNCED: 'deviceWalletSynced', SETTING_DEVICE_WALLET_SYNCED: 'deviceWalletSynced',
SETTING_DHT_ENABLED: 'dhtEnabled', SETTING_DHT_ENABLED: 'dhtEnabled',
ACTION_SDK_READY: 'SDK_READY',
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',

View file

@ -55,6 +55,10 @@ import settingsReducer from 'redux/reducers/settings';
import thunk from 'redux-thunk'; import thunk from 'redux-thunk';
window.__ = __; window.__ = __;
if (!NativeModules.UtilityModule.dhtEnabled) {
Lbry.alternateConnectionString = 'https://api.lbry.tv/api/v1/proxy';
Lbry.methodsUsingAlternateConnectionString = ['claim_search', 'resolve'];
}
const globalExceptionHandler = (error, isFatal) => { const globalExceptionHandler = (error, isFatal) => {
if (error && NativeModules.Firebase) { if (error && NativeModules.Firebase) {

View file

@ -22,6 +22,7 @@ import {
import { doUpdateChannelFormState, doClearChannelFormState } from 'redux/actions/form'; import { doUpdateChannelFormState, doClearChannelFormState } from 'redux/actions/form';
import { selectDrawerStack } from 'redux/selectors/drawer'; import { selectDrawerStack } from 'redux/selectors/drawer';
import { selectChannelFormState, selectHasChannelFormState } from 'redux/selectors/form'; import { selectChannelFormState, selectHasChannelFormState } from 'redux/selectors/form';
import { selectSdkReady } from 'redux/selectors/settings';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import ChannelCreator from './view'; import ChannelCreator from './view';
@ -33,6 +34,7 @@ const select = state => ({
fetchingChannels: selectFetchingMyChannels(state), fetchingChannels: selectFetchingMyChannels(state),
balance: selectBalance(state), balance: selectBalance(state),
hasFormState: selectHasChannelFormState(state), hasFormState: selectHasChannelFormState(state),
sdkReady: selectSdkReady(state),
updatingChannel: selectUpdatingChannel(state), updatingChannel: selectUpdatingChannel(state),
updateChannelError: selectUpdateChannelError(state), updateChannelError: selectUpdateChannelError(state),
}); });
@ -52,7 +54,4 @@ const perform = dispatch => ({
setExplicitNavigateBack: flag => dispatch(doSetExplicitNavigateBack(flag)), setExplicitNavigateBack: flag => dispatch(doSetExplicitNavigateBack(flag)),
}); });
export default connect( export default connect(select, perform)(ChannelCreator);
select,
perform,
)(ChannelCreator);

View file

@ -176,9 +176,7 @@ export default class ChannelCreator extends React.PureComponent {
NativeModules.Firebase.setCurrentScreen('Channels').then(result => { NativeModules.Firebase.setCurrentScreen('Channels').then(result => {
pushDrawerStack(Constants.DRAWER_ROUTE_CHANNEL_CREATOR, navigation.state.params ? navigation.state.params : null); pushDrawerStack(Constants.DRAWER_ROUTE_CHANNEL_CREATOR, navigation.state.params ? navigation.state.params : null);
setPlayerVisible(); setPlayerVisible();
if (!fetchingChannels) {
fetchChannelListMine(); fetchChannelListMine();
}
DeviceEventEmitter.addListener('onDocumentPickerFilePicked', this.onFilePicked); DeviceEventEmitter.addListener('onDocumentPickerFilePicked', this.onFilePicked);
DeviceEventEmitter.addListener('onDocumentPickerCanceled', this.onPickerCanceled); DeviceEventEmitter.addListener('onDocumentPickerCanceled', this.onPickerCanceled);
@ -802,7 +800,15 @@ export default class ChannelCreator extends React.PureComponent {
}; };
render() { render() {
const { abandoningClaimIds, balance, fetchingChannels, updatingChannel, channels = [], navigation } = this.props; const {
abandoningClaimIds,
balance,
fetchingChannels,
sdkReady,
updatingChannel,
channels = [],
navigation,
} = this.props;
const { const {
autoStyle, autoStyle,
autoStyles, autoStyles,
@ -826,6 +832,19 @@ export default class ChannelCreator extends React.PureComponent {
const hasChannels = channels && channels.length > 0; const hasChannels = channels && channels.length > 0;
if (!sdkReady) {
return (
<View style={channelCreatorStyle.container}>
<UriBar navigation={navigation} />
<EmptyStateView
message={__(
'The background service is still initializing. You can still explore and watch content during the initialization process.',
)}
/>
</View>
);
}
return ( return (
<View style={channelCreatorStyle.container}> <View style={channelCreatorStyle.container}>
<UriBar <UriBar

View file

@ -13,7 +13,7 @@ import {
} from 'lbryinc'; } from 'lbryinc';
import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer'; import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import { doSetClientSetting, doSetSortByItem, doSetTimeItem } from 'redux/actions/settings'; import { doSetClientSetting, doSetSortByItem, doSetTimeItem } from 'redux/actions/settings';
import { makeSelectClientSetting, selectSortByItem, selectTimeItem } from 'redux/selectors/settings'; import { makeSelectClientSetting, selectSdkReady, selectSortByItem, selectTimeItem } from 'redux/selectors/settings';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import DiscoverPage from './view'; import DiscoverPage from './view';
@ -27,6 +27,7 @@ const select = state => ({
followedTags: selectFollowedTags(state), followedTags: selectFollowedTags(state),
ratingReminderDisabled: makeSelectClientSetting(Constants.SETTING_RATING_REMINDER_DISABLED)(state), ratingReminderDisabled: makeSelectClientSetting(Constants.SETTING_RATING_REMINDER_DISABLED)(state),
ratingReminderLastShown: makeSelectClientSetting(Constants.SETTING_RATING_REMINDER_LAST_SHOWN)(state), ratingReminderLastShown: makeSelectClientSetting(Constants.SETTING_RATING_REMINDER_LAST_SHOWN)(state),
sdkReady: selectSdkReady(state),
sortByItem: selectSortByItem(state), sortByItem: selectSortByItem(state),
timeItem: selectTimeItem(state), timeItem: selectTimeItem(state),
unreadSubscriptions: selectUnreadSubscriptions(state), unreadSubscriptions: selectUnreadSubscriptions(state),
@ -46,7 +47,4 @@ const perform = dispatch => ({
setTimeItem: item => dispatch(doSetTimeItem(item)), setTimeItem: item => dispatch(doSetTimeItem(item)),
}); });
export default connect( export default connect(select, perform)(DiscoverPage);
select,
perform,
)(DiscoverPage);

View file

@ -23,6 +23,7 @@ import Icon from 'react-native-vector-icons/FontAwesome5';
import Link from 'component/link'; import Link from 'component/link';
import ModalTagSelector from 'component/modalTagSelector'; import ModalTagSelector from 'component/modalTagSelector';
import ModalPicker from 'component/modalPicker'; import ModalPicker from 'component/modalPicker';
import SdkLoadingStatus from 'component/sdkLoadingStatus';
import UriBar from 'component/uriBar'; import UriBar from 'component/uriBar';
import _ from 'lodash'; import _ from 'lodash';
@ -268,8 +269,8 @@ class DiscoverPage extends React.PureComponent {
); );
render() { render() {
const { currentRoute, navigation, sortByItem, timeItem } = this.props; const { currentRoute, navigation, sdkReady, sortByItem, timeItem } = this.props;
const { orderBy, showModalTagSelector, showSortPicker, showTimePicker } = this.state; const { showModalTagSelector, showSortPicker, showTimePicker } = this.state;
return ( return (
<View style={discoverStyle.container}> <View style={discoverStyle.container}>
@ -289,7 +290,7 @@ class DiscoverPage extends React.PureComponent {
keyExtractor={(item, index) => item} keyExtractor={(item, index) => item}
/> />
)} )}
{!showModalTagSelector && !showSortPicker && !showTimePicker && ( {sdkReady && !showModalTagSelector && !showSortPicker && !showTimePicker && (
<FloatingWalletBalance navigation={navigation} /> <FloatingWalletBalance navigation={navigation} />
)} )}
{showModalTagSelector && ( {showModalTagSelector && (
@ -316,6 +317,8 @@ class DiscoverPage extends React.PureComponent {
items={Constants.CLAIM_SEARCH_TIME_ITEMS} items={Constants.CLAIM_SEARCH_TIME_ITEMS}
/> />
)} )}
{!sdkReady && <SdkLoadingStatus />}
</View> </View>
); );
} }

View file

@ -11,15 +11,17 @@ import {
import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer'; import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import { doDeleteFile } from 'redux/actions/file'; import { doDeleteFile } from 'redux/actions/file';
import { selectCurrentRoute } from 'redux/selectors/drawer'; import { selectCurrentRoute } from 'redux/selectors/drawer';
import { selectSdkReady } from 'redux/selectors/settings';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
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),
downloadedUris: selectDownloadedUris(state), downloadedUris: selectDownloadedUris(state),
fileInfos: selectFileInfosDownloaded(state),
fetching: selectIsFetchingFileList(state) || selectIsFetchingClaimListMine(state), fetching: selectIsFetchingFileList(state) || selectIsFetchingClaimListMine(state),
sdkReady: selectSdkReady(state),
}); });
const perform = dispatch => ({ const perform = dispatch => ({
@ -32,7 +34,4 @@ const perform = dispatch => ({
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)), setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
}); });
export default connect( export default connect(select, perform)(DownloadsPage);
select,
perform
)(DownloadsPage);

View file

@ -144,11 +144,24 @@ class DownloadsPage extends React.PureComponent {
}; };
render() { render() {
const { fetching, claims, downloadedUris, fileInfos, navigation } = this.props; const { downloadedUris, fetching, navigation, sdkReady } = this.props;
const { selectionMode, selectedUris } = this.state; const { selectionMode, selectedUris } = this.state;
const filteredUris = this.getFilteredUris(); const filteredUris = this.getFilteredUris();
const hasDownloads = filteredUris && filteredUris.length > 0; const hasDownloads = filteredUris && filteredUris.length > 0;
if (!sdkReady) {
return (
<View style={downloadsStyle.container}>
<UriBar navigation={navigation} />
<EmptyStateView
message={__(
'The background service is still initializing. You can still explore and watch content during the initialization process.',
)}
/>
</View>
);
}
return ( return (
<View style={downloadsStyle.container}> <View style={downloadsStyle.container}>
<UriBar <UriBar

View file

@ -41,6 +41,7 @@ import { doDeleteFile, doStopDownloadingFile } from 'redux/actions/file';
import { doPushDrawerStack, doPopDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer'; import { doPushDrawerStack, doPopDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import { doToggleFullscreenMode } from 'redux/actions/settings'; import { doToggleFullscreenMode } from 'redux/actions/settings';
import { selectDrawerStack, makeSelectPlayerVisible } from 'redux/selectors/drawer'; import { selectDrawerStack, makeSelectPlayerVisible } from 'redux/selectors/drawer';
import { selectSdkReady } from 'redux/selectors/settings';
import FilePage from './view'; import FilePage from './view';
const select = (state, props) => { const select = (state, props) => {
@ -66,6 +67,7 @@ const select = (state, props) => {
failedPurchaseUris: selectFailedPurchaseUris(state), failedPurchaseUris: selectFailedPurchaseUris(state),
myClaimUris: selectMyClaimUrisWithoutChannels(state), myClaimUris: selectMyClaimUrisWithoutChannels(state),
purchaseUriErrorMessage: selectPurchaseUriErrorMessage(state), purchaseUriErrorMessage: selectPurchaseUriErrorMessage(state),
sdkReady: selectSdkReady(state),
streamingUrl: makeSelectStreamingUrlForUri(contentUri)(state), streamingUrl: makeSelectStreamingUrlForUri(contentUri)(state),
thumbnail: makeSelectThumbnailForUri(contentUri)(state), thumbnail: makeSelectThumbnailForUri(contentUri)(state),
title: makeSelectTitleForUri(contentUri)(state), title: makeSelectTitleForUri(contentUri)(state),
@ -99,7 +101,4 @@ const perform = dispatch => ({
toggleFullscreenMode: mode => dispatch(doToggleFullscreenMode(mode)), toggleFullscreenMode: mode => dispatch(doToggleFullscreenMode(mode)),
}); });
export default connect( export default connect(select, perform)(FilePage);
select,
perform,
)(FilePage);

View file

@ -43,7 +43,6 @@ import RelatedContent from 'component/relatedContent';
import SubscribeButton from 'component/subscribeButton'; import SubscribeButton from 'component/subscribeButton';
import SubscribeNotificationButton from 'component/subscribeNotificationButton'; import SubscribeNotificationButton from 'component/subscribeNotificationButton';
import UriBar from 'component/uriBar'; import UriBar from 'component/uriBar';
import Video from 'react-native-video';
import FileRewardsDriver from 'component/fileRewardsDriver'; import FileRewardsDriver from 'component/fileRewardsDriver';
import filePageStyle from 'styles/filePage'; import filePageStyle from 'styles/filePage';
import uriBarStyle from 'styles/uriBar'; import uriBarStyle from 'styles/uriBar';
@ -73,7 +72,7 @@ class FilePage extends React.PureComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
attemptAutoGet: false, autoGetAttempted: false,
autoOpened: false, autoOpened: false,
autoDownloadStarted: false, autoDownloadStarted: false,
autoPlayMedia: false, autoPlayMedia: false,
@ -297,7 +296,17 @@ class FilePage extends React.PureComponent {
} }
componentDidUpdate(prevProps, prevState) { componentDidUpdate(prevProps, prevState) {
const { claim, contentType, costInfo, fileInfo, isResolvingUri, resolveUri, navigation, title } = this.props; const {
claim,
contentType,
costInfo,
fileInfo,
isResolvingUri,
resolveUri,
sdkReady,
navigation,
title,
} = this.props;
const { uri } = this.state; const { uri } = this.state;
if (!isResolvingUri && claim === undefined && uri) { if (!isResolvingUri && claim === undefined && uri) {
resolveUri(uri); resolveUri(uri);
@ -323,10 +332,12 @@ class FilePage extends React.PureComponent {
const isPlayable = mediaType === 'video' || mediaType === 'audio'; const isPlayable = mediaType === 'video' || mediaType === 'audio';
const isViewable = mediaType === 'image' || mediaType === 'text'; const isViewable = mediaType === 'image' || mediaType === 'text';
if (claim && costInfo && costInfo.cost === 0 && !this.state.autoGetAttempted && isViewable) { if (claim && costInfo && costInfo.cost === 0 && !this.state.autoGetAttempted && isViewable) {
this.setState({ autoGetAttempted: true }, () => this.checkStoragePermissionForDownload()); this.setState({ autoGetAttempted: true }, () => {
this.checkStoragePermissionForDownload();
});
} }
if (((costInfo && costInfo.cost > 0) || !isPlayable) && (!fileInfo && !isViewable) && !this.state.showRecommended) { if (((costInfo && costInfo.cost > 0) || !isPlayable) && !fileInfo && !isViewable && !this.state.showRecommended) {
this.setState({ showRecommended: true }); this.setState({ showRecommended: true });
} }
@ -706,7 +717,7 @@ class FilePage extends React.PureComponent {
}; };
confirmPurchaseUri = (uri, costInfo, download) => { confirmPurchaseUri = (uri, costInfo, download) => {
const { notify, purchaseUri, title } = this.props; const { notify, purchaseUri, sdkReady, title } = this.props;
if (!costInfo) { if (!costInfo) {
notify({ message: __('This content cannot be viewed at this time. Please try again in a bit.'), isError: true }); notify({ message: __('This content cannot be viewed at this time. Please try again in a bit.'), isError: true });
this.setState({ downloadPressed: false }); this.setState({ downloadPressed: false });
@ -715,6 +726,11 @@ class FilePage extends React.PureComponent {
} }
const { cost } = costInfo; const { cost } = costInfo;
if (!NativeModules.UtilityModule.dhtEnabled && !sdkReady && parseFloat(cost) === 0) {
this.attemptLbryTvPlayback();
return;
}
if (costInfo.cost > 0) { if (costInfo.cost > 0) {
Alert.alert( Alert.alert(
__('Confirm Purchase'), __('Confirm Purchase'),
@ -742,22 +758,40 @@ class FilePage extends React.PureComponent {
} }
}; };
getStreamUrlForClaim = claim => {
const { name, claim_id: claimId } = claim;
return `https://player.lbry.tv/content/claims/${name}/${claimId}/stream`;
};
attemptLbryTvPlayback = () => {
const { claim } = this.props;
if (claim) {
this.setState({ streamingMode: true, currentStreamUrl: this.getStreamUrlForClaim(claim) });
}
};
onFileDownloadButtonPressed = () => { onFileDownloadButtonPressed = () => {
this.startTime = Date.now(); this.startTime = Date.now();
const { claim, costInfo, contentType, setPlayerVisible } = this.props; const { claim, costInfo, contentType, notify, sdkReady, setPlayerVisible } = this.props;
const mediaType = Lbry.getMediaType(contentType); const mediaType = Lbry.getMediaType(contentType);
const isPlayable = mediaType === 'video' || mediaType === 'audio'; const isPlayable = mediaType === 'video' || mediaType === 'audio';
const isViewable = mediaType === 'image' || mediaType === 'text'; const isViewable = mediaType === 'image' || mediaType === 'text';
const purchaseUrl = this.getPurchaseUrl(); const purchaseUrl = this.getPurchaseUrl();
NativeModules.Firebase.track('purchase_uri', { uri: purchaseUrl });
if (!isPlayable) { if (!isPlayable) {
if (!sdkReady) {
notify({
message: __('The LBRY background service is still initializing. Please wait a few moments and try again.'),
});
return;
}
this.onDownloadPressed(); this.onDownloadPressed();
} else { } else {
this.confirmPurchaseUri(purchaseUrl, costInfo, !isPlayable); this.confirmPurchaseUri(purchaseUrl, costInfo, !isPlayable);
} }
NativeModules.Firebase.track('purchase_uri', { uri: purchaseUrl });
if (isPlayable) { if (isPlayable) {
this.setState({ downloadPressed: true, autoPlayMedia: true, stopDownloadConfirmed: false }); this.setState({ downloadPressed: true, autoPlayMedia: true, stopDownloadConfirmed: false });
} }
@ -1177,7 +1211,7 @@ class FilePage extends React.PureComponent {
style={filePageStyle.mediaContainer} style={filePageStyle.mediaContainer}
onPress={this.onFileDownloadButtonPressed} onPress={this.onFileDownloadButtonPressed}
> >
{(canOpen || (!fileInfo || (isPlayable && !canLoadMedia)) || (!canOpen && fileInfo)) && ( {(canOpen || !fileInfo || (isPlayable && !canLoadMedia) || (!canOpen && fileInfo)) && (
<FileItemMedia <FileItemMedia
duration={duration} duration={duration}
style={filePageStyle.thumbnail} style={filePageStyle.thumbnail}

View file

@ -12,23 +12,22 @@ import {
doFetchInviteStatus, doFetchInviteStatus,
doUserInviteNew, doUserInviteNew,
} from 'lbryinc'; } from 'lbryinc';
import { doPushDrawerStack, doPopDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer'; import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import { doUpdateChannelFormState, doClearChannelFormState } from 'redux/actions/form'; import { selectSdkReady } from 'redux/selectors/settings';
import { selectDrawerStack } from 'redux/selectors/drawer';
import { selectChannelFormState, selectHasChannelFormState } from 'redux/selectors/form';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import InvitesPage from './view'; import InvitesPage from './view';
const select = state => ({ const select = state => ({
channels: selectMyChannelClaims(state), channels: selectMyChannelClaims(state),
errorMessage: selectUserInviteNewErrorMessage(state),
fetchingChannels: selectFetchingMyChannels(state), fetchingChannels: selectFetchingMyChannels(state),
fetchingInvitees: selectUserInviteStatusIsPending(state), fetchingInvitees: selectUserInviteStatusIsPending(state),
errorMessage: selectUserInviteNewErrorMessage(state),
invitesRemaining: selectUserInvitesRemaining(state),
referralCode: selectUserInviteReferralCode(state),
isPending: selectUserInviteNewIsPending(state),
invitees: selectUserInvitees(state), invitees: selectUserInvitees(state),
invitesRemaining: selectUserInvitesRemaining(state),
isPending: selectUserInviteNewIsPending(state),
referralCode: selectUserInviteReferralCode(state),
referralReward: selectReferralReward(state), referralReward: selectReferralReward(state),
sdkReady: selectSdkReady(state),
}); });
const perform = dispatch => ({ const perform = dispatch => ({
@ -40,7 +39,4 @@ const perform = dispatch => ({
notify: data => dispatch(doToast(data)), notify: data => dispatch(doToast(data)),
}); });
export default connect( export default connect(select, perform)(InvitesPage);
select,
perform,
)(InvitesPage);

View file

@ -11,14 +11,10 @@ import {
View, View,
} from 'react-native'; } from 'react-native';
import Colors from 'styles/colors'; import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import Icon from 'react-native-vector-icons/FontAwesome5'; import Icon from 'react-native-vector-icons/FontAwesome5';
import Link from 'component/link';
import Button from 'component/button'; import Button from 'component/button';
import ChannelSelector from 'component/channelSelector'; import ChannelSelector from 'component/channelSelector';
import PageHeader from 'component/pageHeader'; import EmptyStateView from 'component/emptyStateView';
import RewardCard from 'component/rewardCard';
import RewardEnrolment from 'component/rewardEnrolment';
import UriBar from 'component/uriBar'; import UriBar from 'component/uriBar';
import invitesStyle from 'styles/invites'; import invitesStyle from 'styles/invites';
import { fetchReferralCode, logPublish } from 'utils/helper'; import { fetchReferralCode, logPublish } from 'utils/helper';
@ -134,10 +130,23 @@ class InvitesPage extends React.PureComponent {
}; };
render() { render() {
const { fetchingInvitees, user, navigation, notify, isPending, invitees } = this.props; const { fetchingInvitees, invitees, isPending, navigation, sdkReady } = this.props;
const { email, inviteLink } = this.state; const { email } = this.state;
const hasInvitees = invitees && invitees.length > 0; const hasInvitees = invitees && invitees.length > 0;
if (!sdkReady) {
return (
<View style={invitesStyle.container}>
<UriBar navigation={navigation} />
<EmptyStateView
message={__(
'The background service is still initializing. You can still explore and watch content during the initialization process.',
)}
/>
</View>
);
}
return ( return (
<View style={invitesStyle.container}> <View style={invitesStyle.container}>
<UriBar navigation={navigation} /> <UriBar navigation={navigation} />

View file

@ -13,6 +13,7 @@ import { selectDrawerStack } from 'redux/selectors/drawer';
import { doUpdatePublishFormState, doClearPublishFormState, doPendingPublishSuccess } from 'redux/actions/form'; import { doUpdatePublishFormState, doClearPublishFormState, doPendingPublishSuccess } from 'redux/actions/form';
import { doPushDrawerStack, doPopDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer'; import { doPushDrawerStack, doPopDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import { selectPublishFormState, selectHasPublishFormState } from 'redux/selectors/form'; import { selectPublishFormState, selectHasPublishFormState } from 'redux/selectors/form';
import { selectSdkReady } from 'redux/selectors/settings';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import PublishPage from './view'; import PublishPage from './view';
@ -23,6 +24,7 @@ const select = state => ({
myClaims: selectMyClaims(state), myClaims: selectMyClaims(state),
publishFormState: selectPublishFormState(state), publishFormState: selectPublishFormState(state),
publishFormValues: selectPublishFormValues(state), publishFormValues: selectPublishFormValues(state),
sdkReady: selectSdkReady(state),
}); });
const perform = dispatch => ({ const perform = dispatch => ({
@ -39,7 +41,4 @@ const perform = dispatch => ({
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)), setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
}); });
export default connect( export default connect(select, perform)(PublishPage);
select,
perform,
)(PublishPage);

View file

@ -37,6 +37,7 @@ import Button from 'component/button';
import ChannelSelector from 'component/channelSelector'; import ChannelSelector from 'component/channelSelector';
import Colors from 'styles/colors'; import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import EmptyStateView from 'component/emptyStateView';
import FastImage from 'react-native-fast-image'; import FastImage from 'react-native-fast-image';
import FloatingWalletBalance from 'component/floatingWalletBalance'; import FloatingWalletBalance from 'component/floatingWalletBalance';
import Icon from 'react-native-vector-icons/FontAwesome5'; import Icon from 'react-native-vector-icons/FontAwesome5';
@ -974,7 +975,7 @@ class PublishPage extends React.PureComponent {
}; };
render() { render() {
const { balance, navigation, notify, publishFormValues } = this.props; const { balance, navigation, notify, sdkReady } = this.props;
const { const {
allThumbnailsChecked, allThumbnailsChecked,
canUseCamera, canUseCamera,
@ -987,6 +988,19 @@ class PublishPage extends React.PureComponent {
videos, videos,
} = this.state; } = this.state;
if (!sdkReady) {
return (
<View style={publishStyle.container}>
<UriBar navigation={navigation} />
<EmptyStateView
message={__(
'The background service is still initializing. You can still explore and watch content during the initialization process.',
)}
/>
</View>
);
}
let content; let content;
if (Constants.PHASE_SELECTOR === currentPhase) { if (Constants.PHASE_SELECTOR === currentPhase) {
content = ( content = (

View file

@ -9,6 +9,7 @@ import {
selectIsFetchingClaimListMine, selectIsFetchingClaimListMine,
} from 'lbry-redux'; } from 'lbry-redux';
import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer'; import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import { selectSdkReady } from 'redux/selectors/settings';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import PublishesPage from './view'; import PublishesPage from './view';
@ -16,6 +17,7 @@ const select = state => ({
uris: selectMyClaimUrisWithoutChannels(state), uris: selectMyClaimUrisWithoutChannels(state),
fetching: selectIsFetchingClaimListMine(state), fetching: selectIsFetchingClaimListMine(state),
pendingClaims: selectPendingClaims(state), pendingClaims: selectPendingClaims(state),
sdkReady: selectSdkReady(state),
}); });
const perform = dispatch => ({ const perform = dispatch => ({
@ -27,7 +29,4 @@ const perform = dispatch => ({
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)), setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
}); });
export default connect( export default connect(select, perform)(PublishesPage);
select,
perform
)(PublishesPage);

View file

@ -114,14 +114,27 @@ class PublishesPage extends React.PureComponent {
}, },
}, },
], ],
{ cancelable: true } { cancelable: true },
); );
}; };
render() { render() {
const { fetching, navigation, uris } = this.props; const { fetching, navigation, sdkReady, uris } = this.props;
const { selectionMode, selectedUris } = this.state; const { selectionMode, selectedUris } = this.state;
if (!sdkReady) {
return (
<View style={publishStyle.container}>
<UriBar navigation={navigation} />
<EmptyStateView
message={__(
'The background service is still initializing. You can still explore and watch content during the initialization process.',
)}
/>
</View>
);
}
return ( return (
<View style={publishStyle.container}> <View style={publishStyle.container}>
<UriBar <UriBar

View file

@ -12,7 +12,8 @@ import {
import { doToast } from 'lbry-redux'; import { doToast } 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 { selectSdkReady } from 'redux/selectors/settings';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import RewardsPage from './view'; import RewardsPage from './view';
const select = state => ({ const select = state => ({
@ -22,6 +23,7 @@ const select = state => ({
emailVerifyPending: selectEmailVerifyIsPending(state), emailVerifyPending: selectEmailVerifyIsPending(state),
fetching: selectFetchingRewards(state), fetching: selectFetchingRewards(state),
rewards: selectUnclaimedRewards(state), rewards: selectUnclaimedRewards(state),
sdkReady: selectSdkReady(state),
user: selectUser(state), user: selectUser(state),
}); });
@ -33,7 +35,4 @@ const perform = dispatch => ({
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)), setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
}); });
export default connect( export default connect(select, perform)(RewardsPage);
select,
perform
)(RewardsPage);

View file

@ -5,7 +5,7 @@ import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import Link from 'component/link'; import Link from 'component/link';
import CustomRewardCard from 'component/customRewardCard'; import CustomRewardCard from 'component/customRewardCard';
import PageHeader from 'component/pageHeader'; import EmptyStateView from 'component/emptyStateView';
import RewardCard from 'component/rewardCard'; import RewardCard from 'component/rewardCard';
import RewardEnrolment from 'component/rewardEnrolment'; import RewardEnrolment from 'component/rewardEnrolment';
import UriBar from 'component/uriBar'; import UriBar from 'component/uriBar';
@ -192,9 +192,22 @@ class RewardsPage extends React.PureComponent {
}; };
render() { render() {
const { user, navigation } = this.props; const { navigation, sdkReady } = this.props;
const { currentFilter } = this.state; const { currentFilter } = this.state;
if (!sdkReady) {
return (
<View style={rewardStyle.container}>
<UriBar navigation={navigation} />
<EmptyStateView
message={__(
'The background service is still initializing. You can still explore and watch content during the initialization process.',
)}
/>
</View>
);
}
return ( return (
<View style={rewardStyle.container}> <View style={rewardStyle.container}>
<UriBar navigation={navigation} /> <UriBar navigation={navigation} />

View file

@ -1,6 +1,16 @@
import React from 'react'; import React from 'react';
import { SETTINGS } from 'lbry-redux'; import { SETTINGS } from 'lbry-redux';
import { ActivityIndicator, Picker, Platform, Text, View, ScrollView, Switch, NativeModules } from 'react-native'; import {
ActivityIndicator,
Picker,
Platform,
Text,
TextInput,
View,
ScrollView,
Switch,
NativeModules,
} from 'react-native';
import { navigateBack } from 'utils/helper'; import { navigateBack } from 'utils/helper';
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';
@ -328,7 +338,9 @@ class SettingsPage extends React.PureComponent {
<View style={settingsStyle.switchText}> <View style={settingsStyle.switchText}>
<Text style={settingsStyle.label}>{__('Participate in the data network')}</Text> <Text style={settingsStyle.label}>{__('Participate in the data network')}</Text>
<Text style={settingsStyle.description}> <Text style={settingsStyle.description}>
{__('Enable DHT (this will take effect upon app and background service restart)')} {__(
'Enable peer-to-peer functionality (this will take effect upon app and background service restart)',
)}
</Text> </Text>
</View> </View>
<View style={settingsStyle.switchContainer}> <View style={settingsStyle.switchContainer}>

View file

@ -1,11 +1,8 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { SETTINGS, doBalanceSubscribe, doUpdateBlockHeight, doPopulateSharedUserState, doToast } from 'lbry-redux'; import { SETTINGS, doUpdateBlockHeight, doPopulateSharedUserState, doToast } from 'lbry-redux';
import { import {
doAuthenticate, doAuthenticate,
doInstallNew,
doInstallNewWithParams, doInstallNewWithParams,
doBlackListedOutpointsSubscribe,
doFilteredOutpointsSubscribe,
doFetchMySubscriptions, doFetchMySubscriptions,
doFetchRewardedContent, doFetchRewardedContent,
doGetSync, doGetSync,
@ -32,9 +29,6 @@ const perform = dispatch => ({
dispatch(doAuthenticate(appVersion, os, firebaseToken, true, null, callInstall)), dispatch(doAuthenticate(appVersion, os, firebaseToken, true, null, callInstall)),
installNewWithParams: (appVersion, installationId, nodeId, lbrynetVersion, os, platform, firebaseToken) => installNewWithParams: (appVersion, installationId, nodeId, lbrynetVersion, os, platform, firebaseToken) =>
dispatch(doInstallNewWithParams(appVersion, installationId, nodeId, lbrynetVersion, os, platform, firebaseToken)), dispatch(doInstallNewWithParams(appVersion, installationId, nodeId, lbrynetVersion, os, platform, firebaseToken)),
balanceSubscribe: () => dispatch(doBalanceSubscribe()),
blacklistedOutpointsSubscribe: () => dispatch(doBlackListedOutpointsSubscribe()),
filteredOutpointsSubscribe: () => dispatch(doFilteredOutpointsSubscribe()),
fetchRewardedContent: () => dispatch(doFetchRewardedContent()), fetchRewardedContent: () => dispatch(doFetchRewardedContent()),
fetchSubscriptions: callback => dispatch(doFetchMySubscriptions(callback)), fetchSubscriptions: callback => dispatch(doFetchMySubscriptions(callback)),
getSync: (password, callback) => dispatch(doGetSync(password, callback)), getSync: (password, callback) => dispatch(doGetSync(password, callback)),

View file

@ -23,6 +23,7 @@ class SplashScreen extends React.PureComponent {
}; };
state = { state = {
authWithoutSdk: false,
accountUnlockFailed: false, accountUnlockFailed: false,
appVersion: null, appVersion: null,
daemonReady: false, daemonReady: false,
@ -53,7 +54,7 @@ class SplashScreen extends React.PureComponent {
liteModeParams: { liteModeParams: {
installationId: installIdContent, installationId: installIdContent,
nodeId: null, nodeId: null,
lbrynetVersion: '0.62.0', lbrynetVersion: '0.64.0',
platform, platform,
}, },
}, },
@ -66,14 +67,10 @@ class SplashScreen extends React.PureComponent {
}); });
}; };
updateStatus() { authenticateWithoutSdk() {
const { authenticate } = this.props; const { authenticate } = this.props;
const { liteMode } = this.state;
if (liteMode) {
// authenticate immediately
NativeModules.VersionInfo.getAppVersion().then(appVersion => { NativeModules.VersionInfo.getAppVersion().then(appVersion => {
this.setState({ appVersion, shouldAuthenticate: true }); this.setState({ appVersion, shouldAuthenticate: true, authWithoutSdk: true });
NativeModules.Firebase.getMessagingToken() NativeModules.Firebase.getMessagingToken()
.then(firebaseToken => { .then(firebaseToken => {
this.setState({ firebaseToken }, () => authenticate(appVersion, Platform.OS, firebaseToken, false)); this.setState({ firebaseToken }, () => authenticate(appVersion, Platform.OS, firebaseToken, false));
@ -82,10 +79,14 @@ class SplashScreen extends React.PureComponent {
authenticate(appVersion, Platform.OS, null, false); authenticate(appVersion, Platform.OS, null, false);
}); });
}); });
} else { }
Lbry.status().then(status => {
this._updateStatusCallback(status); updateStatus() {
}); const { liteMode } = this.state;
// authenticate immediately
if (!NativeModules.UtilityModule.dhtEnabled) {
this.authenticateWithoutSdk();
} }
} }
@ -121,15 +122,24 @@ class SplashScreen extends React.PureComponent {
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
const { getSync, installNewWithParams } = this.props; const { getSync, installNewWithParams } = this.props;
const { daemonReady, shouldAuthenticate, liteMode, liteModeParams, appVersion, firebaseToken } = this.state; const {
daemonReady,
authWithoutSdk,
shouldAuthenticate,
liteMode,
liteModeParams,
appVersion,
firebaseToken,
} = this.state;
const { user } = nextProps; const { user } = nextProps;
if (liteMode && user && user.id) { if (liteMode && user && user.id) {
this.navigateToLiteMode(); this.navigateToLiteMode();
} else if (daemonReady && shouldAuthenticate && user && user.id) { } else if (shouldAuthenticate && user && user.id) {
if (daemonReady || authWithoutSdk) {
this.setState({ shouldAuthenticate: false }, () => { this.setState({ shouldAuthenticate: false }, () => {
// call install new after successful authentication // call install new after successful authentication
if (liteMode) { if (authWithoutSdk) {
const { installationId, nodeId, lbrynetVersion, platform } = liteModeParams; const { installationId, nodeId, lbrynetVersion, platform } = liteModeParams;
installNewWithParams( installNewWithParams(
appVersion, appVersion,
@ -157,6 +167,7 @@ class SplashScreen extends React.PureComponent {
}); });
} }
} }
}
navigateToLiteMode = () => { navigateToLiteMode = () => {
const { navigation } = this.props; const { navigation } = this.props;
@ -194,22 +205,9 @@ class SplashScreen extends React.PureComponent {
}; };
finishSplashScreen = () => { finishSplashScreen = () => {
const { const { authenticate, getSync, user } = this.props;
authenticate,
balanceSubscribe,
blacklistedOutpointsSubscribe,
filteredOutpointsSubscribe,
getSync,
updateBlockHeight,
user,
} = this.props;
// Lbry.resolve({ urls: 'lbry://one' }).then(() => {
// Leave the splash screen // Leave the splash screen
balanceSubscribe();
blacklistedOutpointsSubscribe();
filteredOutpointsSubscribe();
if (user && user.id && user.has_verified_email) { if (user && user.id && user.has_verified_email) {
// user already authenticated // user already authenticated
NativeModules.UtilityModule.getSecureValue(Constants.KEY_WALLET_PASSWORD).then(walletPassword => { NativeModules.UtilityModule.getSecureValue(Constants.KEY_WALLET_PASSWORD).then(walletPassword => {
@ -229,31 +227,14 @@ class SplashScreen extends React.PureComponent {
}); });
}); });
} }
// });
}; };
handleAccountUnlockFailed() { handleAccountUnlockFailed() {
this.setState({ accountUnlockFailed: true }); this.setState({ accountUnlockFailed: true });
} }
_updateStatusCallback(status) { handleSdkReady = () => {
const { fetchSubscriptions, getSync, setClientSetting } = this.props; this.setState({ daemonReady: true }, () => {
const startupStatus = status.startup_status;
const walletStatus = status.wallet;
// At the minimum, wallet should be started and blocks_behind equal to 0 before calling resolve
const hasStarted = startupStatus.stream_manager && startupStatus.wallet && status.wallet.blocks_behind <= 0;
if (hasStarted) {
// Wait until we are able to resolve a name before declaring
// that we are done.
// TODO: This is a hack, and the logic should live in the daemon
// to give us a better sense of when we are actually started
this.setState({
daemonReady: true,
isLagging: false,
isRunning: true,
});
Lbry.wallet_status().then(secureWalletStatus => { Lbry.wallet_status().then(secureWalletStatus => {
// For now, automatically unlock the wallet if a password is set so that downloads work // For now, automatically unlock the wallet if a password is set so that downloads work
NativeModules.UtilityModule.getSecureValue(Constants.KEY_WALLET_PASSWORD).then(password => { NativeModules.UtilityModule.getSecureValue(Constants.KEY_WALLET_PASSWORD).then(password => {
@ -284,9 +265,12 @@ class SplashScreen extends React.PureComponent {
} }
}); });
}); });
});
};
return; handleSdkStatusResponse = evt => {
} const { status } = evt;
const walletStatus = status.wallet;
const headerSyncProgress = walletStatus ? walletStatus.headers_synchronization_progress : null; const headerSyncProgress = walletStatus ? walletStatus.headers_synchronization_progress : null;
if (headerSyncProgress && headerSyncProgress < 100) { if (headerSyncProgress && headerSyncProgress < 100) {
@ -321,23 +305,24 @@ class SplashScreen extends React.PureComponent {
details: __('Initializing LBRY service'), details: __('Initializing LBRY service'),
}); });
} }
};
setTimeout(() => {
this.updateStatus();
}, 1000);
}
componentWillMount() { componentWillMount() {
DeviceEventEmitter.addListener('onNotificationTargetLaunch', this.onNotificationTargetLaunch); DeviceEventEmitter.addListener('onNotificationTargetLaunch', this.onNotificationTargetLaunch);
DeviceEventEmitter.addListener('onSdkReady', this.handleSdkReady);
DeviceEventEmitter.addListener('onSdkStatusResponse', this.handleSdkStatusResponse);
} }
componentWillUnmount() { componentWillUnmount() {
DeviceEventEmitter.removeListener('onNotificationTargetLaunch', this.onNotificationTargetLaunch); DeviceEventEmitter.removeListener('onNotificationTargetLaunch', this.onNotificationTargetLaunch);
DeviceEventEmitter.removeListener('onSdkReady', this.handleSdkReady);
DeviceEventEmitter.removeListener('onSdkStatusResponse', this.handleSdkStatusResponse);
} }
componentDidMount() { componentDidMount() {
NativeModules.Firebase.track('app_launch', null); NativeModules.Firebase.track('app_launch', null);
NativeModules.Firebase.setCurrentScreen('Splash'); NativeModules.Firebase.setCurrentScreen('Splash');
const { navigation } = this.props; const { navigation } = this.props;
const { resetUrl } = navigation.state.params; const { resetUrl } = navigation.state.params;
const isResetUrlSet = !!resetUrl; const isResetUrlSet = !!resetUrl;
@ -357,11 +342,7 @@ class SplashScreen extends React.PureComponent {
} }
// Only connect after checking initial launch url / notification launch target // Only connect after checking initial launch url / notification launch target
if (liteMode) {
this.initLiteMode(); this.initLiteMode();
} else {
this.lbryConnect();
}
}); });
}); });
@ -377,6 +358,7 @@ class SplashScreen extends React.PureComponent {
} }
lbryConnect = () => { lbryConnect = () => {
if (NativeModules.UtilityModule.dhtEnabled) {
Lbry.connect() Lbry.connect()
.then(() => { .then(() => {
this.updateStatus(); this.updateStatus();
@ -390,6 +372,9 @@ class SplashScreen extends React.PureComponent {
), ),
}); });
}); });
} else {
this.updateStatus(); // skip lbry.connect for now (unless dht flag is enabled)
}
}; };
handleContinueAnywayPressed = () => { handleContinueAnywayPressed = () => {

View file

@ -16,7 +16,7 @@ import {
import { doToast, selectFetchingClaimSearch } from 'lbry-redux'; import { doToast, selectFetchingClaimSearch } from 'lbry-redux';
import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer'; import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import { doSetClientSetting, doSetTimeItem } from 'redux/actions/settings'; import { doSetClientSetting, doSetTimeItem } from 'redux/actions/settings';
import { makeSelectClientSetting, selectTimeItem } from 'redux/selectors/settings'; import { makeSelectClientSetting, selectSdkReady, selectTimeItem } from 'redux/selectors/settings';
import { selectCurrentRoute } from 'redux/selectors/drawer'; import { selectCurrentRoute } from 'redux/selectors/drawer';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import SubscriptionsPage from './view'; import SubscriptionsPage from './view';
@ -34,6 +34,7 @@ const select = state => ({
firstRunCompleted: selectFirstRunCompleted(state), firstRunCompleted: selectFirstRunCompleted(state),
showSuggestedSubs: selectShowSuggestedSubs(state), showSuggestedSubs: selectShowSuggestedSubs(state),
timeItem: selectTimeItem(state), timeItem: selectTimeItem(state),
sdkReady: selectSdkReady(state),
}); });
const perform = dispatch => ({ const perform = dispatch => ({
@ -48,7 +49,4 @@ const perform = dispatch => ({
setTimeItem: item => dispatch(doSetTimeItem(item)), setTimeItem: item => dispatch(doSetTimeItem(item)),
}); });
export default connect( export default connect(select, perform)(SubscriptionsPage);
select,
perform,
)(SubscriptionsPage);

View file

@ -30,6 +30,7 @@ import SubscribedChannelList from 'component/subscribedChannelList';
import SuggestedSubscriptions from 'component/suggestedSubscriptions'; import SuggestedSubscriptions from 'component/suggestedSubscriptions';
import SuggestedSubscriptionsGrid from 'component/suggestedSubscriptionsGrid'; import SuggestedSubscriptionsGrid from 'component/suggestedSubscriptionsGrid';
import UriBar from 'component/uriBar'; import UriBar from 'component/uriBar';
import SdkLoadingStatus from 'component/sdkLoadingStatus';
class SubscriptionsPage extends React.PureComponent { class SubscriptionsPage extends React.PureComponent {
state = { state = {
@ -137,21 +138,7 @@ class SubscriptionsPage extends React.PureComponent {
}; };
render() { render() {
const { const { subscribedChannels, loading, loadingSuggested, sdkReady, timeItem, navigation, notify } = this.props;
suggestedChannels,
subscribedChannels,
allSubscriptions,
doCompleteFirstRun,
doShowSuggestedSubs,
loading,
loadingSuggested,
firstRunCompleted,
showSuggestedSubs,
timeItem,
unreadSubscriptions,
navigation,
notify,
} = this.props;
const { currentSortByItem, filteredChannels, showModalSuggestedSubs, showSortPicker, showTimePicker } = this.state; const { currentSortByItem, filteredChannels, showModalSuggestedSubs, showSortPicker, showTimePicker } = this.state;
const numberOfSubscriptions = subscribedChannels ? subscribedChannels.length : 0; const numberOfSubscriptions = subscribedChannels ? subscribedChannels.length : 0;
@ -296,6 +283,8 @@ class SubscriptionsPage extends React.PureComponent {
onDonePress={() => this.setState({ showModalSuggestedSubs: false })} onDonePress={() => this.setState({ showModalSuggestedSubs: false })}
/> />
)} )}
{!sdkReady && <SdkLoadingStatus />}
</View> </View>
); );
} }

View file

@ -3,11 +3,12 @@ import { selectFollowedTags, doToggleTagFollow } from 'lbry-redux';
import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer'; import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import { doSetSortByItem, doSetTimeItem } from 'redux/actions/settings'; import { doSetSortByItem, doSetTimeItem } from 'redux/actions/settings';
import { selectCurrentRoute } from 'redux/selectors/drawer'; import { selectCurrentRoute } from 'redux/selectors/drawer';
import { selectSortByItem, selectTimeItem } from 'redux/selectors/settings'; import { selectSdkReady, selectSortByItem, selectTimeItem } from 'redux/selectors/settings';
import TagPage from './view'; import TagPage from './view';
const select = state => ({ const select = state => ({
currentRoute: selectCurrentRoute(state), currentRoute: selectCurrentRoute(state),
sdkReady: selectSdkReady(state),
sortByItem: selectSortByItem(state), sortByItem: selectSortByItem(state),
timeItem: selectTimeItem(state), timeItem: selectTimeItem(state),
followedTags: selectFollowedTags(state), followedTags: selectFollowedTags(state),
@ -21,7 +22,4 @@ const perform = dispatch => ({
toggleTagFollow: tag => dispatch(doToggleTagFollow(tag)), toggleTagFollow: tag => dispatch(doToggleTagFollow(tag)),
}); });
export default connect( export default connect(select, perform)(TagPage);
select,
perform
)(TagPage);

View file

@ -14,6 +14,7 @@ import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import FloatingWalletBalance from 'component/floatingWalletBalance'; import FloatingWalletBalance from 'component/floatingWalletBalance';
import Link from 'component/link'; import Link from 'component/link';
import ModalPicker from 'component/modalPicker'; import ModalPicker from 'component/modalPicker';
import SdkLoadingStatus from 'component/sdkLoadingStatus';
import UriBar from 'component/uriBar'; import UriBar from 'component/uriBar';
class TagPage extends React.PureComponent { class TagPage extends React.PureComponent {
@ -127,7 +128,7 @@ class TagPage extends React.PureComponent {
}; };
render() { render() {
const { navigation, sortByItem, timeItem } = this.props; const { navigation, sdkReady, sortByItem, timeItem } = this.props;
const { tag, showSortPicker, showTimePicker } = this.state; const { tag, showSortPicker, showTimePicker } = this.state;
return ( return (
@ -144,7 +145,7 @@ class TagPage extends React.PureComponent {
orientation={Constants.ORIENTATION_VERTICAL} orientation={Constants.ORIENTATION_VERTICAL}
/> />
)} )}
{!showSortPicker && !showTimePicker && <FloatingWalletBalance navigation={navigation} />} {sdkReady && !showSortPicker && !showTimePicker && <FloatingWalletBalance navigation={navigation} />}
{showSortPicker && ( {showSortPicker && (
<ModalPicker <ModalPicker
title={__('Sort content by')} title={__('Sort content by')}
@ -163,6 +164,7 @@ class TagPage extends React.PureComponent {
items={Constants.CLAIM_SEARCH_TIME_ITEMS} items={Constants.CLAIM_SEARCH_TIME_ITEMS}
/> />
)} )}
{!sdkReady && <SdkLoadingStatus />}
</View> </View>
); );
} }

View file

@ -1,6 +1,6 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { doSetClientSetting } from 'redux/actions/settings'; import { doSetClientSetting } from 'redux/actions/settings';
import { makeSelectClientSetting } from 'redux/selectors/settings'; import { makeSelectClientSetting, selectSdkReady } from 'redux/selectors/settings';
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 { selectBalance } from 'lbry-redux'; import { selectBalance } from 'lbry-redux';
@ -15,6 +15,7 @@ const select = state => ({
deviceWalletSynced: makeSelectClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED)(state), deviceWalletSynced: makeSelectClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED)(state),
hasSyncedWallet: selectHasSyncedWallet(state), hasSyncedWallet: selectHasSyncedWallet(state),
rewardsNotInterested: makeSelectClientSetting(Constants.SETTING_REWARDS_NOT_INTERESTED)(state), rewardsNotInterested: makeSelectClientSetting(Constants.SETTING_REWARDS_NOT_INTERESTED)(state),
sdkReady: selectSdkReady(state),
understandsRisks: makeSelectClientSetting(Constants.SETTING_ALPHA_UNDERSTANDS_RISKS)(state), understandsRisks: makeSelectClientSetting(Constants.SETTING_ALPHA_UNDERSTANDS_RISKS)(state),
user: selectUser(state), user: selectUser(state),
}); });
@ -27,7 +28,4 @@ const perform = dispatch => ({
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)), setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
}); });
export default connect( export default connect(select, perform)(WalletPage);
select,
perform
)(WalletPage);

View file

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { NativeModules, ScrollView, Text, View } from 'react-native'; import { NativeModules, ScrollView, Text, View } from 'react-native';
import EmptyStateView from 'component/emptyStateView';
import TransactionListRecent from 'component/transactionListRecent'; import TransactionListRecent from 'component/transactionListRecent';
import WalletAddress from 'component/walletAddress'; import WalletAddress from 'component/walletAddress';
import WalletBalance from 'component/walletBalance'; import WalletBalance from 'component/walletBalance';
@ -8,8 +9,6 @@ import WalletSend from 'component/walletSend';
import WalletRewardsDriver from 'component/walletRewardsDriver'; import WalletRewardsDriver from 'component/walletRewardsDriver';
import WalletSignIn from 'component/walletSignIn'; import WalletSignIn from 'component/walletSignIn';
import WalletSyncDriver from 'component/walletSyncDriver'; import WalletSyncDriver from 'component/walletSyncDriver';
import Button from 'component/button';
import Link from 'component/link';
import UriBar from 'component/uriBar'; import UriBar from 'component/uriBar';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import walletStyle from 'styles/wallet'; import walletStyle from 'styles/wallet';
@ -60,16 +59,20 @@ class WalletPage extends React.PureComponent {
}; };
render() { render() {
const { const { balance, rewardsNotInterested, understandsRisks, navigation, sdkReady, user } = this.props;
balance,
backupDismissed, if (!sdkReady) {
hasSyncedWallet, return (
rewardsNotInterested, <View style={walletStyle.container}>
understandsRisks, <UriBar navigation={navigation} />
setClientSetting, <EmptyStateView
navigation, message={__(
user, 'The background service is still initializing. You can still explore and watch content during the initialization process.',
} = this.props; )}
/>
</View>
);
}
const signedIn = user && user.has_verified_email; const signedIn = user && user.has_verified_email;
if (!signedIn && !understandsRisks) { if (!signedIn && !understandsRisks) {

View file

@ -17,6 +17,14 @@ export function doSetClientSetting(key, value) {
}; };
} }
export function doSetSdkReady() {
return dispatch => {
dispatch({
type: Constants.ACTION_SDK_READY,
});
};
}
export function doSetSortByItem(item) { export function doSetSortByItem(item) {
return dispatch => { return dispatch => {
dispatch({ dispatch({

View file

@ -4,6 +4,7 @@ import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
const reducers = {}; const reducers = {};
const defaultState = { const defaultState = {
clientSettings: {}, clientSettings: {},
sdkReady: false,
sortByItemName: Constants.SORT_BY_HOT, sortByItemName: Constants.SORT_BY_HOT,
timeItemName: Constants.TIME_WEEK, timeItemName: Constants.TIME_WEEK,
fullscreenMode: false, fullscreenMode: false,
@ -20,6 +21,11 @@ reducers[ACTIONS.CLIENT_SETTING_CHANGED] = (state, action) => {
}); });
}; };
reducers[Constants.ACTION_SDK_READY] = (state, action) =>
Object.assign({}, state, {
sdkReady: true,
});
reducers[Constants.ACTION_SORT_BY_ITEM_CHANGED] = (state, action) => reducers[Constants.ACTION_SORT_BY_ITEM_CHANGED] = (state, action) =>
Object.assign({}, state, { Object.assign({}, state, {
sortByItemName: action.data.name, sortByItemName: action.data.name,

View file

@ -4,38 +4,22 @@ import { getSortByItemForName, getTimeItemForName } from 'utils/helper';
const selectState = state => state.settings || {}; const selectState = state => state.settings || {};
export const selectDaemonSettings = createSelector( export const selectDaemonSettings = createSelector(selectState, state => state.daemonSettings);
selectState,
state => state.daemonSettings
);
export const selectClientSettings = createSelector( export const selectClientSettings = createSelector(selectState, state => state.clientSettings || {});
selectState,
state => state.clientSettings || {}
);
export const selectSortByItem = createSelector( export const selectSortByItem = createSelector(selectState, state => getSortByItemForName(state.sortByItemName));
selectState,
state => getSortByItemForName(state.sortByItemName)
);
export const selectTimeItem = createSelector( export const selectTimeItem = createSelector(selectState, state => getTimeItemForName(state.timeItemName));
selectState,
state => getTimeItemForName(state.timeItemName)
);
export const makeSelectClientSetting = setting => export const makeSelectClientSetting = setting =>
createSelector( createSelector(selectClientSettings, settings => (settings ? settings[setting] : undefined));
selectClientSettings,
settings => (settings ? settings[setting] : undefined)
);
// refactor me // refactor me
export const selectShowNsfw = makeSelectClientSetting(SETTINGS.SHOW_NSFW); export const selectShowNsfw = makeSelectClientSetting(SETTINGS.SHOW_NSFW);
export const selectKeepDaemonRunning = makeSelectClientSetting(SETTINGS.KEEP_DAEMON_RUNNING); export const selectKeepDaemonRunning = makeSelectClientSetting(SETTINGS.KEEP_DAEMON_RUNNING);
export const selectFullscreenMode = createSelector( export const selectFullscreenMode = createSelector(selectState, state => state.fullscreenMode);
selectState,
state => state.fullscreenMode export const selectSdkReady = createSelector(selectState, state => state.sdkReady);
);

View file

@ -398,6 +398,23 @@ const discoverStyle = StyleSheet.create({
fontSize: 14, fontSize: 14,
textAlign: 'center', textAlign: 'center',
}, },
sdkLoading: {
position: 'absolute',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
bottom: 0,
left: 0,
right: 0,
padding: 8,
backgroundColor: Colors.LbryGreen,
},
sdkLoadingText: {
fontFamily: 'Inter-Regular',
fontSize: 14,
color: Colors.White,
marginLeft: 8,
},
}); });
export default discoverStyle; export default discoverStyle;

View file

@ -4591,17 +4591,17 @@ latest-version@^3.0.0:
dependencies: dependencies:
package-json "^4.0.0" package-json "^4.0.0"
lbry-redux@lbryio/lbry-redux#84e697079968364fe526020086c8a44f4d2ef391: lbry-redux@lbryio/lbry-redux#69ffd110dbf3633e5847f61f008751edec033017:
version "0.0.1" version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/84e697079968364fe526020086c8a44f4d2ef391" resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/69ffd110dbf3633e5847f61f008751edec033017"
dependencies: dependencies:
proxy-polyfill "0.1.6" proxy-polyfill "0.1.6"
reselect "^3.0.0" reselect "^3.0.0"
uuid "^3.3.2" uuid "^3.3.2"
lbryinc@lbryio/lbryinc#28afb9b06c3d142bad8347939c043a21b6cb1ae1: lbryinc@lbryio/lbryinc#021ac75d9aa2db488cfff8e9be320402f038f955:
version "0.0.1" version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/28afb9b06c3d142bad8347939c043a21b6cb1ae1" resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/021ac75d9aa2db488cfff8e9be320402f038f955"
dependencies: dependencies:
reselect "^3.0.0" reselect "^3.0.0"