Repost creation #133

Merged
akinwale merged 7 commits from repost-creation into master 2020-03-20 14:21:18 +01:00
10 changed files with 380 additions and 19 deletions

@ -1 +1 @@
Subproject commit 56c375f344911bffe64c0564dd37d2bb8b7761f1
Subproject commit 4d1f142f9cf4f221a2110b7d7aa77b9f42731253

View file

@ -99,6 +99,7 @@ class FileListItem extends React.PureComponent {
isRewardContent,
channelClaimId,
fullChannelUri,
repostUrl,
repostChannel,
repostChannelUrl,
shortChannelUri,
@ -114,12 +115,13 @@ class FileListItem extends React.PureComponent {
channelClaimId = signingChannel ? signingChannel.claim_id : null;
fullChannelUri = channelClaimId ? `${channel}#${channelClaimId}` : channel;
shortChannelUri = signingChannel ? signingChannel.short_url : null;
repostUrl = claim.repost_url;
repostChannelUrl = claim.repost_channel_url;
if (repostChannelUrl) {
const { claimName: repostChannelName } = parseURI(repostChannelUrl);
repostChannel = repostChannelName;
if (repostUrl) {
const { claimName: repostName, channelName } = parseURI(repostUrl);
repostChannel = channelName;
}
isRepost = !!repostChannelUrl;
isRepost = !!repostUrl;
if (blackListedOutpoints || filteredOutpoints) {
const outpointsToHide = !blackListedOutpoints
@ -148,8 +150,17 @@ class FileListItem extends React.PureComponent {
<Icon name={'retweet'} size={14} style={fileListStyle.repostIcon} />
<Text style={fileListStyle.repostChannelName}>
<Link
text={repostChannel}
onPress={() => navigateToUri(navigation, normalizeURI(repostChannelUrl), null, false, null, false)}
text={`@${repostChannel}`}
onPress={() =>
navigateToUri(
navigation,
normalizeURI(repostChannelUrl || `@${repostChannel}`),
null,
false,
null,
false,
)
}
/>{' '}
reposted
</Text>

View file

@ -0,0 +1,28 @@
import { connect } from 'react-redux';
import {
doClearRepostError,
doFetchChannelListMine,
doRepost,
doToast,
selectBalance,
selectMyChannelClaims,
selectRepostError,
selectRepostLoading,
} from 'lbry-redux';
import ModalRepostView from './view';
const select = state => ({
balance: selectBalance(state),
channels: selectMyChannelClaims(state),
reposting: selectRepostLoading(state),
error: selectRepostError(state),
});
const perform = dispatch => ({
fetchChannelListMine: () => dispatch(doFetchChannelListMine(1, 99999, true)),
notify: data => dispatch(doToast(data)),
repost: options => dispatch(doRepost(options)),
clearError: () => dispatch(doClearRepostError()),
});
export default connect(select, perform)(ModalRepostView);

View file

@ -0,0 +1,204 @@
import React from 'react';
import { ActivityIndicator, Alert, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { formatCredits, creditsToString } from 'lbry-redux';
import modalStyle from 'styles/modal';
import modalRepostStyle from 'styles/modalRepost';
import ChannelSelector from 'component/channelSelector';
import Button from 'component/button';
import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import Icon from 'react-native-vector-icons/FontAwesome5';
import Link from 'component/link';
import { logPublish } from 'utils/helper';
export default class ModalRepostView extends React.PureComponent {
depositAmountInput;
state = {
channelName: null,
creditsInputFocused: false,
depositAmount: '0.1',
repostName: null,
repostStarted: false,
showAdvanced: false,
};
componentDidMount() {
const { claim, fetchChannelListMine } = this.props;
const { name } = claim;
fetchChannelListMine();
this.setState({ repostName: name });
this.checkChannelSelection(this.props);
}
componentWillReceiveProps(nextProps) {
this.checkChannelSelection(nextProps);
const { notify } = this.props;
const { reposting, error } = nextProps;
if (this.state.repostStarted && !reposting && error) {
this.setState({ repostStarted: false });
notify({ message: error, isError: true });
}
}
checkChannelSelection = props => {
const { channels = [] } = props;
if (!this.state.channelName && channels && channels.length > 0) {
const firstChannel = channels[0];
this.setState({ channelName: firstChannel.name });
}
};
handleChannelChange = channelName => {
const { channels = [] } = this.props;
if (channels && channels.length > 0) {
const filtered = channels.filter(c => c.name === channelName);
if (filtered.length > 0) {
const channel = filtered[0];
this.setState({ channelName });
}
}
};
handleRepost = () => {
const { claim, balance, notify, repost, onRepostSuccessful, channels = [], clearError } = this.props;
const { depositAmount, repostName, channelName } = this.state;
if (parseInt(depositAmount, 10) > balance) {
notify({
message: 'Insufficient credits',
isError: true,
});
return;
}
clearError();
const channel = channels.filter(ch => ch.name === channelName)[0];
this.setState({ repostStarted: true }, () => {
repost({
name: repostName,
bid: creditsToString(depositAmount),
channel_id: channel.claim_id,
claim_id: claim.claim_id,
}).then(repostClaim => {
logPublish(repostClaim);
this.setState({ repostStarted: false });
notify({ message: __('The content was successfully reposted!') });
if (onRepostSuccessful) onRepostSuccessful();
});
});
};
render() {
const { balance, channels, reposting, title, onCancelPress, onOverlayPress } = this.props;
const canRepost = !!this.state.channelName && !!this.state.repostName;
const channelsLoaded = channels && channels.length > 0;
const processing = this.state.repostStarted || reposting || !channelsLoaded;
return (
<TouchableOpacity style={modalStyle.overlay} activeOpacity={1} onPress={onOverlayPress}>
<TouchableOpacity style={modalStyle.container} activeOpacity={1}>
<View
style={modalRepostStyle.container}
onLayout={() => {
if (this.tipAmountInput) {
this.tipAmountInput.focus();
}
}}
>
<Text style={modalRepostStyle.title} numberOfLines={1}>
{__('Repost %title%', { title })}
</Text>
<Text style={modalRepostStyle.infoText}>
{__('Repost your favorite content to help more people discover them!')}
</Text>
<Text style={modalRepostStyle.label}>{__('Channel to post on')}</Text>
<ChannelSelector
showAnonymous={false}
channelName={this.state.channelName}
onChannelChange={this.handleChannelChange}
/>
{this.state.showAdvanced && (
<View>
<Text style={modalRepostStyle.label}>{__('Name')}</Text>
<View style={modalRepostStyle.nameRow}>
<TextInput
editable={false}
value={this.state.channelName ? `lbry://${this.state.channelName}/` : ''}
style={modalRepostStyle.input}
/>
<TextInput
editable={canRepost}
value={this.state.repostName}
underlineColorAndroid={Colors.NextLbryGreen}
selectTextOnFocus
onChangeText={value => this.setState({ repostName: value })}
style={modalRepostStyle.input}
/>
</View>
<Text style={modalRepostStyle.label}>{__('Deposit')}</Text>
<View style={modalRepostStyle.row}>
<View style={modalRepostStyle.amountRow}>
<TextInput
editable={!this.state.repostStarted}
ref={ref => (this.depositAmountInput = ref)}
onChangeText={value => this.setState({ tipAmount: value })}
underlineColorAndroid={Colors.NextLbryGreen}
keyboardType={'numeric'}
onFocus={() => this.setState({ creditsInputFocused: true })}
onBlur={() => this.setState({ creditsInputFocused: false })}
placeholder={'0'}
value={this.state.depositAmount}
selectTextOnFocus
style={modalRepostStyle.depositAmountInput}
/>
<Text style={modalRepostStyle.currency}>LBC</Text>
<View style={modalRepostStyle.balance}>
{this.state.creditsInputFocused && <Icon name="coins" size={12} />}
{this.state.creditsInputFocused && (
<Text style={modalRepostStyle.balanceText}>{formatCredits(parseFloat(balance), 1, true)}</Text>
)}
</View>
</View>
</View>
</View>
)}
<View style={modalRepostStyle.buttonRow}>
{!processing && (
<Link
style={modalRepostStyle.cancelLink}
text={__('Cancel')}
onPress={() => {
if (onCancelPress) onCancelPress();
}}
/>
)}
{processing && <ActivityIndicator size={'small'} color={Colors.NextLbryGreen} />}
<View style={modalRepostStyle.rightButtonRow}>
<Link
style={modalRepostStyle.advancedLink}
text={this.state.showAdvanced ? __('Hide advanced') : __('Show advanced')}
onPress={() => {
this.setState({ showAdvanced: !this.state.showAdvanced });
}}
/>
<Button
text={__('Repost')}
style={modalRepostStyle.button}
disabled={!canRepost || this.state.repostStarted || reposting}
onPress={this.handleRepost}
/>
</View>
</View>
</View>
</TouchableOpacity>
</TouchableOpacity>
);
}
}

View file

@ -5,7 +5,6 @@ import modalStyle from 'styles/modal';
import modalTipStyle from 'styles/modalTip';
import Button from 'component/button';
import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import Icon from 'react-native-vector-icons/FontAwesome5';
import Link from 'component/link';
@ -25,6 +24,7 @@ export default class ModalTipView extends React.PureComponent {
if (tipAmount > balance) {
notify({
message: 'Insufficient credits',
isError: true,
});
return;
}
@ -56,13 +56,13 @@ export default class ModalTipView extends React.PureComponent {
() => {
// error
if (onSendTipFailed) onSendTipFailed();
}
)
},
),
);
},
},
],
{ cancelable: true }
{ cancelable: true },
);
};
@ -115,7 +115,7 @@ export default class ModalTipView extends React.PureComponent {
<Text style={modalTipStyle.infoText}>
{__(
'This will appear as a tip for %content%, which will boost its ability to be discovered while active.',
{ content: contentName }
{ content: contentName },
)}{' '}
<Link
style={modalTipStyle.learnMoreLink}

View file

@ -149,7 +149,7 @@ const Constants = {
TRUE_STRING: 'true',
MINIMUM_TRANSACTION_BALANCE: 0.1,
MINIMUM_TRANSACTION_BALANCE: 0.01,
SHARE_BASE_URL: 'https://open.lbry.com',
};

View file

@ -37,6 +37,7 @@ import FilePrice from 'component/filePrice';
import FloatingWalletBalance from 'component/floatingWalletBalance';
import Link from 'component/link';
import MediaPlayer from 'component/mediaPlayer';
import ModalRepostView from 'component/modalRepostView';
import ModalTipView from 'component/modalTipView';
import ProgressCircle from 'react-native-progress-circle';
import RelatedContent from 'component/relatedContent';
@ -94,6 +95,7 @@ class FilePage extends React.PureComponent {
showImageViewer: false,
showWebView: false,
showTipView: false,
showRepostView: false,
playbackStarted: false,
playerBgHeight: 0,
playerHeight: 0,
@ -265,6 +267,7 @@ class FilePage extends React.PureComponent {
'playerBgHeighht',
'playerHeight',
'relatedY',
'showRepostView',
'showTipView',
'showImageViewer',
'showWebView',
@ -1127,7 +1130,8 @@ class FilePage extends React.PureComponent {
const channelName = signingChannel && signingChannel.name;
const channelClaimId = claim && claim.signing_channel && claim.signing_channel.claim_id;
const fullUri = `${claim.name}#${claim.claim_id}`;
const canEdit = myClaimUris.includes(normalizeURI(fullUri));
const canEdit =
myClaimUris.includes(normalizeURI(fullUri)) || myClaimUris.includes(normalizeURI(claim.canonical_url));
const showActions =
(canEdit || (fileInfo && fileInfo.download_path)) &&
!this.state.fullscreenMode &&
@ -1348,6 +1352,14 @@ class FilePage extends React.PureComponent {
<Text style={filePageStyle.largeButtonText}>{__('Share')}</Text>
</TouchableOpacity>
<TouchableOpacity
style={filePageStyle.largeButton}
onPress={() => this.setState({ showRepostView: true })}
>
<Icon name={'retweet'} size={16} style={filePageStyle.largeButtonIcon} />
<Text style={filePageStyle.largeButtonText}>{__('Repost')}</Text>
</TouchableOpacity>
<TouchableOpacity
style={filePageStyle.largeButton}
onPress={() => this.setState({ showTipView: true })}
@ -1522,7 +1534,17 @@ class FilePage extends React.PureComponent {
onSendTipSuccessful={() => this.setState({ showTipView: false })}
/>
)}
{this.state.showRepostView && (
<ModalRepostView
claim={claim}
title={title}
onCancelPress={() => this.setState({ showRepostView: false })}
onOverlayPress={() => this.setState({ showRepostView: false })}
onRepostSuccessful={() => this.setState({ showRepostView: false })}
/>
)}
{!this.state.fullscreenMode &&
!this.state.showRepostView &&
!this.state.showTipView &&
!this.state.showImageViewer &&
!this.state.showWebView && <FloatingWalletBalance navigation={navigation} />}

View file

@ -322,6 +322,7 @@ class SplashScreen extends React.PureComponent {
componentDidMount() {
NativeModules.Firebase.track('app_launch', null);
NativeModules.Firebase.setCurrentScreen('Splash');
NativeModules.UtilityModule.checkSdkReady();
const { navigation } = this.props;
const { resetUrl } = navigation.state.params;

93
src/styles/modalRepost.js Normal file
View file

@ -0,0 +1,93 @@
import { StyleSheet } from 'react-native';
import Colors from './colors';
const modalRepostStyle = StyleSheet.create({
container: {
padding: 16,
},
title: {
fontFamily: 'Inter-Regular',
fontSize: 24,
marginBottom: 8,
},
row: {
flexDirection: 'row',
flex: 1,
},
amountRow: {
flexDirection: 'row',
alignItems: 'center',
marginRight: 24,
},
depositAmountInput: {
fontFamily: 'Inter-Regular',
fontSize: 14,
alignSelf: 'flex-start',
textAlign: 'right',
width: 80,
letterSpacing: 1,
},
currency: {
fontFamily: 'Inter-Regular',
fontSize: 12,
marginLeft: 4,
},
buttonRow: {
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'space-between',
marginTop: 16,
},
button: {
backgroundColor: Colors.LbryGreen,
},
cancelLink: {
color: Colors.Grey,
},
advancedLink: {
color: Colors.Grey,
marginRight: 16,
},
balance: {
alignItems: 'center',
flexDirection: 'row',
marginLeft: 24,
},
balanceText: {
fontFamily: 'Inter-SemiBold',
fontSize: 14,
marginLeft: 4,
},
info: {
marginTop: 4,
},
infoText: {
fontFamily: 'Inter-Regular',
fontSize: 14,
color: Colors.DescriptionGrey,
},
learnMoreLink: {
fontFamily: 'Inter-Regular',
fontSize: 14,
color: Colors.LbryGreen,
},
label: {
fontFamily: 'Inter-Regular',
fontSize: 14,
marginTop: 8,
},
nameRow: {
flexDirection: 'row',
alignItems: 'center',
},
rightButtonRow: {
flexDirection: 'row',
alignItems: 'center',
},
input: {
fontFamily: 'Inter-Regular',
fontSize: 14,
},
});
export default modalRepostStyle;

View file

@ -411,6 +411,7 @@ const publishStyle = StyleSheet.create({
marginTop: 60,
},
publishesScrollPadding: {
paddingTop: 16,
paddingBottom: 16,
},
listItem: {
@ -418,8 +419,9 @@ const publishStyle = StyleSheet.create({
flexDirection: 'row',
justifyContent: 'space-between',
marginTop: 8,
marginLeft: 8,
marginRight: 8,
marginLeft: 16,
marginRight: 16,
marginBottom: 12,
},
noVideos: {
color: Colors.White,
@ -443,13 +445,13 @@ const publishStyle = StyleSheet.create({
},
publishesFooter: {
marginTop: 2,
marginLeft: 16,
marginRight: 16,
marginLeft: 8,
marginRight: 8,
},
publishesFooterButton: {
alignSelf: 'flex-start',
backgroundColor: Colors.LbryGreen,
marginTop: 16,
marginTop: 8,
},
thumbnailEditOverlay: {
alignItems: 'center',