// @flow import { Form } from 'component/common/form'; import LbcMessage from 'component/common/lbc-message'; import { Lbryio } from 'lbryinc'; import { parseURI } from 'util/lbryURI'; import * as ICONS from 'constants/icons'; import * as PAGES from 'constants/pages'; import Button from 'component/button'; import Card from 'component/common/card'; import ChannelSelector from 'component/channelSelector'; import classnames from 'classnames'; import I18nMessage from 'component/i18nMessage'; import LbcSymbol from 'component/common/lbc-symbol'; import React from 'react'; import usePersistedState from 'effects/use-persisted-state'; import WalletTipAmountSelector from 'component/walletTipAmountSelector'; import { getStripeEnvironment } from 'util/stripe'; const stripeEnvironment = getStripeEnvironment(); const TAB_BOOST = 'TabBoost'; const TAB_FIAT = 'TabFiat'; const TAB_LBC = 'TabLBC'; type SupportParams = { amount: number, claim_id: string, channel_id?: string }; type TipParams = { tipAmount: number, tipChannelName: string, channelClaimId: string }; type UserParams = { activeChannelName: ?string, activeChannelId: ?string }; type Props = { activeChannelId?: string, activeChannelName?: string, balance: number, claimId?: string, claimType?: string, channelClaimId?: string, tipChannelName?: string, claimIsMine: boolean, fetchingChannels: boolean, incognito: boolean, instantTipEnabled: boolean, instantTipMax: { amount: number, currency: string }, isPending: boolean, isSupport: boolean, title: string, uri: string, isTipOnly?: boolean, hasSelectedTab?: string, customText?: string, doHideModal: () => void, doSendCashTip: ( TipParams, anonymous: boolean, UserParams, claimId: string, stripe: ?string, preferredCurrency: string, ?(any) => void ) => string, doSendTip: (SupportParams, boolean) => void, // function that comes from lbry-redux setAmount?: (number) => void, preferredCurrency: string, }; export default function WalletSendTip(props: Props) { const { activeChannelId, activeChannelName, balance, claimId, claimType, channelClaimId, tipChannelName, claimIsMine, fetchingChannels, incognito, instantTipEnabled, instantTipMax, isPending, title, uri, isTipOnly, hasSelectedTab, customText, doHideModal, doSendCashTip, doSendTip, setAmount, preferredCurrency, } = props; /** WHAT TAB TO SHOW **/ // if it's your content, we show boost, otherwise default is LBC const defaultTabToShow = claimIsMine ? TAB_BOOST : TAB_FIAT; // loads the default tab if nothing else is there yet const [persistentTab, setPersistentTab] = usePersistedState('send-tip-modal', defaultTabToShow); const [activeTab, setActiveTab] = React.useState(persistentTab); const [hasSelected, setSelected] = React.useState(false); /** STATE **/ const [tipAmount, setTipAmount] = usePersistedState('comment-support:customTip', 1.0); const [isOnConfirmationPage, setConfirmationPage] = React.useState(false); const [tipError, setTipError] = React.useState(); const [disableSubmitButton, setDisableSubmitButton] = React.useState(); /** CONSTS **/ const claimTypeText = getClaimTypeText(); const isSupport = claimIsMine || activeTab === TAB_BOOST; // text for modal header const titleText = isSupport ? __(claimIsMine ? 'Boost Your %claimTypeText%' : 'Boost This %claimTypeText%', { claimTypeText }) : __('Tip This %claimTypeText%', { claimTypeText }); let channelName; try { ({ channelName } = parseURI(uri)); } catch (e) {} // icon to use or explainer text to show per tab let explainerText = ''; switch (activeTab) { case TAB_BOOST: explainerText = __( 'This refundable boost will improve the discoverability of this %claimTypeText% while active.', { claimTypeText } ); break; case TAB_FIAT: case TAB_LBC: explainerText = __('Show this channel your appreciation by sending a donation.'); break; } /** FUNCTIONS **/ function getClaimTypeText() { switch (claimType) { case 'stream': return __('Content'); case 'channel': return __('Channel'); case 'repost': return __('Repost'); case 'collection': return __('List'); default: return __('Claim'); } } // make call to the backend to send lbc or fiat function sendSupportOrConfirm(instantTipMaxAmount = null) { if (!isOnConfirmationPage && (!instantTipMaxAmount || !instantTipEnabled || tipAmount > instantTipMaxAmount)) { setConfirmationPage(true); } else { const supportParams: SupportParams = { amount: tipAmount, claim_id: claimId || '', channel_id: (!incognito && activeChannelId) || undefined, }; // send tip/boost doSendTip(supportParams, isSupport); doHideModal(); } } // when the form button is clicked function handleSubmit() { if (!tipAmount || !claimId) return; if (setAmount) { setAmount(tipAmount); doHideModal(); return; } // send an instant tip (no need to go to an exchange first) if (instantTipEnabled && activeTab !== TAB_FIAT) { if (instantTipMax.currency === 'LBC') { sendSupportOrConfirm(instantTipMax.amount); } else { // Need to convert currency of instant purchase maximum before trying to send support Lbryio.getExchangeRates().then(({ LBC_USD }) => sendSupportOrConfirm(instantTipMax.amount / LBC_USD)); } // sending fiat tip } else if (activeTab === TAB_FIAT) { if (!isOnConfirmationPage) { setConfirmationPage(true); } else { const tipParams: TipParams = { tipAmount, tipChannelName: tipChannelName || '', channelClaimId: channelClaimId || '', }; const userParams: UserParams = { activeChannelName, activeChannelId }; // hit backend to send tip doSendCashTip( tipParams, !activeChannelId || incognito, userParams, claimId, stripeEnvironment, preferredCurrency ); doHideModal(); } // if it's a boost (?) } else { sendSupportOrConfirm(); } } function buildButtonText() { // test if frontend will show up as isNan function isNan(tipAmount) { // testing for NaN ES5 style https://stackoverflow.com/a/35912757/3973137 // also sometimes it's returned as a string // eslint-disable-next-line return tipAmount !== tipAmount || tipAmount === 'NaN'; } function convertToTwoDecimals(number) { return (Math.round(number * 100) / 100).toFixed(2); } const amountToShow = activeTab === TAB_FIAT ? convertToTwoDecimals(tipAmount) : tipAmount; // if it's a valid number display it, otherwise do an empty string const displayAmount = !isNan(tipAmount) ? amountToShow : ''; // build button text based on tab switch (activeTab) { case TAB_BOOST: return titleText; case TAB_FIAT: return __('Send a %amount% tip', { amount: `${fiatSymbolToUse}${displayAmount}` }); case TAB_LBC: return __('Send a %amount% tip', { amount: `${displayAmount} LBC` }); default: return titleText; } } React.useEffect(() => { if (!hasSelected && hasSelectedTab && activeTab !== hasSelectedTab) { setActiveTab(claimIsMine ? TAB_BOOST : hasSelectedTab); setSelected(true); } }, [activeTab, claimIsMine, hasSelected, hasSelectedTab, setActiveTab]); React.useEffect(() => { if (!hasSelectedTab && activeTab !== hasSelectedTab) { setPersistentTab(claimIsMine ? TAB_BOOST : activeTab); } }, [activeTab, claimIsMine, hasSelectedTab, setPersistentTab]); /** RENDER **/ const tabButtonProps = { isOnConfirmationPage, activeTab, setActiveTab }; let fiatIconToUse = ICONS.FINANCE; let fiatSymbolToUse = '$'; if (preferredCurrency === 'EUR') { fiatIconToUse = ICONS.EURO; fiatSymbolToUse = '€'; } return (
); } type TabButtonProps = { icon: string, label: string, name: string, isOnConfirmationPage: boolean, activeTab: string, setActiveTab: (string) => void, }; const TabSwitchButton = (tabButtonProps: TabButtonProps) => { const { icon, label, name, isOnConfirmationPage, activeTab, setActiveTab } = tabButtonProps; return (