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

View file

@ -12,8 +12,8 @@
"base-64": "^0.1.0",
"@expo/vector-icons": "^8.1.0",
"gfycat-style-urls": "^1.0.3",
"lbry-redux": "lbryio/lbry-redux#7ec72a737bcd336f000c5f5085891643110298c3",
"lbryinc": "lbryio/lbryinc#c55a2c98ab92c72149c824ee5906aed4404fd89b",
"lbry-redux": "lbryio/lbry-redux#9919a8150998822ca997cd23418f023d64d4a3da",
"lbryinc": "lbryio/lbryinc#aebad10a9c5d725c3fedae4236d56f239a0bc1de",
"lodash": ">=4.17.11",
"merge": ">=1.2.1",
"moment": "^2.22.1",

View file

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

View file

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

View file

@ -1,6 +1,7 @@
import React from 'react';
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 Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
@ -117,7 +118,7 @@ export default class ChannelSelector extends React.PureComponent {
};
handleCreateChannelClick = () => {
const { balance, createChannel, onChannelChange, notify } = this.props;
const { balance, createChannel, getSync, onChannelChange, notify } = this.props;
const { newChannelBid, newChannelName } = this.state;
if (newChannelName.trim().length === 0 || !isNameValid(newChannelName.substr(1), false)) {
@ -142,17 +143,21 @@ export default class ChannelSelector extends React.PureComponent {
createChannelError: undefined,
});
const success = () => {
const success = channelClaim => {
this.setState({
creatingChannel: false,
addingChannel: false,
currentSelectedValue: channelName,
showCreateChannel: false,
});
logPublish(channelClaim);
if (onChannelChange) {
onChannelChange(channelName);
}
// sync wallet
NativeModules.UtilityModule.getSecureValue(Constants.KEY_WALLET_PASSWORD).then(password => getSync(password));
};
const failure = () => {

View file

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

View file

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

View file

@ -40,10 +40,12 @@ class FileItem extends React.PureComponent {
render() {
const {
blackListedOutpoints,
claim,
title,
thumbnail,
fileInfo,
filteredOutpoints,
metadata,
isResolvingUri,
rewardedContentClaimIds,
@ -58,7 +60,17 @@ class FileItem extends React.PureComponent {
} = this.props;
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;
}

View file

@ -34,7 +34,7 @@ class FloatingWalletBalance extends React.PureComponent<Props> {
<Icon name="coins" size={12} style={floatingButtonStyle.balanceIcon} />
{isNaN(balance) && <ActivityIndicator size="small" color={Colors.White} />}
{(!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>
</View>

View file

@ -4,8 +4,10 @@ import {
selectSearchState as selectSearch,
selectSearchValue,
selectSearchSuggestions,
SETTINGS,
} from 'lbry-redux';
import { selectCurrentRoute } from 'redux/selectors/drawer';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import UriBar from './view';
const select = state => {
@ -16,6 +18,7 @@ const select = state => {
query: selectSearchValue(state),
currentRoute: selectCurrentRoute(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} />;
break;
case SEARCH_TYPES.TAG:
icon = <Icon name="hashtag" size={18} />;
break;
case SEARCH_TYPES.FILE:
default:
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.CHANNEL && `View the @${shorthand} channel`}
{type === SEARCH_TYPES.FILE && `View content at ${value}`}
{type === SEARCH_TYPES.TAG && `Explore the '${value}' tag`}
</Text>
</View>
</TouchableOpacity>

View file

@ -1,7 +1,7 @@
// @flow
import React from 'react';
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 Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import UriBarItem from './internal/uri-bar-item';
@ -14,6 +14,8 @@ class UriBar extends React.PureComponent {
textInput = null;
keyboardDidShowListener = null;
keyboardDidHideListener = null;
state = {
@ -21,17 +23,19 @@ class UriBar extends React.PureComponent {
currentValue: null,
inputText: null,
focused: false,
// TODO: Add a setting to enable / disable direct search?
directSearch: true,
keyboardHeight: 0,
};
componentDidMount() {
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow);
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
this.setSelection();
}
componentWillUnmount() {
if (this.keyboardDidShowListener) {
this.keyboardDidShowListener.remove();
}
if (this.keyboardDidHideListener) {
this.keyboardDidHideListener.remove();
}
@ -49,25 +53,28 @@ class UriBar extends React.PureComponent {
handleChangeText = text => {
const newValue = text || '';
clearTimeout(this.state.changeTextTimeout);
const { updateSearchQuery, onSearchSubmitted, navigation } = this.props;
const { updateSearchQuery, onSearchSubmitted, showUriBarSuggestions, navigation } = this.props;
let timeout = setTimeout(() => {
if (text.trim().length === 0) {
// don't do anything if the text is empty
return;
}
updateSearchQuery(text);
updateSearchQuery(text);
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 } });
let timeout = -1;
if (!showUriBarSuggestions) {
timeout = setTimeout(() => {
if (text.trim().length === 0) {
// don't do anything if the text is empty
return;
}
}
}, 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 });
};
@ -86,18 +93,34 @@ class UriBar extends React.PureComponent {
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 {
const uri = normalizeURI(value);
navigateToUri(navigation, uri);
}
};
_keyboardDidShow = evt => {
this.setState({ keyboardHeight: evt.endCoordinates.height });
};
_keyboardDidHide = () => {
if (this.textInput) {
this.textInput.blur();
}
this.setState({ focused: false });
this.setState({ focused: false, keyboardHeight: 0 });
};
setSelection() {
@ -152,16 +175,18 @@ class UriBar extends React.PureComponent {
selectedItemCount,
selectionMode,
suggestions,
showUriBarSuggestions,
value,
} = this.props;
if (this.state.currentValue === null) {
this.setState({ currentValue: value });
}
let style = [uriBarStyle.overlay, belowOverlay ? null : uriBarStyle.overlayElevated];
// TODO: Add optional setting to enable URI / search bar suggestions
/* if (this.state.focused) { style.push(uriBarStyle.inFocus); } */
let style = [
uriBarStyle.overlay,
belowOverlay ? null : uriBarStyle.overlayElevated,
this.state.focused && showUriBarSuggestions ? uriBarStyle.inFocus : null,
];
// TODO: selectionModeActions should be dynamically created / specified
return (
@ -248,20 +273,21 @@ class UriBar extends React.PureComponent {
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>
{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 file

@ -32,7 +32,7 @@ const Constants = {
ABOUT_TAB: 'about',
KEY_FIRST_RUN_EMAIL: 'firstRunEmail',
KEY_FIRST_RUN_PASSWORD: 'firstRunPassword',
KEY_WALLET_PASSWORD: 'firstRunPassword',
KEY_FIRST_USER_AUTH: 'firstUserAuth',
KEY_SHOULD_VERIFY_EMAIL: 'shouldVerifyEmail',
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 {
Lbry,
buildSharedStateMiddleware,
blockedReducer,
claimsReducer,
contentReducer,
fileReducer,
@ -13,19 +15,24 @@ import {
searchReducer,
tagsReducer,
walletReducer,
ACTIONS as LBRY_REDUX_ACTIONS,
} from 'lbry-redux';
import {
Lbryio,
authReducer,
blacklistReducer,
costInfoReducer,
doGetSync,
filteredReducer,
homepageReducer,
rewardsReducer,
selectUserVerifiedEmail,
subscriptionsReducer,
syncReducer,
userReducer,
LBRYINC_ACTIONS,
} from 'lbryinc';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import { createStore, applyMiddleware, compose } from 'redux';
import AppWithNavigationState, {
AppNavigator,
@ -33,6 +40,7 @@ import AppWithNavigationState, {
reactNavigationMiddleware,
} from 'component/AppNavigator';
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 FilesystemStorage from 'redux-persist-filesystem-storage';
import createCompressor from 'redux-persist-transform-compress';
@ -42,7 +50,6 @@ import formReducer from 'redux/reducers/form';
import drawerReducer from 'redux/reducers/drawer';
import settingsReducer from 'redux/reducers/settings';
import thunk from 'redux-thunk';
import isEqual from 'utils/deep-equal';
const globalExceptionHandler = (error, isFatal) => {
if (error && NativeModules.Firebase) {
@ -81,6 +88,7 @@ function enableBatching(reducer) {
const compressor = createCompressor();
const authFilter = createFilter('auth', ['authToken']);
const blockedFilter = createFilter('blocked', ['blockedChannels']);
const contentFilter = createFilter('content', ['positions']);
const saveClaimsFilter = createFilter('claims', ['claimsByUri']);
const subscriptionsFilter = createFilter('subscriptions', ['enabledChannelNotifications', 'subscriptions', 'latest']);
@ -89,10 +97,18 @@ const tagsFilter = createFilter('tags', ['followedTags']);
const walletFilter = createFilter('wallet', ['receiveAddress']);
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
// read the data
transforms: [authFilter, saveClaimsFilter, subscriptionsFilter, settingsFilter, walletFilter, compressor],
transforms: [
authFilter,
blockedFilter,
saveClaimsFilter,
subscriptionsFilter,
settingsFilter,
walletFilter,
compressor,
],
debounce: 10000,
storage: FilesystemStorage,
};
@ -105,6 +121,7 @@ const persistOptions = Object.assign({}, v4PersistOptions, {
const reducers = persistCombineReducers(persistOptions, {
auth: authReducer,
blacklist: blacklistReducer,
blocked: blockedReducer,
claims: claimsReducer,
content: contentReducer,
costInfo: costInfoReducer,
@ -127,8 +144,45 @@ const reducers = persistCombineReducers(persistOptions, {
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 middleware = [thunk, bulkThunk, reactNavigationMiddleware];
const middleware = [sharedStateMiddleware, thunk, bulkThunk, reactNavigationMiddleware];
// eslint-disable-next-line no-underscore-dangle
const composeEnhancers = compose;
@ -147,28 +201,6 @@ const persistor = persistStore(store, persistOptions, err => {
});
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
global.__ = str => str;

View file

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

View file

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

View file

@ -517,7 +517,7 @@ class FilePage extends React.PureComponent {
const { claim, notify } = this.props;
if (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);
}
};
@ -950,15 +950,23 @@ class FilePage extends React.PureComponent {
<View style={filePageStyle.largeButtonsRow}>
<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>
</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
style={filePageStyle.largeButton}
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>
</TouchableOpacity>
</View>
@ -999,12 +1007,6 @@ class FilePage extends React.PureComponent {
onPress={this.onSaveFilePressed}
/>
)}
<Button
style={[filePageStyle.actionButton, filePageStyle.tipButton]}
theme={'light'}
icon={'gift'}
onPress={() => this.setState({ showTipView: true })}
/>
{channelName && (
<SubscribeButton
style={filePageStyle.actionButton}

View file

@ -4,7 +4,6 @@ import {
doAuthenticate,
doCheckSync,
doGetSync,
doSetDefaultAccount,
doSyncApply,
doUserEmailNew,
doUserResendVerificationEmail,
@ -41,12 +40,11 @@ const select = state => ({
const perform = dispatch => ({
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)),
syncApply: (hash, data, password) => dispatch(doSyncApply(hash, data, password)),
getSync: password => dispatch(doGetSync(password)),
getSync: (password, callback) => dispatch(doGetSync(password, callback)),
checkSync: () => dispatch(doCheckSync()),
setDefaultAccount: (success, failure) => dispatch(doSetDefaultAccount(success, failure)),
notify: data => dispatch(doToast(data)),
resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email)),
});

View file

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

View file

@ -14,7 +14,7 @@ import {
import { BarPasswordStrengthDisplay } from 'react-native-password-strength-meter';
import AsyncStorage from '@react-native-community/async-storage';
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 Icon from 'react-native-vector-icons/FontAwesome5';
@ -62,7 +62,14 @@ class WalletPage extends React.PureComponent {
};
render() {
const { onPasswordChanged, onWalletViewLayout, getSyncIsPending, hasSyncedWallet, syncApplyIsPending } = this.props;
const {
onPasswordChanged,
onWalletViewLayout,
getSyncIsPending,
hasSyncedWallet,
syncApplyIsPending,
syncApplyStarted,
} = this.props;
let content;
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>
</View>
);
} else if (syncApplyIsPending) {
} else if (syncApplyStarted || syncApplyIsPending) {
content = (
<View style={firstRunStyle.centered}>
<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>
);
} else {

View file

@ -3,7 +3,7 @@ import { Lbry } from 'lbry-redux';
import { ActivityIndicator, NativeModules, Platform, Text, View } from 'react-native';
import AsyncStorage from '@react-native-community/async-storage';
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';
class WelcomePage extends React.PureComponent {
@ -25,7 +25,7 @@ class WelcomePage extends React.PureComponent {
} else {
// first_user_auth because it's the first time
AsyncStorage.getItem(Constants.KEY_FIRST_USER_AUTH).then(firstUserAuth => {
if ('true' !== firstUserAuth) {
if (firstUserAuth !== 'true') {
// first_user_auth
NativeModules.Firebase.track('first_user_auth', null);
AsyncStorage.setItem(Constants.KEY_FIRST_USER_AUTH, 'true');
@ -47,25 +47,27 @@ class WelcomePage extends React.PureComponent {
const { authenticate } = this.props;
this.setState({ authenticationStarted: true, authenticationFailed: false });
NativeModules.VersionInfo.getAppVersion().then(appVersion => {
Lbry.status()
.then(info => {
this.setState({ sdkStarted: true });
NativeModules.Firebase.getMessagingToken().then(firebaseToken => {
Lbry.status()
.then(info => {
this.setState({ sdkStarted: true });
authenticate(appVersion, Platform.OS);
})
.catch(error => {
if (this.state.statusTries >= WelcomePage.MAX_STATUS_TRIES) {
this.setState({ authenticationFailed: true });
authenticate(appVersion, Platform.OS, firebaseToken);
})
.catch(error => {
if (this.state.statusTries >= WelcomePage.MAX_STATUS_TRIES) {
this.setState({ authenticationFailed: true });
// sdk_start_failed
NativeModules.Firebase.track('sdk_start_failed', null);
} else {
setTimeout(() => {
this.startAuthenticating();
this.setState({ statusTries: this.state.statusTries + 1 });
}, 1000); // Retry every second for a maximum of MAX_STATUS_TRIES tries (60 seconds)
}
});
// sdk_start_failed
NativeModules.Firebase.track('sdk_start_failed', null);
} else {
setTimeout(() => {
this.startAuthenticating();
this.setState({ statusTries: this.state.statusTries + 1 });
}, 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) {
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) {
this.setState({ emailSubmitted: false });
@ -81,32 +81,33 @@ class FirstRunScreen extends React.PureComponent {
if (this.state.syncApplyStarted && !syncApplyIsPending) {
if (syncApplyErrorMessage && syncApplyErrorMessage.trim().length > 0) {
notify({ message: syncApplyErrorMessage, syncApplyStarted: false, isError: true });
this.setState({ showBottomContainer: true });
notify({ message: syncApplyErrorMessage, isError: true });
this.setState({ showBottomContainer: true, syncApplyStarted: false });
} else {
// password successfully verified
NativeModules.UtilityModule.setSecureValue(
Constants.KEY_FIRST_RUN_PASSWORD,
Constants.KEY_WALLET_PASSWORD,
this.state.walletPassword ? this.state.walletPassword : ''
);
setDefaultAccount(
() => {
setClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED, true);
// unlock the wallet
Lbry.account_unlock({ password: this.state.walletPassword ? this.state.walletPassword : '' })
.then(() => this.closeFinalPage())
.catch(err =>
notify({ message: 'The wallet could not be unlocked at this time. Please restart the app.' })
);
},
err => {
notify({
message:
'The account restore operation could not be completed successfully. Please restart the app and try again.',
});
setClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED, true);
Lbry.wallet_status().then(status => {
// unlock the wallet
if (status.is_locked) {
Lbry.wallet_unlock({ password: this.state.walletPassword ? this.state.walletPassword : '' }).then(
unlocked => {
if (unlocked) {
this.closeFinalPage();
} else {
notify({ message: 'The wallet could not be unlocked at this time. Please restart the app.' });
}
}
);
} else {
// wallet not locked. close final page
this.closeFinalPage();
}
);
});
}
}
@ -132,9 +133,9 @@ class FirstRunScreen extends React.PureComponent {
const { navigation } = this.props;
const resetAction = StackActions.reset({
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 = () => {
@ -298,8 +299,8 @@ class FirstRunScreen extends React.PureComponent {
const { getSync, setClientSetting } = this.props;
if (NativeModules.UtilityModule) {
const newPassword = this.state.walletPassword ? this.state.walletPassword : '';
NativeModules.UtilityModule.setSecureValue(Constants.KEY_FIRST_RUN_PASSWORD, newPassword);
Lbry.account_encrypt({ new_password: newPassword }).then(() => {
NativeModules.UtilityModule.setSecureValue(Constants.KEY_WALLET_PASSWORD, newPassword);
Lbry.wallet_encrypt({ new_password: newPassword }).then(() => {
// fresh account, new password set
getSync(newPassword);
setClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED, true);
@ -367,6 +368,7 @@ class FirstRunScreen extends React.PureComponent {
hasSyncedWallet={hasSyncedWallet}
getSyncIsPending={getSyncIsPending}
syncApplyIsPending={syncApplyIsPending}
syncApplyStarted={this.state.syncApplyStarted}
onWalletViewLayout={this.onWalletViewLayout}
onPasswordChanged={this.onWalletPasswordChanged}
/>

View file

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

View file

@ -4,7 +4,7 @@ import { doPushDrawerStack, doPopDrawerStack, doSetPlayerVisible } from 'redux/a
import { doSetClientSetting } from 'redux/actions/settings';
import { selectCurrentRoute, selectDrawerStack } from 'redux/selectors/drawer';
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';
const select = state => ({
@ -13,6 +13,7 @@ const select = state => ({
drawerStack: selectDrawerStack(state),
keepDaemonRunning: makeSelectClientSetting(SETTINGS.KEEP_DAEMON_RUNNING)(state),
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_NSFW)(state),
showUriBarSuggestions: makeSelectClientSetting(SETTINGS.SHOW_URI_BAR_SUGGESTIONS)(state),
});
const perform = dispatch => ({

View file

@ -51,6 +51,7 @@ class SettingsPage extends React.PureComponent {
navigation,
popDrawerStack,
showNsfw,
showUriBarSuggestions,
setClientSetting,
} = this.props;
@ -62,6 +63,7 @@ class SettingsPage extends React.PureComponent {
<View style={settingsStyle.container}>
<PageHeader title={'Settings'} onBackPressed={() => navigateBack(navigation, drawerStack, popDrawerStack)} />
<ScrollView style={settingsStyle.scrollContainer}>
<Text style={settingsStyle.sectionTitle}>Content</Text>
<View style={settingsStyle.row}>
<View style={settingsStyle.switchText}>
<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.switchText}>
<Text style={settingsStyle.label}>Show NSFW content</Text>
<Text style={settingsStyle.label}>Show mature content</Text>
</View>
<View style={settingsStyle.switchContainer}>
<Switch value={showNsfw} onValueChange={value => setClientSetting(SETTINGS.SHOW_NSFW, value)} />
</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.switchText}>
<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 => ({
authenticate: (appVersion, os) => dispatch(doAuthenticate(appVersion, os)),
authenticate: (appVersion, os, firebaseToken) => dispatch(doAuthenticate(appVersion, os, firebaseToken)),
balanceSubscribe: () => dispatch(doBalanceSubscribe()),
blacklistedOutpointsSubscribe: () => dispatch(doBlackListedOutpointsSubscribe()),
filteredOutpointsSubscribe: () => dispatch(doFilteredOutpointsSubscribe()),
checkSubscriptionsInit: () => dispatch(doCheckSubscriptionsInit()),
fetchRewardedContent: () => dispatch(doFetchRewardedContent()),
fetchSubscriptions: callback => dispatch(doFetchMySubscriptions(callback)),
getSync: password => dispatch(doGetSync(password)),
getSync: (password, callback) => dispatch(doGetSync(password, callback)),
notify: data => dispatch(doToast(data)),
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
setEmailToVerify: email => dispatch(doUserEmailToVerify(email)),

View file

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

View file

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

View file

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

View file

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

View file

@ -5,7 +5,7 @@ import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import { selectCurrentRoute } from 'redux/selectors/drawer';
import { selectBalance } from 'lbry-redux';
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';
const select = state => ({
@ -21,7 +21,7 @@ const select = state => ({
const perform = dispatch => ({
checkSync: () => dispatch(doCheckSync()),
getSync: password => dispatch(doGetSync(password)),
getSync: (password, callback) => dispatch(doGetSync(password, callback)),
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_WALLET)),
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,6 @@
import { NavigationActions, StackActions } from 'react-navigation';
import { buildURI, isURIValid, normalizeURI } from 'lbry-redux';
import { Lbryio } from 'lbryinc';
import { doPopDrawerStack, doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import Constants, { DrawerRoutes, InnerDrawerRoutes } from 'constants'; // eslint-disable-line node/no-deprecated-api
@ -290,6 +291,23 @@ export function __(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) {
const makeid = () => {
let text = '';