Next RC #57
36 changed files with 434 additions and 247 deletions
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 = () => {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
|
|
84
src/index.js
84
src/index.js
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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 = () => {
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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)),
|
||||
});
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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 => ({
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 = '';
|
||||
|
|
Loading…
Reference in a new issue