Improve comment selectors
This commit is contained in:
parent
2b56ca8599
commit
c758c59066
8 changed files with 121 additions and 61 deletions
|
@ -9,20 +9,26 @@ import React from 'react';
|
|||
import { Tabs, TabList, Tab, TabPanels, TabPanel } from 'component/common/tabs';
|
||||
import { FREE_GLOBAL_STICKERS, PAID_GLOBAL_STICKERS } from 'constants/stickers';
|
||||
|
||||
export const SELECTOR_TABS = {
|
||||
EMOJI: 0,
|
||||
STICKER: 1,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
claimIsMine?: boolean,
|
||||
openTab?: number,
|
||||
addEmoteToComment: (string) => void,
|
||||
handleSelectSticker: (any) => void,
|
||||
closeSelector?: () => void,
|
||||
};
|
||||
|
||||
export default function CommentSelectors(props: Props) {
|
||||
const { claimIsMine, addEmoteToComment, handleSelectSticker, closeSelector } = props;
|
||||
const { claimIsMine, openTab, addEmoteToComment, handleSelectSticker, closeSelector } = props;
|
||||
|
||||
const tabProps = { closeSelector };
|
||||
|
||||
return (
|
||||
<Tabs>
|
||||
<Tabs index={openTab}>
|
||||
<TabList className="tabs__list--comment-selector">
|
||||
<Tab>{__('Emojis')}</Tab>
|
||||
<Tab>{__('Stickers')}</Tab>
|
||||
|
|
|
@ -50,10 +50,6 @@ export const StickerActionButton = (stickerButtonProps: StickerButtonProps) => {
|
|||
const { isReviewingStickerComment, ...buttonProps } = stickerButtonProps;
|
||||
|
||||
return (
|
||||
<Button
|
||||
{...buttonProps}
|
||||
title={__('Stickers')}
|
||||
label={isReviewingStickerComment ? __('Different Sticker') : undefined}
|
||||
/>
|
||||
<Button {...buttonProps} title={__('Stickers')} label={isReviewingStickerComment ? __('Change') : undefined} />
|
||||
);
|
||||
};
|
||||
|
|
|
@ -15,10 +15,12 @@ type Props = {
|
|||
tipAmount: number,
|
||||
activeTab: string,
|
||||
message: string,
|
||||
isReviewingStickerComment?: boolean,
|
||||
stickerPreviewComponent?: any,
|
||||
};
|
||||
|
||||
export const TipReviewBox = (props: Props) => {
|
||||
const { activeChannelUrl, tipAmount, activeTab, message } = props;
|
||||
const { activeChannelUrl, tipAmount, activeTab, message, isReviewingStickerComment, stickerPreviewComponent } = props;
|
||||
|
||||
return (
|
||||
<div className="commentCreate__supportCommentPreview">
|
||||
|
@ -29,11 +31,18 @@ export const TipReviewBox = (props: Props) => {
|
|||
size={activeTab === TAB_LBC ? 18 : 2}
|
||||
/>
|
||||
|
||||
<ChannelThumbnail xsmall uri={activeChannelUrl} />
|
||||
<div className="commentCreate__supportCommentBody">
|
||||
<UriIndicator uri={activeChannelUrl} link />
|
||||
<div>{message}</div>
|
||||
</div>
|
||||
{isReviewingStickerComment ? (
|
||||
stickerPreviewComponent
|
||||
) : (
|
||||
<>
|
||||
<ChannelThumbnail xsmall uri={activeChannelUrl} />
|
||||
|
||||
<div className="commentCreate__supportCommentBody">
|
||||
<UriIndicator uri={activeChannelUrl} link />
|
||||
<div>{message}</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ import * as PAGES from 'constants/pages';
|
|||
import * as MODALS from 'constants/modal_types';
|
||||
import Button from 'component/button';
|
||||
import classnames from 'classnames';
|
||||
import CommentSelectors from './comment-selectors';
|
||||
import CommentSelectors, { SELECTOR_TABS } from './comment-selectors';
|
||||
import React from 'react';
|
||||
import type { ElementRef } from 'react';
|
||||
import usePersistedState from 'effects/use-persisted-state';
|
||||
|
@ -34,7 +34,6 @@ type TipParams = { tipAmount: number, tipChannelName: string, channelClaimId: st
|
|||
type UserParams = { activeChannelName: ?string, activeChannelId: ?string };
|
||||
|
||||
type Props = {
|
||||
activeChannel: string,
|
||||
activeChannelClaimId?: string,
|
||||
activeChannelName?: string,
|
||||
activeChannelUrl?: string,
|
||||
|
@ -106,6 +105,7 @@ export function CommentCreate(props: Props) {
|
|||
|
||||
const formFieldRef: ElementRef<any> = React.useRef();
|
||||
const buttonRef: ElementRef<any> = React.useRef();
|
||||
const slimInputButtonRef: ElementRef<any> = React.useRef();
|
||||
|
||||
const {
|
||||
push,
|
||||
|
@ -126,7 +126,7 @@ export function CommentCreate(props: Props) {
|
|||
const [activeTab, setActiveTab] = React.useState();
|
||||
const [tipError, setTipError] = React.useState();
|
||||
const [deletedComment, setDeletedComment] = React.useState(false);
|
||||
const [showSelectors, setShowSelectors] = React.useState(false);
|
||||
const [showSelectors, setShowSelectors] = React.useState({ tab: undefined, open: false });
|
||||
const [disableReviewButton, setDisableReviewButton] = React.useState();
|
||||
const [exchangeRate, setExchangeRate] = React.useState();
|
||||
const [canReceiveFiatTip, setCanReceiveFiatTip] = React.useState(undefined);
|
||||
|
@ -180,9 +180,15 @@ export function CommentCreate(props: Props) {
|
|||
}
|
||||
|
||||
function handleStickerComment() {
|
||||
if (selectedSticker) setReviewingStickerComment(false);
|
||||
if (selectedSticker) {
|
||||
setReviewingStickerComment(false);
|
||||
setSelectedSticker(undefined);
|
||||
setShowSelectors({ tab: SELECTOR_TABS.STICKER, open: true });
|
||||
} else {
|
||||
setShowSelectors({ tab: showSelectors.tab || undefined, open: !showSelectors.open });
|
||||
}
|
||||
|
||||
setTipSelector(false);
|
||||
setShowSelectors(!showSelectors);
|
||||
}
|
||||
|
||||
function handleSelectSticker(sticker: any) {
|
||||
|
@ -190,7 +196,7 @@ export function CommentCreate(props: Props) {
|
|||
setSelectedSticker(sticker);
|
||||
setReviewingStickerComment(true);
|
||||
setTipAmount(sticker.price || 0);
|
||||
setShowSelectors(false);
|
||||
setShowSelectors({ tab: showSelectors.tab || undefined, open: false });
|
||||
|
||||
if (sticker.price && sticker.price > 0) {
|
||||
setActiveTab(canReceiveFiatTip ? TAB_FIAT : TAB_LBC);
|
||||
|
@ -209,7 +215,7 @@ export function CommentCreate(props: Props) {
|
|||
|
||||
if (stickerPrice) {
|
||||
setReviewingStickerComment(false);
|
||||
setShowSelectors(false);
|
||||
setShowSelectors({ tab: showSelectors.tab || undefined, open: false });
|
||||
setSelectedSticker(null);
|
||||
}
|
||||
}
|
||||
|
@ -362,7 +368,7 @@ export function CommentCreate(props: Props) {
|
|||
|
||||
setSelectedSticker(null);
|
||||
setReviewingStickerComment(false);
|
||||
setShowSelectors(false);
|
||||
setShowSelectors({ tab: showSelectors.tab || undefined, open: false });
|
||||
setTipSelector(false);
|
||||
}
|
||||
|
||||
|
@ -377,6 +383,13 @@ export function CommentCreate(props: Props) {
|
|||
}
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
// change sticker selection
|
||||
React.useEffect(() => {
|
||||
if (isMobile && showSelectors.tab && slimInputButtonRef && slimInputButtonRef.current) {
|
||||
slimInputButtonRef.current.click();
|
||||
}
|
||||
}, [isMobile, showSelectors.tab]);
|
||||
|
||||
// Notifications: Fetch top-level comments to identify if it has been deleted and can reply to it
|
||||
React.useEffect(() => {
|
||||
if (shouldFetchComment && doCommentById) {
|
||||
|
@ -394,7 +407,7 @@ export function CommentCreate(props: Props) {
|
|||
// Stickers: Check if creator has a tip account saved (on selector so that if a paid sticker is selected,
|
||||
// it defaults to LBC tip instead of USD)
|
||||
React.useEffect(() => {
|
||||
if (!stripeEnvironment || !showSelectors || canReceiveFiatTip !== undefined || !tipChannelName) return;
|
||||
if (!stripeEnvironment || canReceiveFiatTip !== undefined || !tipChannelName) return;
|
||||
|
||||
Lbryio.call(
|
||||
'account',
|
||||
|
@ -414,7 +427,7 @@ export function CommentCreate(props: Props) {
|
|||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}, [canReceiveFiatTip, channelClaimId, showSelectors, tipChannelName]);
|
||||
}, [canReceiveFiatTip, channelClaimId, tipChannelName]);
|
||||
|
||||
// Handle keyboard shortcut comment creation
|
||||
React.useEffect(() => {
|
||||
|
@ -486,7 +499,12 @@ export function CommentCreate(props: Props) {
|
|||
);
|
||||
}
|
||||
|
||||
const commentSelectorsProps = { claimIsMine, addEmoteToComment, handleSelectSticker };
|
||||
const commentSelectorsProps = {
|
||||
claimIsMine,
|
||||
addEmoteToComment,
|
||||
handleSelectSticker,
|
||||
openTab: showSelectors.tab || undefined,
|
||||
};
|
||||
const submitButtonProps = { button: 'primary', type: 'submit', requiresAuth: true };
|
||||
const actionButtonProps = { button: 'alt' };
|
||||
const tipButtonProps = {
|
||||
|
@ -497,6 +515,12 @@ export function CommentCreate(props: Props) {
|
|||
onClick: handleSelectTipComment,
|
||||
};
|
||||
const cancelButtonProps = { button: 'link', label: __('Cancel') };
|
||||
const stickerReviewProps = {
|
||||
activeChannelUrl,
|
||||
src: selectedSticker ? selectedSticker.url : '',
|
||||
price: selectedSticker ? selectedSticker.price : 0,
|
||||
exchangeRate,
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
|
@ -507,16 +531,7 @@ export function CommentCreate(props: Props) {
|
|||
'commentCreate--bottom': bottom,
|
||||
})}
|
||||
>
|
||||
{selectedSticker ? (
|
||||
activeChannelUrl && (
|
||||
<StickerReviewBox
|
||||
activeChannelUrl={activeChannelUrl}
|
||||
src={selectedSticker.url}
|
||||
price={selectedSticker.price || 0}
|
||||
exchangeRate={exchangeRate}
|
||||
/>
|
||||
)
|
||||
) : isReviewingSupportComment ? (
|
||||
{isReviewingSupportComment ? (
|
||||
activeChannelUrl &&
|
||||
activeTab && (
|
||||
<TipReviewBox
|
||||
|
@ -524,12 +539,19 @@ export function CommentCreate(props: Props) {
|
|||
tipAmount={tipAmount}
|
||||
activeTab={activeTab}
|
||||
message={commentValue}
|
||||
isReviewingStickerComment={isReviewingStickerComment}
|
||||
stickerPreviewComponent={selectedSticker && <StickerReviewBox {...stickerReviewProps} />}
|
||||
/>
|
||||
)
|
||||
) : selectedSticker ? (
|
||||
activeChannelUrl && <StickerReviewBox {...stickerReviewProps} />
|
||||
) : (
|
||||
<>
|
||||
{!isMobile && showSelectors && (
|
||||
<CommentSelectors {...commentSelectorsProps} closeSelector={() => setShowSelectors(false)} />
|
||||
{!isMobile && showSelectors.open && (
|
||||
<CommentSelectors
|
||||
{...commentSelectorsProps}
|
||||
closeSelector={() => setShowSelectors({ tab: showSelectors.tab || undefined, open: false })}
|
||||
/>
|
||||
)}
|
||||
|
||||
<FormField
|
||||
|
@ -548,7 +570,8 @@ export function CommentCreate(props: Props) {
|
|||
onChange={(e) => setCommentValue(SIMPLE_SITE || !advancedEditor || isReply ? e.target.value : e)}
|
||||
handleTip={(isLBC) => handleSelectTipComment(isLBC ? TAB_LBC : TAB_FIAT)}
|
||||
handleSubmit={handleCreateComment}
|
||||
slimInput={isMobile}
|
||||
slimInput={isMobile && uri} // "uri": make sure it's on a file page
|
||||
slimInputButtonRef={slimInputButtonRef}
|
||||
commentSelectorsProps={commentSelectorsProps}
|
||||
submitButtonRef={buttonRef}
|
||||
setShowSelectors={setShowSelectors}
|
||||
|
@ -631,21 +654,23 @@ export function CommentCreate(props: Props) {
|
|||
)
|
||||
)}
|
||||
|
||||
{!isMobile && (
|
||||
<StickerActionButton
|
||||
{...actionButtonProps}
|
||||
isReviewingStickerComment={isReviewingStickerComment}
|
||||
icon={ICONS.STICKER}
|
||||
onClick={handleStickerComment}
|
||||
/>
|
||||
)}
|
||||
|
||||
{(!isMobile || isReviewingStickerComment) && !supportDisabled && (
|
||||
{(!isMobile || isReviewingStickerComment) && (
|
||||
<>
|
||||
<TipActionButton {...tipButtonProps} name={__('Credits')} icon={ICONS.LBC} tab={TAB_LBC} />
|
||||
<StickerActionButton
|
||||
{...actionButtonProps}
|
||||
isReviewingStickerComment={isReviewingStickerComment}
|
||||
icon={ICONS.STICKER}
|
||||
onClick={handleStickerComment}
|
||||
/>
|
||||
|
||||
{stripeEnvironment && (
|
||||
<TipActionButton {...tipButtonProps} name={__('Cash')} icon={ICONS.FINANCE} tab={TAB_FIAT} />
|
||||
{!supportDisabled && !claimIsMine && (
|
||||
<>
|
||||
<TipActionButton {...tipButtonProps} name={__('Credits')} icon={ICONS.LBC} tab={TAB_LBC} />
|
||||
|
||||
{stripeEnvironment && (
|
||||
<TipActionButton {...tipButtonProps} name={__('Cash')} icon={ICONS.FINANCE} tab={TAB_FIAT} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -45,13 +45,14 @@ type Props = {
|
|||
type?: string,
|
||||
value?: string | number,
|
||||
slimInput?: boolean,
|
||||
slimInputButtonRef?: any,
|
||||
commentSelectorsProps?: any,
|
||||
showSelectors?: boolean,
|
||||
showSelectors?: any,
|
||||
submitButtonRef?: any,
|
||||
tipModalOpen?: boolean,
|
||||
noticeLabel?: any,
|
||||
onChange?: (any) => any,
|
||||
setShowSelectors?: (boolean) => void,
|
||||
setShowSelectors?: ({ tab?: string, open: boolean }) => void,
|
||||
quickActionHandler?: (any) => any,
|
||||
render?: () => React$Node,
|
||||
handleTip?: (isLBC: boolean) => any,
|
||||
|
@ -88,7 +89,7 @@ export class FormField extends React.PureComponent<Props, State> {
|
|||
const input = this.input.current;
|
||||
|
||||
// Opened selectors (emoji/sticker) -> blur input and hide keyboard
|
||||
if (slimInput && showSelectors && input) input.blur();
|
||||
if (slimInput && showSelectors && showSelectors.open && input) input.blur();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -114,6 +115,7 @@ export class FormField extends React.PureComponent<Props, State> {
|
|||
textAreaMaxLength,
|
||||
type,
|
||||
slimInput,
|
||||
slimInputButtonRef,
|
||||
commentSelectorsProps,
|
||||
showSelectors,
|
||||
submitButtonRef,
|
||||
|
@ -270,10 +272,15 @@ export class FormField extends React.PureComponent<Props, State> {
|
|||
<TextareaWrapper
|
||||
isDrawerOpen={Boolean(this.state.drawerOpen)}
|
||||
toggleDrawer={() => this.setState({ drawerOpen: !this.state.drawerOpen })}
|
||||
closeSelector={setShowSelectors ? () => setShowSelectors(false) : () => {}}
|
||||
closeSelector={
|
||||
setShowSelectors && showSelectors
|
||||
? () => setShowSelectors({ tab: showSelectors.tab || undefined, open: false })
|
||||
: () => {}
|
||||
}
|
||||
commentSelectorsProps={commentSelectorsProps}
|
||||
showSelectors={Boolean(showSelectors)}
|
||||
showSelectors={Boolean(showSelectors && showSelectors.open)}
|
||||
slimInput={slimInput}
|
||||
slimInputButtonRef={slimInputButtonRef}
|
||||
tipModalOpen={tipModalOpen}
|
||||
>
|
||||
{(!slimInput || this.state.drawerOpen) && (label || quickAction) && (
|
||||
|
@ -303,7 +310,11 @@ export class FormField extends React.PureComponent<Props, State> {
|
|||
maxLength={textAreaMaxLength || FF_MAX_CHARS_DEFAULT}
|
||||
inputRef={this.input}
|
||||
isLivestream={isLivestream}
|
||||
toggleSelectors={setShowSelectors ? () => setShowSelectors(!showSelectors) : undefined}
|
||||
toggleSelectors={
|
||||
setShowSelectors && showSelectors
|
||||
? () => setShowSelectors({ tab: showSelectors.tab || undefined, open: !showSelectors.open })
|
||||
: undefined
|
||||
}
|
||||
handleTip={handleTip}
|
||||
handleSubmit={() => {
|
||||
if (handleSubmit) handleSubmit();
|
||||
|
@ -311,6 +322,7 @@ export class FormField extends React.PureComponent<Props, State> {
|
|||
}}
|
||||
claimIsMine={commentSelectorsProps && commentSelectorsProps.claimIsMine}
|
||||
{...inputProps}
|
||||
slimInput={slimInput}
|
||||
handlePreventClick={
|
||||
!this.state.drawerOpen ? () => this.setState({ drawerOpen: true }) : undefined
|
||||
}
|
||||
|
@ -360,6 +372,7 @@ export default FormField;
|
|||
|
||||
type TextareaWrapperProps = {
|
||||
slimInput?: boolean,
|
||||
slimInputButtonRef?: any,
|
||||
children: Node,
|
||||
isDrawerOpen: boolean,
|
||||
showSelectors?: boolean,
|
||||
|
@ -373,6 +386,7 @@ function TextareaWrapper(wrapperProps: TextareaWrapperProps) {
|
|||
const {
|
||||
children,
|
||||
slimInput,
|
||||
slimInputButtonRef,
|
||||
isDrawerOpen,
|
||||
commentSelectorsProps,
|
||||
showSelectors,
|
||||
|
@ -388,7 +402,7 @@ function TextareaWrapper(wrapperProps: TextareaWrapperProps) {
|
|||
|
||||
return slimInput ? (
|
||||
!isDrawerOpen ? (
|
||||
<div role="button" onClick={toggleDrawer}>
|
||||
<div ref={slimInputButtonRef} role="button" onClick={toggleDrawer}>
|
||||
{children}
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
// @flow
|
||||
import { useIsMobile } from 'effects/use-screensize';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import React from 'react';
|
||||
import TextField from '@mui/material/TextField';
|
||||
|
@ -13,6 +12,7 @@ type Props = {
|
|||
inputRef: any,
|
||||
submitButtonRef?: any,
|
||||
claimIsMine?: boolean,
|
||||
slimInput?: boolean,
|
||||
toggleSelectors: () => any,
|
||||
handleTip: (isLBC: boolean) => void,
|
||||
handleSubmit: () => any,
|
||||
|
@ -27,19 +27,18 @@ const TextareaSuggestionsInput = (props: Props) => {
|
|||
inputDefaultProps,
|
||||
submitButtonRef,
|
||||
claimIsMine,
|
||||
slimInput,
|
||||
toggleSelectors,
|
||||
handleTip,
|
||||
handleSubmit,
|
||||
handlePreventClick,
|
||||
} = props;
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const { InputProps, disabled, fullWidth, id, inputProps: autocompleteInputProps } = params;
|
||||
const inputProps = { ...autocompleteInputProps, ...inputDefaultProps };
|
||||
const autocompleteProps = { InputProps, disabled, fullWidth, id, inputProps };
|
||||
|
||||
if (isMobile) {
|
||||
if (slimInput) {
|
||||
InputProps.startAdornment = (
|
||||
<Button
|
||||
icon={ICONS.STICKER}
|
||||
|
|
|
@ -59,6 +59,7 @@ type Props = {
|
|||
autoFocus?: boolean,
|
||||
submitButtonRef?: any,
|
||||
claimIsMine?: boolean,
|
||||
slimInput?: boolean,
|
||||
doResolveUris: (uris: Array<string>, cache: boolean) => void,
|
||||
doSetMentionSearchResults: (query: string, uris: Array<string>) => void,
|
||||
onBlur: (any) => any,
|
||||
|
@ -92,6 +93,7 @@ export default function TextareaWithSuggestions(props: Props) {
|
|||
autoFocus,
|
||||
submitButtonRef,
|
||||
claimIsMine,
|
||||
slimInput,
|
||||
doResolveUris,
|
||||
doSetMentionSearchResults,
|
||||
onBlur,
|
||||
|
@ -421,6 +423,7 @@ export default function TextareaWithSuggestions(props: Props) {
|
|||
handlePreventClick={handlePreventClick}
|
||||
submitButtonRef={submitButtonRef}
|
||||
claimIsMine={claimIsMine}
|
||||
slimInput={slimInput}
|
||||
/>
|
||||
)}
|
||||
renderOption={(optionProps, option) => (
|
||||
|
|
|
@ -171,6 +171,10 @@
|
|||
@media (max-width: $breakpoint-small) {
|
||||
margin-top: var(--spacing-xxs);
|
||||
|
||||
> *:not(:last-child) {
|
||||
margin-right: var(--spacing-xxs);
|
||||
}
|
||||
|
||||
button {
|
||||
height: 2rem;
|
||||
padding: 0px var(--spacing-s);
|
||||
|
@ -183,6 +187,10 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button--link {
|
||||
padding: 0px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue