improve support/tip for 0 balance and own claims

This commit is contained in:
Sean Yesmunt 2020-06-09 18:09:24 -04:00
parent 019d1f9176
commit 01e0a2a5db
7 changed files with 212 additions and 191 deletions

View file

@ -47,8 +47,8 @@ class ChannelSelection extends React.PureComponent<Props, State> {
}
componentDidUpdate() {
const { channels, fetchingChannels } = this.props;
if (!fetchingChannels && !channels) {
const { channels, fetchingChannels, hideAnon } = this.props;
if (!fetchingChannels && !channels && hideAnon) {
this.setState({ addingChannel: true });
}
}

View file

@ -1,5 +1,5 @@
import { connect } from 'react-redux';
import { selectBalance } from 'lbry-redux';
import { selectBalance, selectMyChannelClaims } from 'lbry-redux';
import { doOpenModal } from 'redux/actions/app';
import WalletSend from './view';
import { withRouter } from 'react-router';
@ -10,11 +10,7 @@ const perform = dispatch => ({
const select = state => ({
balance: selectBalance(state),
channels: selectMyChannelClaims(state),
});
export default withRouter(
connect(
select,
perform
)(WalletSend)
);
export default withRouter(connect(select, perform)(WalletSend));

View file

@ -6,6 +6,8 @@ import {
selectIsSendingSupport,
selectBalance,
SETTINGS,
selectMyChannelClaims,
makeSelectClaimIsMine,
} from 'lbry-redux';
import WalletSendTip from './view';
import { doOpenModal, doHideModal } from 'redux/actions/app';
@ -19,6 +21,8 @@ const select = (state, props) => ({
balance: selectBalance(state),
instantTipEnabled: makeSelectClientSetting(SETTINGS.INSTANT_PURCHASE_ENABLED)(state),
instantTipMax: makeSelectClientSetting(SETTINGS.INSTANT_PURCHASE_MAX)(state),
channels: selectMyChannelClaims(state),
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
});
const perform = dispatch => ({

View file

@ -1,6 +1,7 @@
// @flow
import * as MODALS from 'constants/modal_types';
import * as ICONS from 'constants/icons';
import * as PAGES from 'constants/pages';
import React from 'react';
import Button from 'component/button';
import { FormField, Form } from 'component/common/form';
@ -11,12 +12,15 @@ import I18nMessage from 'component/i18nMessage';
import { Lbryio } from 'lbryinc';
import Card from 'component/common/card';
import classnames from 'classnames';
import SelectChannel from 'component/selectChannel';
import { parseURI } from 'lbry-redux';
import usePersistedState from 'effects/use-persisted-state';
const DEFAULT_TIP_AMOUNTS = [5, 10, 50];
const DEFAULT_TIP_AMOUNTS = [5, 25, 100, 1000];
type Props = {
uri: string,
// claimIsMine: boolean,
claimIsMine: boolean,
title: string,
claim: StreamClaim,
isPending: boolean,
@ -27,6 +31,7 @@ type Props = {
openModal: (id: string, { tipAmount: number, claimId: string, isSupport: boolean }) => void,
instantTipEnabled: boolean,
instantTipMax: { amount: number, currency: string },
channels: ?Array<ChannelClaim>,
};
function WalletSendTip(props: Props) {
@ -34,7 +39,7 @@ function WalletSendTip(props: Props) {
uri,
title,
isPending,
// claimIsMine,
claimIsMine,
balance,
claim = {},
instantTipEnabled,
@ -42,13 +47,46 @@ function WalletSendTip(props: Props) {
openModal,
sendSupport,
closeModal,
channels,
} = props;
const [tipAmount, setTipAmount] = React.useState(DEFAULT_TIP_AMOUNTS[0]);
const [tipError, setTipError] = React.useState();
const [isSupport, setIsSupport] = React.useState(false);
const [isSupport, setIsSupport] = React.useState(claimIsMine);
const [showMore, setShowMore] = React.useState(false);
const { claim_id: claimId } = claim;
const isMobile = useIsMobile();
const [selectedChannel, setSelectedChannel] = usePersistedState('comment-support:channel');
const { claim_id: claimId } = claim;
const channelStrings = channels && channels.map(channel => channel.permanent_url).join(',');
React.useEffect(() => {
if (!selectedChannel && channelStrings) {
const channels = channelStrings.split(',');
const newChannelUrl = channels[0];
const { claimName } = parseURI(newChannelUrl);
setSelectedChannel(claimName);
}
}, [channelStrings]);
React.useEffect(() => {
const regexp = RegExp(/^(\d*([.]\d{0,8})?)$/);
const validTipInput = regexp.test(String(tipAmount));
let tipError;
if (!tipAmount) {
tipError = __('Amount must be a number');
} else if (tipAmount <= 0) {
tipError = __('Amount must be a positive number');
} else if (tipAmount < MINIMUM_PUBLISH_BID) {
tipError = __('Amount must be higher');
} else if (!validTipInput) {
tipError = __('Amount must have no more than 8 decimal places');
} else if (tipAmount === balance) {
tipError = __('Please decrease the amount to account for transaction fees');
} else if (tipAmount > balance) {
tipError = __('Not enough credits');
}
setTipError(tipError);
}, [tipAmount, balance, setTipError]);
function sendSupportOrConfirm(instantTipMaxAmount = null) {
if (!isSupport && (!instantTipMaxAmount || !instantTipEnabled || tipAmount > instantTipMaxAmount)) {
@ -78,160 +116,130 @@ function WalletSendTip(props: Props) {
}
function handleSupportPriceChange(event: SyntheticInputEvent<*>) {
const regexp = RegExp(/^(\d*([.]\d{0,8})?)$/);
const validTipInput = regexp.test(event.target.value);
const tipAmount = parseFloat(event.target.value);
let tipError;
if (!tipAmount) {
tipError = __('Amount must be a number');
} else if (tipAmount <= 0) {
tipError = __('Amount must be a positive number');
} else if (tipAmount < MINIMUM_PUBLISH_BID) {
tipError = __('Amount must be higher');
} else if (!validTipInput) {
tipError = __('Amount must have no more than 8 decimal places');
} else if (tipAmount === balance) {
tipError = __('Please decrease the amount to account for transaction fees');
} else if (tipAmount > balance) {
tipError = __('Not enough credits');
}
setTipAmount(tipAmount);
setTipError(tipError);
}
// const label =
// tipAmount && tipAmount !== 0
// ? __(isSupport ? 'Support %amount% LBC' : 'Tip %amount% LBC', {
// amount: tipAmount.toFixed(8).replace(/\.?0+$/, ''),
// })
// : __('Amount');
return (
<Form onSubmit={handleSubmit}>
<Card
title={__('Support This Content')}
subtitle={
<React.Fragment>
{__(
'This will increase the overall bid amount for this content, which will boost its ability to be discovered while active.'
)}{' '}
<Button label={__('Learn more')} button="link" href="https://lbry.com/faq/tipping" />.
</React.Fragment>
}
actions={
<>
<div className="section">
{DEFAULT_TIP_AMOUNTS.map(amount => (
<Button
key={amount}
button="alt"
className={classnames('button-toggle', { 'button-toggle--active': tipAmount === amount })}
label={`${amount} LBC`}
onClick={() => setTipAmount(amount)}
/>
))}
<Button
button="alt"
className={classnames('button-toggle', {
'button-toggle--active': !DEFAULT_TIP_AMOUNTS.includes(tipAmount),
})}
label={__('Custom')}
onClick={() => setShowMore(true)}
/>
{balance === 0 ? (
<Card
title={__('Supporting Content Requires LBC')}
subtitle={__(
'With LBC, you can send tips to your favorite creators, or help boost their content for more people to see.'
)}
actions={
<div className="section__actions">
<Button icon={ICONS.BUY} button="primary" label={__('Buy LBC')} navigate={`/$/${PAGES.BUY}`} />
<Button button="link" label={__('Nevermind')} onClick={closeModal} />
</div>
{showMore && (
}
/>
) : (
<Card
title={claimIsMine ? __('Boost Your Content') : __('Support This Content')}
subtitle={
<React.Fragment>
{__(
'This will increase the overall bid amount for this content, which will boost its ability to be discovered while active.'
)}{' '}
<Button label={__('Learn more')} button="link" href="https://lbry.com/faq/tipping" />.
</React.Fragment>
}
actions={
<>
<div className="section">
<FormField
autoFocus
name="tip-input"
label={
<React.Fragment>
{'Custom support amount'}{' '}
{isMobile && (
<I18nMessage tokens={{ lbc_balance: <CreditAmount badge={false} amount={balance} /> }}>
(%lbc_balance% available)
</I18nMessage>
)}
</React.Fragment>
}
className="form-field--price-amount"
error={tipError}
min="0"
step="any"
type="number"
placeholder="1.23"
onChange={event => handleSupportPriceChange(event)}
<SelectChannel
label="Channel to show support as"
channel={selectedChannel}
onChannelChange={newChannel => setSelectedChannel(newChannel)}
/>
</div>
)}
<div className="section__actions">
<Button
autoFocus
icon={isSupport ? undefined : ICONS.SUPPORT}
button="primary"
type="submit"
label={
isSupport
? __('Send Revokable Support')
: __('Send a %amount% Tip', { amount: tipAmount ? `${tipAmount} LBC` : '' })
}
disabled={isPending || tipError || !tipAmount}
/>
<FormField
name="toggle-is-support"
type="checkbox"
label={__('Make this support permanent')}
checked={!isSupport}
onChange={() => setIsSupport(!isSupport)}
/>
</div>
</>
}
/>
<div className="section">
{DEFAULT_TIP_AMOUNTS.map(amount => (
<Button
key={amount}
disabled={amount > balance}
button="alt"
className={classnames('button-toggle', {
'button-toggle--active': tipAmount === amount,
'button-toggle--disabled': amount > balance,
})}
label={`${amount} LBC`}
onClick={() => setTipAmount(amount)}
/>
))}
<Button
button="alt"
className={classnames('button-toggle', {
'button-toggle--active': !DEFAULT_TIP_AMOUNTS.includes(tipAmount),
})}
label={__('Custom')}
onClick={() => setShowMore(true)}
/>
</div>
{showMore && (
<div className="section">
<FormField
autoFocus
name="tip-input"
label={
<React.Fragment>
{'Custom support amount'}{' '}
{isMobile && (
<I18nMessage tokens={{ lbc_balance: <CreditAmount badge={false} amount={balance} /> }}>
(%lbc_balance% available)
</I18nMessage>
)}
</React.Fragment>
}
className="form-field--price-amount"
error={tipError}
min="0"
step="any"
type="number"
placeholder="1.23"
onChange={event => handleSupportPriceChange(event)}
/>
</div>
)}
<div className="section__actions">
<Button
autoFocus
icon={isSupport ? undefined : ICONS.SUPPORT}
button="primary"
type="submit"
disabled={isPending || tipError || !tipAmount}
label={
isSupport
? __('Send Revokable Support')
: __('Send a %amount% Tip', { amount: tipAmount ? `${tipAmount} LBC` : '' })
}
/>
{!claimIsMine && (
<FormField
name="toggle-is-support"
type="checkbox"
label={__('Make this support permanent')}
checked={!isSupport}
onChange={() => setIsSupport(!isSupport)}
/>
)}
</div>
{DEFAULT_TIP_AMOUNTS.some(val => val > balance) && (
<div className="section">
<Button button="link" label={__('Buy More LBC')} />
</div>
)}
</>
}
/>
)}
</Form>
);
}
export default WalletSendTip;
// <Button
// button="primary"
// type="submit"
// label={__('Confirm')}
// disabled={isPending || tipError || !tipAmount}
// />;
// <div className="section__actions">
// <FormField
// autoFocus
// name="tip-input"
// label={
// <React.Fragment>
// {label}{' '}
// {isMobile && (
// <I18nMessage tokens={{ lbc_balance: <CreditAmount badge={false} amount={balance} /> }}>
// (%lbc_balance% available)
// </I18nMessage>
// )}
// </React.Fragment>
// }
// className="form-field--price-amount"
// error={tipError}
// min="0"
// step="any"
// type="number"
// placeholder="1.23"
// onChange={event => handleSupportPriceChange(event)}
// />
// <FormField
// name="toggle-is-support"
// type="checkbox"
// label={__('Send this as a tip instead')}
// checked={!isSupport}
// onChange={() => setIsSupport(!isSupport)}
// />
// </div>;

View file

@ -3,6 +3,7 @@ import React from 'react';
import { Modal } from 'modal/modal';
import Button from 'component/button';
import * as PAGES from 'constants/pages';
import Card from 'component/common/card';
type Props = {
closeModal: () => void,
@ -22,42 +23,48 @@ const ModalFirstSubscription = (props: Props) => {
location: { pathname },
} = props;
const title = __('You Followed Your First Channel!');
return (
<Modal type="custom" isOpen contentLabel="Subscriptions 101" title={__('Subscriptions 101')}>
<div className="section__subtitle">
<p>{__('Awesome! You just subscribed to your first channel.')}{' '}
{ user && user.primary_email ? (
<Modal type="card" isOpen contentLabel={title}>
<Card
title={title}
subtitle={
<>
{__('Awesome! You just followed your first first channel.')}{' '}
{user && user.primary_email
? __('You will receive notifications related to new content.')
: __('Sign in with lbry.tv to receive notifications about new content.')}
</>
}
actions={
<div className="section__actions">
<Button button="primary" onClick={closeModal} label={__('Got it')} />
<React.Fragment>
{__('You will receive notifications related to new content.')}
{user && user.primary_email ? (
<React.Fragment>
<Button
button="link"
href={`https://lbry.com/list/edit/${accessToken}`}
label={__('Update email preferences')}
/>
</React.Fragment>
) : (
<React.Fragment>
<Button
button="link"
onClick={() => {
closeModal();
history.push(`/$/${PAGES.AUTH}?redirect=${pathname}`);
}}
label={__('Sign in')}
/>
</React.Fragment>
)}
</React.Fragment>
) : (
<React.Fragment>
{ __('Sign in with lbry.tv to receive notifications about new content.')}
</React.Fragment>
)}
</p>
</div>
<div className="section__actions">
<Button button="primary" onClick={closeModal} label={__('Got it')} />
<React.Fragment>
{user && user.primary_email ? (
<React.Fragment>
<Button
button="link"
href={`https://lbry.com/list/edit/${accessToken}`}
label={__('Update email preferences')}
/>
</React.Fragment>
) : (
<React.Fragment>
<Button button="link" onClick={() => {
closeModal()
history.push(`/$/${PAGES.AUTH}?redirect=${pathname}`);
}} label={__('Sign in')} />
</React.Fragment>
)}
</React.Fragment>
</div>
</div>
}
/>
</Modal>
);
};

View file

@ -142,10 +142,16 @@ svg + .button__label,
}
}
.button-toggle__label {
@extend label;
display: inline-block;
margin-top: -4px;
}
.button-group {
display: flex;
.button:first-child {
.button:first-child:not(:only-child) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-right: 1px solid var(--color-button-border);

View file

@ -8,7 +8,7 @@
font-family: sans-serif;
display: block;
position: absolute;
z-index: 2;
z-index: 10000;
font-size: var(--font-body);
}
@ -22,7 +22,7 @@
[data-reach-menu-item] {
display: block;
z-index: 2;
z-index: 10000;
&:focus {
box-shadow: none;