Compare commits
10 commits
lbry-tv-ex
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
aa5dd878c0 | ||
|
47c6fb5889 | ||
|
89a9571ce1 | ||
|
689e30a5f0 | ||
|
33b4806c84 | ||
|
7e1794ce29 | ||
|
4c4d561f30 | ||
|
a5953dcef0 | ||
|
d33ed55bdf | ||
|
12d7fbd472 |
42 changed files with 813 additions and 122 deletions
8
.gitlab-ci.yml
Normal file
8
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
stages:
|
||||
- build
|
||||
|
||||
build:
|
||||
variables:
|
||||
REACT_NATIVE_BRANCH: $CI_COMMIT_REF_NAME
|
||||
stage: build
|
||||
trigger: lbry/lbry-android
|
2
android
2
android
|
@ -1 +1 @@
|
|||
Subproject commit e9f25546979eee36751353c187dc9944ab1e508e
|
||||
Subproject commit ff30e7f6a4358fd997a9e6d9f75bfe6959eafcb6
|
|
@ -304,5 +304,23 @@
|
|||
"This content cannot be viewed at this time. Please try again in a bit.": "This content cannot be viewed at this time. Please try again in a bit.",
|
||||
"Download file": "Download file",
|
||||
"Save %title% (%size%) to your device": "Save %title% (%size%) to your device",
|
||||
"Save \"%title%\" (%size%) to your device": "Save \"%title%\" (%size%) to your device"
|
||||
"Save \"%title%\" (%size%) to your device": "Save \"%title%\" (%size%) to your device",
|
||||
"Find Channels to follow": "Find Channels to follow",
|
||||
"LBRY works better if you follow at least 5 creators you like. Sign in to show creators you follow if you already have an account.": "LBRY works better if you follow at least 5 creators you like. Sign in to show creators you follow if you already have an account.",
|
||||
"%remaining% more...": "%remaining% more...",
|
||||
"Did you know that you can earn free credits worth up to %amount%?": "Did you know that you can earn free credits worth up to %amount%?",
|
||||
"SHOW ME": "SHOW ME",
|
||||
"Convert credits to USD on Bittrex": "Convert credits to USD on Bittrex",
|
||||
"You also have": "You also have",
|
||||
"in tips": "in tips",
|
||||
"Earn more tips by uploading cool videos": "Earn more tips by uploading cool videos",
|
||||
"You staked": "You staked",
|
||||
"in your publishes": "in your publishes",
|
||||
"in your supports": "in your supports",
|
||||
"Your wallet is not currently synced with lbry.tv. You are responsible for backing up your wallet.": "Your wallet is not currently synced with lbry.tv. You are responsible for backing up your wallet.",
|
||||
"A backup of your wallet is synced with lbry.tv": "A backup of your wallet is synced with lbry.tv",
|
||||
"What does this mean?": "What does this mean?",
|
||||
"LBRY credits allow you to publish or purchase content.": "LBRY credits allow you to publish or purchase content.",
|
||||
"You can obtain free credits worth %amount% after you provide an email address.": "You can obtain free credits worth %amount% after you provide an email address.",
|
||||
"up to": "up to"
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
"@expo/vector-icons": "^8.1.0",
|
||||
"gfycat-style-urls": "^1.0.3",
|
||||
"lbry-redux": "lbryio/lbry-redux#69ffd110dbf3633e5847f61f008751edec033017",
|
||||
"lbryinc": "lbryio/lbryinc#021ac75d9aa2db488cfff8e9be320402f038f955",
|
||||
"lbryinc": "lbryio/lbryinc#667024ebb7cb207609273174ca422cee47469270",
|
||||
"lodash": ">=4.17.11",
|
||||
"merge": ">=1.2.1",
|
||||
"moment": "^2.22.1",
|
||||
|
|
|
@ -54,7 +54,9 @@ import {
|
|||
} from 'lbry-redux';
|
||||
import {
|
||||
Lbryio,
|
||||
rewards as REWARD_TYPES,
|
||||
doBlackListedOutpointsSubscribe,
|
||||
doClaimRewardType,
|
||||
doFilteredOutpointsSubscribe,
|
||||
doGetSync,
|
||||
doUserCheckEmailVerified,
|
||||
|
@ -372,6 +374,28 @@ class AppWithNavigationState extends React.Component {
|
|||
);
|
||||
};
|
||||
|
||||
checkNewAndroidReward = () => {
|
||||
const { dispatch, doToast } = this.props;
|
||||
const claimRewardCallback = err => {
|
||||
if (err) {
|
||||
// an error occurred, do not display anything
|
||||
return;
|
||||
}
|
||||
// reward successfully claimed
|
||||
NativeModules.UtilityModule.setNativeBooleanSetting(Constants.SETTING_NEW_ANDROID_REWARD_CLAIMED, true);
|
||||
};
|
||||
|
||||
NativeModules.UtilityModule.getNativeBooleanSetting(Constants.SETTING_NEW_ANDROID_REWARD_CLAIMED, false).then(
|
||||
rewardClaimed => {
|
||||
if (!rewardClaimed) {
|
||||
dispatch(
|
||||
doClaimRewardType(REWARD_TYPES.TYPE_NEW_ANDROID, { notifyError: false, callback: claimRewardCallback }),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
handleSdkReady = () => {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(doSetSdkReady());
|
||||
|
@ -398,6 +422,8 @@ class AppWithNavigationState extends React.Component {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.checkNewAndroidReward();
|
||||
};
|
||||
|
||||
handleAccountUnlockFailed() {
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doToast, selectMyChannelClaims } from 'lbry-redux';
|
||||
import { selectUser } from 'lbryinc';
|
||||
import { doToast, selectBalance, selectMyChannelClaims } from 'lbry-redux';
|
||||
import { selectUnclaimedRewardValue, selectUser } from 'lbryinc';
|
||||
import { selectSdkReady } from 'redux/selectors/settings';
|
||||
import DrawerContent from './view';
|
||||
|
||||
const select = state => ({
|
||||
balance: selectBalance(state),
|
||||
channels: selectMyChannelClaims(state),
|
||||
sdkReady: selectSdkReady(state),
|
||||
unclaimedRewardAmount: selectUnclaimedRewardValue(state),
|
||||
user: selectUser(state),
|
||||
});
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
|||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||
import channelIconStyle from 'styles/channelIcon';
|
||||
import discoverStyle from 'styles/discover';
|
||||
import { Lbryio } from 'lbryinc';
|
||||
import { formatUsd } from 'utils/helper';
|
||||
|
||||
const groupedMenuItems = {
|
||||
'Find content': [
|
||||
|
@ -42,6 +44,18 @@ const routesRequiringSdkReady = [
|
|||
];
|
||||
|
||||
class DrawerContent extends React.PureComponent {
|
||||
state = {
|
||||
usdExchangeRate: 0,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
Lbryio.getExchangeRates().then(rates => {
|
||||
if (!isNaN(rates.LBC_USD)) {
|
||||
this.setState({ usdExchangeRate: rates.LBC_USD });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getAvatarImageUrl = () => {
|
||||
const { channels = [] } = this.props;
|
||||
if (channels) {
|
||||
|
@ -82,7 +96,7 @@ class DrawerContent extends React.PureComponent {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { activeTintColor, navigation, user, onItemPress } = this.props;
|
||||
const { activeTintColor, balance, navigation, unclaimedRewardAmount, user, onItemPress } = this.props;
|
||||
const { state } = navigation;
|
||||
|
||||
const activeItemKey = state.routes[state.index] ? state.routes[state.index].key : null;
|
||||
|
@ -189,6 +203,15 @@ class DrawerContent extends React.PureComponent {
|
|||
</View>
|
||||
<Text style={[discoverStyle.menuItem, focused ? discoverStyle.menuItemFocused : null]}>
|
||||
{__(item.label)}
|
||||
{item.label === 'Wallet' && this.state.usdExchangeRate > 0 && (
|
||||
<Text> ({formatUsd(parseFloat(balance) * parseFloat(this.state.usdExchangeRate))})</Text>
|
||||
)}
|
||||
{item.label === 'Rewards' && this.state.usdExchangeRate > 0 && (
|
||||
<Text>
|
||||
{' '}
|
||||
({formatUsd(parseFloat(unclaimedRewardAmount) * parseFloat(this.state.usdExchangeRate))})
|
||||
</Text>
|
||||
)}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
|
|
@ -70,7 +70,9 @@ class FileItem extends React.PureComponent {
|
|||
const outpointsToHide = !blackListedOutpoints
|
||||
? filteredOutpoints
|
||||
: blackListedOutpoints.concat(filteredOutpoints);
|
||||
shouldHide = outpointsToHide.some(outpoint => outpoint.txid === claim.txid && outpoint.nout === claim.nout);
|
||||
shouldHide = outpointsToHide.some(
|
||||
outpoint => outpoint && outpoint.txid === claim.txid && outpoint.nout === claim.nout,
|
||||
);
|
||||
}
|
||||
if (shouldHide) {
|
||||
// don't display blacklisted or filtered outpoints on the Your tags page
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -2,7 +2,7 @@ import { connect } from 'react-redux';
|
|||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
import { selectBalance } from 'lbry-redux';
|
||||
import { selectUnclaimedRewardValue } from 'lbryinc';
|
||||
import Constants from 'constants';
|
||||
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||
import FloatingWalletBalance from './view';
|
||||
|
||||
const select = state => ({
|
||||
|
@ -11,7 +11,4 @@ const select = state => ({
|
|||
rewardsNotInterested: makeSelectClientSetting(Constants.SETTING_REWARDS_NOT_INTERESTED)(state),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
select,
|
||||
null
|
||||
)(FloatingWalletBalance);
|
||||
export default connect(select, null)(FloatingWalletBalance);
|
||||
|
|
28
src/component/modalRepostView/index.js
Normal file
28
src/component/modalRepostView/index.js
Normal 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);
|
204
src/component/modalRepostView/view.js
Normal file
204
src/component/modalRepostView/view.js
Normal 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.01',
|
||||
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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doSendTip, doToast, selectBalance } from 'lbry-redux';
|
||||
import { selectSdkReady } from 'redux/selectors/settings';
|
||||
import ModalTipView from './view';
|
||||
|
||||
const select = state => ({
|
||||
balance: selectBalance(state),
|
||||
sdkReady: selectSdkReady(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
|
@ -12,7 +14,4 @@ const perform = dispatch => ({
|
|||
dispatch(doSendTip(amount, claimId, isSupport, successCallback, errorCallback)),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
select,
|
||||
perform
|
||||
)(ModalTipView);
|
||||
export default connect(select, perform)(ModalTipView);
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
@ -19,12 +18,22 @@ export default class ModalTipView extends React.PureComponent {
|
|||
};
|
||||
|
||||
handleSendTip = () => {
|
||||
const { claim, balance, notify, onSendTipFailed, onSendTipSuccessful, sendTip } = this.props;
|
||||
const { claim, balance, notify, onSendTipFailed, onSendTipSuccessful, sdkReady, sendTip } = this.props;
|
||||
const { tipAmount } = this.state;
|
||||
|
||||
if (!sdkReady) {
|
||||
notify({
|
||||
message: __(
|
||||
'The background service is still initializing. You can still explore and watch content during the initialization process.',
|
||||
),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (tipAmount > balance) {
|
||||
notify({
|
||||
message: 'Insufficient credits',
|
||||
isError: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -56,13 +65,13 @@ export default class ModalTipView extends React.PureComponent {
|
|||
() => {
|
||||
// error
|
||||
if (onSendTipFailed) onSendTipFailed();
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
{ cancelable: true }
|
||||
{ cancelable: true },
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -115,7 +124,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}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import { ActivityIndicator, Text, TouchableOpacity, View } from 'react-native';
|
||||
import Colors from '../../styles/colors';
|
||||
import { formatUsd } from 'utils/helper';
|
||||
import Colors from 'styles/colors';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||
import Link from '../link';
|
||||
import rewardStyle from '../../styles/reward';
|
||||
import Link from 'component/link';
|
||||
import rewardStyle from 'styles/reward';
|
||||
|
||||
type Props = {
|
||||
canClaim: boolean,
|
||||
|
@ -61,7 +62,7 @@ class RewardCard extends React.PureComponent<Props> {
|
|||
if (reward) {
|
||||
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('-')[1];
|
||||
} else if (reward.reward_amount > 0) {
|
||||
return reward.reward_amount;
|
||||
}
|
||||
|
@ -72,7 +73,7 @@ class RewardCard extends React.PureComponent<Props> {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { canClaim, isPending, onClaimPress, reward } = this.props;
|
||||
const { canClaim, isPending, onClaimPress, reward, usdExchangeRate } = this.props;
|
||||
const claimed = !!reward.transaction_id;
|
||||
|
||||
return (
|
||||
|
@ -117,8 +118,16 @@ class RewardCard extends React.PureComponent<Props> {
|
|||
)}
|
||||
</View>
|
||||
<View style={rewardStyle.rightCol}>
|
||||
{reward.reward_range && reward.reward_range.indexOf('-') > -1 && (
|
||||
<Text style={rewardStyle.rightColHeader}>{__('up to')}</Text>
|
||||
)}
|
||||
<Text style={rewardStyle.rewardAmount}>{this.getDisplayAmount()}</Text>
|
||||
<Text style={rewardStyle.rewardCurrency}>LBC</Text>
|
||||
{usdExchangeRate > 0 && (
|
||||
<Text style={rewardStyle.rewardUsd}>
|
||||
≈{formatUsd(parseFloat(this.getDisplayAmount()) * parseFloat(usdExchangeRate))}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
|
|
@ -7,6 +7,7 @@ import Link from 'component/link';
|
|||
import Colors from 'styles/colors';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||
import rewardStyle from 'styles/reward';
|
||||
import { formatUsd } from '../../utils/helper';
|
||||
|
||||
class RewardEnrolment extends React.Component {
|
||||
componentDidMount() {
|
||||
|
@ -29,7 +30,7 @@ class RewardEnrolment extends React.Component {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { fetching, navigation, unclaimedRewardAmount, user } = this.props;
|
||||
const { unclaimedRewardAmount, usdExchangeRate } = this.props;
|
||||
|
||||
return (
|
||||
<View style={rewardStyle.enrollContainer}>
|
||||
|
@ -43,9 +44,11 @@ class RewardEnrolment extends React.Component {
|
|||
|
||||
<View style={rewardStyle.onboarding}>
|
||||
<Text style={rewardStyle.enrollDescText}>
|
||||
{__('LBRY credits allow you to purchase content, publish content, and influence the network.')}
|
||||
{__('LBRY credits allow you to publish or purchase content.')}
|
||||
{'\n\n'}
|
||||
{__('You get credits for free for providing an email address and taking other basic actions.')}
|
||||
{__('You can obtain free credits worth %amount% after you provide an email address.', {
|
||||
amount: formatUsd(parseFloat(unclaimedRewardAmount) * parseFloat(usdExchangeRate)),
|
||||
})}
|
||||
{'\n\n'}
|
||||
<Link style={rewardStyle.learnMoreLink} text={__('Learn more')} onPress={this.onLearnMorePressed} />.
|
||||
</Text>
|
||||
|
|
|
@ -58,7 +58,7 @@ class SuggestedSubscriptionsGrid extends React.PureComponent {
|
|||
const uris = claimSearchByQuery[claimSearchKey];
|
||||
if (
|
||||
lastPageReached[claimSearchKey] ||
|
||||
((uris.length > 0 && uris.length < suggestedPageSize) || uris.length >= softLimit)
|
||||
(uris.length > 0 && uris.length < suggestedPageSize) || uris.length >= softLimit
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ class SuggestedSubscriptionsGrid extends React.PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { claimSearchByQuery, suggested, inModal, navigation } = this.props;
|
||||
const { claimSearchByQuery, inModal, navigation } = this.props;
|
||||
const options = this.buildClaimSearchOptions();
|
||||
const claimSearchKey = createNormalizedClaimSearchKey(options);
|
||||
const claimSearchUris = claimSearchByQuery[claimSearchKey];
|
||||
|
@ -92,7 +92,7 @@ class SuggestedSubscriptionsGrid extends React.PureComponent {
|
|||
maxToRenderPerBatch={48}
|
||||
removeClippedSubviews
|
||||
itemDimension={120}
|
||||
spacing={2}
|
||||
spacing={1}
|
||||
items={claimSearchUris}
|
||||
style={inModal ? subscriptionsStyle.modalScrollContainer : subscriptionsStyle.scrollContainer}
|
||||
contentContainerStyle={
|
||||
|
|
|
@ -6,7 +6,4 @@ const select = state => ({
|
|||
balance: selectBalance(state),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
select,
|
||||
null
|
||||
)(WalletBalance);
|
||||
export default connect(select, null)(WalletBalance);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import { Image, Text, View } from 'react-native';
|
||||
import { Lbry, formatCredits } from 'lbry-redux';
|
||||
import Address from 'component/address';
|
||||
import Button from 'component/button';
|
||||
import { formatCredits } from 'lbry-redux';
|
||||
import { Lbryio } from 'lbryinc';
|
||||
import { formatUsd } from 'utils/helper';
|
||||
import walletStyle from 'styles/wallet';
|
||||
|
||||
type Props = {
|
||||
|
@ -11,6 +11,18 @@ type Props = {
|
|||
};
|
||||
|
||||
class WalletBalance extends React.PureComponent<Props> {
|
||||
state = {
|
||||
usdExchangeRate: 0,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
Lbryio.getExchangeRates().then(rates => {
|
||||
if (!isNaN(rates.LBC_USD)) {
|
||||
this.setState({ usdExchangeRate: rates.LBC_USD });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { balance } = this.props;
|
||||
return (
|
||||
|
@ -21,6 +33,13 @@ class WalletBalance extends React.PureComponent<Props> {
|
|||
<Text style={walletStyle.balance}>
|
||||
{(balance || balance === 0) && formatCredits(parseFloat(balance), 2) + ' LBC'}
|
||||
</Text>
|
||||
<Text style={walletStyle.usdBalance}>
|
||||
{this.state.usdExchangeRate > 0 && (
|
||||
<Text>
|
||||
≈{formatUsd(isNaN(balance) ? 0 : parseFloat(balance) * parseFloat(this.state.usdExchangeRate))}
|
||||
</Text>
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { selectClaimsBalance, selectSupportsBalance, selectTipsBalance } from 'lbry-redux';
|
||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
import WalletBalanceExtra from './view';
|
||||
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||
|
||||
const select = state => ({
|
||||
claimsBalance: selectClaimsBalance(state) || 0,
|
||||
deviceWalletSynced: makeSelectClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED)(state),
|
||||
supportsBalance: selectSupportsBalance(state) || 0,
|
||||
tipsBalance: selectTipsBalance(state) || 0,
|
||||
});
|
||||
|
||||
export default connect(
|
||||
select,
|
||||
null,
|
||||
)(WalletBalanceExtra);
|
||||
export default connect(select, null)(WalletBalanceExtra);
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import { Image, Text, View } from 'react-native';
|
||||
import { Lbry, formatCredits } from 'lbry-redux';
|
||||
import Address from 'component/address';
|
||||
import Button from 'component/button';
|
||||
import { Text, View } from 'react-native';
|
||||
import { formatCredits } from 'lbry-redux';
|
||||
import { Lbryio } from 'lbryinc';
|
||||
import { formatUsd } from 'utils/helper';
|
||||
import Colors from 'styles/colors';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||
import Link from 'component/link';
|
||||
import walletStyle from 'styles/wallet';
|
||||
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||
|
||||
type Props = {
|
||||
claimsBalance: number,
|
||||
|
@ -15,10 +17,40 @@ type Props = {
|
|||
};
|
||||
|
||||
class WalletBalanceExtra extends React.PureComponent<Props> {
|
||||
state = {
|
||||
usdExchangeRate: 0,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
Lbryio.getExchangeRates().then(rates => {
|
||||
if (!isNaN(rates.LBC_USD)) {
|
||||
this.setState({ usdExchangeRate: rates.LBC_USD });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { claimsBalance, supportsBalance, tipsBalance } = this.props;
|
||||
const { claimsBalance, deviceWalletSynced, navigation, supportsBalance, tipsBalance } = this.props;
|
||||
|
||||
return (
|
||||
<View style={walletStyle.balanceExtra}>
|
||||
<View style={walletStyle.usdInfoCard}>
|
||||
<Text style={walletStyle.usdInfoText}>
|
||||
You can convert your credits to USD and withdraw the converted amount using an exchange.{' '}
|
||||
<Link
|
||||
style={walletStyle.usdConvertFaqLink}
|
||||
href={'https://lbry.com/faq/exchanges'}
|
||||
text={__('Learn more')}
|
||||
/>
|
||||
.
|
||||
</Text>
|
||||
<Link
|
||||
style={walletStyle.usdConvertLink}
|
||||
href={'https://bittrex.com/Account/Register?referralCode=4M1-P30-BON'}
|
||||
text={__('Convert credits to USD on Bittrex')}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={walletStyle.balanceExtraCard}>
|
||||
<View style={walletStyle.walletExtraRow}>
|
||||
<View style={walletStyle.walletExtraCol}>
|
||||
|
@ -28,7 +60,18 @@ class WalletBalanceExtra extends React.PureComponent<Props> {
|
|||
<Text style={walletStyle.walletExtraBalance}>{formatCredits(parseFloat(tipsBalance), 2)}</Text>
|
||||
<Text style={walletStyle.walletExtraCurrency}>LBC</Text>
|
||||
</View>
|
||||
<Text style={walletStyle.usdWalletExtraBalance}>
|
||||
≈{formatUsd(parseFloat(tipsBalance) * parseFloat(this.state.usdExchangeRate))}
|
||||
</Text>
|
||||
<Text style={walletStyle.text}>{__('in tips')}</Text>
|
||||
|
||||
<Link
|
||||
style={walletStyle.earnTipsLink}
|
||||
onPress={() => {
|
||||
navigation.navigate({ routeName: Constants.DRAWER_ROUTE_PUBLISH });
|
||||
}}
|
||||
text={__('Earn more tips by uploading cool videos')}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={walletStyle.walletExtraCol}>
|
||||
|
@ -47,6 +90,24 @@ class WalletBalanceExtra extends React.PureComponent<Props> {
|
|||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={walletStyle.syncDriverCustody}>
|
||||
<Text style={walletStyle.syncInfoText}>
|
||||
{deviceWalletSynced
|
||||
? __('A backup of your wallet is synced with lbry.tv')
|
||||
: __('Your wallet is not currently synced with lbry.tv. You are responsible for backing up your wallet.')}
|
||||
</Text>
|
||||
<Link
|
||||
text={__('What does this mean?')}
|
||||
href={
|
||||
deviceWalletSynced
|
||||
? 'https://lbry.com/faq/account-sync'
|
||||
: 'https://lbry.com/faq/how-to-backup-wallet#android'
|
||||
}
|
||||
style={walletStyle.syncInfoLink}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ class WalletSyncDriver extends React.PureComponent<Props> {
|
|||
},
|
||||
},
|
||||
],
|
||||
{ cancelable: true }
|
||||
{ cancelable: true },
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -45,6 +45,7 @@ const Constants = {
|
|||
SETTING_REWARDS_NOT_INTERESTED: 'rewardsNotInterested',
|
||||
SETTING_DEVICE_WALLET_SYNCED: 'deviceWalletSynced',
|
||||
SETTING_DHT_ENABLED: 'dhtEnabled',
|
||||
SETTING_NEW_ANDROID_REWARD_CLAIMED: 'newAndroidRewardClaimed',
|
||||
|
||||
ACTION_SDK_READY: 'SDK_READY',
|
||||
|
||||
|
@ -149,7 +150,7 @@ const Constants = {
|
|||
|
||||
TRUE_STRING: 'true',
|
||||
|
||||
MINIMUM_TRANSACTION_BALANCE: 0.1,
|
||||
MINIMUM_TRANSACTION_BALANCE: 0.01,
|
||||
|
||||
SHARE_BASE_URL: 'https://open.lbry.com',
|
||||
};
|
||||
|
|
|
@ -47,7 +47,7 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
channelNameUserEdited: false,
|
||||
newChannelTitle: '',
|
||||
newChannelName: '',
|
||||
newChannelBid: 0.1,
|
||||
newChannelBid: 0.01,
|
||||
addingChannel: false,
|
||||
creatingChannel: false,
|
||||
editChannelUrl: null,
|
||||
|
@ -280,7 +280,7 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
handleCreateCancel = () => {
|
||||
const { clearChannelFormState } = this.props;
|
||||
clearChannelFormState(); // explicitly clear state on cancel?
|
||||
this.setState({ showCreateChannel: false, newChannelName: '', newChannelBid: 0.1 });
|
||||
this.setState({ showCreateChannel: false, newChannelName: '', newChannelBid: 0.01 });
|
||||
};
|
||||
|
||||
handlePickerValueChange = (itemValue, itemIndex) => {
|
||||
|
@ -589,7 +589,7 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
channelNameUserEdited: false,
|
||||
newChannelTitle: '',
|
||||
newChannelName: '',
|
||||
newChannelBid: 0.1,
|
||||
newChannelBid: 0.01,
|
||||
addingChannel: false,
|
||||
creatingChannel: false,
|
||||
newChannelNameError: '',
|
||||
|
|
|
@ -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',
|
||||
|
@ -817,7 +820,16 @@ class FilePage extends React.PureComponent {
|
|||
};
|
||||
|
||||
onDownloadPressed = () => {
|
||||
const { claim, title } = this.props;
|
||||
const { claim, notify, sdkReady, title } = this.props;
|
||||
if (!sdkReady) {
|
||||
notify({
|
||||
message: __(
|
||||
'The background service is still initializing. You can still explore and watch content during the initialization process.',
|
||||
),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const fileSize = claim && claim.value && claim.value.source ? claim.value.source.size : 0;
|
||||
Alert.alert(
|
||||
__('Download file'),
|
||||
|
@ -1017,7 +1029,6 @@ class FilePage extends React.PureComponent {
|
|||
myClaimUris,
|
||||
navigation,
|
||||
position,
|
||||
purchaseUri,
|
||||
pushDrawerStack,
|
||||
setPlayerVisible,
|
||||
thumbnail,
|
||||
|
@ -1127,7 +1138,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 +1360,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 +1542,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} />}
|
||||
|
|
|
@ -38,15 +38,19 @@ class LiteFilePage extends React.PureComponent {
|
|||
|
||||
player = null;
|
||||
|
||||
startTime = null;
|
||||
|
||||
state = {
|
||||
channelName: null,
|
||||
channelUrl: null,
|
||||
title: null,
|
||||
fileViewLogged: false,
|
||||
fullscreenMode: false,
|
||||
playbackStarted: false,
|
||||
playerHeight: null,
|
||||
isLandscape: false,
|
||||
sdkReady: false, // TODO: progressively enable features (e.g. tip) when sdk is ready
|
||||
showRecommended: false,
|
||||
title: null,
|
||||
viewCount: 0,
|
||||
};
|
||||
|
||||
|
@ -127,6 +131,10 @@ class LiteFilePage extends React.PureComponent {
|
|||
);
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.startTime = Date.now();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { navigation } = this.props;
|
||||
const { uri } = navigation.state.params;
|
||||
|
|
|
@ -121,7 +121,7 @@ class PublishPage extends React.PureComponent {
|
|||
|
||||
// input data
|
||||
hasEditedContentAddress: false,
|
||||
bid: 0.1,
|
||||
bid: 0.01,
|
||||
description: null,
|
||||
title: null,
|
||||
language: 'en',
|
||||
|
|
|
@ -194,7 +194,7 @@ class PublishesPage extends React.PureComponent {
|
|||
});
|
||||
} else {
|
||||
// TODO: when shortUrl is available for my claims, navigate to that URL instead
|
||||
navigateToUri(navigation, item);
|
||||
navigateToUri(navigation, claim.permanent_url);
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Lbry } from 'lbry-redux';
|
||||
import { Lbryio } from 'lbryinc';
|
||||
import { ActivityIndicator, NativeModules, ScrollView, Text, View } from 'react-native';
|
||||
import Colors from 'styles/colors';
|
||||
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||
|
@ -17,13 +17,14 @@ const FILTER_CLAIMED = 'claimed';
|
|||
|
||||
class RewardsPage extends React.PureComponent {
|
||||
state = {
|
||||
currentFilter: FILTER_AVAILABLE,
|
||||
firstRewardClaimed: false,
|
||||
isEmailVerified: false,
|
||||
isIdentityVerified: false,
|
||||
isRewardApproved: false,
|
||||
verifyRequestStarted: false,
|
||||
revealVerification: true,
|
||||
firstRewardClaimed: false,
|
||||
currentFilter: FILTER_AVAILABLE,
|
||||
usdExchangeRate: 0,
|
||||
verifyRequestStarted: false,
|
||||
};
|
||||
|
||||
scrollView = null;
|
||||
|
@ -48,6 +49,12 @@ class RewardsPage extends React.PureComponent {
|
|||
setPlayerVisible();
|
||||
NativeModules.Firebase.setCurrentScreen('Rewards');
|
||||
|
||||
Lbryio.getExchangeRates().then(rates => {
|
||||
if (!isNaN(rates.LBC_USD)) {
|
||||
this.setState({ usdExchangeRate: rates.LBC_USD });
|
||||
}
|
||||
});
|
||||
|
||||
fetchRewards();
|
||||
|
||||
this.setState({
|
||||
|
@ -158,6 +165,7 @@ class RewardsPage extends React.PureComponent {
|
|||
canClaim={!isNotEligible}
|
||||
reward={reward}
|
||||
reward_type={reward.reward_type}
|
||||
usdExchangeRate={this.state.usdExchangeRate}
|
||||
/>
|
||||
))}
|
||||
<CustomRewardCard canClaim={!isNotEligible} showVerification={this.showVerification} />
|
||||
|
@ -211,7 +219,9 @@ class RewardsPage extends React.PureComponent {
|
|||
return (
|
||||
<View style={rewardStyle.container}>
|
||||
<UriBar navigation={navigation} />
|
||||
{(!this.state.isEmailVerified || !this.state.isRewardApproved) && <RewardEnrolment navigation={navigation} />}
|
||||
{(!this.state.isEmailVerified || !this.state.isRewardApproved) && (
|
||||
<RewardEnrolment usdExchangeRate={this.state.usdExchangeRate} navigation={navigation} />
|
||||
)}
|
||||
|
||||
{this.state.isEmailVerified && this.state.isRewardApproved && (
|
||||
<ScrollView
|
||||
|
|
|
@ -2,6 +2,7 @@ import { connect } from 'react-redux';
|
|||
import { SETTINGS, doUpdateBlockHeight, doPopulateSharedUserState, doToast } from 'lbry-redux';
|
||||
import {
|
||||
doAuthenticate,
|
||||
doClaimRewardType,
|
||||
doInstallNewWithParams,
|
||||
doFetchMySubscriptions,
|
||||
doFetchRewardedContent,
|
||||
|
|
|
@ -159,6 +159,7 @@ class SplashScreen extends React.PureComponent {
|
|||
this.getUserSettings();
|
||||
});
|
||||
});
|
||||
|
||||
this.navigateToMain();
|
||||
return;
|
||||
}
|
||||
|
@ -322,6 +323,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;
|
||||
|
|
|
@ -12,6 +12,8 @@ import {
|
|||
selectViewMode,
|
||||
selectFirstRunCompleted,
|
||||
selectShowSuggestedSubs,
|
||||
selectUnclaimedRewardValue,
|
||||
selectUser,
|
||||
} from 'lbryinc';
|
||||
import { doToast, selectFetchingClaimSearch } from 'lbry-redux';
|
||||
import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
|
||||
|
@ -32,9 +34,12 @@ const select = state => ({
|
|||
unreadSubscriptions: selectUnreadSubscriptions(state),
|
||||
viewMode: selectViewMode(state),
|
||||
firstRunCompleted: selectFirstRunCompleted(state),
|
||||
rewardsNotInterested: makeSelectClientSetting(Constants.SETTING_REWARDS_NOT_INTERESTED)(state),
|
||||
showSuggestedSubs: selectShowSuggestedSubs(state),
|
||||
timeItem: selectTimeItem(state),
|
||||
sdkReady: selectSdkReady(state),
|
||||
unclaimedRewardAmount: selectUnclaimedRewardValue(state),
|
||||
user: selectUser(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
View,
|
||||
} from 'react-native';
|
||||
import { buildURI, parseURI } from 'lbry-redux';
|
||||
import { getOrderBy } from 'utils/helper';
|
||||
import { formatUsd, getOrderBy } from 'utils/helper';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import moment from 'moment';
|
||||
import Button from 'component/button';
|
||||
|
@ -31,16 +31,21 @@ import SuggestedSubscriptions from 'component/suggestedSubscriptions';
|
|||
import SuggestedSubscriptionsGrid from 'component/suggestedSubscriptionsGrid';
|
||||
import UriBar from 'component/uriBar';
|
||||
import SdkLoadingStatus from 'component/sdkLoadingStatus';
|
||||
import Snackbar from 'react-native-snackbar';
|
||||
import { Lbryio } from 'lbryinc';
|
||||
|
||||
class SubscriptionsPage extends React.PureComponent {
|
||||
state = {
|
||||
currentSortByItem: Constants.CLAIM_SEARCH_SORT_BY_ITEMS[1], // should always default to sorting subscriptions by new
|
||||
filteredChannels: [],
|
||||
orderBy: ['release_time'],
|
||||
showRewardsNag: true,
|
||||
showingSuggestedSubs: false,
|
||||
showSortPicker: false,
|
||||
showTimePicker: false,
|
||||
showModalSuggestedSubs: false,
|
||||
orderBy: ['release_time'],
|
||||
filteredChannels: [],
|
||||
currentSortByItem: Constants.CLAIM_SEARCH_SORT_BY_ITEMS[1], // should always default to sorting subscriptions by new
|
||||
usdExchangeRate: 0,
|
||||
userEmailVerified: false,
|
||||
};
|
||||
|
||||
didFocusListener;
|
||||
|
@ -57,15 +62,7 @@ class SubscriptionsPage extends React.PureComponent {
|
|||
}
|
||||
|
||||
onComponentFocused = () => {
|
||||
const {
|
||||
currentRoute,
|
||||
doFetchMySubscriptions,
|
||||
doFetchRecommendedSubscriptions,
|
||||
doSetViewMode,
|
||||
pushDrawerStack,
|
||||
setPlayerVisible,
|
||||
subscriptionsViewMode,
|
||||
} = this.props;
|
||||
const { currentRoute, doFetchMySubscriptions, pushDrawerStack, sdkReady, setPlayerVisible, user } = this.props;
|
||||
|
||||
if (currentRoute === Constants.DRAWER_ROUTE_SUBSCRIPTIONS) {
|
||||
pushDrawerStack();
|
||||
|
@ -73,6 +70,17 @@ class SubscriptionsPage extends React.PureComponent {
|
|||
setPlayerVisible();
|
||||
NativeModules.Firebase.setCurrentScreen('Subscriptions');
|
||||
|
||||
Lbryio.getExchangeRates().then(rates => {
|
||||
if (!isNaN(rates.LBC_USD)) {
|
||||
this.setState({ usdExchangeRate: rates.LBC_USD }, () => {
|
||||
if (sdkReady && parseFloat(this.state.usdExchangeRate) > 0 && user && !user.is_reward_approved) {
|
||||
this.showRewardsAvailable();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.setState({ userEmailVerified: user && user.has_verified_email });
|
||||
doFetchMySubscriptions();
|
||||
};
|
||||
|
||||
|
@ -81,15 +89,56 @@ class SubscriptionsPage extends React.PureComponent {
|
|||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const { currentRoute } = nextProps;
|
||||
const { currentRoute: prevRoute } = this.props;
|
||||
const { currentRoute, user, sdkReady } = nextProps;
|
||||
const { currentRoute: prevRoute, doFetchMySubscriptions } = this.props;
|
||||
if (Constants.DRAWER_ROUTE_SUBSCRIPTIONS === currentRoute && currentRoute !== prevRoute) {
|
||||
this.onComponentFocused();
|
||||
}
|
||||
|
||||
if (user && user.has_verified_email && !this.state.userEmailVerified) {
|
||||
// user just signed in
|
||||
this.setState({ showingSuggestedSubs: false, userEmailVerified: true }, () => {
|
||||
doFetchMySubscriptions();
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
sdkReady &&
|
||||
parseFloat(this.state.usdExchangeRate) > 0 &&
|
||||
this.state.showRewardsNag &&
|
||||
user &&
|
||||
!user.is_reward_approved
|
||||
) {
|
||||
this.showRewardsAvailable();
|
||||
}
|
||||
|
||||
this.unsubscribeShortChannelUrls();
|
||||
}
|
||||
|
||||
showRewardsAvailable = () => {
|
||||
const { navigation, unclaimedRewardAmount, rewardsNotInterested } = this.props;
|
||||
if (rewardsNotInterested) {
|
||||
this.setState({ showRewardsNag: false });
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ showRewardsNag: false }, () => {
|
||||
Snackbar.show({
|
||||
title: __('Did you know that you can earn free credits worth up to %amount%?', {
|
||||
amount: formatUsd(parseFloat(this.state.usdExchangeRate) * parseFloat(unclaimedRewardAmount)),
|
||||
}),
|
||||
duration: Snackbar.LENGTH_LONG,
|
||||
action: {
|
||||
title: __('SHOW ME'),
|
||||
color: Colors.LbryGreen,
|
||||
onPress: () => {
|
||||
navigation.navigate({ routeName: Constants.DRAWER_ROUTE_REWARDS });
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
handleSortByItemSelected = item => {
|
||||
this.setState({ currentSortByItem: item, orderBy: getOrderBy(item), showSortPicker: false });
|
||||
};
|
||||
|
|
|
@ -94,7 +94,7 @@ class WalletPage extends React.PureComponent {
|
|||
>
|
||||
{!rewardsNotInterested && (!balance || balance === 0) && <WalletRewardsDriver navigation={navigation} />}
|
||||
<WalletBalance />
|
||||
<WalletBalanceExtra />
|
||||
<WalletBalanceExtra navigation={navigation} />
|
||||
<WalletAddress />
|
||||
<WalletSend />
|
||||
<TransactionListRecent navigation={navigation} />
|
||||
|
|
93
src/styles/modalRepost.js
Normal file
93
src/styles/modalRepost.js
Normal 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;
|
|
@ -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,
|
||||
|
@ -449,7 +451,7 @@ const publishStyle = StyleSheet.create({
|
|||
publishesFooterButton: {
|
||||
alignSelf: 'flex-start',
|
||||
backgroundColor: Colors.LbryGreen,
|
||||
marginTop: 16,
|
||||
marginTop: 8,
|
||||
},
|
||||
thumbnailEditOverlay: {
|
||||
alignItems: 'center',
|
||||
|
|
|
@ -147,6 +147,10 @@ const rewardStyle = StyleSheet.create({
|
|||
width: '18%',
|
||||
alignItems: 'center',
|
||||
},
|
||||
rightColHeader: {
|
||||
fontFamily: 'Inter-Regular',
|
||||
fontSize: 12,
|
||||
},
|
||||
rewardAmount: {
|
||||
fontFamily: 'Inter-Regular',
|
||||
fontSize: 26,
|
||||
|
@ -154,6 +158,7 @@ const rewardStyle = StyleSheet.create({
|
|||
},
|
||||
rewardCurrency: {
|
||||
fontFamily: 'Inter-Regular',
|
||||
fontSize: 12,
|
||||
},
|
||||
rewardTitle: {
|
||||
fontFamily: 'Inter-Regular',
|
||||
|
@ -322,6 +327,12 @@ const rewardStyle = StyleSheet.create({
|
|||
activeFilterLink: {
|
||||
fontFamily: 'Inter-SemiBold',
|
||||
},
|
||||
rewardUsd: {
|
||||
fontFamily: 'Inter-Regular',
|
||||
fontSize: 12,
|
||||
color: Colors.DescriptionGrey,
|
||||
marginTop: 6,
|
||||
},
|
||||
});
|
||||
|
||||
export default rewardStyle;
|
||||
|
|
|
@ -192,8 +192,6 @@ const subscriptionsStyle = StyleSheet.create({
|
|||
suggestedItem: {
|
||||
alignItems: 'center',
|
||||
marginBottom: 16,
|
||||
marginLeft: 16,
|
||||
marginRight: 16,
|
||||
height: 140,
|
||||
},
|
||||
suggestedItemThumbnailContainer: {
|
||||
|
@ -209,8 +207,8 @@ const subscriptionsStyle = StyleSheet.create({
|
|||
height: '100%',
|
||||
},
|
||||
suggestedItemDetails: {
|
||||
marginLeft: 16,
|
||||
marginRight: 16,
|
||||
marginLeft: 8,
|
||||
marginRight: 8,
|
||||
alignItems: 'center',
|
||||
},
|
||||
suggestedItemSubscribe: {
|
||||
|
@ -229,7 +227,7 @@ const subscriptionsStyle = StyleSheet.create({
|
|||
suggestedItemTitle: {
|
||||
fontFamily: 'Inter-Regular',
|
||||
textAlign: 'center',
|
||||
fontSize: 14,
|
||||
fontSize: 13,
|
||||
marginTop: 4,
|
||||
marginBottom: 2,
|
||||
},
|
||||
|
|
|
@ -23,7 +23,7 @@ const tagStyle = StyleSheet.create({
|
|||
},
|
||||
text: {
|
||||
fontFamily: 'Inter-Regular',
|
||||
fontSize: 14,
|
||||
fontSize: 12,
|
||||
marginRight: 8,
|
||||
},
|
||||
tagResultsList: {
|
||||
|
|
|
@ -103,10 +103,12 @@ const walletStyle = StyleSheet.create({
|
|||
marginLeft: 16,
|
||||
marginRight: 16,
|
||||
},
|
||||
balanceExtraCard: {
|
||||
backgroundColor: Colors.White,
|
||||
balanceExtra: {
|
||||
marginLeft: 16,
|
||||
marginRight: 16,
|
||||
},
|
||||
balanceExtraCard: {
|
||||
backgroundColor: Colors.White,
|
||||
padding: 16,
|
||||
},
|
||||
balanceBackground: {
|
||||
|
@ -135,6 +137,13 @@ const walletStyle = StyleSheet.create({
|
|||
fontFamily: 'Inter-Bold',
|
||||
fontSize: 36,
|
||||
marginLeft: 16,
|
||||
},
|
||||
usdBalance: {
|
||||
color: Colors.White,
|
||||
fontFamily: 'Inter-Regular',
|
||||
fontSize: 20,
|
||||
marginLeft: 16,
|
||||
marginTop: 2,
|
||||
marginBottom: 16,
|
||||
},
|
||||
balanceFocus: {
|
||||
|
@ -252,6 +261,22 @@ const walletStyle = StyleSheet.create({
|
|||
borderBottomWidth: 1,
|
||||
borderBottomColor: Colors.PageBackground,
|
||||
},
|
||||
syncDriverCustody: {
|
||||
backgroundColor: Colors.LbryGreen,
|
||||
padding: 16,
|
||||
},
|
||||
syncInfoText: {
|
||||
color: Colors.White,
|
||||
fontFamily: 'Inter-Regular',
|
||||
fontSize: 16,
|
||||
marginBottom: 8,
|
||||
},
|
||||
syncInfoLink: {
|
||||
color: Colors.White,
|
||||
fontFamily: 'Inter-Regular',
|
||||
fontSize: 14,
|
||||
textDecorationLine: 'underline',
|
||||
},
|
||||
syncDriverLink: {
|
||||
color: Colors.LbryGreen,
|
||||
fontFamily: 'Inter-Regular',
|
||||
|
@ -384,6 +409,11 @@ const walletStyle = StyleSheet.create({
|
|||
fontFamily: 'Inter-SemiBold',
|
||||
fontSize: 28,
|
||||
},
|
||||
usdWalletExtraBalance: {
|
||||
fontFamily: 'Inter-Regular',
|
||||
fontSize: 16,
|
||||
color: Colors.DescriptionGrey,
|
||||
},
|
||||
balanceRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
|
@ -406,6 +436,34 @@ const walletStyle = StyleSheet.create({
|
|||
left: 0,
|
||||
top: 0,
|
||||
},
|
||||
usdInfoCard: {
|
||||
backgroundColor: Colors.White,
|
||||
padding: 16,
|
||||
borderBottomColor: Colors.VeryLightGrey,
|
||||
borderBottomWidth: 1,
|
||||
},
|
||||
usdInfoText: {
|
||||
fontFamily: 'Inter-Regular',
|
||||
fontSize: 14,
|
||||
marginBottom: 8,
|
||||
},
|
||||
usdConvertLink: {
|
||||
fontFamily: 'Inter-Regular',
|
||||
fontSize: 16,
|
||||
color: Colors.LbryGreen,
|
||||
},
|
||||
usdConvertFaqLink: {
|
||||
fontFamily: 'Inter-Regular',
|
||||
fontSize: 14,
|
||||
color: Colors.LbryGreen,
|
||||
},
|
||||
earnTipsLink: {
|
||||
fontFamily: 'Inter-Regular',
|
||||
fontSize: 14,
|
||||
color: Colors.LbryGreen,
|
||||
marginTop: 12,
|
||||
marginRight: 16,
|
||||
},
|
||||
});
|
||||
|
||||
export default walletStyle;
|
||||
|
|
|
@ -436,3 +436,10 @@ export function fetchReferralCode(successCallback, errorCallback) {
|
|||
export function decode(value) {
|
||||
return decodeURIComponent(value).replace(/\+/g, ' ');
|
||||
}
|
||||
|
||||
export function formatUsd(value) {
|
||||
if (isNaN(parseFloat(value))) {
|
||||
value = 0;
|
||||
}
|
||||
return '$' + parseFloat(value).toFixed(2);
|
||||
}
|
||||
|
|
|
@ -4599,9 +4599,9 @@ lbry-redux@lbryio/lbry-redux#69ffd110dbf3633e5847f61f008751edec033017:
|
|||
reselect "^3.0.0"
|
||||
uuid "^3.3.2"
|
||||
|
||||
lbryinc@lbryio/lbryinc#021ac75d9aa2db488cfff8e9be320402f038f955:
|
||||
lbryinc@lbryio/lbryinc#667024ebb7cb207609273174ca422cee47469270:
|
||||
version "0.0.1"
|
||||
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/021ac75d9aa2db488cfff8e9be320402f038f955"
|
||||
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/667024ebb7cb207609273174ca422cee47469270"
|
||||
dependencies:
|
||||
reselect "^3.0.0"
|
||||
|
||||
|
|
Loading…
Reference in a new issue