Creator: Enable "min tips" and "min hyperchat" #6824
2 changed files with 111 additions and 21 deletions
|
@ -6,6 +6,7 @@ import * as ICONS from 'constants/icons';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { FormField, Form } from 'component/common/form';
|
import { FormField, Form } from 'component/common/form';
|
||||||
|
import Icon from 'component/common/icon';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import SelectChannel from 'component/selectChannel';
|
import SelectChannel from 'component/selectChannel';
|
||||||
import usePersistedState from 'effects/use-persisted-state';
|
import usePersistedState from 'effects/use-persisted-state';
|
||||||
|
@ -14,10 +15,11 @@ import { useHistory } from 'react-router';
|
||||||
import WalletTipAmountSelector from 'component/walletTipAmountSelector';
|
import WalletTipAmountSelector from 'component/walletTipAmountSelector';
|
||||||
import CreditAmount from 'component/common/credit-amount';
|
import CreditAmount from 'component/common/credit-amount';
|
||||||
import ChannelThumbnail from 'component/channelThumbnail';
|
import ChannelThumbnail from 'component/channelThumbnail';
|
||||||
|
import I18nMessage from 'component/i18nMessage';
|
||||||
import UriIndicator from 'component/uriIndicator';
|
import UriIndicator from 'component/uriIndicator';
|
||||||
import Empty from 'component/common/empty';
|
import Empty from 'component/common/empty';
|
||||||
import { Lbryio } from 'lbryinc';
|
|
||||||
import { getChannelIdFromClaim } from 'util/claim';
|
import { getChannelIdFromClaim } from 'util/claim';
|
||||||
|
import { Lbryio } from 'lbryinc';
|
||||||
|
|
||||||
let stripeEnvironment = 'test';
|
let stripeEnvironment = 'test';
|
||||||
// if the key contains pk_live it's a live key
|
// if the key contains pk_live it's a live key
|
||||||
|
@ -50,7 +52,7 @@ type Props = {
|
||||||
sendTip: ({}, (any) => void, (any) => void) => void,
|
sendTip: ({}, (any) => void, (any) => void) => void,
|
||||||
doToast: ({ message: string }) => void,
|
doToast: ({ message: string }) => void,
|
||||||
disabled: boolean,
|
disabled: boolean,
|
||||||
doFetchCreatorSettings: (channelId: string) => void,
|
doFetchCreatorSettings: (channelId: string) => Promise<any>,
|
||||||
settingsByChannelId: { [channelId: string]: PerChannelSettings },
|
settingsByChannelId: { [channelId: string]: PerChannelSettings },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -97,6 +99,38 @@ export function CommentCreate(props: Props) {
|
||||||
const [shouldDisableReviewButton, setShouldDisableReviewButton] = React.useState();
|
const [shouldDisableReviewButton, setShouldDisableReviewButton] = React.useState();
|
||||||
const channelId = getChannelIdFromClaim(claim);
|
const channelId = getChannelIdFromClaim(claim);
|
||||||
const channelSettings = channelId ? settingsByChannelId[channelId] : undefined;
|
const channelSettings = channelId ? settingsByChannelId[channelId] : undefined;
|
||||||
|
const minSuper = (channelSettings && channelSettings.min_tip_amount_super_chat) || 0;
|
||||||
|
const minTip = (channelSettings && channelSettings.min_tip_amount_comment) || 0;
|
||||||
|
const minAmount = minTip || minSuper || 0;
|
||||||
|
const minAmountMet = minAmount === 0 || tipAmount >= minAmount;
|
||||||
|
|
||||||
|
const minAmountRef = React.useRef(minAmount);
|
||||||
|
minAmountRef.current = minAmount;
|
||||||
|
|
||||||
|
const MinAmountNotice = minAmount ? (
|
||||||
|
<div className="help--notice comment--min-amount-notice">
|
||||||
|
<I18nMessage tokens={{ lbc: <CreditAmount noFormat amount={minAmount} /> }}>
|
||||||
|
{minTip ? 'Comment min: %lbc%' : minSuper ? 'HyperChat min: %lbc%' : ''}
|
||||||
|
</I18nMessage>
|
||||||
|
<Icon
|
||||||
|
customTooltipText={
|
||||||
|
minTip
|
||||||
|
? __('This channel requires a minimum tip for each comment.')
|
||||||
|
: minSuper
|
||||||
|
? __('This channel requires a minimum amount for HyperChats to be visible.')
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
className="icon--help"
|
||||||
|
icon={ICONS.HELP}
|
||||||
|
tooltip
|
||||||
|
size={16}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// Functions
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
function handleCommentChange(event) {
|
function handleCommentChange(event) {
|
||||||
let commentValue;
|
let commentValue;
|
||||||
|
@ -136,6 +170,14 @@ export function CommentCreate(props: Props) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!channelId) {
|
||||||
|
doToast({
|
||||||
|
message: __('Unable to verify channel settings. Try refreshing the page.'),
|
||||||
|
isError: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// if comment post didn't work, but tip was already made, try again to create comment
|
// if comment post didn't work, but tip was already made, try again to create comment
|
||||||
if (commentFailure && tipAmount === successTip.tipAmount) {
|
if (commentFailure && tipAmount === successTip.tipAmount) {
|
||||||
handleCreateComment(successTip.txid);
|
handleCreateComment(successTip.txid);
|
||||||
|
@ -144,6 +186,29 @@ export function CommentCreate(props: Props) {
|
||||||
setSuccessTip({ txid: undefined, tipAmount: undefined });
|
setSuccessTip({ txid: undefined, tipAmount: undefined });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// !! Beware of stale closure when editing the then-block, including doSubmitTip().
|
||||||
|
doFetchCreatorSettings(channelId).then(() => {
|
||||||
|
const lockedMinAmount = minAmount; // value during closure.
|
||||||
|
const currentMinAmount = minAmountRef.current; // value from latest doFetchCreatorSettings().
|
||||||
|
|
||||||
|
if (lockedMinAmount !== currentMinAmount) {
|
||||||
|
doToast({
|
||||||
|
message: __('The creator just updated the minimum setting. Please revise or double-check your tip amount.'),
|
||||||
|
isError: true,
|
||||||
|
});
|
||||||
|
setIsReviewingSupportComment(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
doSubmitTip();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function doSubmitTip() {
|
||||||
|
if (!activeChannelClaim) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
amount: tipAmount,
|
amount: tipAmount,
|
||||||
claim_id: claimId,
|
claim_id: claimId,
|
||||||
|
@ -273,6 +338,12 @@ export function CommentCreate(props: Props) {
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
setCommentFailure(true);
|
setCommentFailure(true);
|
||||||
|
|
||||||
|
if (channelId) {
|
||||||
|
// It could be that the creator added a minimum tip setting.
|
||||||
|
// Manually update for now until a websocket msg is available.
|
||||||
|
doFetchCreatorSettings(channelId);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,12 +351,21 @@ export function CommentCreate(props: Props) {
|
||||||
setAdvancedEditor(!advancedEditor);
|
setAdvancedEditor(!advancedEditor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// Effects
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// Fetch channel constraints if not already.
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!channelSettings && channelId) {
|
if (!channelSettings && channelId) {
|
||||||
doFetchCreatorSettings(channelId);
|
doFetchCreatorSettings(channelId);
|
||||||
}
|
}
|
||||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// Render
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
if (channelSettings && !channelSettings.comments_enabled) {
|
if (channelSettings && !channelSettings.comments_enabled) {
|
||||||
return <Empty padded text={__('This channel has disabled comments on their page.')} />;
|
return <Empty padded text={__('This channel has disabled comments on their page.')} />;
|
||||||
}
|
}
|
||||||
|
@ -342,7 +422,7 @@ export function CommentCreate(props: Props) {
|
||||||
<Button
|
<Button
|
||||||
autoFocus
|
autoFocus
|
||||||
button="primary"
|
button="primary"
|
||||||
disabled={disabled}
|
disabled={disabled || !minAmountMet}
|
||||||
label={
|
label={
|
||||||
isSubmitting
|
isSubmitting
|
||||||
? __('Sending...')
|
? __('Sending...')
|
||||||
|
@ -358,6 +438,7 @@ export function CommentCreate(props: Props) {
|
||||||
label={__('Cancel')}
|
label={__('Cancel')}
|
||||||
onClick={() => setIsReviewingSupportComment(false)}
|
onClick={() => setIsReviewingSupportComment(false)}
|
||||||
/>
|
/>
|
||||||
|
{MinAmountNotice}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -411,7 +492,7 @@ export function CommentCreate(props: Props) {
|
||||||
{isSupportComment ? (
|
{isSupportComment ? (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
disabled={disabled || tipError || shouldDisableReviewButton}
|
disabled={disabled || tipError || shouldDisableReviewButton || !minAmountMet}
|
||||||
type="button"
|
type="button"
|
||||||
button="primary"
|
button="primary"
|
||||||
icon={activeTab === TAB_LBC ? ICONS.LBC : ICONS.FINANCE}
|
icon={activeTab === TAB_LBC ? ICONS.LBC : ICONS.FINANCE}
|
||||||
|
@ -423,6 +504,7 @@ export function CommentCreate(props: Props) {
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
{(!minTip || claimIsMine) && (
|
||||||
<Button
|
<Button
|
||||||
ref={buttonRef}
|
ref={buttonRef}
|
||||||
button="primary"
|
button="primary"
|
||||||
|
@ -439,6 +521,7 @@ export function CommentCreate(props: Props) {
|
||||||
}
|
}
|
||||||
requiresAuth={IS_WEB}
|
requiresAuth={IS_WEB}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
{!claimIsMine && (
|
{!claimIsMine && (
|
||||||
<Button
|
<Button
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
@ -463,7 +546,7 @@ export function CommentCreate(props: Props) {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isReply && (
|
{isReply && !minTip && (
|
||||||
<Button
|
<Button
|
||||||
button="link"
|
button="link"
|
||||||
label={__('Cancel')}
|
label={__('Cancel')}
|
||||||
|
@ -476,6 +559,7 @@ export function CommentCreate(props: Props) {
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{MinAmountNotice}
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
|
|
|
@ -438,3 +438,9 @@ $thumbnailWidthSmall: 1rem;
|
||||||
.comment--blocked {
|
.comment--blocked {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comment--min-amount-notice {
|
||||||
|
.icon {
|
||||||
|
margin-bottom: -3px; // TODO fix few instances of these (find "-2px")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue