fix: merge conflict

This commit is contained in:
Akinwale Ariwodola 2019-09-24 17:25:14 +01:00
commit ab4e7ee84d
21 changed files with 318 additions and 117 deletions

BIN
src/assets/gerbil-happy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

BIN
src/assets/gerbil-sad.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

View file

@ -29,8 +29,9 @@ import {
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { AppState, BackHandler, Linking, NativeModules, TextInput, ToastAndroid } from 'react-native'; import { AppState, BackHandler, Linking, NativeModules, TextInput, ToastAndroid } from 'react-native';
import { selectDrawerStack } from 'redux/selectors/drawer'; import { selectDrawerStack } from 'redux/selectors/drawer';
import { SETTINGS, doDismissToast, doToast, selectToast } from 'lbry-redux'; import { SETTINGS, doDismissToast, doPopulateSharedUserState, doToast, selectToast } from 'lbry-redux';
import { import {
Lbryio,
doGetSync, doGetSync,
doUserCheckEmailVerified, doUserCheckEmailVerified,
doUserEmailVerify, doUserEmailVerify,
@ -305,6 +306,13 @@ class AppWithNavigationState extends React.Component {
}); });
}; };
getUserSettings = () => {
const { dispatch } = this.props;
Lbryio.call('user_settings', 'get').then(settings => {
dispatch(doPopulateSharedUserState(settings));
});
};
componentWillUnmount() { componentWillUnmount() {
AppState.removeEventListener('change', this._handleAppStateChange); AppState.removeEventListener('change', this._handleAppStateChange);
BackHandler.removeEventListener('hardwareBackPress'); BackHandler.removeEventListener('hardwareBackPress');
@ -321,12 +329,8 @@ class AppWithNavigationState extends React.Component {
NativeModules.Firebase.track('email_verified', { email: user.primary_email }); NativeModules.Firebase.track('email_verified', { email: user.primary_email });
ToastAndroid.show('Your email address was successfully verified.', ToastAndroid.LONG); ToastAndroid.show('Your email address was successfully verified.', ToastAndroid.LONG);
// upon successful email verification, do wallet sync (if password has been set) // get user settings after email verification
NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(walletPassword => { this.getUserSettings();
if (walletPassword && walletPassword.trim().length > 0) {
dispatch(doGetSync(walletPassword));
}
});
} }
} }

View file

@ -27,7 +27,7 @@ export default class ChannelSelector extends React.PureComponent {
componentDidMount() { componentDidMount() {
const { channels = [], channelName, fetchChannelListMine, fetchingChannels } = this.props; const { channels = [], channelName, fetchChannelListMine, fetchingChannels } = this.props;
if (!channels.length && !fetchingChannels) { if ((!channels || channels.length === 0) && !fetchingChannels) {
fetchChannelListMine(); fetchChannelListMine();
} }
this.setState({ currentSelectedValue: channelName }); this.setState({ currentSelectedValue: channelName });
@ -37,7 +37,7 @@ export default class ChannelSelector extends React.PureComponent {
const { channels: prevChannels = [], channelName } = this.props; const { channels: prevChannels = [], channelName } = this.props;
const { channels = [] } = nextProps; const { channels = [] } = nextProps;
if (channels.length !== prevChannels.length && channelName !== this.state.currentSelectedValue) { if (channels && channels.length !== prevChannels.length && channelName !== this.state.currentSelectedValue) {
this.setState({ currentSelectedValue: channelName }); this.setState({ currentSelectedValue: channelName });
} }
} }
@ -182,7 +182,9 @@ export default class ChannelSelector extends React.PureComponent {
render() { render() {
const channel = this.state.addingChannel ? 'new' : this.props.channel; const channel = this.state.addingChannel ? 'new' : this.props.channel;
const { enabled, fetchingChannels, channels = [] } = this.props; const { enabled, fetchingChannels, channels = [] } = this.props;
const pickerItems = [Constants.ITEM_ANONYMOUS, Constants.ITEM_CREATE_A_CHANNEL].concat(channels.map(ch => ch.name)); const pickerItems = [Constants.ITEM_ANONYMOUS, Constants.ITEM_CREATE_A_CHANNEL].concat(
channels ? channels.map(ch => ch.name) : []
);
const { const {
newChannelName, newChannelName,

View file

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

View file

@ -0,0 +1,26 @@
import React from 'react';
import { NativeModules, Text, View, Image, TouchableOpacity } from 'react-native';
import Button from '../button';
import emptyStateStyle from 'styles/emptyState';
class EmptyStateView extends React.PureComponent {
render() {
const { message, buttonText, inner, onButtonPress } = this.props;
return (
<View
style={[emptyStateStyle.container, inner ? emptyStateStyle.innerContainer : emptyStateStyle.outerContainer]}
>
<Image style={emptyStateStyle.image} resizeMode={'stretch'} source={require('../../assets/gerbil-happy.png')} />
<Text style={emptyStateStyle.message}>{message}</Text>
{buttonText && (
<View style={emptyStateStyle.buttonContainer}>
<Button style={emptyStateStyle.button} text={buttonText} onPress={onButtonPress} />
</View>
)}
</View>
);
}
}
export default EmptyStateView;

View file

@ -59,7 +59,8 @@ class RewardCard extends React.PureComponent<Props> {
getDisplayAmount = () => { getDisplayAmount = () => {
const { reward } = this.props; const { reward } = this.props;
if (reward) { if (reward) {
if (reward.reward_range && reward.reward_range.includes('-')) { const claimed = !!reward.transaction_id;
if (!claimed && reward.reward_range && reward.reward_range.includes('-')) {
return reward.reward_range.split('-')[0] + '+'; // ex: 5+ return reward.reward_range.split('-')[0] + '+'; // ex: 5+
} else if (reward.reward_amount > 0) { } else if (reward.reward_amount > 0) {
return reward.reward_amount; return reward.reward_amount;

View file

@ -1,5 +1,5 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { makeSelectClaimForUri, selectMyChannelClaims } from 'lbry-redux'; import { doAbandonClaim, doFetchChannelListMine, makeSelectClaimForUri, selectMyChannelClaims } from 'lbry-redux';
import { doPopDrawerStack } from 'redux/actions/drawer'; import { doPopDrawerStack } from 'redux/actions/drawer';
import { doSetSortByItem, doSetTimeItem } from 'redux/actions/settings'; import { doSetSortByItem, doSetTimeItem } from 'redux/actions/settings';
import { selectDrawerStack } from 'redux/selectors/drawer'; import { selectDrawerStack } from 'redux/selectors/drawer';
@ -15,6 +15,8 @@ const select = (state, props) => ({
}); });
const perform = dispatch => ({ const perform = dispatch => ({
abandonClaim: (txid, nout) => dispatch(doAbandonClaim(txid, nout)),
fetchChannelListMine: () => dispatch(doFetchChannelListMine()),
popDrawerStack: () => dispatch(doPopDrawerStack()), popDrawerStack: () => dispatch(doPopDrawerStack()),
setSortByItem: item => dispatch(doSetSortByItem(item)), setSortByItem: item => dispatch(doSetSortByItem(item)),
setTimeItem: item => dispatch(doSetTimeItem(item)), setTimeItem: item => dispatch(doSetTimeItem(item)),

View file

@ -19,6 +19,7 @@ import ClaimList from 'component/claimList';
import Colors from 'styles/colors'; import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import Button from 'component/button'; import Button from 'component/button';
import EmptyStateView from 'component/emptyStateView';
import Icon from 'react-native-vector-icons/FontAwesome5'; import Icon from 'react-native-vector-icons/FontAwesome5';
import Link from 'component/link'; import Link from 'component/link';
import ModalPicker from 'component/modalPicker'; import ModalPicker from 'component/modalPicker';
@ -47,7 +48,9 @@ class ChannelPage extends React.PureComponent {
} }
componentDidMount() { componentDidMount() {
const { fetchChannelListMine } = this.props;
NativeModules.Firebase.setCurrentScreen('Channel'); NativeModules.Firebase.setCurrentScreen('Channel');
fetchChannelListMine();
} }
handleSortByItemSelected = item => { handleSortByItemSelected = item => {
@ -128,9 +131,7 @@ class ChannelPage extends React.PureComponent {
return ( return (
<View style={channelPageStyle.aboutTab}> <View style={channelPageStyle.aboutTab}>
{!websiteUrl && !email && !description && ( {!websiteUrl && !email && !description && (
<View style={channelPageStyle.busyContainer}> <EmptyStateView message={"There's nothing here yet.\nPlease check back later."} />
<Text style={channelPageStyle.infoText}>Nothing here yet. Please check back later.</Text>
</View>
)} )}
{(websiteUrl || email || description) && ( {(websiteUrl || email || description) && (
@ -266,7 +267,7 @@ class ChannelPage extends React.PureComponent {
)} )}
{ownedChannel && ( {ownedChannel && (
<Button <Button
style={channelPageStyle.deleteButton} style={[channelPageStyle.actionButton, channelPageStyle.deleteButton]}
theme={'light'} theme={'light'}
icon={'trash-alt'} icon={'trash-alt'}
text={'Delete'} text={'Delete'}

View file

@ -19,6 +19,7 @@ import Button from 'component/button';
import ChannelIconItem from 'component/channelIconItem'; import ChannelIconItem from 'component/channelIconItem';
import Colors from 'styles/colors'; import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import EmptyStateView from 'component/emptyStateView';
import FloatingWalletBalance from 'component/floatingWalletBalance'; import FloatingWalletBalance from 'component/floatingWalletBalance';
import Icon from 'react-native-vector-icons/FontAwesome5'; import Icon from 'react-native-vector-icons/FontAwesome5';
import Link from 'component/link'; import Link from 'component/link';
@ -175,10 +176,8 @@ export default class ChannelCreator extends React.PureComponent {
if (!isEditMode && hasFormState) { if (!isEditMode && hasFormState) {
this.loadPendingFormState(); this.loadPendingFormState();
this.setState({ currentPhase: Constants.PHASE_CREATE });
} else {
this.setState({ currentPhase: Constants.PHASE_LIST });
} }
this.setState({ currentPhase: isEditMode || hasFormState ? Constants.PHASE_CREATE : Constants.PHASE_LIST });
}); });
}; };
@ -240,7 +239,7 @@ export default class ChannelCreator extends React.PureComponent {
componentDidUpdate() { componentDidUpdate() {
const { channels = [] } = this.props; const { channels = [] } = this.props;
const { editChannelUrl } = this.state; const { editChannelUrl } = this.state;
if (channels.length > 0) { if (channels && channels.length > 0) {
if (this.state.autoStyles.length !== channels.length) { if (this.state.autoStyles.length !== channels.length) {
this.setState({ this.setState({
autoStyles: this.generateAutoStyles(channels.length), autoStyles: this.generateAutoStyles(channels.length),
@ -534,10 +533,12 @@ export default class ChannelCreator extends React.PureComponent {
}; };
showChannelList = () => { showChannelList = () => {
const { popDrawerStack } = this.props; const { drawerStack, popDrawerStack } = this.props;
popDrawerStack(); if (drawerStack[drawerStack.length - 1].route === Constants.DRAWER_ROUTE_CHANNEL_CREATOR_FORM) {
this.resetChannelCreator(); popDrawerStack();
}
this.setState({ currentPhase: Constants.PHASE_LIST }); this.setState({ currentPhase: Constants.PHASE_LIST });
this.resetChannelCreator();
}; };
resetChannelCreator = () => { resetChannelCreator = () => {
@ -606,6 +607,7 @@ export default class ChannelCreator extends React.PureComponent {
this.setState({ this.setState({
claimId: channel.claim_id, claimId: channel.claim_id,
currentPhase: Constants.PHASE_CREATE, currentPhase: Constants.PHASE_CREATE,
displayName: value && value.title ? value.title : channel.name.substring(1),
editMode: true, editMode: true,
coverImageUrl: value && value.cover ? value.cover.url : null, coverImageUrl: value && value.cover ? value.cover.url : null,
currentChannelName: channel.name.substring(1), currentChannelName: channel.name.substring(1),
@ -744,6 +746,8 @@ export default class ChannelCreator extends React.PureComponent {
uploadingImage, uploadingImage,
} = this.state; } = this.state;
const hasChannels = channels && channels.length > 0;
return ( return (
<View style={channelCreatorStyle.container}> <View style={channelCreatorStyle.container}>
<UriBar <UriBar
@ -756,34 +760,34 @@ export default class ChannelCreator extends React.PureComponent {
onExitSelectionMode={this.onExitSelectionMode} onExitSelectionMode={this.onExitSelectionMode}
/> />
{fetchingChannels && (
<View style={channelCreatorStyle.loading}>
<ActivityIndicator size={'large'} color={Colors.NextLbryGreen} />
</View>
)}
{currentPhase === Constants.PHASE_LIST && !fetchingChannels && !hasChannels && (
<EmptyStateView
message={'You have not created a channel.\nStart now by creating a new channel!'}
buttonText={'Create a channel'}
onButtonPress={this.handleNewChannelPress}
/>
)}
{currentPhase === Constants.PHASE_LIST && ( {currentPhase === Constants.PHASE_LIST && (
<FlatList <FlatList
extraData={this.state} extraData={this.state}
ListHeaderComponent={ ListFooterComponent={
fetchingChannels ? ( !channels || channels.length === 0 ? null : (
<View style={channelCreatorStyle.listHeader}> <View style={channelCreatorStyle.listFooter}>
<ActivityIndicator size={'small'} color={Colors.NextLbryGreen} /> <Button
</View> style={channelCreatorStyle.createChannelButton}
) : null text={'Create a channel'}
} onPress={this.handleNewChannelPress}
ListEmptyComponent={ />
fetchingChannels ? null : (
<View style={channelCreatorStyle.listEmpty}>
<Text style={channelCreatorStyle.listEmptyText}>
You have not created a channel. Start now by creating a new channel!
</Text>
</View> </View>
) )
} }
ListFooterComponent={
<View style={channelCreatorStyle.listFooter}>
<Button
style={channelCreatorStyle.createChannelButton}
text={'Create a channel'}
onPress={this.handleNewChannelPress}
/>
</View>
}
style={channelCreatorStyle.scrollContainer} style={channelCreatorStyle.scrollContainer}
contentContainerStyle={channelCreatorStyle.scrollPadding} contentContainerStyle={channelCreatorStyle.scrollPadding}
initialNumToRender={10} initialNumToRender={10}
@ -825,7 +829,7 @@ export default class ChannelCreator extends React.PureComponent {
</TouchableOpacity> </TouchableOpacity>
); );
}} }}
data={channels.filter(channel => !abandoningClaimIds.includes(channel.claim_id))} data={channels ? channels.filter(channel => !abandoningClaimIds.includes(channel.claim_id)) : []}
keyExtractor={(item, index) => item.claim_id} keyExtractor={(item, index) => item.claim_id}
/> />
)} )}
@ -864,7 +868,7 @@ export default class ChannelCreator extends React.PureComponent {
source={{ uri: thumbnailUrl }} source={{ uri: thumbnailUrl }}
/> />
)} )}
{thumbnailUrl !== null && thumbnailUrl.trim().length === 0 && newChannelName.length > 1 && ( {(thumbnailUrl === null || thumbnailUrl.trim().length === 0) && newChannelName.length > 1 && (
<Text style={channelIconStyle.autothumbCharacter}> <Text style={channelIconStyle.autothumbCharacter}>
{newChannelName.substring(0, 1).toUpperCase()} {newChannelName.substring(0, 1).toUpperCase()}
</Text> </Text>

View file

@ -15,6 +15,7 @@ import { __, navigateToUri, uriFromFileInfo } from 'utils/helper';
import Colors from 'styles/colors'; import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import PageHeader from 'component/pageHeader'; import PageHeader from 'component/pageHeader';
import EmptyStateView from 'component/emptyStateView';
import FileListItem from 'component/fileListItem'; import FileListItem from 'component/fileListItem';
import FloatingWalletBalance from 'component/floatingWalletBalance'; import FloatingWalletBalance from 'component/floatingWalletBalance';
import StorageStatsCard from 'component/storageStatsCard'; import StorageStatsCard from 'component/storageStatsCard';
@ -154,6 +155,10 @@ class DownloadsPage extends React.PureComponent {
onDeleteActionPressed={this.onDeleteActionPressed} onDeleteActionPressed={this.onDeleteActionPressed}
/> />
{!fetching && !hasDownloads && (
<EmptyStateView message={'You do not have any\ndownloaded content on this device.'} />
)}
<View style={downloadsStyle.subContainer}> <View style={downloadsStyle.subContainer}>
{hasDownloads && <StorageStatsCard fileInfos={this.getFilteredFileInfos()} />} {hasDownloads && <StorageStatsCard fileInfos={this.getFilteredFileInfos()} />}
{fetching && ( {fetching && (
@ -161,11 +166,7 @@ class DownloadsPage extends React.PureComponent {
<ActivityIndicator size="large" color={Colors.NextLbryGreen} style={downloadsStyle.loading} /> <ActivityIndicator size="large" color={Colors.NextLbryGreen} style={downloadsStyle.loading} />
</View> </View>
)} )}
{!fetching && !hasDownloads && (
<View style={downloadsStyle.busyContainer}>
<Text style={downloadsStyle.noDownloadsText}>You do not have any downloaded content on this device.</Text>
</View>
)}
{!fetching && hasDownloads && ( {!fetching && hasDownloads && (
<FlatList <FlatList
extraData={this.state} extraData={this.state}

View file

@ -22,6 +22,7 @@ import { navigateBack, navigateToUri } from 'utils/helper';
import Icon from 'react-native-vector-icons/FontAwesome5'; import Icon from 'react-native-vector-icons/FontAwesome5';
import ImageViewer from 'react-native-image-zoom-viewer'; import ImageViewer from 'react-native-image-zoom-viewer';
import Button from 'component/button'; import Button from 'component/button';
import EmptyStateView from 'component/emptyStateView';
import Tag from 'component/tag'; import Tag from 'component/tag';
import ChannelPage from 'page/channel'; import ChannelPage from 'page/channel';
import Colors from 'styles/colors'; import Colors from 'styles/colors';
@ -100,7 +101,7 @@ class FilePage extends React.PureComponent {
DeviceEventEmitter.addListener('onDownloadUpdated', this.handleDownloadUpdated); DeviceEventEmitter.addListener('onDownloadUpdated', this.handleDownloadUpdated);
DeviceEventEmitter.addListener('onDownloadCompleted', this.handleDownloadCompleted); DeviceEventEmitter.addListener('onDownloadCompleted', this.handleDownloadCompleted);
const { fetchChannelListMine, fileInfo, isResolvingUri, resolveUri, navigation } = this.props; const { fetchMyClaims, fileInfo, isResolvingUri, resolveUri, navigation } = this.props;
const { uri, uriVars } = navigation.state.params; const { uri, uriVars } = navigation.state.params;
this.setState({ uri, uriVars }); this.setState({ uri, uriVars });
@ -108,7 +109,7 @@ class FilePage extends React.PureComponent {
this.fetchFileInfo(this.props); this.fetchFileInfo(this.props);
this.fetchCostInfo(this.props); this.fetchCostInfo(this.props);
fetchChannelListMine(); fetchMyClaims();
if (NativeModules.Firebase) { if (NativeModules.Firebase) {
NativeModules.Firebase.track('open_file_page', { uri: uri }); NativeModules.Firebase.track('open_file_page', { uri: uri });
@ -590,14 +591,15 @@ class FilePage extends React.PureComponent {
} = this.props; } = this.props;
const { uri, autoplay } = navigation.state.params; const { uri, autoplay } = navigation.state.params;
const { isChannel } = parseURI(uri);
const myChannelUris = channels ? channels.map(channel => channel.permanent_url) : []; const myChannelUris = channels ? channels.map(channel => channel.permanent_url) : [];
const ownedClaim = myClaimUris.includes(uri) || myChannelUris.includes(uri); const ownedClaim = myClaimUris.includes(uri) || myChannelUris.includes(uri);
const { isChannel } = parseURI(uri);
let innerContent = null; let innerContent = null;
if ((isResolvingUri && !claim) || !claim) { if ((isResolvingUri && !claim) || !claim) {
return ( return (
<View style={filePageStyle.container}> <View style={filePageStyle.container}>
<UriBar value={uri} navigation={navigation} />
{isResolvingUri && ( {isResolvingUri && (
<View style={filePageStyle.busyContainer}> <View style={filePageStyle.busyContainer}>
<ActivityIndicator size="large" color={Colors.NextLbryGreen} /> <ActivityIndicator size="large" color={Colors.NextLbryGreen} />
@ -607,16 +609,29 @@ class FilePage extends React.PureComponent {
{claim === null && !isResolvingUri && ( {claim === null && !isResolvingUri && (
<View style={filePageStyle.container}> <View style={filePageStyle.container}>
{ownedClaim && ( {ownedClaim && (
<Text style={filePageStyle.emptyClaimText}> <EmptyStateView
{isChannel message={
? 'It looks like you just created this channel. It will appear in a few minutes.' isChannel
: 'It looks you just published this content. It will appear in a few minutes.'} ? 'It looks like you just created this channel. It will appear in a few minutes.'
</Text> : 'It looks you just published this content. It will appear in a few minutes.'
}
/>
)}
{!ownedClaim && (
<EmptyStateView
message={"There's nothing at this location."}
buttonText={'Publish something here'}
onButtonPress={() =>
navigation.navigate({
routeName: Constants.DRAWER_ROUTE_PUBLISH,
params: { vanityUrl: uri.trim() },
})
}
/>
)} )}
{!ownedClaim && <Text style={filePageStyle.emptyClaimText}>There's nothing at this location.</Text>}
</View> </View>
)} )}
<UriBar value={uri} navigation={navigation} /> <FloatingWalletBalance navigation={navigation} />
</View> </View>
); );
} }

View file

@ -4,7 +4,6 @@ import {
doResolveUri, doResolveUri,
doToast, doToast,
doUpdatePublishForm, doUpdatePublishForm,
doUploadThumbnail,
selectBalance, selectBalance,
selectPublishFormValues, selectPublishFormValues,
} from 'lbry-redux'; } from 'lbry-redux';
@ -28,7 +27,6 @@ const perform = dispatch => ({
clearPublishFormState: () => dispatch(doClearPublishFormState()), clearPublishFormState: () => dispatch(doClearPublishFormState()),
updatePublishForm: value => dispatch(doUpdatePublishForm(value)), updatePublishForm: value => dispatch(doUpdatePublishForm(value)),
updatePublishFormState: data => dispatch(doUpdatePublishFormState(data)), updatePublishFormState: data => dispatch(doUpdatePublishFormState(data)),
uploadThumbnail: (filePath, fsAdapter) => dispatch(doUploadThumbnail(filePath, null, fsAdapter)),
publish: (success, fail) => dispatch(doPublish(success, fail)), publish: (success, fail) => dispatch(doPublish(success, fail)),
resolveUri: uri => dispatch(doResolveUri(uri)), resolveUri: uri => dispatch(doResolveUri(uri)),
pushDrawerStack: (routeName, params) => dispatch(doPushDrawerStack(routeName, params)), pushDrawerStack: (routeName, params) => dispatch(doPushDrawerStack(routeName, params)),

View file

@ -43,7 +43,7 @@ import Tag from 'component/tag';
import TagSearch from 'component/tagSearch'; import TagSearch from 'component/tagSearch';
import UriBar from 'component/uriBar'; import UriBar from 'component/uriBar';
import publishStyle from 'styles/publish'; import publishStyle from 'styles/publish';
import { __, navigateToUri } from 'utils/helper'; import { __, navigateToUri, uploadImageAsset } from 'utils/helper';
const languages = { const languages = {
en: 'English', en: 'English',
@ -129,6 +129,8 @@ class PublishPage extends React.PureComponent {
uploadedThumbnailUri: null, uploadedThumbnailUri: null,
vanityUrlSet: false, vanityUrlSet: false,
thumbnailImagePickerOpen: false,
// other // other
publishStarted: false, publishStarted: false,
}; };
@ -218,10 +220,8 @@ class PublishPage extends React.PureComponent {
// replace name with the specified vanity URL if there was one in the pending state // replace name with the specified vanity URL if there was one in the pending state
this.setState({ name: this.state.vanityUrl }); this.setState({ name: this.state.vanityUrl });
} }
this.setState({ currentPhase: Constants.PHASE_DETAILS });
} else {
this.setState({ currentPhase: Constants.PHASE_SELECTOR });
} }
this.setState({ currentPhase: isEditMode || hasFormState ? Constants.PHASE_DETAILS : Constants.PHASE_SELECTOR });
}); });
}; };
@ -256,6 +256,7 @@ class PublishPage extends React.PureComponent {
this.setState( this.setState(
{ {
editMode: true, editMode: true,
publishStarted: false,
currentPhase: Constants.PHASE_DETAILS, currentPhase: Constants.PHASE_DETAILS,
hasEditedContentAddress: true, hasEditedContentAddress: true,
@ -382,10 +383,12 @@ class PublishPage extends React.PureComponent {
}; };
handlePublishSuccess = data => { handlePublishSuccess = data => {
const { navigation, notify } = this.props; const { clearPublishFormState, navigation, notify } = this.props;
notify({ notify({
message: `Your content was successfully published to ${this.state.uri}. It will be available in a few mintues.`, message: `Your content was successfully published to ${this.state.uri}. It will be available in a few mintues.`,
}); });
clearPublishFormState();
this.setState({ publishStarted: false });
navigation.navigate({ routeName: Constants.DRAWER_ROUTE_PUBLISHES, params: { publishSuccess: true } }); navigation.navigate({ routeName: Constants.DRAWER_ROUTE_PUBLISHES, params: { publishSuccess: true } });
}; };
@ -436,6 +439,7 @@ class PublishPage extends React.PureComponent {
updatePublishFormState({ currentMedia: media, name: newName }); updatePublishFormState({ currentMedia: media, name: newName });
this.setState( this.setState(
{ {
publishStarted: false,
currentMedia: media, currentMedia: media,
title: null, // no title autogeneration (user will fill this in) title: null, // no title autogeneration (user will fill this in)
name: newName, name: newName,
@ -453,7 +457,7 @@ class PublishPage extends React.PureComponent {
}; };
showSelector() { showSelector() {
const { updatePublishForm } = this.props; const { clearPublishFormState, updatePublishForm } = this.props;
this.setState( this.setState(
{ {
@ -490,9 +494,12 @@ class PublishPage extends React.PureComponent {
selectedChannel: null, selectedChannel: null,
uploadedThumbnailUri: null, uploadedThumbnailUri: null,
thumbnailImagePickerOpen: false,
vanityUrlSet: false, vanityUrlSet: false,
}, },
() => { () => {
clearPublishFormState();
// reset thumbnail // reset thumbnail
updatePublishForm({ thumbnail: null }); updatePublishForm({ thumbnail: null });
} }
@ -526,19 +533,59 @@ class PublishPage extends React.PureComponent {
); );
}; };
onFilePicked = evt => { handleThumbnailUploadSuccess = ({ url }) => {
this.setState({ documentPickerOpen: false }, () => { const { updatePublishFormState } = this.props;
const currentMedia = {
id: -1, this.setState({
filePath: `file://${evt.path}`, uploadThumbnailStarted: false,
duration: 0, currentThumbnailUri: url,
}; uploadedThumbnailUri: url,
this.setCurrentMedia(currentMedia);
}); });
updatePublishFormState({ currentThumbnailUri: url, uploadedThumbnailUri: url });
};
handleThumbnailUploadFailure = err => {
const { notify } = this.props;
this.setState({ uploadThumbnailStarted: false });
notify({ message: 'The thumbnail could not be uploaded. Please try again.' });
};
onFilePicked = evt => {
const { notify } = this.props;
if (evt.path && evt.path.length > 0) {
const fileUrl = `file://${evt.path}`;
if (this.state.documentPickerOpen) {
this.setState({ documentPickerOpen: false, thumbnailImagePickerOpen: false }, () => {
const currentMedia = {
id: -1,
filePath: fileUrl,
duration: 0,
};
this.setCurrentMedia(currentMedia);
});
} else if (this.state.thumbnailImagePickerOpen) {
this.setState(
{
documentPickerOpen: false,
thumbnailImagePickerOpen: false,
uploadThumbnailStarted: true,
currentThumbnailUri: fileUrl,
},
() => {
// upload a new thumbnail
uploadImageAsset(fileUrl, this.handleThumbnailUploadSuccess, this.handleThumbnailUploadFailure);
}
);
}
} else {
// could not determine the file path
notify({ message: 'The path could not be determined. Please try a different file.' });
}
}; };
onPickerCanceled = () => { onPickerCanceled = () => {
this.setState({ documentPickerOpen: false }); this.setState({ documentPickerOpen: false, thumbnailImagePickerOpen: false });
}; };
handleCloseCameraPressed = () => { handleCloseCameraPressed = () => {
@ -573,6 +620,7 @@ class PublishPage extends React.PureComponent {
{ {
currentThumbnailUri: null, currentThumbnailUri: null,
updatingThumbnailUri: false, updatingThumbnailUri: false,
publishStarted: false,
currentPhase: Constants.PHASE_DETAILS, currentPhase: Constants.PHASE_DETAILS,
showCameraOverlay: false, showCameraOverlay: false,
videoRecordingMode: false, videoRecordingMode: false,
@ -597,6 +645,7 @@ class PublishPage extends React.PureComponent {
{ {
currentPhase: Constants.PHASE_DETAILS, currentPhase: Constants.PHASE_DETAILS,
currentThumbnailUri: null, currentThumbnailUri: null,
publishStarted: false,
updatingThumbnailUri: false, updatingThumbnailUri: false,
showCameraOverlay: false, showCameraOverlay: false,
videoRecordingMode: false, videoRecordingMode: false,
@ -701,7 +750,7 @@ class PublishPage extends React.PureComponent {
return; return;
} }
const { notify, uploadThumbnail } = this.props; const { notify } = this.props;
const { thumbnailPath } = this.state; const { thumbnailPath } = this.state;
this.setState({ updatingThumbnailUri: true }); this.setState({ updatingThumbnailUri: true });
@ -716,7 +765,13 @@ class PublishPage extends React.PureComponent {
// upload the thumbnail // upload the thumbnail
if (!this.state.uploadedThumbnailUri) { if (!this.state.uploadedThumbnailUri) {
this.setState({ uploadThumbnailStarted: true }, () => uploadThumbnail(this.getFilePathFromUri(uri), RNFS)); this.setState({ uploadThumbnailStarted: true }, () =>
uploadImageAsset(
this.getFilePathFromUri(uri),
this.handleThumbnailUploadSuccess,
this.handleThumbnailUploadFailure
)
);
} }
} else if (mediaType === 'image' || mediaType === 'video') { } else if (mediaType === 'image' || mediaType === 'video') {
const create = const create =
@ -727,7 +782,9 @@ class PublishPage extends React.PureComponent {
.then(path => { .then(path => {
this.setState({ currentThumbnailUri: `file://${path}`, updatingThumbnailUri: false }); this.setState({ currentThumbnailUri: `file://${path}`, updatingThumbnailUri: false });
if (!this.state.uploadedThumbnailUri) { if (!this.state.uploadedThumbnailUri) {
this.setState({ uploadThumbnailStarted: true }, () => uploadThumbnail(path, RNFS)); this.setState({ uploadThumbnailStarted: true }, () =>
uploadImageAsset(path, this.handleThumbnailUploadSuccess, this.handleThumbnailUploadFailure)
);
} }
}) })
.catch(err => { .catch(err => {
@ -783,7 +840,7 @@ class PublishPage extends React.PureComponent {
: ''; : '';
const licenseUrl = LICENSES.CC_LICENSES.reduce((value, item) => { const licenseUrl = LICENSES.CC_LICENSES.reduce((value, item) => {
if (typeof value === 'object') { if (typeof value === 'object') {
value = ''; value = license === value.value ? item.url : '';
} }
if (license === item.value) { if (license === item.value) {
value = item.url; value = item.url;
@ -801,6 +858,25 @@ class PublishPage extends React.PureComponent {
this.setState({ otherLicenseDescription }); this.setState({ otherLicenseDescription });
}; };
handleThumbnailPressed = () => {
const { notify } = this.props;
if (this.state.thumbnailImagePickerOpen || this.state.uploadThumbnailStarted) {
if (this.state.uploadThumbnailStarted) {
notify({ message: 'A thumbnail is already being uploaded. Please wait for the upload to finish.' });
}
return;
}
this.setState(
{
thumbnailImagePickerOpen: true,
},
() => {
NativeModules.UtilityModule.openDocumentPicker('image/*');
}
);
};
render() { render() {
const { balance, navigation, notify, publishFormValues } = this.props; const { balance, navigation, notify, publishFormValues } = this.props;
const { const {
@ -895,22 +971,24 @@ class PublishPage extends React.PureComponent {
} }
content = ( content = (
<ScrollView style={publishStyle.publishDetails}> <ScrollView style={publishStyle.publishDetails}>
{currentThumbnailUri && currentThumbnailUri.trim().length > 0 && ( <TouchableOpacity style={publishStyle.mainThumbnailContainer} onPress={this.handleThumbnailPressed}>
<View style={publishStyle.mainThumbnailContainer}> <FastImage
<FastImage style={publishStyle.mainThumbnail}
style={publishStyle.mainThumbnail} resizeMode={FastImage.resizeMode.contain}
resizeMode={FastImage.resizeMode.contain} source={{ uri: currentThumbnailUri }}
source={{ uri: currentThumbnailUri }} />
/>
{this.state.uploadThumbnailStarted && !this.state.uploadedThumbnailUri && ( <View style={publishStyle.thumbnailEditOverlay}>
<View style={publishStyle.thumbnailUploadContainer}> <Icon name={'edit'} style={publishStyle.editIcon} />
<ActivityIndicator size={'small'} color={Colors.NextLbryGreen} />
<Text style={publishStyle.thumbnailUploadText}>Uploading thumbnail...</Text>
</View>
)}
</View> </View>
)}
{this.state.uploadThumbnailStarted && (
<View style={publishStyle.thumbnailUploadContainer}>
<ActivityIndicator size={'small'} color={Colors.NextLbryGreen} />
<Text style={publishStyle.thumbnailUploadText}>Uploading thumbnail...</Text>
</View>
)}
</TouchableOpacity>
{!this.state.canPublish && <PublishRewardsDriver navigation={navigation} />} {!this.state.canPublish && <PublishRewardsDriver navigation={navigation} />}
<View style={publishStyle.card}> <View style={publishStyle.card}>
@ -1107,17 +1185,17 @@ class PublishPage extends React.PureComponent {
</View> </View>
<View style={publishStyle.actionButtons}> <View style={publishStyle.actionButtons}>
{(this.state.publishStarted || publishFormValues.publishing) && ( {this.state.publishStarted && (
<View style={publishStyle.progress}> <View style={publishStyle.progress}>
<ActivityIndicator size={'small'} color={Colors.NextLbryGreen} /> <ActivityIndicator size={'small'} color={Colors.NextLbryGreen} />
</View> </View>
)} )}
{!publishFormValues.publishing && !this.state.publishStarted && ( {!this.state.publishStarted && (
<Link style={publishStyle.cancelLink} text="Cancel" onPress={() => this.showSelector()} /> <Link style={publishStyle.cancelLink} text="Cancel" onPress={() => this.showSelector()} />
)} )}
{!publishFormValues.publishing && !this.state.publishStarted && ( {!this.state.publishStarted && (
<View style={publishStyle.rightActionButtons}> <View style={publishStyle.rightActionButtons}>
<Button <Button
style={publishStyle.publishButton} style={publishStyle.publishButton}

View file

@ -3,6 +3,7 @@ import { ActivityIndicator, Alert, FlatList, NativeModules, Text, TouchableOpaci
import Button from 'component/button'; import Button from 'component/button';
import Colors from 'styles/colors'; import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import EmptyStateView from 'component/emptyStateView';
import FileListItem from 'component/fileListItem'; import FileListItem from 'component/fileListItem';
import FloatingWalletBalance from 'component/floatingWalletBalance'; import FloatingWalletBalance from 'component/floatingWalletBalance';
import UriBar from 'component/uriBar'; import UriBar from 'component/uriBar';
@ -137,16 +138,11 @@ class PublishesPage extends React.PureComponent {
)} )}
{!fetching && (!uris || uris.length === 0) && ( {!fetching && (!uris || uris.length === 0) && (
<View style={publishStyle.noPublishes}> <EmptyStateView
<Text style={publishStyle.noPublishText}> message={__('It looks like you have not\npublished any content to LBRY yet.')}
{__('It looks like you have not published anything to LBRY yet.')} buttonText={__('Publish something new')}
</Text> onButtonPress={() => navigation.navigate({ routeName: Constants.DRAWER_ROUTE_PUBLISH })}
<Button />
style={publishStyle.publishNowButton}
text={__('Publish something new')}
onPress={() => navigation.navigate({ routeName: Constants.DRAWER_ROUTE_PUBLISH })}
/>
</View>
)} )}
{uris && uris.length > 0 && ( {uris && uris.length > 0 && (

View file

@ -2,6 +2,7 @@ import React from 'react';
import { ActivityIndicator, NativeModules, View, ScrollView, Text } from 'react-native'; import { ActivityIndicator, NativeModules, View, ScrollView, Text } from 'react-native';
import Colors from 'styles/colors'; import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import EmptyStateView from 'component/emptyStateView';
import TransactionList from 'component/transactionList'; import TransactionList from 'component/transactionList';
import UriBar from 'component/uriBar'; import UriBar from 'component/uriBar';
import walletStyle from 'styles/wallet'; import walletStyle from 'styles/wallet';
@ -53,11 +54,9 @@ class TransactionHistoryPage extends React.PureComponent {
<Text style={walletStyle.loadingText}>Loading transactions...</Text> <Text style={walletStyle.loadingText}>Loading transactions...</Text>
</View> </View>
)} )}
{!fetchingTransactions && transactions.length === 0 && <EmptyStateView message={'No transactions to list.'} />}
<ScrollView style={walletStyle.transactionHistoryScroll}> <ScrollView style={walletStyle.transactionHistoryScroll}>
<View style={walletStyle.historyList}> <View style={walletStyle.historyList}>
{!fetchingTransactions && transactions.length === 0 && (
<Text style={walletStyle.infoText}>No transactions to list.</Text>
)}
{!fetchingTransactions && transactions && transactions.length > 0 && ( {!fetchingTransactions && transactions && transactions.length > 0 && (
<TransactionList navigation={navigation} transactions={transactions} /> <TransactionList navigation={navigation} transactions={transactions} />
)} )}

View file

@ -43,7 +43,7 @@ class SyncVerifyPage extends React.PureComponent {
navigation.goBack(); navigation.goBack();
}); });
} else { } else {
syncApply(syncHash, syncData, this.state.password); syncApply(syncHash, syncData, this.state.password ? this.state.password : '');
} }
}); });
}; };

View file

@ -121,7 +121,7 @@ const channelCreatorStyle = StyleSheet.create({
height: '100%', height: '100%',
}, },
listFooter: { listFooter: {
marginTop: 24, marginTop: 8,
}, },
createChannelButton: { createChannelButton: {
backgroundColor: Colors.LbryGreen, backgroundColor: Colors.LbryGreen,
@ -254,6 +254,15 @@ const channelCreatorStyle = StyleSheet.create({
fontSize: 12, fontSize: 12,
marginLeft: 4, marginLeft: 4,
}, },
loading: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center',
},
}); });
export default channelCreatorStyle; export default channelCreatorStyle;

View file

@ -169,6 +169,9 @@ const channelPageStyle = StyleSheet.create({
actionButton: { actionButton: {
backgroundColor: Colors.White, backgroundColor: Colors.White,
}, },
deleteButton: {
marginLeft: 8,
},
}); });
export default channelPageStyle; export default channelPageStyle;

40
src/styles/emptyState.js Normal file
View file

@ -0,0 +1,40 @@
import { StyleSheet } from 'react-native';
import Colors from './colors';
const emptyStateStyle = StyleSheet.create({
container: {
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
paddingLeft: 24,
paddingRight: 24,
alignItems: 'center',
justifyContent: 'center',
zIndex: 99,
},
outerContainer: {
top: 60,
},
innerContainer: {
top: 0,
},
button: {
backgroundColor: Colors.LbryGreen,
fontSize: 18,
},
image: {
width: 128,
height: 170,
},
message: {
marginTop: 24,
textAlign: 'center',
fontFamily: 'Inter-UI-Regular',
fontSize: 18,
lineHeight: 28,
marginBottom: 24,
},
});
export default emptyStateStyle;

View file

@ -1,6 +1,9 @@
import { StyleSheet } from 'react-native'; import { Dimensions, StyleSheet } from 'react-native';
import Colors from './colors'; import Colors from './colors';
const screenDimension = Dimensions.get('window');
const screenWidth = screenDimension.width;
const publishStyle = StyleSheet.create({ const publishStyle = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
@ -434,7 +437,7 @@ const publishStyle = StyleSheet.create({
color: Colors.DescriptionGrey, color: Colors.DescriptionGrey,
}, },
publishesFooter: { publishesFooter: {
marginTop: 16, marginTop: 2,
marginLeft: 16, marginLeft: 16,
marginRight: 16, marginRight: 16,
}, },
@ -443,6 +446,21 @@ const publishStyle = StyleSheet.create({
backgroundColor: Colors.LbryGreen, backgroundColor: Colors.LbryGreen,
marginTop: 16, marginTop: 16,
}, },
thumbnailEditOverlay: {
alignItems: 'center',
justifyContent: 'center',
borderRadius: 24,
position: 'absolute',
padding: 8,
left: screenWidth / 2 - 32 / 2,
bottom: 8,
backgroundColor: '#00000077',
},
editIcon: {
color: Colors.White,
fontFamily: 'Inter-UI-SemiBold',
fontSize: 12,
},
}); });
export default publishStyle; export default publishStyle;