lbry-desktop/ui/component/walletTipAmountSelector/view.jsx

311 lines
10 KiB
React
Raw Permalink Normal View History

2021-04-23 21:59:48 +02:00
// @flow
import * as ICONS from 'constants/icons';
import * as PAGES from 'constants/pages';
import React from 'react';
import Button from 'component/button';
import { FormField } from 'component/common/form';
import { MINIMUM_PUBLISH_BID } from 'constants/claim';
import CreditAmount from 'component/common/credit-amount';
import I18nMessage from 'component/i18nMessage';
import classnames from 'classnames';
import usePersistedState from 'effects/use-persisted-state';
import WalletSpendableBalanceHelp from 'component/walletSpendableBalanceHelp';
import { Lbryio } from 'lbryinc';
import { getStripeEnvironment } from 'util/stripe';
let stripeEnvironment = getStripeEnvironment();
2021-04-23 21:59:48 +02:00
const DEFAULT_TIP_AMOUNTS = [1, 5, 25, 100];
const TAB_FIAT = 'TabFiat';
const TAB_LBC = 'TabLBC';
2021-04-23 21:59:48 +02:00
type Props = {
balance: number,
amount: number,
onChange: (number) => void,
isAuthenticated: boolean,
claim: StreamClaim,
uri: string,
onTipErrorChange: (string) => void,
2021-07-17 18:08:53 +02:00
activeTab: string,
2021-07-22 23:29:57 +02:00
shouldDisableReviewButton: (boolean) => void,
2021-04-23 21:59:48 +02:00
};
function WalletTipAmountSelector(props: Props) {
const { balance, amount, onChange, activeTab, claim, onTipErrorChange, shouldDisableReviewButton } = props;
2021-04-23 21:59:48 +02:00
const [useCustomTip, setUseCustomTip] = usePersistedState('comment-support:useCustomTip', false);
const [tipError, setTipError] = React.useState();
const [canReceiveFiatTip, setCanReceiveFiatTip] = React.useState(); // dont persist because it needs to be calc'd per creator
const [hasCardSaved, setHasSavedCard] = usePersistedState('comment-support:hasCardSaved', false);
// if it's fiat but there's no card saved OR the creator can't receive fiat tips
2021-07-22 23:29:57 +02:00
const shouldDisableFiatSelectors = activeTab === TAB_FIAT && (!hasCardSaved || !canReceiveFiatTip);
/**
* whether tip amount selection/review functionality should be disabled
* @param [amount] LBC amount (optional)
* @returns {boolean}
*/
function shouldDisableAmountSelector(amount) {
// if it's LBC but the balance isn't enough, or fiat conditions met
// $FlowFixMe
return (amount > balance && activeTab !== TAB_FIAT) || shouldDisableFiatSelectors;
}
shouldDisableReviewButton(shouldDisableFiatSelectors);
// setup variables for tip API
let channelClaimId, tipChannelName;
// if there is a signing channel it's on a file
if (claim.signing_channel) {
channelClaimId = claim.signing_channel.claim_id;
tipChannelName = claim.signing_channel.name;
// otherwise it's on the channel page
} else {
channelClaimId = claim.claim_id;
tipChannelName = claim.name;
}
// check if creator has a payment method saved
React.useEffect(() => {
if (stripeEnvironment) {
Lbryio.call(
'customer',
'status',
{
environment: stripeEnvironment,
},
'post'
).then((customerStatusResponse) => {
const defaultPaymentMethodId =
customerStatusResponse.Customer &&
customerStatusResponse.Customer.invoice_settings &&
customerStatusResponse.Customer.invoice_settings.default_payment_method &&
customerStatusResponse.Customer.invoice_settings.default_payment_method.id;
setHasSavedCard(Boolean(defaultPaymentMethodId));
});
}
}, [stripeEnvironment]);
//
2021-04-23 21:59:48 +02:00
React.useEffect(() => {
if (stripeEnvironment) {
Lbryio.call(
'account',
'check',
{
channel_claim_id: channelClaimId,
channel_name: tipChannelName,
environment: stripeEnvironment,
},
'post'
)
.then((accountCheckResponse) => {
if (accountCheckResponse === true && canReceiveFiatTip !== true) {
setCanReceiveFiatTip(true);
}
})
.catch(function (error) {
// console.log(error);
});
}
}, [stripeEnvironment]);
React.useEffect(() => {
// setHasSavedCard(false);
// setCanReceiveFiatTip(true);
2021-07-22 23:29:57 +02:00
let regexp,
tipError = '';
2021-04-23 21:59:48 +02:00
if (amount === 0) {
2021-04-23 21:59:48 +02:00
tipError = __('Amount must be a positive number');
} else if (!amount || typeof amount !== 'number') {
tipError = __('Amount must be a number');
2021-04-23 21:59:48 +02:00
}
// if it's not fiat, aka it's boost or lbc tip
else if (activeTab !== TAB_FIAT) {
regexp = RegExp(/^(\d*([.]\d{0,8})?)$/);
const validTipInput = regexp.test(String(amount));
if (!validTipInput) {
tipError = __('Amount must have no more than 8 decimal places');
} else if (amount === balance) {
tipError = __('Please decrease the amount to account for transaction fees');
} else if (amount > balance) {
tipError = __('Not enough Credits');
} else if (amount < MINIMUM_PUBLISH_BID) {
tipError = __('Amount must be higher');
}
// if tip fiat tab
} else {
regexp = RegExp(/^(\d*([.]\d{0,2})?)$/);
const validTipInput = regexp.test(String(amount));
if (!validTipInput) {
tipError = __('Amount must have no more than 2 decimal places');
} else if (amount < 1) {
tipError = __('Amount must be at least one dollar');
} else if (amount > 1000) {
tipError = __('Amount cannot be over 1000 dollars');
}
}
2021-04-23 21:59:48 +02:00
setTipError(tipError);
onTipErrorChange(tipError);
}, [amount, balance, setTipError, activeTab]);
2021-04-23 21:59:48 +02:00
// parse number as float and sets it in the parent component
2021-04-23 21:59:48 +02:00
function handleCustomPriceChange(amount: number) {
const tipAmount = parseFloat(amount);
2021-04-23 21:59:48 +02:00
onChange(tipAmount);
}
return (
<>
<div className="section">
{DEFAULT_TIP_AMOUNTS.map((defaultAmount) => (
<Button
key={defaultAmount}
disabled={shouldDisableAmountSelector(defaultAmount)}
2021-04-23 21:59:48 +02:00
button="alt"
className={classnames('button-toggle button-toggle--expandformobile', {
'button-toggle--active': defaultAmount === amount && !useCustomTip,
2021-04-23 21:59:48 +02:00
'button-toggle--disabled': amount > balance,
})}
label={defaultAmount}
icon={activeTab === TAB_LBC ? ICONS.LBC : ICONS.FINANCE}
2021-04-23 21:59:48 +02:00
onClick={() => {
handleCustomPriceChange(defaultAmount);
setUseCustomTip(false);
}}
/>
))}
<Button
button="alt"
disabled={activeTab === TAB_FIAT && (!hasCardSaved || !canReceiveFiatTip)}
2021-04-23 21:59:48 +02:00
className={classnames('button-toggle button-toggle--expandformobile', {
'button-toggle--active': useCustomTip,
2021-04-23 21:59:48 +02:00
})}
icon={activeTab === TAB_LBC ? ICONS.LBC : ICONS.FINANCE}
2021-04-23 21:59:48 +02:00
label={__('Custom')}
onClick={() => setUseCustomTip(true)}
/>
{activeTab === TAB_LBC && DEFAULT_TIP_AMOUNTS.some((val) => val > balance) && (
2021-04-23 21:59:48 +02:00
<Button
button="secondary"
className="button-toggle-group-action"
icon={ICONS.BUY}
title={__('Buy or swap more LBRY Credits')}
2021-04-23 21:59:48 +02:00
navigate={`/$/${PAGES.BUY}`}
/>
)}
</div>
2021-07-17 18:08:53 +02:00
{useCustomTip && activeTab === TAB_FIAT && !hasCardSaved && (
<>
<div className="help">
<span className="help--spendable">
<Button navigate={`/$/${PAGES.SETTINGS_STRIPE_CARD}`} label={__('Add a Card ')} button="link" /> To{' '}
{__('Tip Creators')}
2021-07-17 18:08:53 +02:00
</span>
</div>
</>
)}
{/* has card saved but cant creator cant receive tips */}
2021-07-17 18:08:53 +02:00
{useCustomTip && activeTab === TAB_FIAT && hasCardSaved && !canReceiveFiatTip && (
<>
<div className="help">
<span className="help--spendable">Only creators that verify cash accounts can receive tips</span>
2021-07-17 18:08:53 +02:00
</div>
</>
)}
{/* has card saved but cant creator cant receive tips */}
2021-07-17 18:08:53 +02:00
{useCustomTip && activeTab === TAB_FIAT && hasCardSaved && canReceiveFiatTip && (
<>
<div className="help">
<span className="help--spendable">Send a tip directly from your attached card</span>
</div>
</>
)}
{/* custom number input form */}
2021-04-23 21:59:48 +02:00
{useCustomTip && (
<div className="comment__tip-input">
<FormField
autoFocus
name="tip-input"
disabled={shouldDisableAmountSelector()}
2021-07-17 18:08:53 +02:00
label={
activeTab === TAB_LBC ? (
<React.Fragment>
{__('Custom support amount')}{' '}
<I18nMessage tokens={{ lbc_balance: <CreditAmount precision={4} amount={balance} /> }}>
(%lbc_balance% available)
</I18nMessage>
</React.Fragment>
) : (
<></>
)
// <>
// <div className="">
// <span className="help--spendable">Send a tip directly from your attached card</span>
// </div>
// </>
2021-04-23 21:59:48 +02:00
}
error={tipError}
min="0"
step="any"
type="number"
placeholder="1.23"
value={amount}
onChange={(event) => handleCustomPriceChange(event.target.value)}
/>
</div>
)}
{/* lbc tab */}
{activeTab === TAB_LBC && <WalletSpendableBalanceHelp />}
{/* fiat button but no card saved */}
2021-07-17 18:08:53 +02:00
{!useCustomTip && activeTab === TAB_FIAT && !hasCardSaved && (
<>
<div className="help">
<span className="help--spendable">
<Button navigate={`/$/${PAGES.SETTINGS_STRIPE_CARD}`} label={__('Add a Card ')} button="link" /> To{' '}
{__('Tip Creators')}
2021-07-17 18:08:53 +02:00
</span>
</div>
</>
)}
{/* has card saved but cant creator cant receive tips */}
2021-07-17 18:08:53 +02:00
{!useCustomTip && activeTab === TAB_FIAT && hasCardSaved && !canReceiveFiatTip && (
<>
<div className="help">
<span className="help--spendable">Only creators that verify cash accounts can receive tips</span>
2021-07-17 18:08:53 +02:00
</div>
</>
)}
{/* has card saved but cant creator cant receive tips */}
2021-07-17 18:08:53 +02:00
{!useCustomTip && activeTab === TAB_FIAT && hasCardSaved && canReceiveFiatTip && (
<>
<div className="help">
<span className="help--spendable">Send a tip directly from your attached card</span>
</div>
</>
)}
2021-04-23 21:59:48 +02:00
</>
);
}
export default WalletTipAmountSelector;