Creator: Enable "min tips" and "min hyperchat" #6824

Merged
infinite-persistence merged 6 commits from ip/min.tips into master 2021-08-13 04:41:02 +02:00
2 changed files with 111 additions and 21 deletions
Showing only changes of commit 0ebb9420ef - Show all commits

View file

@ -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>
); );

View file

@ -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")
}
}