0b41fc041a
* [New Feature] Comment Emotes (#125) * Refactor form-field * Create new Emote Menu * Add Emotes * Add Emote Selector and Emote Comment creation ability * Fix and Split CSS * [New Feature] Stickers (#131) * Refactor filePrice * Refactor Wallet Tip Components * Add backend sticker support for comments * Add stickers * Refactor commentCreate * Add Sticker Selector and sticker comment creation * Add stickers display to comments and hyperchats * Fix wrong checks for total Super Chats * Stickers/emojis fall out / improvements (#220) * Fix error logs * Improve LBC sticker flow/clarity * Show inline error if custom sticker amount below min * Sort emojis alphabetically * Improve loading of Images * Improve quality and display of emojis and fix CSS * Display both USD and LBC prices * Default to LBC tip if creator can't receive USD * Don't clear text-field after sticker is sent * Refactor notification component * Handle notifications * Don't show profile pic on sticker livestream comments * Change Sticker icon * Fix wording and number rounding * Fix blurring emojis * Disable non functional emote buttons * new Stickers! (#248) * Add new stickers (#347) * Fix cancel sending sticker (#447) * Refactor scrollbar CSS for portal components outside of main Refactor channelMention suggestions into new textareaSuggestions component Install @mui/material packages Move channel mentioning to use @mui/Autocomplete combobox without search functionality Add support for suggesting Emotes while typing ':' Improve label to display matching term Add back and improved support for searching while mentioning Add support for suggesting emojis Fix non concatenated strings Add key to groups and options Fix dispatch props Fix Popper positioning to be consistent Fix and Improve searching Add back support for Winning Uri Filter default emojis with the same name as emotes Remove unused topSuggestion component Fix text color on darkmode Fix livestream updating state from both websocket and reducer and causing double of the same comments to appear Fix blur and focus commentCreate events Fix no name after @ error * desktop tweaks Co-authored-by: saltrafael <76502841+saltrafael@users.noreply.github.com> Co-authored-by: Thomas Zarebczan <tzarebczan@users.noreply.github.com> Co-authored-by: Rafael <rafael.saes@odysee.com>
318 lines
11 KiB
JavaScript
318 lines
11 KiB
JavaScript
// @flow
|
|
import 'scss/component/_wallet-tip-selector.scss';
|
|
import { FormField } from 'component/common/form';
|
|
import { Lbryio } from 'lbryinc';
|
|
import { MINIMUM_PUBLISH_BID } from 'constants/claim';
|
|
import { useIsMobile } from 'effects/use-screensize';
|
|
import * as ICONS from 'constants/icons';
|
|
import * as PAGES from 'constants/pages';
|
|
import Button from 'component/button';
|
|
import classnames from 'classnames';
|
|
import React from 'react';
|
|
import usePersistedState from 'effects/use-persisted-state';
|
|
import WalletSpendableBalanceHelp from 'component/walletSpendableBalanceHelp';
|
|
|
|
import { getStripeEnvironment } from 'util/stripe';
|
|
const stripeEnvironment = getStripeEnvironment();
|
|
|
|
const DEFAULT_TIP_AMOUNTS = [1, 5, 25, 100];
|
|
const TAB_FIAT = 'TabFiat';
|
|
const TAB_LBC = 'TabLBC';
|
|
|
|
type Props = {
|
|
activeTab: string,
|
|
amount: number,
|
|
balance: number,
|
|
claim: StreamClaim,
|
|
convertedAmount?: number,
|
|
customTipAmount?: number,
|
|
exchangeRate?: any,
|
|
fiatConversion?: boolean,
|
|
tipError: boolean,
|
|
tipError: string,
|
|
uri: string,
|
|
onChange: (number) => void,
|
|
setConvertedAmount?: (number) => void,
|
|
setDisableSubmitButton: (boolean) => void,
|
|
setTipError: (any) => void,
|
|
};
|
|
|
|
function WalletTipAmountSelector(props: Props) {
|
|
const {
|
|
activeTab,
|
|
amount,
|
|
balance,
|
|
claim,
|
|
convertedAmount,
|
|
customTipAmount,
|
|
exchangeRate,
|
|
fiatConversion,
|
|
tipError,
|
|
onChange,
|
|
setConvertedAmount,
|
|
setDisableSubmitButton,
|
|
setTipError,
|
|
} = props;
|
|
|
|
const isMobile = useIsMobile();
|
|
const [useCustomTip, setUseCustomTip] = usePersistedState('comment-support:useCustomTip', true);
|
|
const [hasCardSaved, setHasSavedCard] = usePersistedState('comment-support:hasCardSaved', false);
|
|
const [canReceiveFiatTip, setCanReceiveFiatTip] = React.useState(); // dont persist because it needs to be calc'd per creator
|
|
|
|
const convertToTwoDecimalsOrMore = (number: number, decimals: number = 2) =>
|
|
Number((Math.round(number * 10 ** decimals) / 10 ** decimals).toFixed(decimals));
|
|
|
|
const tipAmountsToDisplay =
|
|
customTipAmount && fiatConversion && activeTab === TAB_FIAT
|
|
? [customTipAmount]
|
|
: customTipAmount && exchangeRate
|
|
? [convertToTwoDecimalsOrMore(customTipAmount / exchangeRate)]
|
|
: DEFAULT_TIP_AMOUNTS;
|
|
|
|
// if it's fiat but there's no card saved OR the creator can't receive fiat tips
|
|
const shouldDisableFiatSelectors = activeTab === TAB_FIAT && (!hasCardSaved || !canReceiveFiatTip);
|
|
if (setDisableSubmitButton) setDisableSubmitButton(shouldDisableFiatSelectors);
|
|
|
|
// setup variables for tip API
|
|
const channelClaimId = claim.signing_channel ? claim.signing_channel.claim_id : claim.claim_id;
|
|
const tipChannelName = claim.signing_channel ? claim.signing_channel.name : claim.name;
|
|
|
|
/**
|
|
* whether tip amount selection/review functionality should be disabled
|
|
* @param [amount] LBC amount (optional)
|
|
* @returns {boolean}
|
|
*/
|
|
function shouldDisableAmountSelector(amount: number) {
|
|
// if it's LBC but the balance isn't enough, or fiat conditions met
|
|
// $FlowFixMe
|
|
return (
|
|
((amount > balance || balance === 0) && activeTab !== TAB_FIAT) ||
|
|
shouldDisableFiatSelectors ||
|
|
(customTipAmount && fiatConversion && activeTab !== TAB_FIAT && exchangeRate
|
|
? convertToTwoDecimalsOrMore(amount * exchangeRate) < customTipAmount
|
|
: customTipAmount && amount < customTipAmount)
|
|
);
|
|
}
|
|
|
|
// parse number as float and sets it in the parent component
|
|
function handleCustomPriceChange(amount: number) {
|
|
const tipAmountValue = parseFloat(amount);
|
|
onChange(tipAmountValue);
|
|
if (fiatConversion && exchangeRate && setConvertedAmount && convertedAmount !== tipAmountValue * exchangeRate) {
|
|
setConvertedAmount(tipAmountValue * exchangeRate);
|
|
}
|
|
}
|
|
|
|
React.useEffect(() => {
|
|
if (setConvertedAmount && exchangeRate && (!convertedAmount || convertedAmount !== amount * exchangeRate)) {
|
|
setConvertedAmount(amount * exchangeRate);
|
|
}
|
|
}, [amount, convertedAmount, exchangeRate, setConvertedAmount]);
|
|
|
|
// check if user has a payment method saved
|
|
// REMOVE
|
|
React.useEffect(() => {
|
|
if (!stripeEnvironment) return;
|
|
|
|
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));
|
|
});
|
|
}, [setHasSavedCard]);
|
|
|
|
// check if creator has a tip account saved REMOVE
|
|
React.useEffect(() => {
|
|
if (!stripeEnvironment) return;
|
|
|
|
Lbryio.call(
|
|
'account',
|
|
'check',
|
|
{
|
|
channel_claim_id: channelClaimId,
|
|
channel_name: tipChannelName,
|
|
environment: stripeEnvironment,
|
|
},
|
|
'post'
|
|
)
|
|
.then((accountCheckResponse) => {
|
|
if (accountCheckResponse === true && canReceiveFiatTip !== true) {
|
|
setCanReceiveFiatTip(true);
|
|
}
|
|
})
|
|
.catch(() => {});
|
|
}, [canReceiveFiatTip, channelClaimId, tipChannelName]);
|
|
|
|
React.useEffect(() => {
|
|
let regexp;
|
|
|
|
if (amount === 0) {
|
|
setTipError(__('Amount cannot be zero.'));
|
|
} else if (!amount || typeof amount !== 'number') {
|
|
setTipError(__('Amount must be a number.'));
|
|
} else {
|
|
// if it's not fiat, aka it's boost or lbc tip
|
|
if (activeTab !== TAB_FIAT) {
|
|
regexp = RegExp(/^(\d*([.]\d{0,8})?)$/);
|
|
const validTipInput = regexp.test(String(amount));
|
|
|
|
if (!validTipInput) {
|
|
setTipError(__('Amount must have no more than 8 decimal places'));
|
|
} else if (amount === balance) {
|
|
setTipError(__('Please decrease the amount to account for transaction fees'));
|
|
} else if (amount > balance || balance === 0) {
|
|
setTipError(__('Not enough Credits'));
|
|
} else if (amount < MINIMUM_PUBLISH_BID) {
|
|
setTipError(__('Amount must be higher'));
|
|
} else if (
|
|
convertedAmount &&
|
|
exchangeRate &&
|
|
customTipAmount &&
|
|
amount < convertToTwoDecimalsOrMore(customTipAmount / exchangeRate)
|
|
) {
|
|
regexp = RegExp(/^(\d*([.]\d{0,2})?)$/);
|
|
const validCustomTipInput = regexp.test(String(amount));
|
|
|
|
if (validCustomTipInput) {
|
|
setTipError(
|
|
__('Amount of $%input_amount% LBC in USB is lower than price of $%price_amount%', {
|
|
input_amount: convertToTwoDecimalsOrMore(convertedAmount, 4),
|
|
price_amount: convertToTwoDecimalsOrMore(customTipAmount),
|
|
})
|
|
);
|
|
} else {
|
|
setTipError(__('Amount must have no more than 2 decimal places'));
|
|
}
|
|
} else {
|
|
setTipError(false);
|
|
}
|
|
// if tip fiat tab REMOVE
|
|
} else {
|
|
regexp = RegExp(/^(\d*([.]\d{0,2})?)$/);
|
|
const validTipInput = regexp.test(String(amount));
|
|
|
|
if (!validTipInput) {
|
|
setTipError(__('Amount must have no more than 2 decimal places'));
|
|
} else if (amount < 1) {
|
|
setTipError(__('Amount must be at least one dollar'));
|
|
} else if (amount > 1000) {
|
|
setTipError(__('Amount cannot be over 1000 dollars'));
|
|
} else if (customTipAmount && amount < customTipAmount) {
|
|
setTipError(
|
|
__('Amount is lower than price of $%price_amount%', {
|
|
price_amount: convertToTwoDecimalsOrMore(customTipAmount),
|
|
})
|
|
);
|
|
} else {
|
|
setTipError(false);
|
|
}
|
|
}
|
|
}
|
|
}, [activeTab, amount, balance, convertedAmount, customTipAmount, exchangeRate, setTipError]);
|
|
|
|
const getHelpMessage = (helpMessage: any) => <div className="help">{helpMessage}</div>;
|
|
|
|
return (
|
|
<>
|
|
<div className="section">
|
|
{tipAmountsToDisplay &&
|
|
tipAmountsToDisplay.map((defaultAmount) => (
|
|
<Button
|
|
key={defaultAmount}
|
|
disabled={shouldDisableAmountSelector(defaultAmount)}
|
|
button="alt"
|
|
className={classnames('button-toggle button-toggle--expandformobile', {
|
|
'button-toggle--active':
|
|
convertToTwoDecimalsOrMore(defaultAmount) === convertToTwoDecimalsOrMore(amount) && !useCustomTip,
|
|
'button-toggle--disabled': amount > balance,
|
|
})}
|
|
label={defaultAmount}
|
|
icon={activeTab === TAB_LBC ? ICONS.LBC : ICONS.FINANCE}
|
|
onClick={() => {
|
|
handleCustomPriceChange(defaultAmount);
|
|
setUseCustomTip(false);
|
|
}}
|
|
/>
|
|
))}
|
|
|
|
<Button
|
|
button="alt"
|
|
disabled={shouldDisableFiatSelectors}
|
|
className={classnames('button-toggle button-toggle--expandformobile', {
|
|
'button-toggle--active': useCustomTip,
|
|
})}
|
|
icon={activeTab === TAB_LBC ? ICONS.LBC : ICONS.FINANCE}
|
|
label={__('Custom')}
|
|
onClick={() => setUseCustomTip(true)}
|
|
/>
|
|
{activeTab === TAB_LBC && DEFAULT_TIP_AMOUNTS.some((val) => val > balance) && (
|
|
<Button
|
|
button="secondary"
|
|
className="button-toggle-group-action"
|
|
icon={ICONS.BUY}
|
|
title={__('Buy or swap more LBRY Credits')}
|
|
navigate={`/$/${PAGES.BUY}`}
|
|
/>
|
|
)}
|
|
</div>
|
|
|
|
{customTipAmount &&
|
|
fiatConversion &&
|
|
activeTab !== TAB_FIAT &&
|
|
getHelpMessage(
|
|
__('This support is priced in $USD.') +
|
|
(convertedAmount
|
|
? ' ' +
|
|
__('The current exchange rate for the submitted LBC amount is ~ $%exchange_amount%.', {
|
|
exchange_amount: convertToTwoDecimalsOrMore(convertedAmount),
|
|
})
|
|
: '')
|
|
)}
|
|
|
|
{/* custom number input form */}
|
|
{useCustomTip && (
|
|
<div className="walletTipSelector__input">
|
|
<FormField
|
|
autoFocus={!isMobile}
|
|
name="tip-input"
|
|
disabled={!customTipAmount && shouldDisableAmountSelector(0)}
|
|
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 />}
|
|
{activeTab === TAB_FIAT &&
|
|
(!hasCardSaved
|
|
? getHelpMessage(
|
|
<>
|
|
<Button navigate={`/$/${PAGES.SETTINGS_STRIPE_CARD}`} label={__('Add a Card')} button="link" />
|
|
{' ' + __('To Tip Creators')}
|
|
</>
|
|
)
|
|
: !canReceiveFiatTip
|
|
? getHelpMessage(__('Only creators that verify cash accounts can receive tips'))
|
|
: getHelpMessage(__('Send a tip directly from your attached card')))}
|
|
</>
|
|
);
|
|
}
|
|
|
|
export default WalletTipAmountSelector;
|