Next RC #57

Merged
akinwale merged 21 commits from rc-0.9.4 into master 2019-10-17 15:10:49 +02:00
36 changed files with 434 additions and 247 deletions

8
package-lock.json generated
View file

@ -5640,8 +5640,8 @@
} }
}, },
"lbry-redux": { "lbry-redux": {
"version": "github:lbryio/lbry-redux#7ec72a737bcd336f000c5f5085891643110298c3", "version": "github:lbryio/lbry-redux#9919a8150998822ca997cd23418f023d64d4a3da",
"from": "github:lbryio/lbry-redux#7ec72a737bcd336f000c5f5085891643110298c3", "from": "github:lbryio/lbry-redux#9919a8150998822ca997cd23418f023d64d4a3da",
"requires": { "requires": {
"proxy-polyfill": "0.1.6", "proxy-polyfill": "0.1.6",
"reselect": "^3.0.0", "reselect": "^3.0.0",
@ -5649,8 +5649,8 @@
} }
}, },
"lbryinc": { "lbryinc": {
"version": "github:lbryio/lbryinc#c55a2c98ab92c72149c824ee5906aed4404fd89b", "version": "github:lbryio/lbryinc#aebad10a9c5d725c3fedae4236d56f239a0bc1de",
"from": "github:lbryio/lbryinc#c55a2c98ab92c72149c824ee5906aed4404fd89b", "from": "github:lbryio/lbryinc#aebad10a9c5d725c3fedae4236d56f239a0bc1de",
"requires": { "requires": {
"reselect": "^3.0.0" "reselect": "^3.0.0"
} }

View file

@ -12,8 +12,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#7ec72a737bcd336f000c5f5085891643110298c3", "lbry-redux": "lbryio/lbry-redux#9919a8150998822ca997cd23418f023d64d4a3da",
"lbryinc": "lbryio/lbryinc#c55a2c98ab92c72149c824ee5906aed4404fd89b", "lbryinc": "lbryio/lbryinc#aebad10a9c5d725c3fedae4236d56f239a0bc1de",
"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

@ -29,7 +29,7 @@ import {
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { AppState, BackHandler, Linking, NativeModules, TextInput, ToastAndroid } from 'react-native'; import { AppState, BackHandler, Linking, NativeModules, TextInput, ToastAndroid } from 'react-native';
import { selectDrawerStack } from 'redux/selectors/drawer'; import { selectDrawerStack } from 'redux/selectors/drawer';
import { SETTINGS, doDismissToast, doPopulateSharedUserState, doToast, selectToast } from 'lbry-redux'; import { SETTINGS, doDismissToast, doPopulateSharedUserState, doPreferenceGet, doToast, selectToast } from 'lbry-redux';
import { import {
Lbryio, Lbryio,
doGetSync, doGetSync,
@ -39,6 +39,7 @@ import {
selectEmailToVerify, selectEmailToVerify,
selectEmailVerifyIsPending, selectEmailVerifyIsPending,
selectEmailVerifyErrorMessage, selectEmailVerifyErrorMessage,
selectHashChanged,
selectUser, selectUser,
} from 'lbryinc'; } from 'lbryinc';
import { makeSelectClientSetting } from 'redux/selectors/settings'; import { makeSelectClientSetting } from 'redux/selectors/settings';
@ -53,6 +54,8 @@ 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 SYNC_GET_INTERVAL = 1000 * 60 * 5; // every 5 minutes
const menuNavigationButton = navigation => ( const menuNavigationButton = navigation => (
<NavigationButton <NavigationButton
name="bars" name="bars"
@ -269,9 +272,11 @@ class AppWithNavigationState extends React.Component {
constructor() { constructor() {
super(); super();
this.emailVerifyCheckInterval = null; this.emailVerifyCheckInterval = null;
this.syncGetInterval = null;
this.state = { this.state = {
emailVerifyDone: false, emailVerifyDone: false,
verifyPending: false, verifyPending: false,
syncHashChanged: false,
}; };
} }
@ -292,8 +297,17 @@ class AppWithNavigationState extends React.Component {
} }
componentDidMount() { componentDidMount() {
const { dispatch } = this.props;
this.emailVerifyCheckInterval = setInterval(() => this.checkEmailVerification(), 5000); this.emailVerifyCheckInterval = setInterval(() => this.checkEmailVerification(), 5000);
Linking.addEventListener('url', this._handleUrl); Linking.addEventListener('url', this._handleUrl);
// call /sync/get with interval
this.syncGetInterval = setInterval(() => {
this.setState({ syncHashChanged: false }); // reset local state
NativeModules.UtilityModule.getSecureValue(Constants.KEY_WALLET_PASSWORD).then(walletPassword => {
dispatch(doGetSync(walletPassword, () => this.getUserSettings()));
});
}, SYNC_GET_INTERVAL);
} }
checkEmailVerification = () => { checkEmailVerification = () => {
@ -308,19 +322,31 @@ class AppWithNavigationState extends React.Component {
getUserSettings = () => { getUserSettings = () => {
const { dispatch } = this.props; const { dispatch } = this.props;
Lbryio.call('user_settings', 'get').then(settings => { doPreferenceGet(
dispatch(doPopulateSharedUserState(settings)); 'shared',
}); preference => {
dispatch(doPopulateSharedUserState(preference));
},
error => {
/* failed */
}
);
}; };
componentWillUnmount() { componentWillUnmount() {
AppState.removeEventListener('change', this._handleAppStateChange); AppState.removeEventListener('change', this._handleAppStateChange);
BackHandler.removeEventListener('hardwareBackPress'); BackHandler.removeEventListener('hardwareBackPress');
Linking.removeEventListener('url', this._handleUrl); Linking.removeEventListener('url', this._handleUrl);
if (this.emailVerifyCheckInterval > -1) {
clearInterval(this.emailVerifyCheckInterval);
}
if (this.syncGetInterval > -1) {
clearInterval(this.syncGetInterval);
}
} }
componentDidUpdate() { componentDidUpdate() {
const { dispatch, user } = this.props; const { dispatch, user, hashChanged } = this.props;
if (this.state.verifyPending && this.emailVerifyCheckInterval > 0 && user && user.has_verified_email) { if (this.state.verifyPending && this.emailVerifyCheckInterval > 0 && user && user.has_verified_email) {
clearInterval(this.emailVerifyCheckInterval); clearInterval(this.emailVerifyCheckInterval);
AsyncStorage.setItem(Constants.KEY_EMAIL_VERIFY_PENDING, 'false'); AsyncStorage.setItem(Constants.KEY_EMAIL_VERIFY_PENDING, 'false');
@ -332,6 +358,11 @@ class AppWithNavigationState extends React.Component {
// get user settings after email verification // get user settings after email verification
this.getUserSettings(); this.getUserSettings();
} }
if (hashChanged && !this.state.syncHashChanged) {
this.setState({ syncHashChanged: true });
this.getUserSettings();
}
} }
componentWillUpdate(nextProps) { componentWillUpdate(nextProps) {
@ -438,6 +469,7 @@ class AppWithNavigationState extends React.Component {
const mapStateToProps = state => ({ const mapStateToProps = state => ({
backgroundPlayEnabled: makeSelectClientSetting(SETTINGS.BACKGROUND_PLAY_ENABLED)(state), backgroundPlayEnabled: makeSelectClientSetting(SETTINGS.BACKGROUND_PLAY_ENABLED)(state),
hashChanged: selectHashChanged(state),
keepDaemonRunning: makeSelectClientSetting(SETTINGS.KEEP_DAEMON_RUNNING)(state), keepDaemonRunning: makeSelectClientSetting(SETTINGS.KEEP_DAEMON_RUNNING)(state),
nav: state.nav, nav: state.nav,
toast: selectToast(state), toast: selectToast(state),

View file

@ -7,6 +7,7 @@ import {
doCreateChannel, doCreateChannel,
doToast, doToast,
} from 'lbry-redux'; } from 'lbry-redux';
import { doGetSync } from 'lbryinc';
import ChannelSelector from './view'; import ChannelSelector from './view';
const select = state => ({ const select = state => ({
@ -19,6 +20,7 @@ const perform = dispatch => ({
notify: data => dispatch(doToast(data)), notify: data => dispatch(doToast(data)),
createChannel: (name, amount) => dispatch(doCreateChannel(name, amount)), createChannel: (name, amount) => dispatch(doCreateChannel(name, amount)),
fetchChannelListMine: () => dispatch(doFetchChannelListMine()), fetchChannelListMine: () => dispatch(doFetchChannelListMine()),
getSync: (password, callback) => dispatch(doGetSync(password, callback)),
}); });
export default connect( export default connect(

View file

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import { CLAIM_VALUES, isNameValid } from 'lbry-redux'; import { CLAIM_VALUES, isNameValid } from 'lbry-redux';
import { ActivityIndicator, Picker, Text, TextInput, TouchableOpacity, View } from 'react-native'; import { ActivityIndicator, NativeModules, Picker, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { logPublish } from 'utils/helper';
import Button from 'component/button'; import Button from 'component/button';
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
@ -117,7 +118,7 @@ export default class ChannelSelector extends React.PureComponent {
}; };
handleCreateChannelClick = () => { handleCreateChannelClick = () => {
const { balance, createChannel, onChannelChange, notify } = this.props; const { balance, createChannel, getSync, onChannelChange, notify } = this.props;
const { newChannelBid, newChannelName } = this.state; const { newChannelBid, newChannelName } = this.state;
if (newChannelName.trim().length === 0 || !isNameValid(newChannelName.substr(1), false)) { if (newChannelName.trim().length === 0 || !isNameValid(newChannelName.substr(1), false)) {
@ -142,17 +143,21 @@ export default class ChannelSelector extends React.PureComponent {
createChannelError: undefined, createChannelError: undefined,
}); });
const success = () => { const success = channelClaim => {
this.setState({ this.setState({
creatingChannel: false, creatingChannel: false,
addingChannel: false, addingChannel: false,
currentSelectedValue: channelName, currentSelectedValue: channelName,
showCreateChannel: false, showCreateChannel: false,
}); });
logPublish(channelClaim);
if (onChannelChange) { if (onChannelChange) {
onChannelChange(channelName); onChannelChange(channelName);
} }
// sync wallet
NativeModules.UtilityModule.getSecureValue(Constants.KEY_WALLET_PASSWORD).then(password => getSync(password));
}; };
const failure = () => { const failure = () => {

View file

@ -12,7 +12,7 @@ import claimListStyle from 'styles/claimList';
import discoverStyle from 'styles/discover'; import discoverStyle from 'styles/discover';
import moment from 'moment'; import moment from 'moment';
const horizontalLimit = 7; const horizontalLimit = 10;
const softLimit = 500; const softLimit = 500;
class ClaimList extends React.PureComponent { class ClaimList extends React.PureComponent {

View file

@ -10,13 +10,15 @@ import {
makeSelectClaimIsNsfw, makeSelectClaimIsNsfw,
makeSelectShortUrlForUri, makeSelectShortUrlForUri,
} from 'lbry-redux'; } from 'lbry-redux';
import { selectRewardContentClaimIds } from 'lbryinc'; import { selectBlackListedOutpoints, selectFilteredOutpoints, selectRewardContentClaimIds } from 'lbryinc';
import { selectShowNsfw } from 'redux/selectors/settings'; import { selectShowNsfw } from 'redux/selectors/settings';
import FileItem from './view'; import FileItem from './view';
const select = (state, props) => ({ const select = (state, props) => ({
blackListedOutpoints: selectBlackListedOutpoints(state),
claim: makeSelectClaimForUri(props.uri)(state), claim: makeSelectClaimForUri(props.uri)(state),
fileInfo: makeSelectFileInfoForUri(props.uri)(state), fileInfo: makeSelectFileInfoForUri(props.uri)(state),
filteredOutpoints: selectFilteredOutpoints(state),
metadata: makeSelectMetadataForUri(props.uri)(state), metadata: makeSelectMetadataForUri(props.uri)(state),
rewardedContentClaimIds: selectRewardContentClaimIds(state), rewardedContentClaimIds: selectRewardContentClaimIds(state),
isResolvingUri: makeSelectIsUriResolving(props.uri)(state), isResolvingUri: makeSelectIsUriResolving(props.uri)(state),

View file

@ -40,10 +40,12 @@ class FileItem extends React.PureComponent {
render() { render() {
const { const {
blackListedOutpoints,
claim, claim,
title, title,
thumbnail, thumbnail,
fileInfo, fileInfo,
filteredOutpoints,
metadata, metadata,
isResolvingUri, isResolvingUri,
rewardedContentClaimIds, rewardedContentClaimIds,
@ -58,7 +60,17 @@ class FileItem extends React.PureComponent {
} = this.props; } = this.props;
if (claim && claim.value_type === 'channel') { if (claim && claim.value_type === 'channel') {
// don't display channels in the lists on the Explore page // don't display channels in the lists on the Your tags page
return null;
}
let shouldHide = false;
if (blackListedOutpoints || filteredOutpoints) {
const outpointsToHide = blackListedOutpoints.concat(filteredOutpoints);
shouldHide = outpointsToHide.some(outpoint => outpoint.txid === claim.txid && outpoint.nout === claim.nout);
}
if (shouldHide) {
// don't display blacklisted or filtered outpoints on the Your tags page
return null; return null;
} }

View file

@ -34,7 +34,7 @@ class FloatingWalletBalance extends React.PureComponent<Props> {
<Icon name="coins" size={12} style={floatingButtonStyle.balanceIcon} /> <Icon name="coins" size={12} style={floatingButtonStyle.balanceIcon} />
{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), 0, true)}</Text> <Text style={floatingButtonStyle.text}>{formatCredits(parseFloat(balance), 1, true)}</Text>
)} )}
</TouchableOpacity> </TouchableOpacity>
</View> </View>

View file

@ -4,8 +4,10 @@ import {
selectSearchState as selectSearch, selectSearchState as selectSearch,
selectSearchValue, selectSearchValue,
selectSearchSuggestions, selectSearchSuggestions,
SETTINGS,
} from 'lbry-redux'; } from 'lbry-redux';
import { selectCurrentRoute } from 'redux/selectors/drawer'; import { selectCurrentRoute } from 'redux/selectors/drawer';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import UriBar from './view'; import UriBar from './view';
const select = state => { const select = state => {
@ -16,6 +18,7 @@ const select = state => {
query: selectSearchValue(state), query: selectSearchValue(state),
currentRoute: selectCurrentRoute(state), currentRoute: selectCurrentRoute(state),
suggestions: selectSearchSuggestions(state), suggestions: selectSearchSuggestions(state),
showUriBarSuggestions: makeSelectClientSetting(SETTINGS.SHOW_URI_BAR_SUGGESTIONS)(state),
}; };
}; };

View file

@ -20,6 +20,10 @@ class UriBarItem extends React.PureComponent {
icon = <Icon name="search" size={18} />; icon = <Icon name="search" size={18} />;
break; break;
case SEARCH_TYPES.TAG:
icon = <Icon name="hashtag" size={18} />;
break;
case SEARCH_TYPES.FILE: case SEARCH_TYPES.FILE:
default: default:
icon = <Icon name="file" size={18} />; icon = <Icon name="file" size={18} />;
@ -37,6 +41,7 @@ class UriBarItem extends React.PureComponent {
{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`}
{type === SEARCH_TYPES.FILE && `View content at ${value}`} {type === SEARCH_TYPES.FILE && `View content at ${value}`}
{type === SEARCH_TYPES.TAG && `Explore the '${value}' tag`}
</Text> </Text>
</View> </View>
</TouchableOpacity> </TouchableOpacity>

View file

@ -1,7 +1,7 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import { SEARCH_TYPES, isNameValid, isURIValid, normalizeURI } from 'lbry-redux'; import { SEARCH_TYPES, isNameValid, isURIValid, normalizeURI } from 'lbry-redux';
import { FlatList, Keyboard, Text, TextInput, TouchableOpacity, View } from 'react-native'; import { Dimensions, FlatList, Keyboard, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { navigateToUri, transformUrl } from 'utils/helper'; import { navigateToUri, transformUrl } from 'utils/helper';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import UriBarItem from './internal/uri-bar-item'; import UriBarItem from './internal/uri-bar-item';
@ -14,6 +14,8 @@ class UriBar extends React.PureComponent {
textInput = null; textInput = null;
keyboardDidShowListener = null;
keyboardDidHideListener = null; keyboardDidHideListener = null;
state = { state = {
@ -21,17 +23,19 @@ class UriBar extends React.PureComponent {
currentValue: null, currentValue: null,
inputText: null, inputText: null,
focused: false, focused: false,
keyboardHeight: 0,
// TODO: Add a setting to enable / disable direct search?
directSearch: true,
}; };
componentDidMount() { componentDidMount() {
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow);
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide); this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
this.setSelection(); this.setSelection();
} }
componentWillUnmount() { componentWillUnmount() {
if (this.keyboardDidShowListener) {
this.keyboardDidShowListener.remove();
}
if (this.keyboardDidHideListener) { if (this.keyboardDidHideListener) {
this.keyboardDidHideListener.remove(); this.keyboardDidHideListener.remove();
} }
@ -49,16 +53,18 @@ class UriBar extends React.PureComponent {
handleChangeText = text => { handleChangeText = text => {
const newValue = text || ''; const newValue = text || '';
clearTimeout(this.state.changeTextTimeout); clearTimeout(this.state.changeTextTimeout);
const { updateSearchQuery, onSearchSubmitted, navigation } = this.props; const { updateSearchQuery, onSearchSubmitted, showUriBarSuggestions, navigation } = this.props;
let timeout = setTimeout(() => { updateSearchQuery(text);
let timeout = -1;
if (!showUriBarSuggestions) {
timeout = setTimeout(() => {
if (text.trim().length === 0) { if (text.trim().length === 0) {
// don't do anything if the text is empty // don't do anything if the text is empty
return; return;
} }
updateSearchQuery(text);
if (!text.startsWith('lbry://')) { if (!text.startsWith('lbry://')) {
// not a URI input, so this is a search, perform a direct search // not a URI input, so this is a search, perform a direct search
if (onSearchSubmitted) { if (onSearchSubmitted) {
@ -68,6 +74,7 @@ class UriBar extends React.PureComponent {
} }
} }
}, UriBar.INPUT_TIMEOUT); }, UriBar.INPUT_TIMEOUT);
}
this.setState({ inputText: newValue, currentValue: newValue, changeTextTimeout: timeout }); this.setState({ inputText: newValue, currentValue: newValue, changeTextTimeout: timeout });
}; };
@ -86,18 +93,34 @@ class UriBar extends React.PureComponent {
return; return;
} }
navigation.navigate({ routeName: 'Search', key: 'searchPage', params: { searchQuery: value } }); navigation.navigate({
routeName: Constants.DRAWER_ROUTE_SEARCH,
key: 'searchPage',
params: { searchQuery: value },
});
} else if (SEARCH_TYPES.TAG === type) {
navigation.navigate({
routeName: Constants.DRAWER_ROUTE_TAG,
key: 'tagPage',
params: {
tag: value.toLowerCase(),
},
});
} else { } else {
const uri = normalizeURI(value); const uri = normalizeURI(value);
navigateToUri(navigation, uri); navigateToUri(navigation, uri);
} }
}; };
_keyboardDidShow = evt => {
this.setState({ keyboardHeight: evt.endCoordinates.height });
};
_keyboardDidHide = () => { _keyboardDidHide = () => {
if (this.textInput) { if (this.textInput) {
this.textInput.blur(); this.textInput.blur();
} }
this.setState({ focused: false }); this.setState({ focused: false, keyboardHeight: 0 });
}; };
setSelection() { setSelection() {
@ -152,16 +175,18 @@ class UriBar extends React.PureComponent {
selectedItemCount, selectedItemCount,
selectionMode, selectionMode,
suggestions, suggestions,
showUriBarSuggestions,
value, value,
} = this.props; } = this.props;
if (this.state.currentValue === null) { if (this.state.currentValue === null) {
this.setState({ currentValue: value }); this.setState({ currentValue: value });
} }
let style = [uriBarStyle.overlay, belowOverlay ? null : uriBarStyle.overlayElevated]; let style = [
uriBarStyle.overlay,
// TODO: Add optional setting to enable URI / search bar suggestions belowOverlay ? null : uriBarStyle.overlayElevated,
/* if (this.state.focused) { style.push(uriBarStyle.inFocus); } */ this.state.focused && showUriBarSuggestions ? uriBarStyle.inFocus : null,
];
// TODO: selectionModeActions should be dynamically created / specified // TODO: selectionModeActions should be dynamically created / specified
return ( return (
@ -248,21 +273,22 @@ class UriBar extends React.PureComponent {
onSubmitEditing={this.handleSubmitEditing} onSubmitEditing={this.handleSubmitEditing}
/> />
)} )}
{this.state.focused && !this.state.directSearch && ( </View>
<View style={uriBarStyle.suggestions}> {this.state.focused && showUriBarSuggestions && (
<FlatList <FlatList
style={uriBarStyle.suggestionList} style={[
uriBarStyle.suggestions,
{ height: Dimensions.get('window').height - this.state.keyboardHeight - 60 },
]}
data={suggestions} data={suggestions}
keyboardShouldPersistTaps={'handled'} keyboardShouldPersistTaps={'handled'}
keyExtractor={(item, value) => item.value} keyExtractor={(item, value) => `${item.value}-${item.type}`}
renderItem={({ item }) => ( renderItem={({ item }) => (
<UriBarItem item={item} navigation={navigation} onPress={() => this.handleItemPress(item)} /> <UriBarItem item={item} navigation={navigation} onPress={() => this.handleItemPress(item)} />
)} )}
/> />
</View>
)} )}
</View> </View>
</View>
); );
} }
} }

View file

@ -32,7 +32,7 @@ const Constants = {
ABOUT_TAB: 'about', ABOUT_TAB: 'about',
KEY_FIRST_RUN_EMAIL: 'firstRunEmail', KEY_FIRST_RUN_EMAIL: 'firstRunEmail',
KEY_FIRST_RUN_PASSWORD: 'firstRunPassword', KEY_WALLET_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',

View file

@ -4,6 +4,8 @@ import { Provider, connect } from 'react-redux';
import { AppRegistry, Text, View, NativeModules } from 'react-native'; import { AppRegistry, Text, View, NativeModules } from 'react-native';
import { import {
Lbry, Lbry,
buildSharedStateMiddleware,
blockedReducer,
claimsReducer, claimsReducer,
contentReducer, contentReducer,
fileReducer, fileReducer,
@ -13,19 +15,24 @@ import {
searchReducer, searchReducer,
tagsReducer, tagsReducer,
walletReducer, walletReducer,
ACTIONS as LBRY_REDUX_ACTIONS,
} from 'lbry-redux'; } from 'lbry-redux';
import { import {
Lbryio, Lbryio,
authReducer, authReducer,
blacklistReducer, blacklistReducer,
costInfoReducer, costInfoReducer,
doGetSync,
filteredReducer, filteredReducer,
homepageReducer, homepageReducer,
rewardsReducer, rewardsReducer,
selectUserVerifiedEmail,
subscriptionsReducer, subscriptionsReducer,
syncReducer, syncReducer,
userReducer, userReducer,
LBRYINC_ACTIONS,
} from 'lbryinc'; } from 'lbryinc';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import { createStore, applyMiddleware, compose } from 'redux'; import { createStore, applyMiddleware, compose } from 'redux';
import AppWithNavigationState, { import AppWithNavigationState, {
AppNavigator, AppNavigator,
@ -33,6 +40,7 @@ import AppWithNavigationState, {
reactNavigationMiddleware, reactNavigationMiddleware,
} from 'component/AppNavigator'; } from 'component/AppNavigator';
import { REHYDRATE, PURGE, persistCombineReducers, persistStore } from 'redux-persist'; import { REHYDRATE, PURGE, persistCombineReducers, persistStore } from 'redux-persist';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import getStoredStateMigrateV4 from 'redux-persist/lib/integration/getStoredStateMigrateV4'; import getStoredStateMigrateV4 from 'redux-persist/lib/integration/getStoredStateMigrateV4';
import FilesystemStorage from 'redux-persist-filesystem-storage'; import FilesystemStorage from 'redux-persist-filesystem-storage';
import createCompressor from 'redux-persist-transform-compress'; import createCompressor from 'redux-persist-transform-compress';
@ -42,7 +50,6 @@ import formReducer from 'redux/reducers/form';
import drawerReducer from 'redux/reducers/drawer'; 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';
import isEqual from 'utils/deep-equal';
const globalExceptionHandler = (error, isFatal) => { const globalExceptionHandler = (error, isFatal) => {
if (error && NativeModules.Firebase) { if (error && NativeModules.Firebase) {
@ -81,6 +88,7 @@ function enableBatching(reducer) {
const compressor = createCompressor(); const compressor = createCompressor();
const authFilter = createFilter('auth', ['authToken']); const authFilter = createFilter('auth', ['authToken']);
const blockedFilter = createFilter('blocked', ['blockedChannels']);
const contentFilter = createFilter('content', ['positions']); const contentFilter = createFilter('content', ['positions']);
const saveClaimsFilter = createFilter('claims', ['claimsByUri']); const saveClaimsFilter = createFilter('claims', ['claimsByUri']);
const subscriptionsFilter = createFilter('subscriptions', ['enabledChannelNotifications', 'subscriptions', 'latest']); const subscriptionsFilter = createFilter('subscriptions', ['enabledChannelNotifications', 'subscriptions', 'latest']);
@ -89,10 +97,18 @@ const tagsFilter = createFilter('tags', ['followedTags']);
const walletFilter = createFilter('wallet', ['receiveAddress']); const walletFilter = createFilter('wallet', ['receiveAddress']);
const v4PersistOptions = { const v4PersistOptions = {
whitelist: ['auth', 'claims', 'content', 'subscriptions', 'settings', 'tags', 'wallet'], whitelist: ['auth', 'blocked', 'claims', 'content', 'subscriptions', 'settings', 'tags', 'wallet'],
// Order is important. Needs to be compressed last or other transforms can't // Order is important. Needs to be compressed last or other transforms can't
// read the data // read the data
transforms: [authFilter, saveClaimsFilter, subscriptionsFilter, settingsFilter, walletFilter, compressor], transforms: [
authFilter,
blockedFilter,
saveClaimsFilter,
subscriptionsFilter,
settingsFilter,
walletFilter,
compressor,
],
debounce: 10000, debounce: 10000,
storage: FilesystemStorage, storage: FilesystemStorage,
}; };
@ -105,6 +121,7 @@ const persistOptions = Object.assign({}, v4PersistOptions, {
const reducers = persistCombineReducers(persistOptions, { const reducers = persistCombineReducers(persistOptions, {
auth: authReducer, auth: authReducer,
blacklist: blacklistReducer, blacklist: blacklistReducer,
blocked: blockedReducer,
claims: claimsReducer, claims: claimsReducer,
content: contentReducer, content: contentReducer,
costInfo: costInfoReducer, costInfo: costInfoReducer,
@ -127,8 +144,45 @@ const reducers = persistCombineReducers(persistOptions, {
wallet: walletReducer, wallet: walletReducer,
}); });
/**
* source: the reducer name
* property: the property in the reducer-specific state
* transform: optional method to modify the value to be stored
*/
const sharedStateActions = [
LBRYINC_ACTIONS.CHANNEL_SUBSCRIBE,
LBRYINC_ACTIONS.CHANNEL_UNSUBSCRIBE,
LBRY_REDUX_ACTIONS.CREATE_CHANNEL_COMPLETED,
LBRY_REDUX_ACTIONS.TOGGLE_TAG_FOLLOW,
LBRY_REDUX_ACTIONS.TOGGLE_BLOCK_CHANNEL,
];
const sharedStateFilters = {
tags: { source: 'tags', property: 'followedTags' },
subscriptions: {
source: 'subscriptions',
property: 'subscriptions',
transform: function(value) {
return value.map(({ uri }) => uri);
},
},
blocked: { source: 'blocked', property: 'blockedChannels' },
};
const sharedStateCallback = ({ dispatch, getState }) => {
const state = getState();
const syncEnabled = makeSelectClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED)(state);
const emailVerified = selectUserVerifiedEmail(state);
if (syncEnabled && emailVerified) {
NativeModules.UtilityModule.getSecureValue(Constants.KEY_WALLET_PASSWORD).then(password =>
dispatch(doGetSync(password))
);
}
};
const sharedStateMiddleware = buildSharedStateMiddleware(sharedStateActions, sharedStateFilters, sharedStateCallback);
const bulkThunk = createBulkThunkMiddleware(); const bulkThunk = createBulkThunkMiddleware();
const middleware = [thunk, bulkThunk, reactNavigationMiddleware]; const middleware = [sharedStateMiddleware, thunk, bulkThunk, reactNavigationMiddleware];
// eslint-disable-next-line no-underscore-dangle // eslint-disable-next-line no-underscore-dangle
const composeEnhancers = compose; const composeEnhancers = compose;
@ -147,28 +201,6 @@ const persistor = persistStore(store, persistOptions, err => {
}); });
window.persistor = persistor; window.persistor = persistor;
let currentPayload;
store.subscribe(() => {
const state = store.getState();
const subscriptions = state.subscriptions.subscriptions.map(({ uri }) => uri);
const tags = state.tags.followedTags;
const newPayload = {
version: '0.1',
shared: {
subscriptions,
tags,
},
};
if (!isEqual(newPayload, currentPayload)) {
currentPayload = newPayload;
if (Lbryio.authToken) {
Lbryio.call('user_settings', 'set', { settings: JSON.stringify(newPayload) });
}
}
});
// 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;

View file

@ -12,6 +12,7 @@ import {
doUpdateChannel, doUpdateChannel,
doToast, doToast,
} from 'lbry-redux'; } from 'lbry-redux';
import { doGetSync } from 'lbryinc';
import { doPushDrawerStack, doPopDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer'; import { doPushDrawerStack, doPopDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
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';
@ -37,6 +38,7 @@ const perform = dispatch => ({
clearChannelFormState: () => dispatch(doClearChannelFormState()), clearChannelFormState: () => dispatch(doClearChannelFormState()),
createChannel: (name, amount, optionalParams) => dispatch(doCreateChannel(name, amount, optionalParams)), createChannel: (name, amount, optionalParams) => dispatch(doCreateChannel(name, amount, optionalParams)),
fetchChannelListMine: () => dispatch(doFetchChannelListMine()), fetchChannelListMine: () => dispatch(doFetchChannelListMine()),
getSync: (password, callback) => dispatch(doGetSync(password, callback)),
updateChannel: params => dispatch(doUpdateChannel(params)), updateChannel: params => dispatch(doUpdateChannel(params)),
updateChannelFormState: data => dispatch(doUpdateChannelFormState(data)), updateChannelFormState: data => dispatch(doUpdateChannelFormState(data)),
pushDrawerStack: (routeName, params) => dispatch(doPushDrawerStack(routeName, params)), pushDrawerStack: (routeName, params) => dispatch(doPushDrawerStack(routeName, params)),

View file

@ -14,7 +14,7 @@ import {
TouchableOpacity, TouchableOpacity,
View, View,
} from 'react-native'; } from 'react-native';
import { navigateToUri, uploadImageAsset } from 'utils/helper'; import { navigateToUri, logPublish, uploadImageAsset } from 'utils/helper';
import Button from 'component/button'; import Button from 'component/button';
import ChannelIconItem from 'component/channelIconItem'; import ChannelIconItem from 'component/channelIconItem';
import ChannelRewardsDriver from 'component/channelRewardsDriver'; import ChannelRewardsDriver from 'component/channelRewardsDriver';
@ -375,7 +375,15 @@ export default class ChannelCreator extends React.PureComponent {
}; };
handleCreateChannelClick = () => { handleCreateChannelClick = () => {
const { balance, clearChannelFormState, createChannel, onChannelChange, notify, updateChannel } = this.props; const {
balance,
clearChannelFormState,
createChannel,
onChannelChange,
getSync,
notify,
updateChannel,
} = this.props;
const { const {
claimId, claimId,
coverImageUrl, coverImageUrl,
@ -432,7 +440,7 @@ export default class ChannelCreator extends React.PureComponent {
createChannelError: undefined, createChannelError: undefined,
}); });
const success = () => { const success = channelClaim => {
this.setState({ this.setState({
creatingChannel: false, creatingChannel: false,
addingChannel: false, addingChannel: false,
@ -448,6 +456,8 @@ export default class ChannelCreator extends React.PureComponent {
clearChannelFormState(); clearChannelFormState();
notify({ message: 'The channel was successfully created.' }); notify({ message: 'The channel was successfully created.' });
this.showChannelList(); this.showChannelList();
logPublish(channelClaim);
}; };
const failure = () => { const failure = () => {

View file

@ -517,7 +517,7 @@ class FilePage extends React.PureComponent {
const { claim, notify } = this.props; const { claim, notify } = this.props;
if (claim) { if (claim) {
const { canonical_url: canonicalUrl, short_url: shortUrl, permanent_url: permanentUrl } = claim; const { canonical_url: canonicalUrl, short_url: shortUrl, permanent_url: permanentUrl } = claim;
const url = 'https://lbry.tv' + this.formatLbryUrlForWeb(canonicalUrl || shortUrl || permanentUrl); const url = 'https://open.lbry.com' + this.formatLbryUrlForWeb(canonicalUrl || shortUrl || permanentUrl);
NativeModules.UtilityModule.shareUrl(url); NativeModules.UtilityModule.shareUrl(url);
} }
}; };
@ -950,15 +950,23 @@ class FilePage extends React.PureComponent {
<View style={filePageStyle.largeButtonsRow}> <View style={filePageStyle.largeButtonsRow}>
<TouchableOpacity style={filePageStyle.largeButton} onPress={this.handleSharePress}> <TouchableOpacity style={filePageStyle.largeButton} onPress={this.handleSharePress}>
<Icon name={'share-alt'} size={24} style={filePageStyle.largeButtonIcon} /> <Icon name={'share-alt'} size={20} style={filePageStyle.largeButtonIcon} />
<Text style={filePageStyle.largeButtonText}>Share</Text> <Text style={filePageStyle.largeButtonText}>Share</Text>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity
style={filePageStyle.largeButton}
onPress={() => this.setState({ showTipView: true })}
>
<Icon name={'gift'} size={20} style={filePageStyle.largeButtonIcon} />
<Text style={filePageStyle.largeButtonText}>Tip</Text>
</TouchableOpacity>
<TouchableOpacity <TouchableOpacity
style={filePageStyle.largeButton} style={filePageStyle.largeButton}
onPress={() => Linking.openURL(`https://lbry.com/dmca/${claim.claim_id}`)} onPress={() => Linking.openURL(`https://lbry.com/dmca/${claim.claim_id}`)}
> >
<Icon name={'flag'} size={24} style={filePageStyle.largeButtonIcon} /> <Icon name={'flag'} size={20} style={filePageStyle.largeButtonIcon} />
<Text style={filePageStyle.largeButtonText}>Report</Text> <Text style={filePageStyle.largeButtonText}>Report</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
@ -999,12 +1007,6 @@ class FilePage extends React.PureComponent {
onPress={this.onSaveFilePressed} onPress={this.onSaveFilePressed}
/> />
)} )}
<Button
style={[filePageStyle.actionButton, filePageStyle.tipButton]}
theme={'light'}
icon={'gift'}
onPress={() => this.setState({ showTipView: true })}
/>
{channelName && ( {channelName && (
<SubscribeButton <SubscribeButton
style={filePageStyle.actionButton} style={filePageStyle.actionButton}

View file

@ -4,7 +4,6 @@ import {
doAuthenticate, doAuthenticate,
doCheckSync, doCheckSync,
doGetSync, doGetSync,
doSetDefaultAccount,
doSyncApply, doSyncApply,
doUserEmailNew, doUserEmailNew,
doUserResendVerificationEmail, doUserResendVerificationEmail,
@ -41,12 +40,11 @@ const select = state => ({
const perform = dispatch => ({ const perform = dispatch => ({
addUserEmail: email => dispatch(doUserEmailNew(email)), addUserEmail: email => dispatch(doUserEmailNew(email)),
authenticate: (appVersion, os) => dispatch(doAuthenticate(appVersion, os)), authenticate: (appVersion, os, firebaseToken) => dispatch(doAuthenticate(appVersion, os, firebaseToken)),
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)), setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
syncApply: (hash, data, password) => dispatch(doSyncApply(hash, data, password)), syncApply: (hash, data, password) => dispatch(doSyncApply(hash, data, password)),
getSync: password => dispatch(doGetSync(password)), getSync: (password, callback) => dispatch(doGetSync(password, callback)),
checkSync: () => dispatch(doCheckSync()), checkSync: () => dispatch(doCheckSync()),
setDefaultAccount: (success, failure) => dispatch(doSetDefaultAccount(success, failure)),
notify: data => dispatch(doToast(data)), notify: data => dispatch(doToast(data)),
resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email)), resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email)),
}); });

View file

@ -12,7 +12,7 @@ import {
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'; // eslint-disable-line node/no-deprecated-api
import Icon from 'react-native-vector-icons/FontAwesome5'; import Icon from 'react-native-vector-icons/FontAwesome5';
import firstRunStyle from 'styles/firstRun'; import firstRunStyle from 'styles/firstRun';
@ -45,8 +45,8 @@ class SkipAccountPage extends React.PureComponent {
/> />
</View> </View>
<Text style={firstRunStyle.rowParagraph}> <Text style={firstRunStyle.rowParagraph}>
I understand that by uninstalling LBRY I will lose any balances or published content with no recovery I understand that by uninstalling LBRY I will lose any balances or published content with no recovery option
option if it is not backed up manually (see wallet page) if it is not backed up manually (see wallet page)
</Text> </Text>
</View> </View>
</View> </View>

View file

@ -14,7 +14,7 @@ import {
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';
import Colors from 'styles/colors'; import Colors from 'styles/colors';
import Constants from 'constants'; import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import firstRunStyle from 'styles/firstRun'; import firstRunStyle from 'styles/firstRun';
import Icon from 'react-native-vector-icons/FontAwesome5'; import Icon from 'react-native-vector-icons/FontAwesome5';
@ -62,7 +62,14 @@ class WalletPage extends React.PureComponent {
}; };
render() { render() {
const { onPasswordChanged, onWalletViewLayout, getSyncIsPending, hasSyncedWallet, syncApplyIsPending } = this.props; const {
onPasswordChanged,
onWalletViewLayout,
getSyncIsPending,
hasSyncedWallet,
syncApplyIsPending,
syncApplyStarted,
} = this.props;
let content; let content;
if (!this.state.walletReady || !this.state.hasCheckedSync || getSyncIsPending) { if (!this.state.walletReady || !this.state.hasCheckedSync || getSyncIsPending) {
@ -72,11 +79,11 @@ class WalletPage extends React.PureComponent {
<Text style={firstRunStyle.paragraph}>Retrieving your account information...</Text> <Text style={firstRunStyle.paragraph}>Retrieving your account information...</Text>
</View> </View>
); );
} else if (syncApplyIsPending) { } else if (syncApplyStarted || syncApplyIsPending) {
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}>{syncApplyIsPending ? 'Validating password' : 'Synchronizing'}...</Text>
</View> </View>
); );
} else { } else {

View file

@ -3,7 +3,7 @@ import { Lbry } from 'lbry-redux';
import { ActivityIndicator, NativeModules, Platform, Text, View } from 'react-native'; import { 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'; // eslint-disable-line node/no-deprecated-api
import firstRunStyle from 'styles/firstRun'; import firstRunStyle from 'styles/firstRun';
class WelcomePage extends React.PureComponent { class WelcomePage extends React.PureComponent {
@ -25,7 +25,7 @@ class WelcomePage extends React.PureComponent {
} else { } else {
// first_user_auth because it's the first time // first_user_auth because it's the first time
AsyncStorage.getItem(Constants.KEY_FIRST_USER_AUTH).then(firstUserAuth => { AsyncStorage.getItem(Constants.KEY_FIRST_USER_AUTH).then(firstUserAuth => {
if ('true' !== firstUserAuth) { if (firstUserAuth !== 'true') {
// first_user_auth // first_user_auth
NativeModules.Firebase.track('first_user_auth', null); NativeModules.Firebase.track('first_user_auth', null);
AsyncStorage.setItem(Constants.KEY_FIRST_USER_AUTH, 'true'); AsyncStorage.setItem(Constants.KEY_FIRST_USER_AUTH, 'true');
@ -47,11 +47,12 @@ 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 => {
NativeModules.Firebase.getMessagingToken().then(firebaseToken => {
Lbry.status() Lbry.status()
.then(info => { .then(info => {
this.setState({ sdkStarted: true }); this.setState({ sdkStarted: true });
authenticate(appVersion, Platform.OS); authenticate(appVersion, Platform.OS, firebaseToken);
}) })
.catch(error => { .catch(error => {
if (this.state.statusTries >= WelcomePage.MAX_STATUS_TRIES) { if (this.state.statusTries >= WelcomePage.MAX_STATUS_TRIES) {
@ -67,6 +68,7 @@ class WelcomePage extends React.PureComponent {
} }
}); });
}); });
});
}; };
render() { render() {

View file

@ -66,7 +66,7 @@ class FirstRunScreen extends React.PureComponent {
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
const { emailNewErrorMessage, emailNewPending, syncApplyErrorMessage, syncApplyIsPending, user } = nextProps; const { emailNewErrorMessage, emailNewPending, syncApplyErrorMessage, syncApplyIsPending, user } = nextProps;
const { notify, isApplyingSync, setClientSetting, setDefaultAccount } = this.props; const { notify, isApplyingSync, setClientSetting } = this.props;
if (this.state.emailSubmitted && !emailNewPending) { if (this.state.emailSubmitted && !emailNewPending) {
this.setState({ emailSubmitted: false }); this.setState({ emailSubmitted: false });
@ -81,32 +81,33 @@ class FirstRunScreen extends React.PureComponent {
if (this.state.syncApplyStarted && !syncApplyIsPending) { if (this.state.syncApplyStarted && !syncApplyIsPending) {
if (syncApplyErrorMessage && syncApplyErrorMessage.trim().length > 0) { if (syncApplyErrorMessage && syncApplyErrorMessage.trim().length > 0) {
notify({ message: syncApplyErrorMessage, syncApplyStarted: false, isError: true }); notify({ message: syncApplyErrorMessage, isError: true });
this.setState({ showBottomContainer: true }); this.setState({ showBottomContainer: true, syncApplyStarted: false });
} else { } else {
// password successfully verified // password successfully verified
NativeModules.UtilityModule.setSecureValue( NativeModules.UtilityModule.setSecureValue(
Constants.KEY_FIRST_RUN_PASSWORD, Constants.KEY_WALLET_PASSWORD,
this.state.walletPassword ? this.state.walletPassword : '' this.state.walletPassword ? this.state.walletPassword : ''
); );
setDefaultAccount(
() => {
setClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED, true);
setClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED, true);
Lbry.wallet_status().then(status => {
// unlock the wallet // unlock the wallet
Lbry.account_unlock({ password: this.state.walletPassword ? this.state.walletPassword : '' }) if (status.is_locked) {
.then(() => this.closeFinalPage()) Lbry.wallet_unlock({ password: this.state.walletPassword ? this.state.walletPassword : '' }).then(
.catch(err => unlocked => {
notify({ message: 'The wallet could not be unlocked at this time. Please restart the app.' }) if (unlocked) {
); this.closeFinalPage();
}, } else {
err => { notify({ message: 'The wallet could not be unlocked at this time. Please restart the app.' });
notify({ }
message:
'The account restore operation could not be completed successfully. Please restart the app and try again.',
});
} }
); );
} else {
// wallet not locked. close final page
this.closeFinalPage();
}
});
} }
} }
@ -132,9 +133,9 @@ class FirstRunScreen extends React.PureComponent {
const { navigation } = this.props; const { navigation } = this.props;
const resetAction = StackActions.reset({ const resetAction = StackActions.reset({
index: 0, index: 0,
actions: [NavigationActions.navigate({ routeName: 'Splash', params: { launchUri: this.state.launchUri } })], actions: [NavigationActions.navigate({ routeName: 'Splash', params: { launchUri: this.state.launchUrl } })],
}); });
navigation.dispatch(resetAction); setTimeout(() => navigation.dispatch(resetAction), 1000);
} }
handleLeftButtonPressed = () => { handleLeftButtonPressed = () => {
@ -298,8 +299,8 @@ class FirstRunScreen extends React.PureComponent {
const { getSync, setClientSetting } = this.props; const { getSync, setClientSetting } = this.props;
if (NativeModules.UtilityModule) { if (NativeModules.UtilityModule) {
const newPassword = this.state.walletPassword ? this.state.walletPassword : ''; const newPassword = this.state.walletPassword ? this.state.walletPassword : '';
NativeModules.UtilityModule.setSecureValue(Constants.KEY_FIRST_RUN_PASSWORD, newPassword); NativeModules.UtilityModule.setSecureValue(Constants.KEY_WALLET_PASSWORD, newPassword);
Lbry.account_encrypt({ new_password: newPassword }).then(() => { Lbry.wallet_encrypt({ new_password: newPassword }).then(() => {
// fresh account, new password set // fresh account, new password set
getSync(newPassword); getSync(newPassword);
setClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED, true); setClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED, true);
@ -367,6 +368,7 @@ class FirstRunScreen extends React.PureComponent {
hasSyncedWallet={hasSyncedWallet} hasSyncedWallet={hasSyncedWallet}
getSyncIsPending={getSyncIsPending} getSyncIsPending={getSyncIsPending}
syncApplyIsPending={syncApplyIsPending} syncApplyIsPending={syncApplyIsPending}
syncApplyStarted={this.state.syncApplyStarted}
onWalletViewLayout={this.onWalletViewLayout} onWalletViewLayout={this.onWalletViewLayout}
onPasswordChanged={this.onWalletPasswordChanged} onPasswordChanged={this.onWalletPasswordChanged}
/> />

View file

@ -43,7 +43,7 @@ import Tag from 'component/tag';
import TagSearch from 'component/tagSearch'; import TagSearch from 'component/tagSearch';
import UriBar from 'component/uriBar'; import UriBar from 'component/uriBar';
import publishStyle from 'styles/publish'; import publishStyle from 'styles/publish';
import { __, navigateToUri, uploadImageAsset } from 'utils/helper'; import { __, navigateToUri, logPublish, uploadImageAsset } from 'utils/helper';
const languages = { const languages = {
en: 'English', en: 'English',
@ -371,7 +371,7 @@ class PublishPage extends React.PureComponent {
filePath: currentMedia ? currentMedia.filePath : null, filePath: currentMedia ? currentMedia.filePath : null,
bid: bid || 0.1, bid: bid || 0.1,
title: title || '', title: title || '',
thumbnail, thumbnail: thumbnail || '',
description: description || '', description: description || '',
language, language,
license, license,
@ -395,6 +395,11 @@ class PublishPage extends React.PureComponent {
handlePublishSuccess = data => { handlePublishSuccess = data => {
const { clearPublishFormState, navigation, notify } = this.props; const { clearPublishFormState, navigation, notify } = this.props;
const pendingClaim = data.outputs[0];
logPublish(pendingClaim);
// TODO: fake temp claim for claim_list_mine
notify({ notify({
message: `Your content was successfully published to ${this.state.uri}. It will be available in a few mintues.`, message: `Your content was successfully published to ${this.state.uri}. It will be available in a few mintues.`,
}); });
@ -404,6 +409,7 @@ class PublishPage extends React.PureComponent {
}; };
handlePublishFailure = error => { handlePublishFailure = error => {
console.log(error);
const { notify } = this.props; const { notify } = this.props;
notify({ message: __('Your content could not be published at this time. Please try again.') }); notify({ message: __('Your content could not be published at this time. Please try again.') });
this.setState({ publishStarted: false }); this.setState({ publishStarted: false });

View file

@ -4,7 +4,7 @@ import { doPushDrawerStack, doPopDrawerStack, doSetPlayerVisible } from 'redux/a
import { doSetClientSetting } from 'redux/actions/settings'; import { doSetClientSetting } from 'redux/actions/settings';
import { selectCurrentRoute, selectDrawerStack } from 'redux/selectors/drawer'; import { selectCurrentRoute, selectDrawerStack } from 'redux/selectors/drawer';
import { makeSelectClientSetting } from 'redux/selectors/settings'; import { makeSelectClientSetting } from 'redux/selectors/settings';
import Constants from 'constants'; import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import SettingsPage from './view'; import SettingsPage from './view';
const select = state => ({ const select = state => ({
@ -13,6 +13,7 @@ const select = state => ({
drawerStack: selectDrawerStack(state), drawerStack: selectDrawerStack(state),
keepDaemonRunning: makeSelectClientSetting(SETTINGS.KEEP_DAEMON_RUNNING)(state), keepDaemonRunning: makeSelectClientSetting(SETTINGS.KEEP_DAEMON_RUNNING)(state),
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_NSFW)(state), showNsfw: makeSelectClientSetting(SETTINGS.SHOW_NSFW)(state),
showUriBarSuggestions: makeSelectClientSetting(SETTINGS.SHOW_URI_BAR_SUGGESTIONS)(state),
}); });
const perform = dispatch => ({ const perform = dispatch => ({

View file

@ -51,6 +51,7 @@ class SettingsPage extends React.PureComponent {
navigation, navigation,
popDrawerStack, popDrawerStack,
showNsfw, showNsfw,
showUriBarSuggestions,
setClientSetting, setClientSetting,
} = this.props; } = this.props;
@ -62,6 +63,7 @@ class SettingsPage extends React.PureComponent {
<View style={settingsStyle.container}> <View style={settingsStyle.container}>
<PageHeader title={'Settings'} onBackPressed={() => navigateBack(navigation, drawerStack, popDrawerStack)} /> <PageHeader title={'Settings'} onBackPressed={() => navigateBack(navigation, drawerStack, popDrawerStack)} />
<ScrollView style={settingsStyle.scrollContainer}> <ScrollView style={settingsStyle.scrollContainer}>
<Text style={settingsStyle.sectionTitle}>Content</Text>
<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>
@ -79,13 +81,29 @@ class SettingsPage extends React.PureComponent {
<View style={settingsStyle.row}> <View style={settingsStyle.row}>
<View style={settingsStyle.switchText}> <View style={settingsStyle.switchText}>
<Text style={settingsStyle.label}>Show NSFW content</Text> <Text style={settingsStyle.label}>Show mature 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.sectionDivider} />
<Text style={settingsStyle.sectionTitle}>Search</Text>
<View style={settingsStyle.row}>
<View style={settingsStyle.switchText}>
<Text style={settingsStyle.label}>Show URL suggestions</Text>
</View>
<View style={settingsStyle.switchContainer}>
<Switch
value={showUriBarSuggestions}
onValueChange={value => setClientSetting(SETTINGS.SHOW_URI_BAR_SUGGESTIONS, value)}
/>
</View>
</View>
<View style={settingsStyle.sectionDivider} />
<Text style={settingsStyle.sectionTitle}>Other</Text>
<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>

View file

@ -23,14 +23,14 @@ const select = state => ({
}); });
const perform = dispatch => ({ const perform = dispatch => ({
authenticate: (appVersion, os) => dispatch(doAuthenticate(appVersion, os)), authenticate: (appVersion, os, firebaseToken) => dispatch(doAuthenticate(appVersion, os, firebaseToken)),
balanceSubscribe: () => dispatch(doBalanceSubscribe()), balanceSubscribe: () => dispatch(doBalanceSubscribe()),
blacklistedOutpointsSubscribe: () => dispatch(doBlackListedOutpointsSubscribe()), blacklistedOutpointsSubscribe: () => dispatch(doBlackListedOutpointsSubscribe()),
filteredOutpointsSubscribe: () => dispatch(doFilteredOutpointsSubscribe()), filteredOutpointsSubscribe: () => dispatch(doFilteredOutpointsSubscribe()),
checkSubscriptionsInit: () => dispatch(doCheckSubscriptionsInit()), checkSubscriptionsInit: () => dispatch(doCheckSubscriptionsInit()),
fetchRewardedContent: () => dispatch(doFetchRewardedContent()), fetchRewardedContent: () => dispatch(doFetchRewardedContent()),
fetchSubscriptions: callback => dispatch(doFetchMySubscriptions(callback)), fetchSubscriptions: callback => dispatch(doFetchMySubscriptions(callback)),
getSync: password => dispatch(doGetSync(password)), getSync: (password, callback) => dispatch(doGetSync(password, callback)),
notify: data => dispatch(doToast(data)), notify: data => dispatch(doToast(data)),
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)), setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
setEmailToVerify: email => dispatch(doUserEmailToVerify(email)), setEmailToVerify: email => dispatch(doUserEmailToVerify(email)),

View file

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Lbry } from 'lbry-redux'; import { Lbry, doPreferenceGet } from 'lbry-redux';
import { Lbryio } from 'lbryinc'; import { Lbryio } from 'lbryinc';
import { ActivityIndicator, Linking, NativeModules, Platform, Text, View } from 'react-native'; import { ActivityIndicator, Linking, NativeModules, Platform, Text, View } from 'react-native';
import { NavigationActions, StackActions } from 'react-navigation'; import { NavigationActions, StackActions } from 'react-navigation';
@ -16,8 +16,6 @@ import splashStyle from 'styles/splash';
const BLOCK_HEIGHT_INTERVAL = 1000 * 60 * 2.5; // every 2.5 minutes const BLOCK_HEIGHT_INTERVAL = 1000 * 60 * 2.5; // every 2.5 minutes
const SETTINGS_GET_INTERVAL = 1000 * 60 * 5; // every 5 minutes
const testingNetwork = 'Testing network'; const testingNetwork = 'Testing network';
const waitingForResolution = 'Waiting for name resolution'; const waitingForResolution = 'Waiting for name resolution';
@ -40,12 +38,6 @@ class SplashScreen extends React.PureComponent {
subscriptionsFetched: false, subscriptionsFetched: false,
}; };
componentWillMount() {
if (NativeModules.DaemonServiceControl) {
NativeModules.DaemonServiceControl.startService();
}
}
updateStatus() { updateStatus() {
Lbry.status().then(status => { Lbry.status().then(status => {
this._updateStatusCallback(status); this._updateStatusCallback(status);
@ -60,7 +52,7 @@ class SplashScreen extends React.PureComponent {
}); });
navigation.dispatch(resetAction); navigation.dispatch(resetAction);
const launchUrl = navigation.state.params.launchUrl || this.state.launchUrl; const launchUrl = navigation.state.params ? navigation.state.params.launchUrl : this.state.launchUrl;
if (launchUrl) { if (launchUrl) {
if (launchUrl.startsWith('lbry://?verify=')) { if (launchUrl.startsWith('lbry://?verify=')) {
let verification = {}; let verification = {};
@ -91,18 +83,19 @@ class SplashScreen extends React.PureComponent {
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
const { emailToVerify, getSync, setEmailToVerify, verifyUserEmail, verifyUserEmailFailure } = this.props; const { emailToVerify, getSync, setEmailToVerify, verifyUserEmail, verifyUserEmailFailure } = this.props;
const { daemonReady, shouldAuthenticate } = this.state;
const { user } = nextProps; const { user } = nextProps;
if (this.state.daemonReady && this.state.shouldAuthenticate && user && user.id) { if (daemonReady && shouldAuthenticate && user && user.id) {
this.setState({ shouldAuthenticate: false }, () => { this.setState({ shouldAuthenticate: false }, () => {
// user is authenticated, navigate to the main view // user is authenticated, navigate to the main view
if (user.has_verified_email) { if (user.has_verified_email) {
NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(walletPassword => { NativeModules.UtilityModule.getSecureValue(Constants.KEY_WALLET_PASSWORD).then(walletPassword => {
if (walletPassword) { getSync(walletPassword, () => {
getSync(walletPassword); this.getUserSettings();
}
this.navigateToMain(); this.navigateToMain();
}); });
});
return; return;
} }
@ -113,9 +106,16 @@ class SplashScreen extends React.PureComponent {
getUserSettings = () => { getUserSettings = () => {
const { populateSharedUserState } = this.props; const { populateSharedUserState } = this.props;
Lbryio.call('user_settings', 'get').then(settings => {
populateSharedUserState(settings); doPreferenceGet(
}); 'shared',
preference => {
populateSharedUserState(preference);
},
error => {
/* failed */
}
);
}; };
finishSplashScreen = () => { finishSplashScreen = () => {
@ -137,22 +137,20 @@ class SplashScreen extends React.PureComponent {
filteredOutpointsSubscribe(); filteredOutpointsSubscribe();
checkSubscriptionsInit(); checkSubscriptionsInit();
// get user settings interval
this.getUserSettings();
setInterval(() => this.getUserSettings(), SETTINGS_GET_INTERVAL);
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_FIRST_RUN_PASSWORD).then(walletPassword => { NativeModules.UtilityModule.getSecureValue(Constants.KEY_WALLET_PASSWORD).then(walletPassword => {
if (walletPassword) { getSync(walletPassword, err => {
getSync(walletPassword); this.getUserSettings();
}
this.navigateToMain(); this.navigateToMain();
}); });
});
} else { } else {
NativeModules.VersionInfo.getAppVersion().then(appVersion => { NativeModules.VersionInfo.getAppVersion().then(appVersion => {
NativeModules.Firebase.getMessagingToken().then(firebaseToken => {
this.setState({ shouldAuthenticate: true }); this.setState({ shouldAuthenticate: true });
authenticate(appVersion, Platform.OS); authenticate(appVersion, Platform.OS, firebaseToken);
});
}); });
} }
}); });
@ -181,24 +179,27 @@ class SplashScreen extends React.PureComponent {
isRunning: true, isRunning: true,
}); });
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_FIRST_RUN_PASSWORD).then(password => { NativeModules.UtilityModule.getSecureValue(Constants.KEY_WALLET_PASSWORD).then(password => {
if (walletStatus.is_locked) { if (secureWalletStatus.is_locked) {
this.setState({ this.setState({
message: 'Unlocking account', message: 'Unlocking account',
details: 'Decrypting wallet', details: 'Decrypting wallet',
}); });
// unlock the wallet and then finish the splash screen // unlock the wallet and then finish the splash screen
Lbry.account_unlock({ password: password || '' }) Lbry.wallet_unlock({ password: password || '' }).then(unlocked => {
.then(() => { if (unlocked) {
this.setState({ this.setState({
message: testingNetwork, message: testingNetwork,
details: waitingForResolution, details: waitingForResolution,
}); });
this.finishSplashScreen(); this.finishSplashScreen();
}) } else {
.catch(() => this.handleAccountUnlockFailed()); this.handleAccountUnlockFailed();
}
});
} else { } else {
this.setState({ this.setState({
message: testingNetwork, message: testingNetwork,
@ -207,6 +208,7 @@ class SplashScreen extends React.PureComponent {
this.finishSplashScreen(); this.finishSplashScreen();
} }
}); });
});
return; return;
} }
@ -249,10 +251,7 @@ class SplashScreen extends React.PureComponent {
} }
componentDidMount() { componentDidMount() {
if (NativeModules.Firebase) {
NativeModules.Firebase.track('app_launch', null); NativeModules.Firebase.track('app_launch', null);
}
NativeModules.Firebase.setCurrentScreen('Splash'); NativeModules.Firebase.setCurrentScreen('Splash');
this.props.fetchRewardedContent(); this.props.fetchRewardedContent();

View file

@ -3,7 +3,6 @@ import { doToast } from 'lbry-redux';
import { import {
doCheckSync, doCheckSync,
doGetSync, doGetSync,
doSetDefaultAccount,
doSyncApply, doSyncApply,
doUserEmailNew, doUserEmailNew,
doUserEmailToVerify, doUserEmailToVerify,
@ -55,12 +54,11 @@ const select = state => ({
const perform = dispatch => ({ const perform = dispatch => ({
addUserEmail: email => dispatch(doUserEmailNew(email)), addUserEmail: email => dispatch(doUserEmailNew(email)),
addUserPhone: (phone, countryCode) => dispatch(doUserPhoneNew(phone, countryCode)), addUserPhone: (phone, countryCode) => dispatch(doUserPhoneNew(phone, countryCode)),
getSync: password => dispatch(doGetSync(password)), getSync: (password, callback) => dispatch(doGetSync(password, callback)),
checkSync: () => dispatch(doCheckSync()), checkSync: () => dispatch(doCheckSync()),
verifyPhone: verificationCode => dispatch(doUserPhoneVerify(verificationCode)), verifyPhone: verificationCode => dispatch(doUserPhoneVerify(verificationCode)),
notify: data => dispatch(doToast(data)), notify: data => dispatch(doToast(data)),
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)), setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
setDefaultAccount: (success, failure) => dispatch(doSetDefaultAccount(success, failure)),
setEmailToVerify: email => dispatch(doUserEmailToVerify(email)), setEmailToVerify: email => dispatch(doUserEmailToVerify(email)),
syncApply: (hash, data, password) => dispatch(doSyncApply(hash, data, password)), syncApply: (hash, data, password) => dispatch(doSyncApply(hash, data, password)),
resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email)), resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email)),

View file

@ -37,7 +37,7 @@ class SyncVerifyPage extends React.PureComponent {
if (!hasSyncedWallet) { if (!hasSyncedWallet) {
// fresh account with no sync // fresh account with no sync
const newPassword = this.state.password ? this.state.password : ''; const newPassword = this.state.password ? this.state.password : '';
Lbry.account_encrypt({ new_password: newPassword }).then(() => { Lbry.wallet_encrypt({ new_password: newPassword }).then(() => {
getSync(newPassword); getSync(newPassword);
setClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED, true); setClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED, true);
navigation.goBack(); navigation.goBack();
@ -50,7 +50,7 @@ class SyncVerifyPage extends React.PureComponent {
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
const { getSyncIsPending, syncApplyIsPending, syncApplyErrorMessage } = nextProps; const { getSyncIsPending, syncApplyIsPending, syncApplyErrorMessage } = nextProps;
const { getSync, hasSyncedWallet, navigation, notify, setClientSetting, setDefaultAccount } = this.props; const { getSync, hasSyncedWallet, navigation, notify, setClientSetting } = this.props;
if (this.state.checkSyncStarted && !getSyncIsPending) { if (this.state.checkSyncStarted && !getSyncIsPending) {
this.setState({ syncChecked: true }); this.setState({ syncChecked: true });
} }
@ -63,17 +63,12 @@ class SyncVerifyPage extends React.PureComponent {
// password successfully verified // password successfully verified
if (NativeModules.UtilityModule) { if (NativeModules.UtilityModule) {
NativeModules.UtilityModule.setSecureValue( NativeModules.UtilityModule.setSecureValue(
Constants.KEY_FIRST_RUN_PASSWORD, Constants.KEY_WALLET_PASSWORD,
this.state.password ? this.state.password : '' this.state.password ? this.state.password : ''
); );
} }
setDefaultAccount(
() => this.finishSync(true), this.finishSync(true);
err => {
// fail silently and still finish
this.finishSync();
}
);
} }
} }
} }
@ -84,14 +79,16 @@ class SyncVerifyPage extends React.PureComponent {
setClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED, true); setClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED, true);
// unlock the wallet (if locked) // unlock the wallet (if locked)
Lbry.status().then(status => { Lbry.wallet_status().then(status => {
if (status.wallet.is_locked) { if (status.is_locked) {
Lbry.account_unlock({ password: this.state.password ? this.state.password : '' }) Lbry.wallet_unlock({ password: this.state.password ? this.state.password : '' }).then(unlocked => {
.then(() => navigation.goBack()) if (unlocked) {
.catch(err => { navigation.goBack();
} else {
if (notifyUnlockFailed) { if (notifyUnlockFailed) {
notify({ message: 'The wallet could not be unlocked at this time. Please restart the app.' }); notify({ message: 'The wallet could not be unlocked at this time. Please restart the app.' });
} }
}
}); });
} else { } else {
navigation.goBack(); navigation.goBack();

View file

@ -101,7 +101,6 @@ class VerificationScreen extends React.PureComponent {
notify, notify,
addUserPhone, addUserPhone,
getSyncIsPending, getSyncIsPending,
setDefaultAccount,
hasSyncedWallet, hasSyncedWallet,
setSyncIsPending, setSyncIsPending,
syncApplyIsPending, syncApplyIsPending,
@ -162,7 +161,6 @@ class VerificationScreen extends React.PureComponent {
notify={notify} notify={notify}
setEmailVerificationPhase={this.setEmailVerificationPhase} setEmailVerificationPhase={this.setEmailVerificationPhase}
setClientSetting={setClientSetting} setClientSetting={setClientSetting}
setDefaultAccount={setDefaultAccount}
setSyncIsPending={setSyncIsPending} setSyncIsPending={setSyncIsPending}
syncApplyIsPending={syncApplyIsPending} syncApplyIsPending={syncApplyIsPending}
syncApplyErrorMessage={syncApplyErrorMessage} syncApplyErrorMessage={syncApplyErrorMessage}

View file

@ -5,7 +5,7 @@ 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';
import { doCheckSync, doGetSync, selectUser, selectHasSyncedWallet } from 'lbryinc'; import { doCheckSync, doGetSync, selectUser, selectHasSyncedWallet } from 'lbryinc';
import Constants from 'constants'; import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import WalletPage from './view'; import WalletPage from './view';
const select = state => ({ const select = state => ({
@ -21,7 +21,7 @@ const select = state => ({
const perform = dispatch => ({ const perform = dispatch => ({
checkSync: () => dispatch(doCheckSync()), checkSync: () => dispatch(doCheckSync()),
getSync: password => dispatch(doGetSync(password)), getSync: (password, callback) => dispatch(doGetSync(password, callback)),
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)), setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_WALLET)), pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_WALLET)),
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)), setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),

View file

@ -46,10 +46,8 @@ class WalletPage extends React.PureComponent {
const { deviceWalletSynced, getSync, user } = this.props; const { deviceWalletSynced, getSync, user } = this.props;
if (deviceWalletSynced && user && user.has_verified_email) { if (deviceWalletSynced && user && user.has_verified_email) {
NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(walletPassword => { NativeModules.UtilityModule.getSecureValue(Constants.KEY_WALLET_PASSWORD).then(walletPassword => {
if (walletPassword) {
getSync(walletPassword); getSync(walletPassword);
}
}); });
} }
}; };

View file

@ -384,6 +384,7 @@ const filePageStyle = StyleSheet.create({
largeButton: { largeButton: {
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
marginRight: 48,
}, },
largeButtonIcon: { largeButtonIcon: {
color: Colors.DescriptionGrey, color: Colors.DescriptionGrey,
@ -396,8 +397,10 @@ const filePageStyle = StyleSheet.create({
largeButtonsRow: { largeButtonsRow: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between', marginLeft: 20,
margin: 16, marginRight: 20,
marginTop: 12,
marginBottom: 12,
}, },
}); });

View file

@ -39,6 +39,14 @@ const settingsStyle = StyleSheet.create({
fontFamily: 'Inter-UI-Regular', fontFamily: 'Inter-UI-Regular',
lineHeight: 18, lineHeight: 18,
}, },
sectionTitle: {
fontFamily: 'Inter-UI-Regular',
fontSize: 20,
marginBottom: 4,
},
sectionDivider: {
marginTop: 24,
},
}); });
export default settingsStyle; export default settingsStyle;

View file

@ -36,7 +36,7 @@ const uriBarStyle = StyleSheet.create({
}, },
overlay: { overlay: {
position: 'absolute', position: 'absolute',
backgroundColor: 'transparent', backgroundColor: 'red',
top: 0, top: 0,
width: '100%', width: '100%',
zIndex: 200, zIndex: 200,
@ -54,9 +54,10 @@ const uriBarStyle = StyleSheet.create({
item: { item: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
padding: 12, paddingLeft: 16,
paddingTop: 8, paddingRight: 16,
paddingBottom: 8, paddingTop: 12,
paddingBottom: 12,
}, },
itemContent: { itemContent: {
marginLeft: 12, marginLeft: 12,

View file

@ -1,5 +1,6 @@
import { NavigationActions, StackActions } from 'react-navigation'; import { NavigationActions, StackActions } from 'react-navigation';
import { buildURI, isURIValid, normalizeURI } from 'lbry-redux'; import { buildURI, isURIValid, normalizeURI } from 'lbry-redux';
import { Lbryio } from 'lbryinc';
import { doPopDrawerStack, doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer'; import { doPopDrawerStack, doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import Constants, { DrawerRoutes, InnerDrawerRoutes } from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants, { DrawerRoutes, InnerDrawerRoutes } from 'constants'; // eslint-disable-line node/no-deprecated-api
@ -290,6 +291,23 @@ export function __(str) {
return str; return str;
} }
export function logPublish(claimResult) {
// eslint-disable-next-line no-undef
if (!__DEV__) {
const { permanent_url: uri, claim_id: claimId, txid, nout, signing_channel: signingChannel } = claimResult;
let channelClaimId;
if (signingChannel) {
channelClaimId = signingChannel.claim_id;
}
const outpoint = `${txid}:${nout}`;
const params = { uri, claim_id: claimId, outpoint };
if (channelClaimId) {
params['channel_claim_id'] = channelClaimId;
}
Lbryio.call('event', 'publish', params);
}
}
export function uploadImageAsset(filePath, success, failure) { export function uploadImageAsset(filePath, success, failure) {
const makeid = () => { const makeid = () => {
let text = ''; let text = '';