Improve CommentCreate style on mobile view

This commit is contained in:
Rafael 2022-02-02 09:47:04 -03:00 committed by Thomas Zarebczan
parent c1b84368a9
commit 55e0a7effe
11 changed files with 344 additions and 207 deletions

View file

@ -494,6 +494,19 @@ export function CommentCreate(props: Props) {
name={isReply ? 'create__reply' : 'create__comment'} name={isReply ? 'create__reply' : 'create__comment'}
onChange={(e) => setCommentValue(SIMPLE_SITE || !advancedEditor || isReply ? e.target.value : e)} onChange={(e) => setCommentValue(SIMPLE_SITE || !advancedEditor || isReply ? e.target.value : e)}
openEmoteMenu={() => setShowEmotes(!showEmotes)} openEmoteMenu={() => setShowEmotes(!showEmotes)}
handleTip={(isLBC) =>
doOpenModal(MODALS.SEND_TIP, {
uri,
isTipOnly: true,
hasSelectedTab: isLBC ? TAB_LBC : TAB_FIAT,
setAmount: (amount) => {
setTipAmount(amount);
setReviewingSupportComment(true);
},
})
}
handleSubmit={handleCreateComment}
noEmojis={isMobile}
placeholder={__('Say something about this...')} placeholder={__('Say something about this...')}
quickActionHandler={!SIMPLE_SITE ? () => setAdvancedEditor(!advancedEditor) : undefined} quickActionHandler={!SIMPLE_SITE ? () => setAdvancedEditor(!advancedEditor) : undefined}
quickActionLabel={ quickActionLabel={
@ -526,195 +539,197 @@ export function CommentCreate(props: Props) {
)} )}
{/* Bottom Action Buttons */} {/* Bottom Action Buttons */}
<div className="section__actions section__actions--no-margin"> {!isMobile && (
{/* Submit Button */} <div className="section__actions section__actions--no-margin">
{isReviewingSupportComment ? ( {/* Submit Button */}
<Button {isReviewingSupportComment ? (
autoFocus
button="primary"
disabled={disabled || !minAmountMet}
label={
isSubmitting
? __('Sending...')
: commentFailure && tipAmount === successTip.tipAmount
? __('Re-submit')
: __('Send')
}
onClick={handleSupportComment}
/>
) : isReviewingStickerComment && selectedSticker ? (
<Button
button="primary"
label={__('Send')}
disabled={(isSupportComment && (tipError || disableReviewButton)) || disableInput}
onClick={() => {
if (isSupportComment) {
handleSupportComment();
} else {
handleCreateComment();
}
setSelectedSticker(null);
setReviewingStickerComment(false);
setStickerSelector(false);
setIsSupportComment(false);
}}
/>
) : isSupportComment ? (
<Button
disabled={disabled || tipError || disableReviewButton || !minAmountMet}
type="button"
button="primary"
icon={activeTab === TAB_LBC ? ICONS.LBC : ICONS.FINANCE}
label={__('Review')}
onClick={() => setReviewingSupportComment(true)}
requiresAuth
/>
) : (
(!minTip || claimIsMine) && (
<Button <Button
ref={buttonRef} autoFocus
button="primary" button="primary"
disabled={disabled || stickerSelector} disabled={disabled || !minAmountMet}
type="submit"
label={ label={
isReply isSubmitting
? isSubmitting ? __('Sending...')
? __('Replying...') : commentFailure && tipAmount === successTip.tipAmount
: __('Reply') ? __('Re-submit')
: isSubmitting : __('Send')
? __('Commenting...')
: __('Comment --[button to submit something]--')
} }
requiresAuth onClick={handleSupportComment}
onClick={() => activeChannelClaim && commentValue.length && handleCreateComment()}
/> />
) ) : isReviewingStickerComment && selectedSticker ? (
)} <Button
button="primary"
{/** Stickers/Support Buttons **/} label={__('Send')}
{!supportDisabled && !stickerSelector && ( disabled={(isSupportComment && (tipError || disableReviewButton)) || disableInput}
<> onClick={() => {
{getActionButton( if (isSupportComment) {
__('Stickers'), handleSupportComment();
isReviewingStickerComment ? __('Different Sticker') : undefined, } else {
ICONS.STICKER, handleCreateComment();
() => { }
if (isReviewingStickerComment) setReviewingStickerComment(false); setSelectedSticker(null);
setReviewingStickerComment(false);
setStickerSelector(false);
setIsSupportComment(false); setIsSupportComment(false);
setStickerSelector(true); }}
} />
)} ) : isSupportComment ? (
<Button
disabled={disabled || tipError || disableReviewButton || !minAmountMet}
type="button"
button="primary"
icon={activeTab === TAB_LBC ? ICONS.LBC : ICONS.FINANCE}
label={__('Review')}
onClick={() => setReviewingSupportComment(true)}
requiresAuth
/>
) : (
(!minTip || claimIsMine) && (
<Button
ref={buttonRef}
button="primary"
disabled={disabled || stickerSelector}
type="submit"
label={
isReply
? isSubmitting
? __('Replying...')
: __('Reply')
: isSubmitting
? __('Commenting...')
: __('Comment --[button to submit something]--')
}
requiresAuth
onClick={() => activeChannelClaim && commentValue.length && handleCreateComment()}
/>
)
)}
{!claimIsMine && ( {/** Stickers/Support Buttons **/}
<> {!supportDisabled && !stickerSelector && (
{(!isSupportComment || activeTab !== TAB_LBC) && <>
getActionButton( {getActionButton(
__('Credits'), __('Stickers'),
isSupportComment ? __('Switch to Credits') : undefined, isReviewingStickerComment ? __('Different Sticker') : undefined,
ICONS.LBC, ICONS.STICKER,
() => { () => {
setActiveTab(TAB_LBC); if (isReviewingStickerComment) setReviewingStickerComment(false);
setIsSupportComment(false);
setStickerSelector(true);
}
)}
if (isMobile) { {!claimIsMine && (
doOpenModal(MODALS.SEND_TIP, { <>
uri, {(!isSupportComment || activeTab !== TAB_LBC) &&
isTipOnly: true, getActionButton(
hasSelectedTab: TAB_LBC, __('Credits'),
setAmount: (amount) => { isSupportComment ? __('Switch to Credits') : undefined,
setTipAmount(amount); ICONS.LBC,
setReviewingSupportComment(true); () => {
}, setActiveTab(TAB_LBC);
});
} else {
setIsSupportComment(true);
}
},
!commentValue.length
)}
{stripeEnvironment && if (isMobile) {
(!isSupportComment || activeTab !== TAB_FIAT) && doOpenModal(MODALS.SEND_TIP, {
getActionButton( uri,
__('Cash'), isTipOnly: true,
isSupportComment ? __('Switch to Cash') : undefined, hasSelectedTab: TAB_LBC,
ICONS.FINANCE, setAmount: (amount) => {
() => { setTipAmount(amount);
setActiveTab(TAB_FIAT); setReviewingSupportComment(true);
},
});
} else {
setIsSupportComment(true);
}
},
!commentValue.length
)}
if (isMobile) { {stripeEnvironment &&
doOpenModal(MODALS.SEND_TIP, { (!isSupportComment || activeTab !== TAB_FIAT) &&
uri, getActionButton(
isTipOnly: true, __('Cash'),
hasSelectedTab: TAB_FIAT, isSupportComment ? __('Switch to Cash') : undefined,
setAmount: (amount) => { ICONS.FINANCE,
setTipAmount(amount); () => {
setReviewingSupportComment(true); setActiveTab(TAB_FIAT);
},
});
} else {
setIsSupportComment(true);
}
},
!commentValue.length
)}
</>
)}
</>
)}
{/* Cancel Button */} if (isMobile) {
{(isSupportComment || doOpenModal(MODALS.SEND_TIP, {
isReviewingSupportComment || uri,
stickerSelector || isTipOnly: true,
isReviewingStickerComment || hasSelectedTab: TAB_FIAT,
(isReply && !minTip)) && ( setAmount: (amount) => {
<Button setTipAmount(amount);
disabled={isSupportComment && isSubmitting} setReviewingSupportComment(true);
button="link" },
label={__('Cancel')} });
onClick={() => { } else {
if (isSupportComment || isReviewingSupportComment) { setIsSupportComment(true);
if (!isReviewingSupportComment) setIsSupportComment(false); }
setReviewingSupportComment(false); },
if (stickerPrice) { !commentValue.length
)}
</>
)}
</>
)}
{/* Cancel Button */}
{(isSupportComment ||
isReviewingSupportComment ||
stickerSelector ||
isReviewingStickerComment ||
(isReply && !minTip)) && (
<Button
disabled={isSupportComment && isSubmitting}
button="link"
label={__('Cancel')}
onClick={() => {
if (isSupportComment || isReviewingSupportComment) {
if (!isReviewingSupportComment) setIsSupportComment(false);
setReviewingSupportComment(false);
if (stickerPrice) {
setReviewingStickerComment(false);
setStickerSelector(false);
setSelectedSticker(null);
}
} else if (stickerSelector || isReviewingStickerComment) {
setReviewingStickerComment(false); setReviewingStickerComment(false);
setStickerSelector(false); setStickerSelector(false);
setSelectedSticker(null); setSelectedSticker(null);
} else if (isReply && !minTip && onCancelReplying) {
onCancelReplying();
} }
} else if (stickerSelector || isReviewingStickerComment) { }}
setReviewingStickerComment(false);
setStickerSelector(false);
setSelectedSticker(null);
} else if (isReply && !minTip && onCancelReplying) {
onCancelReplying();
}
}}
/>
)}
{/* Help Text */}
{deletedComment && <div className="error__text">{__('This comment has been deleted.')}</div>}
{!!minAmount && (
<div className="help--notice commentCreate__minAmountNotice">
<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> )}
)}
</div> {/* Help Text */}
{deletedComment && <div className="error__text">{__('This comment has been deleted.')}</div>}
{!!minAmount && (
<div className="help--notice commentCreate__minAmountNotice">
<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>
)}
</div>
)}
</Form> </Form>
); );
} }

View file

@ -48,6 +48,8 @@ type Props = {
openEmoteMenu?: () => void, openEmoteMenu?: () => void,
quickActionHandler?: (any) => any, quickActionHandler?: (any) => any,
render?: () => React$Node, render?: () => React$Node,
handleTip?: (isLBC: boolean) => any,
handleSubmit?: () => any,
}; };
export class FormField extends React.PureComponent<Props> { export class FormField extends React.PureComponent<Props> {
@ -93,6 +95,8 @@ export class FormField extends React.PureComponent<Props> {
openEmoteMenu, openEmoteMenu,
quickActionHandler, quickActionHandler,
render, render,
handleTip,
handleSubmit,
...inputProps ...inputProps
} = this.props; } = this.props;
@ -239,7 +243,7 @@ export class FormField extends React.PureComponent<Props> {
{(label || quickAction) && ( {(label || quickAction) && (
<div className="form-field__two-column"> <div className="form-field__two-column">
<label htmlFor={name}>{label}</label> <label htmlFor={name}>{label}</label>
{quickAction} {countInfo}
</div> </div>
)} )}
@ -260,6 +264,9 @@ export class FormField extends React.PureComponent<Props> {
maxLength={textAreaMaxLength || FF_MAX_CHARS_DEFAULT} maxLength={textAreaMaxLength || FF_MAX_CHARS_DEFAULT}
inputRef={this.input} inputRef={this.input}
isLivestream={isLivestream} isLivestream={isLivestream}
handleEmojis={openEmoteMenu}
handleTip={handleTip}
handleSubmit={handleSubmit}
{...inputProps} {...inputProps}
/> />
</React.Suspense> </React.Suspense>
@ -276,7 +283,6 @@ export class FormField extends React.PureComponent<Props> {
iconSize={20} iconSize={20}
/> />
)} )}
{countInfo}
</div> </div>
</fieldset-section> </fieldset-section>
); );

View file

@ -2765,4 +2765,5 @@ export const icons = {
</svg> </svg>
); );
}, },
[ICONS.SUBMIT]: buildIcon(<path d="M2.01 21 23 12 2.01 3 2 10l15 2-15 2z" />),
}; };

View file

@ -206,7 +206,7 @@ export default function LivestreamChatLayout(props: Props) {
/> />
</div> </div>
<div className="livestream__commentCreate"> <div className="livestream__comment-create">
<CommentCreate isLivestream bottom uri={uri} disableInput /> <CommentCreate isLivestream bottom uri={uri} disableInput />
</div> </div>
</div> </div>
@ -293,7 +293,7 @@ export default function LivestreamChatLayout(props: Props) {
/> />
)} )}
<div className="livestream__commentCreate"> <div className="livestream__comment-create">
<CommentCreate <CommentCreate
isLivestream isLivestream
bottom bottom

View file

@ -2,6 +2,7 @@
import { EMOTES_48px as EMOTES } from 'constants/emotes'; import { EMOTES_48px as EMOTES } from 'constants/emotes';
import { matchSorter } from 'match-sorter'; import { matchSorter } from 'match-sorter';
import { SEARCH_OPTIONS } from 'constants/search'; import { SEARCH_OPTIONS } from 'constants/search';
import * as ICONS from 'constants/icons';
import * as KEYCODES from 'constants/keycodes'; import * as KEYCODES from 'constants/keycodes';
import Autocomplete from '@mui/material/Autocomplete'; import Autocomplete from '@mui/material/Autocomplete';
import BusyIndicator from 'component/common/busy-indicator'; import BusyIndicator from 'component/common/busy-indicator';
@ -15,6 +16,8 @@ import TextField from '@mui/material/TextField';
import useLighthouse from 'effects/use-lighthouse'; import useLighthouse from 'effects/use-lighthouse';
import useThrottle from 'effects/use-throttle'; import useThrottle from 'effects/use-throttle';
import { parseURI } from 'util/lbryURI'; import { parseURI } from 'util/lbryURI';
import Button from 'component/button';
import { useIsMobile } from 'effects/use-screensize';
const SUGGESTION_REGEX = new RegExp( const SUGGESTION_REGEX = new RegExp(
'((?:^| |\n)@[^\\s=&#$@%?:;/\\"<>%{}|^~[]*(?::[\\w]+)?)|((?:^| |\n):[\\w+-]*:?)', '((?:^| |\n)@[^\\s=&#$@%?:;/\\"<>%{}|^~[]*(?::[\\w]+)?)|((?:^| |\n):[\\w+-]*:?)',
@ -63,6 +66,9 @@ type Props = {
onBlur: (any) => any, onBlur: (any) => any,
onChange: (any) => any, onChange: (any) => any,
onFocus: (any) => any, onFocus: (any) => any,
handleEmojis: () => any,
handleTip: (isLBC: boolean) => any,
handleSubmit: () => any,
}; };
export default function TextareaWithSuggestions(props: Props) { export default function TextareaWithSuggestions(props: Props) {
@ -90,8 +96,13 @@ export default function TextareaWithSuggestions(props: Props) {
onBlur, onBlur,
onChange, onChange,
onFocus, onFocus,
handleEmojis,
handleTip,
handleSubmit,
} = props; } = props;
const isMobile = useIsMobile();
const inputDefaultProps = { className, placeholder, maxLength, type, disabled }; const inputDefaultProps = { className, placeholder, maxLength, type, disabled };
const [suggestionValue, setSuggestionValue] = React.useState(undefined); const [suggestionValue, setSuggestionValue] = React.useState(undefined);
@ -378,10 +389,29 @@ export default function TextareaWithSuggestions(props: Props) {
const renderInput = (params: any) => { const renderInput = (params: any) => {
const { InputProps, disabled, fullWidth, id, inputProps: autocompleteInputProps } = params; const { InputProps, disabled, fullWidth, id, inputProps: autocompleteInputProps } = params;
if (isMobile) {
InputProps.startAdornment = <Button icon={ICONS.STICKER} onClick={handleEmojis} />;
InputProps.endAdornment = (
<>
<Button icon={ICONS.LBC} onClick={() => handleTip(true)} />
<Button icon={ICONS.FINANCE} onClick={() => handleTip(false)} />
{messageValue && messageValue.length > 0 && (
<Button button="primary" icon={ICONS.SUBMIT} iconColor="red" onClick={() => handleSubmit()} />
)}
</>
);
}
const inputProps = { ...autocompleteInputProps, ...inputDefaultProps }; const inputProps = { ...autocompleteInputProps, ...inputDefaultProps };
const autocompleteProps = { InputProps, disabled, fullWidth, id, inputProps }; const autocompleteProps = { InputProps, disabled, fullWidth, id, inputProps };
return <TextField inputRef={inputRef} multiline select={false} {...autocompleteProps} />; return !isMobile ? (
<TextField inputRef={inputRef} multiline select={false} {...autocompleteProps} />
) : (
<TextField inputRef={inputRef} variant="outlined" multiline minRows={1} select={false} {...autocompleteProps} />
);
}; };
const renderOption = (optionProps: any, label: string) => { const renderOption = (optionProps: any, label: string) => {

View file

@ -191,3 +191,4 @@ export const ODYSEE_WHITE_TEXT = 'OdyseeLogoWhiteText';
export const ODYSEE_DARK_TEXT = 'OdyseeLogoDarkText'; export const ODYSEE_DARK_TEXT = 'OdyseeLogoDarkText';
export const FEATURED = 'Featured'; export const FEATURED = 'Featured';
export const DISMISS_ALL = 'DismissAll'; export const DISMISS_ALL = 'DismissAll';
export const SUBMIT = 'Submit';

View file

@ -1,7 +1,6 @@
@import '../init/mixins'; @import '../init/mixins';
input, input,
textarea,
select, select,
.date-picker-input { .date-picker-input {
height: var(--height-input); height: var(--height-input);
@ -37,6 +36,42 @@ select,
} }
} }
@media (min-width: $breakpoint-small) {
textarea {
height: var(--height-input);
border-radius: var(--border-radius);
border: 1px solid;
color: var(--color-input);
border-color: var(--color-input-border);
background-color: var(--color-input-bg);
padding-right: var(--spacing-s);
padding-left: var(--spacing-s);
&:focus {
@include focus;
}
&::placeholder {
color: var(--color-input-placeholder);
opacity: 0.4;
}
&:disabled {
opacity: 0.4;
& + label {
opacity: 0.4;
}
}
&[type='range'] {
height: auto;
height: 0.5rem;
background-color: var(--color-secondary);
}
}
}
checkbox-element, checkbox-element,
radio-element, radio-element,
select { select {
@ -480,12 +515,18 @@ fieldset-section {
@media (min-width: $breakpoint-small) { @media (min-width: $breakpoint-small) {
max-width: none; max-width: none;
select {
max-height: 1.5rem !important;
padding: 0 var(--spacing-xs);
padding-right: var(--spacing-l);
}
} }
select { @media (max-width: $breakpoint-small) {
max-height: 1.5rem !important; select {
padding: 0 var(--spacing-xs); max-height: 1.25rem !important;
padding-right: var(--spacing-l); }
} }
} }

View file

@ -112,7 +112,7 @@ $recent-msg-button__height: 2rem;
} }
@media (min-width: $breakpoint-small) { @media (min-width: $breakpoint-small) {
height: calc(100% - var(--header-height) - #{$discussion-header__height}); height: calc(100vh - var(--header-height) - #{$discussion-header__height});
} }
@media (max-width: $breakpoint-small) { @media (max-width: $breakpoint-small) {
@ -144,13 +144,27 @@ $recent-msg-button__height: 2rem;
} }
} }
.livestream__commentCreate { .livestream__comment-create {
padding: var(--spacing-s); padding: var(--spacing-s);
border-top: 1px solid var(--color-border); border-top: 1px solid var(--color-border);
margin-top: auto; margin-top: auto;
@media (max-width: $breakpoint-small) { @media (max-width: $breakpoint-small) {
padding: var(--spacing-xxs); padding: var(--spacing-xxs);
span,
select,
option {
font-size: var(--font-xxsmall);
}
select {
padding: 0px var(--spacing-xxs) !important;
}
.select--slim {
margin: 0px;
}
} }
} }
@ -197,9 +211,9 @@ $recent-msg-button__height: 2rem;
} }
.livestreamPinned__wrapper { .livestreamPinned__wrapper {
@extend .livestreamSuperchats__wrapper;
display: flex; display: flex;
flex-shrink: 0; flex-shrink: 0;
position: relative;
padding: var(--spacing-s) var(--spacing-xs); padding: var(--spacing-s) var(--spacing-xs);
border-bottom: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border);
font-size: var(--font-small); font-size: var(--font-small);
@ -225,6 +239,7 @@ $recent-msg-button__height: 2rem;
} }
@media (max-width: $breakpoint-small) { @media (max-width: $breakpoint-small) {
z-index: 1300;
max-width: 100%; max-width: 100%;
padding: 0; padding: 0;
padding-left: var(--spacing-xxs); padding-left: var(--spacing-xxs);

View file

@ -1,18 +1,43 @@
.MuiAutocomplete-inputRoot { .MuiAutocomplete-inputRoot {
padding: 0 !important; @media (min-width: $breakpoint-small) {
font-family: inherit !important; padding: 0 !important;
font-weight: inherit !important; font-family: inherit !important;
font-size: inherit !important; font-weight: inherit !important;
color: var(--color-text) !important; font-size: inherit !important;
color: var(--color-text) !important;
.MuiOutlinedInput-notchedOutline { .MuiOutlinedInput-notchedOutline {
visibility: hidden; visibility: hidden;
}
.create__comment {
min-height: calc(var(--height-input) * 1.5) !important;
}
}
}
@media (max-width: $breakpoint-small) {
.MuiOutlinedInput-input {
padding: 0px var(--spacing-xxs);
} }
.create__comment { .MuiOutlinedInput-root {
@extend textarea; font-size: var(--font-xsmall) !important;
flex-wrap: nowrap !important;
min-height: calc(var(--height-input) * 1.5) !important; textarea {
border: none;
}
.button--primary {
border-radius: 100%;
height: unset;
padding: var(--spacing-xxs);
.button__content {
height: unset;
}
}
} }
} }

View file

@ -178,8 +178,10 @@ img {
text-indent: -9999px; text-indent: -9999px;
} }
textarea { @media (min-width: $breakpoint-small) {
min-height: calc(var(--height-input) * 2); textarea {
min-height: calc(var(--height-input) * 2);
}
} }
.columns { .columns {

View file

@ -156,18 +156,19 @@ select {
outline: none; outline: none;
} }
textarea { @media (min-width: 900px) {
width: 100%; textarea {
min-height: var(--spacing-xxl); width: 100%;
padding: var(--spacing-s); min-height: var(--spacing-xxl);
// border-color should be added in apps for blur/focus padding: var(--spacing-s);
border: 1px solid; // border-color should be added in apps for blur/focus
border: 1px solid;
&:not([disabled]) { &:not([disabled]) {
resize: vertical; resize: vertical;
}
} }
} }
@media print { @media print {
// sass-lint:disable-block no-important // sass-lint:disable-block no-important
// Intelligent print styles // Intelligent print styles