Compare commits

...

21 commits

Author SHA1 Message Date
Akinwale Ariwodola 3ddd0b5b50 remove getSync from channel creation success 2019-10-17 13:57:22 +01:00
Akinwale Ariwodola 5d38da2d46 log publish 2019-10-17 13:43:30 +01:00
Akinwale Ariwodola d5f6d494a4 filtered homepage. other updates. 2019-10-17 06:29:50 +01:00
Akinwale Ariwodola 48030a97f2 more updates for release 2019-10-16 17:08:16 +01:00
Akinwale Ariwodola eb12a361b7 sdk calls: account_* -> wallet_* 2019-10-15 23:13:41 +01:00
Akinwale Ariwodola d18ac68e2c merge uri bar suggestions branch 2019-10-15 17:58:27 +01:00
Akinwale Ariwodola a9b7c9590d Merge remote-tracking branch 'origin/master' into rc-0.9.4 2019-10-15 17:56:49 +01:00
Akinwale Ariwodola a8daf84c52 NSFW -> mature 2019-10-15 13:51:33 +01:00
Akinwale Ariwodola b5d9e44445 add URI bar suggestions setting. reorganise settings page 2019-10-15 13:49:29 +01:00
Akinwale Ariwodola 60b0667546 cleanup 2019-10-15 10:27:11 +01:00
Akinwale Ariwodola 194e29356f preference updates. send firebase token. 2019-10-15 10:24:03 +01:00
Akinwale Ariwodola dcbb7f62d6 fix merge conflict 2019-10-15 08:32:39 +01:00
Akinwale Ariwodola 4ac36b0bc7 updates for release 2019-10-15 08:28:37 +01:00
Akinwale Ariwodola b82759c78e Merge remote-tracking branch 'origin/share' into rc-0.9.4 2019-10-11 17:26:06 +01:00
Akinwale Ariwodola 2cb393e6ee fix merge conflicts 2019-10-11 17:25:32 +01:00
Akinwale Ariwodola 79c02825d1 preferences sync and wallet sync updates 2019-10-11 17:12:51 +01:00
Akinwale Ariwodola c85581b98f remove extra slash 2019-10-11 15:17:49 +01:00
Akinwale Ariwodola ae05269417 fix merge conflict 2019-10-11 15:16:05 +01:00
Akinwale Ariwodola 03b321c742 add share button 2019-10-11 15:10:54 +01:00
Akinwale Ariwodola f42cbb90a0 use doPreferenceGet for retrieving saved user state 2019-10-02 10:29:08 +01:00
Akinwale Ariwodola 5eba4ab2ec use shared state subscriber for preferences_set and preferences_get 2019-09-30 22:54:31 +01: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,25 +53,28 @@ 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);
if (text.trim().length === 0) {
// don't do anything if the text is empty
return;
}
updateSearchQuery(text); let timeout = -1;
if (!showUriBarSuggestions) {
if (!text.startsWith('lbry://')) { timeout = setTimeout(() => {
// not a URI input, so this is a search, perform a direct search if (text.trim().length === 0) {
if (onSearchSubmitted) { // don't do anything if the text is empty
onSearchSubmitted(text); return;
} else {
navigation.navigate({ routeName: 'Search', key: 'searchPage', params: { searchQuery: text } });
} }
}
}, UriBar.INPUT_TIMEOUT); if (!text.startsWith('lbry://')) {
// not a URI input, so this is a search, perform a direct search
if (onSearchSubmitted) {
onSearchSubmitted(text);
} else {
navigation.navigate({ routeName: 'Search', key: 'searchPage', params: { searchQuery: text } });
}
}
}, 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,20 +273,21 @@ class UriBar extends React.PureComponent {
onSubmitEditing={this.handleSubmitEditing} onSubmitEditing={this.handleSubmitEditing}
/> />
)} )}
{this.state.focused && !this.state.directSearch && (
<View style={uriBarStyle.suggestions}>
<FlatList
style={uriBarStyle.suggestionList}
data={suggestions}
keyboardShouldPersistTaps={'handled'}
keyExtractor={(item, value) => item.value}
renderItem={({ item }) => (
<UriBarItem item={item} navigation={navigation} onPress={() => this.handleItemPress(item)} />
)}
/>
</View>
)}
</View> </View>
{this.state.focused && showUriBarSuggestions && (
<FlatList
style={[
uriBarStyle.suggestions,
{ height: Dimensions.get('window').height - this.state.keyboardHeight - 60 },
]}
data={suggestions}
keyboardShouldPersistTaps={'handled'}
keyExtractor={(item, value) => `${item.value}-${item.type}`}
renderItem={({ item }) => (
<UriBarItem item={item} navigation={navigation} onPress={() => this.handleItemPress(item)} />
)}
/>
)}
</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,25 +47,27 @@ class WelcomePage extends React.PureComponent {
const { authenticate } = this.props; const { authenticate } = this.props;
this.setState({ authenticationStarted: true, authenticationFailed: false }); this.setState({ authenticationStarted: true, authenticationFailed: false });
NativeModules.VersionInfo.getAppVersion().then(appVersion => { NativeModules.VersionInfo.getAppVersion().then(appVersion => {
Lbry.status() NativeModules.Firebase.getMessagingToken().then(firebaseToken => {
.then(info => { Lbry.status()
this.setState({ sdkStarted: true }); .then(info => {
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) {
this.setState({ authenticationFailed: true }); this.setState({ authenticationFailed: true });
// sdk_start_failed // sdk_start_failed
NativeModules.Firebase.track('sdk_start_failed', null); NativeModules.Firebase.track('sdk_start_failed', null);
} else { } else {
setTimeout(() => { setTimeout(() => {
this.startAuthenticating(); this.startAuthenticating();
this.setState({ statusTries: this.state.statusTries + 1 }); this.setState({ statusTries: this.state.statusTries + 1 });
}, 1000); // Retry every second for a maximum of MAX_STATUS_TRIES tries (60 seconds) }, 1000); // Retry every second for a maximum of MAX_STATUS_TRIES tries (60 seconds)
} }
}); });
});
}); });
}; };

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);
// unlock the wallet setClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED, true);
Lbry.account_unlock({ password: this.state.walletPassword ? this.state.walletPassword : '' }) Lbry.wallet_status().then(status => {
.then(() => this.closeFinalPage()) // unlock the wallet
.catch(err => if (status.is_locked) {
notify({ message: 'The wallet could not be unlocked at this time. Please restart the app.' }) Lbry.wallet_unlock({ password: this.state.walletPassword ? this.state.walletPassword : '' }).then(
); unlocked => {
}, if (unlocked) {
err => { this.closeFinalPage();
notify({ } else {
message: notify({ message: 'The wallet could not be unlocked at this time. Please restart the app.' });
'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,17 +83,18 @@ 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 => {
this.setState({ shouldAuthenticate: true }); NativeModules.Firebase.getMessagingToken().then(firebaseToken => {
authenticate(appVersion, Platform.OS); this.setState({ shouldAuthenticate: true });
authenticate(appVersion, Platform.OS, firebaseToken);
});
}); });
} }
}); });
@ -181,31 +179,35 @@ class SplashScreen extends React.PureComponent {
isRunning: true, isRunning: true,
}); });
// For now, automatically unlock the wallet if a password is set so that downloads work Lbry.wallet_status().then(secureWalletStatus => {
NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(password => { // For now, automatically unlock the wallet if a password is set so that downloads work
if (walletStatus.is_locked) { NativeModules.UtilityModule.getSecureValue(Constants.KEY_WALLET_PASSWORD).then(password => {
this.setState({ if (secureWalletStatus.is_locked) {
message: 'Unlocking account', this.setState({
details: 'Decrypting wallet', message: 'Unlocking account',
}); 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 { }
this.setState({ });
message: testingNetwork, } else {
details: waitingForResolution, this.setState({
}); message: testingNetwork,
this.finishSplashScreen(); details: waitingForResolution,
} });
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,15 +79,17 @@ 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 = '';