diff --git a/flow-typed/Comment.js b/flow-typed/Comment.js index 36c970fac9..ecc30f9ea1 100644 --- a/flow-typed/Comment.js +++ b/flow-typed/Comment.js @@ -73,6 +73,8 @@ declare type PerChannelSettings = { comments_enabled?: boolean, min_tip_amount_comment?: number, min_tip_amount_super_chat?: number, + min_usdc_tip_amount_comment?: number, + min_usdc_tip_amount_super_chat?: number, slow_mode_min_gap?: number, time_since_first_comment?: number, livestream_chat_members_only?: boolean, @@ -345,6 +347,8 @@ declare type SettingsResponse = { comments_enabled: boolean, min_tip_amount_comment: number, min_tip_amount_super_chat: number, + min_usdc_tip_amount_comment: number, + min_usdc_tip_amount_super_chat: number, slow_mode_min_gap: number, curse_jar_amount: number, filters_enabled?: boolean, @@ -360,6 +364,8 @@ declare type UpdateSettingsParams = { comments_enabled?: boolean, min_tip_amount_comment?: number, min_tip_amount_super_chat?: number, + min_usdc_tip_amount_comment?: number, + min_usdc_tip_amount_super_chat?: number, slow_mode_min_gap?: number, time_since_first_comment?: number, livestream_chat_members_only?: boolean, diff --git a/ui/component/commentCreate/internal/extra-contents.jsx b/ui/component/commentCreate/internal/extra-contents.jsx index e74c275809..cc8b6acdfb 100644 --- a/ui/component/commentCreate/internal/extra-contents.jsx +++ b/ui/component/commentCreate/internal/extra-contents.jsx @@ -30,19 +30,31 @@ type HelpTextProps = { minAmount: number, minSuper: number, minTip: number, + minUSDCAmount: number, + minUSDCSuper: number, + minUSDCTip: number, }; export const HelpText = (helpTextProps: HelpTextProps) => { - const { deletedComment, minAmount, minSuper, minTip } = helpTextProps; + const { deletedComment, minAmount, minSuper, minTip, minUSDCAmount, minUSDCSuper, minUSDCTip } = helpTextProps; return ( <> {deletedComment &&
{__('This comment has been deleted.')}
} - {!!minAmount && ( + {(!!minAmount || !!minUSDCAmount) && (
- }}> - {minTip ? 'Comment min: %lbc%' : minSuper ? 'HyperChat min: %lbc%' : ''} + , + minUSDCAmount, + lbc: , + }} + > + {(minTip || minUSDCTip ? 'Comment min: ' : minSuper || minUSDCSuper ? 'HyperChat min: ' : '') + + (minTip || minSuper ? '%lbc%' : '') + + (minAmount && minUSDCAmount ? ' or ' : '') + + (minUSDCTip || minUSDCSuper ? '%usdc%' : '')} = minAmount; + const minUSDCAmount = minUSDCTip || minUSDCSuper || 0; + const minAmountMet = + (activeTab !== TAB_LBC && activeTab !== TAB_FIAT && !minTip && !minUSDCTip) || + (activeTab === TAB_LBC && tipAmount >= minAmount) || + (activeTab === TAB_FIAT && tipAmount >= minUSDCAmount); const stickerPrice = selectedSticker && selectedSticker.price; const tipSelectorError = tipError || disableReviewButton; const fiatIcon = STRIPE.CURRENCY[preferredCurrency].icon; const minAmountRef = React.useRef(minAmount); minAmountRef.current = minAmount; + const minUSDCAmountRef = React.useRef(minUSDCAmount); + minUSDCAmountRef.current = minUSDCAmount; const addEmoteToComment = React.useCallback((emote: string) => { setCommentValue((prev) => prev + (prev && prev.charAt(prev.length - 1) !== ' ' ? ` ${emote} ` : `${emote} `)); @@ -389,7 +397,13 @@ export function CommentCreate(props: Props) { const lockedMinAmount = minAmount; // value during closure. const currentMinAmount = minAmountRef.current; // value from latest doFetchCreatorSettings(). - if (lockedMinAmount !== currentMinAmount) { + const lockedMinUSDCAmount = minUSDCAmount; // value during closure. + const currentMinUSDCAmount = minUSDCAmountRef.current; // value from latest doFetchCreatorSettings(). + + if ( + (activeTab === TAB_LBC && lockedMinAmount !== currentMinAmount) || + (activeTab === TAB_FIAT && lockedMinUSDCAmount !== currentMinUSDCAmount) + ) { doToast({ message: __('The creator just updated the minimum setting. Please revise or double-check your tip amount.'), isError: true, @@ -524,7 +538,8 @@ export function CommentCreate(props: Props) { environment, sticker: !!stickerValue, is_protected: Boolean(isLivestreamChatMembersOnly || areCommentsMembersOnly), - amount: activeTab === TAB_LBC ? tipAmount * 100000000 : undefined, + amount: activeTab === TAB_LBC || activeTab === TAB_FIAT ? tipAmount : undefined, + currency: activeTab === TAB_LBC ? 'LBC' : activeTab === TAB_FIAT ? 'USDC' : undefined, dry_run: dryRun, }) .then((res) => { @@ -764,8 +779,16 @@ export function CommentCreate(props: Props) { isLivestream={isLivestream} label={} noticeLabel={ - isMobile && ( - + (isMobile || isLivestream) && ( + ) } name={isReply ? 'create__reply' : 'create__comment'} @@ -860,7 +883,7 @@ export function CommentCreate(props: Props) { /> ) : ( (!isMobile || selectedSticker) && - (!minTip || claimIsMine) && ( + ((!minTip && !minUSDCTip) || claimIsMine) && (
)}
diff --git a/ui/component/settingsRow/view.jsx b/ui/component/settingsRow/view.jsx index 916c6a79c4..ba670aa68f 100644 --- a/ui/component/settingsRow/view.jsx +++ b/ui/component/settingsRow/view.jsx @@ -7,6 +7,7 @@ import classnames from 'classnames'; type Props = { title: string, subtitle?: string, + warning?: string, multirow?: boolean, // Displays the Value widget(s) below the Label instead of on the right. useVerticalSeparator?: boolean, // Show a separator line between Label and Value. Useful when there are multiple Values. disabled?: boolean, @@ -16,7 +17,8 @@ type Props = { }; export default function SettingsRow(props: Props) { - const { title, subtitle, multirow, useVerticalSeparator, disabled, highlighted, membersOnly, children } = props; + const { title, subtitle, warning, multirow, useVerticalSeparator, disabled, highlighted, membersOnly, children } = + props; return (
{subtitle &&

{subtitle}

}
+ {warning &&
{warning}
}
debounce(pushSlowModeMin, 1000), []); // eslint-disable-line react-hooks/exhaustive-deps const pushMinTipDebounced = React.useMemo(() => debounce(pushMinTip, 1000), []); // eslint-disable-line react-hooks/exhaustive-deps const pushMinSuperDebounced = React.useMemo(() => debounce(pushMinSuper, 1000), []); // eslint-disable-line react-hooks/exhaustive-deps + const pushMinUSDCTipDebounced = React.useMemo(() => debounce(pushMinUSDCTip, 1000), []); // eslint-disable-line react-hooks/exhaustive-deps + const pushMinUSDCSuperDebounced = React.useMemo(() => debounce(pushMinUSDCSuper, 1000), []); // eslint-disable-line react-hooks/exhaustive-deps // ************************************************************************** // ************************************************************************** @@ -103,9 +116,12 @@ export default function CreatorSettingsTab(props: Props) { if (fullSync) { setCommentsEnabled(settings.comments_enabled || false); - setMinTip(settings.min_tip_amount_comment || 0); - setMinSuper(settings.min_tip_amount_super_chat || 0); - setSlowModeMin(settings.slow_mode_min_gap || 0); + focusedField.current !== FIELD_NAMES.MIN_TIP && setMinTip(settings.min_tip_amount_comment || 0); + focusedField.current !== FIELD_NAMES.MIN_SUPER && setMinSuper(settings.min_tip_amount_super_chat || 0); + focusedField.current !== FIELD_NAMES.MIN_USDC_TIP && setMinUSDCTip(settings.min_usdc_tip_amount_comment || 0); + focusedField.current !== FIELD_NAMES.MIN_USDC_SUPER && + setMinUSDCSuper(settings.min_usdc_tip_amount_super_chat || 0); + focusedField.current !== FIELD_NAMES.SLOW_MODE && setSlowModeMin(settings.slow_mode_min_gap || 0); setMinChannelAgeMinutes(settings.time_since_first_comment || 0); setCommentsMembersOnly(settings.comments_members_only); setLivestreamChatMembersOnly(settings.livestream_chat_members_only || false); @@ -120,6 +136,12 @@ export default function CreatorSettingsTab(props: Props) { if (settings.min_tip_amount_super_chat !== undefined) { setMinSuper(settings.min_tip_amount_super_chat); } + if (settings.min_usdc_tip_amount_comment !== undefined) { + setMinUSDCTip(settings.min_usdc_tip_amount_comment); + } + if (settings.min_usdc_tip_amount_super_chat !== undefined) { + setMinUSDCSuper(settings.min_usdc_tip_amount_super_chat); + } if (settings.slow_mode_min_gap !== undefined) { setSlowModeMin(settings.slow_mode_min_gap); } @@ -152,10 +174,18 @@ export default function CreatorSettingsTab(props: Props) { updateCreatorSettings(activeChannelClaim, { min_tip_amount_comment: value }); } + function pushMinUSDCTip(value: number, activeChannelClaim: ChannelClaim) { + updateCreatorSettings(activeChannelClaim, { min_usdc_tip_amount_comment: value }); + } + function pushMinSuper(value: number, activeChannelClaim: ChannelClaim) { updateCreatorSettings(activeChannelClaim, { min_tip_amount_super_chat: value }); } + function pushMinUSDCSuper(value: number, activeChannelClaim: ChannelClaim) { + updateCreatorSettings(activeChannelClaim, { min_usdc_tip_amount_super_chat: value }); + } + function parseModUri(uri) { try { return parseURI(uri); @@ -320,13 +350,14 @@ export default function CreatorSettingsTab(props: Props) { min={0} step={1} type="number" - placeholder="1" + placeholder="0" value={slowModeMin} onChange={(e) => { const value = parseInt(e.target.value); setSlowModeMin(value); - pushSlowModeMinDebounced(value, activeChannelClaim); + pushSlowModeMinDebounced(value || 0, activeChannelClaim); }} + onFocus={() => (focusedField.current = FIELD_NAMES.SLOW_MODE)} onBlur={() => setLastUpdated(Date.now())} /> @@ -371,6 +402,14 @@ export default function CreatorSettingsTab(props: Props) { }}>Minimum %lbc% tip amount for comments } subtitle={__(HELP.MIN_TIP)} + warning={ + !minTip && + !!minUSDCTip && ( + }}> + All %lbc% comments allowed, please set a value if you want to limit this. + + ) + } > { const newMinTip = parseFloat(e.target.value); setMinTip(newMinTip); - pushMinTipDebounced(newMinTip, activeChannelClaim); - if (newMinTip !== 0 && minSuper !== 0) { - setMinSuper(0); - pushMinSuperDebounced(0, activeChannelClaim); + pushMinTipDebounced(newMinTip || 0, activeChannelClaim); + if (newMinTip !== 0) { + if (minSuper !== 0) { + setMinSuper(0); + pushMinSuperDebounced(0, activeChannelClaim); + } + if (minUSDCSuper !== 0) { + setMinUSDCSuper(0); + pushMinUSDCSuperDebounced(0, activeChannelClaim); + } } }} + onFocus={() => (focusedField.current = FIELD_NAMES.MIN_TIP)} onBlur={() => setLastUpdated(Date.now())} /> @@ -408,6 +454,14 @@ export default function CreatorSettingsTab(props: Props) { )} } + warning={ + !minSuper && + !!minUSDCSuper && ( + }}> + All %lbc% hyperchats allowed, please set a value if you want to limit this. + + ) + } > { const newMinSuper = parseFloat(e.target.value); setMinSuper(newMinSuper); - pushMinSuperDebounced(newMinSuper, activeChannelClaim); + pushMinSuperDebounced(newMinSuper || 0, activeChannelClaim); + }} + onFocus={() => (focusedField.current = FIELD_NAMES.MIN_SUPER)} + onBlur={() => setLastUpdated(Date.now())} + /> + + + Minimum %lbc% tip amount for comments} + subtitle={__(HELP.MIN_TIP)} + warning={ + !!minTip && + !minUSDCTip && ( + + All %lbc% comments allowed, please set a value if you want to limit this. + + ) + } + > + { + const newMinUSDCTip = parseFloat(e.target.value); + setMinUSDCTip(newMinUSDCTip); + pushMinUSDCTipDebounced(newMinUSDCTip || 0, activeChannelClaim); + if (newMinUSDCTip !== 0) { + if (minSuper !== 0) { + setMinSuper(0); + pushMinSuperDebounced(0, activeChannelClaim); + } + if (minUSDCSuper !== 0) { + setMinUSDCSuper(0); + pushMinUSDCSuperDebounced(0, activeChannelClaim); + } + } + }} + onFocus={() => (focusedField.current = FIELD_NAMES.MIN_USDC_TIP)} + onBlur={() => setLastUpdated(Date.now())} + /> + + + Minimum %lbc% tip amount for hyperchats} + subtitle={ + <> + {__(HELP.MIN_SUPER)} + {minTip !== 0 && ( +

+ {__(HELP.MIN_SUPER_OFF)} +

+ )} + + } + warning={ + !!minSuper && + !minUSDCSuper && ( + + All %lbc% hyperchats allowed, please set a value if you want to limit this. + + ) + } + > + { + const newMinUSDCSuper = parseFloat(e.target.value); + setMinUSDCSuper(newMinUSDCSuper); + pushMinUSDCSuperDebounced(newMinUSDCSuper || 0, activeChannelClaim); }} + onFocus={() => (focusedField.current = FIELD_NAMES.MIN_USDC_SUPER)} onBlur={() => setLastUpdated(Date.now())} />
diff --git a/ui/scss/component/_card.scss b/ui/scss/component/_card.scss index f03cbe422b..d907affe21 100644 --- a/ui/scss/component/_card.scss +++ b/ui/scss/component/_card.scss @@ -694,8 +694,8 @@ .icon { width: 12px; height: 12px; - margin-bottom: -1px; - margin-right: var(--spacing-xs); + margin-bottom: 2px; + margin-right: var(--spacing-xxxxxs); } .button__label { margin-top: 0; diff --git a/ui/scss/component/_comments.scss b/ui/scss/component/_comments.scss index 558fb53395..2538ce192e 100644 --- a/ui/scss/component/_comments.scss +++ b/ui/scss/component/_comments.scss @@ -370,7 +370,7 @@ $thumbnailWidthSmall: 2rem; font-weight: var(--font-weight-bold); color: white; svg { - margin-bottom: -1.5px; + margin-bottom: 2px; } } diff --git a/ui/scss/component/section.scss b/ui/scss/component/section.scss index 98523372f7..efdd2ef4ba 100644 --- a/ui/scss/component/section.scss +++ b/ui/scss/component/section.scss @@ -328,6 +328,11 @@ padding: var(--spacing-s); border-bottom: 1px solid var(--color-border); + .help--warning { + margin-top: 0px; + margin-bottom: 0px; + } + &:first-child, &:only-child { border-top: none;