Fix keyboard & comment selectors open affecting scroll

This commit is contained in:
Rafael 2022-02-08 13:49:46 -03:00 committed by Thomas Zarebczan
parent e42090d3b6
commit dae0f9c3d5
5 changed files with 57 additions and 10 deletions

View file

@ -54,6 +54,7 @@ type Props = {
supportDisabled: boolean, supportDisabled: boolean,
uri: string, uri: string,
disableInput?: boolean, disableInput?: boolean,
onSlimInputClose?: () => void,
setQuickReply: (any) => void, setQuickReply: (any) => void,
onCancelReplying?: () => void, onCancelReplying?: () => void,
onDoneReplying?: () => void, onDoneReplying?: () => void,
@ -89,6 +90,7 @@ export function CommentCreate(props: Props) {
supportDisabled, supportDisabled,
uri, uri,
disableInput, disableInput,
onSlimInputClose,
doCommentCreate, doCommentCreate,
doFetchCreatorSettings, doFetchCreatorSettings,
doToast, doToast,
@ -177,6 +179,8 @@ export function CommentCreate(props: Props) {
} else { } else {
setTipSelector(true); setTipSelector(true);
} }
if (onSlimInputClose) onSlimInputClose();
} }
function handleStickerComment() { function handleStickerComment() {
@ -198,6 +202,9 @@ export function CommentCreate(props: Props) {
setTipAmount(sticker.price || 0); setTipAmount(sticker.price || 0);
setShowSelectors({ tab: showSelectors.tab || undefined, open: false }); setShowSelectors({ tab: showSelectors.tab || undefined, open: false });
// added this here since selecting a sticker can cause scroll issues
if (onSlimInputClose) onSlimInputClose();
if (sticker.price && sticker.price > 0) { if (sticker.price && sticker.price > 0) {
setActiveTab(canReceiveFiatTip ? TAB_FIAT : TAB_LBC); setActiveTab(canReceiveFiatTip ? TAB_FIAT : TAB_LBC);
setTipSelector(true); setTipSelector(true);
@ -207,6 +214,8 @@ export function CommentCreate(props: Props) {
function handleCancelSticker() { function handleCancelSticker() {
setReviewingStickerComment(false); setReviewingStickerComment(false);
setSelectedSticker(null); setSelectedSticker(null);
if (onSlimInputClose) onSlimInputClose();
} }
function handleCancelSupport() { function handleCancelSupport() {
@ -218,6 +227,8 @@ export function CommentCreate(props: Props) {
setShowSelectors({ tab: showSelectors.tab || undefined, open: false }); setShowSelectors({ tab: showSelectors.tab || undefined, open: false });
setSelectedSticker(null); setSelectedSticker(null);
} }
if (onSlimInputClose) onSlimInputClose();
} }
function handleSupportComment() { function handleSupportComment() {
@ -574,6 +585,7 @@ export function CommentCreate(props: Props) {
handleSubmit={handleCreateComment} handleSubmit={handleCreateComment}
slimInput={isMobile && uri} // "uri": make sure it's on a file page slimInput={isMobile && uri} // "uri": make sure it's on a file page
slimInputButtonRef={slimInputButtonRef} slimInputButtonRef={slimInputButtonRef}
onSlimInputClose={onSlimInputClose}
commentSelectorsProps={commentSelectorsProps} commentSelectorsProps={commentSelectorsProps}
submitButtonRef={buttonRef} submitButtonRef={buttonRef}
setShowSelectors={setShowSelectors} setShowSelectors={setShowSelectors}
@ -633,7 +645,10 @@ export function CommentCreate(props: Props) {
disabled={disabled || tipSelectorError || !minAmountMet} disabled={disabled || tipSelectorError || !minAmountMet}
icon={activeTab === TAB_LBC ? ICONS.LBC : ICONS.FINANCE} icon={activeTab === TAB_LBC ? ICONS.LBC : ICONS.FINANCE}
label={__('Review')} label={__('Review')}
onClick={() => setReviewingSupportComment(true)} onClick={() => {
setReviewingSupportComment(true);
if (onSlimInputClose) onSlimInputClose();
}}
/> />
) : ( ) : (
(!isMobile || selectedSticker) && (!isMobile || selectedSticker) &&

View file

@ -53,6 +53,7 @@ type Props = {
submitButtonRef?: any, submitButtonRef?: any,
tipModalOpen?: boolean, tipModalOpen?: boolean,
noticeLabel?: any, noticeLabel?: any,
onSlimInputClose?: () => void,
onChange?: (any) => any, onChange?: (any) => any,
setShowSelectors?: ({ tab?: string, open: boolean }) => void, setShowSelectors?: ({ tab?: string, open: boolean }) => void,
quickActionHandler?: (any) => any, quickActionHandler?: (any) => any,
@ -116,6 +117,7 @@ export class FormField extends React.PureComponent<Props, State> {
submitButtonRef, submitButtonRef,
tipModalOpen, tipModalOpen,
noticeLabel, noticeLabel,
onSlimInputClose,
quickActionHandler, quickActionHandler,
setShowSelectors, setShowSelectors,
render, render,
@ -277,6 +279,7 @@ export class FormField extends React.PureComponent<Props, State> {
showSelectors={Boolean(showSelectors && showSelectors.open)} showSelectors={Boolean(showSelectors && showSelectors.open)}
slimInput={slimInput} slimInput={slimInput}
slimInputButtonRef={slimInputButtonRef} slimInputButtonRef={slimInputButtonRef}
onSlimInputClose={onSlimInputClose}
tipModalOpen={tipModalOpen} tipModalOpen={tipModalOpen}
> >
{(!slimInput || this.state.drawerOpen) && label && ( {(!slimInput || this.state.drawerOpen) && label && (

View file

@ -11,6 +11,7 @@ type TextareaWrapperProps = {
showSelectors?: boolean, showSelectors?: boolean,
commentSelectorsProps?: any, commentSelectorsProps?: any,
tipModalOpen?: boolean, tipModalOpen?: boolean,
onSlimInputClose?: () => void,
toggleDrawer: () => void, toggleDrawer: () => void,
closeSelector?: () => void, closeSelector?: () => void,
}; };
@ -24,6 +25,7 @@ export const TextareaWrapper = (wrapperProps: TextareaWrapperProps) => {
commentSelectorsProps, commentSelectorsProps,
showSelectors, showSelectors,
tipModalOpen, tipModalOpen,
onSlimInputClose,
toggleDrawer, toggleDrawer,
closeSelector, closeSelector,
} = wrapperProps; } = wrapperProps;
@ -31,6 +33,7 @@ export const TextareaWrapper = (wrapperProps: TextareaWrapperProps) => {
function handleCloseAll() { function handleCloseAll() {
toggleDrawer(); toggleDrawer();
if (closeSelector) closeSelector(); if (closeSelector) closeSelector();
if (onSlimInputClose) onSlimInputClose();
} }
return slimInput ? ( return slimInput ? (

View file

@ -82,6 +82,7 @@ export default function LivestreamChatLayout(props: Props) {
const [chatHidden, setChatHidden] = React.useState(false); const [chatHidden, setChatHidden] = React.useState(false);
const [didInitialScroll, setDidInitialScroll] = React.useState(false); const [didInitialScroll, setDidInitialScroll] = React.useState(false);
const [minScrollHeight, setMinScrollHeight] = React.useState(0); const [minScrollHeight, setMinScrollHeight] = React.useState(0);
const [keyboardOpened, setKeyboardOpened] = React.useState(false);
const claimId = claim && claim.claim_id; const claimId = claim && claim.claim_id;
const commentsToDisplay = viewMode === VIEW_MODES.CHAT ? commentsByChronologicalOrder : superChatsByAmount; const commentsToDisplay = viewMode === VIEW_MODES.CHAT ? commentsByChronologicalOrder : superChatsByAmount;
@ -89,10 +90,7 @@ export default function LivestreamChatLayout(props: Props) {
const pinnedComment = pinnedComments.length > 0 ? pinnedComments[0] : null; const pinnedComment = pinnedComments.length > 0 ? pinnedComments[0] : null;
const { superChatsChannelUrls, superChatsFiatAmount, superChatsLBCAmount } = getTipValues(superChatsByAmount); const { superChatsChannelUrls, superChatsFiatAmount, superChatsLBCAmount } = getTipValues(superChatsByAmount);
const scrolledPastRecent = Boolean( const scrolledPastRecent = Boolean(
(scrollPos || scrollPos === 0) && viewMode === VIEW_MODES.CHAT && !isMobile ? scrollPos < 0 : scrollPos < minScrollHeight
(!isMobile || minScrollHeight) &&
scrollPos < minScrollHeight &&
viewMode === VIEW_MODES.CHAT
); );
const restoreScrollPos = React.useCallback(() => { const restoreScrollPos = React.useCallback(() => {
@ -149,7 +147,7 @@ export default function LivestreamChatLayout(props: Props) {
if (discussionElement) { if (discussionElement) {
const scrollTop = discussionElement.scrollTop; const scrollTop = discussionElement.scrollTop;
if (!scrollPos || scrollTop !== scrollPos) { if (scrollTop !== scrollPos) {
setScrollPos(scrollTop); setScrollPos(scrollTop);
} }
} }
@ -171,7 +169,11 @@ export default function LivestreamChatLayout(props: Props) {
// -ve scrollPos: user scrolled. // -ve scrollPos: user scrolled.
const timer = setTimeout(() => { const timer = setTimeout(() => {
// Use a timer here to ensure we reset after the new comment has been rendered. // Use a timer here to ensure we reset after the new comment has been rendered.
restoreScrollPos(); if (!isMobile) {
discussionElement.scrollTop = 0;
} else {
restoreScrollPos();
}
}, COMMENT_SCROLL_TIMEOUT); }, COMMENT_SCROLL_TIMEOUT);
return () => clearTimeout(timer); return () => clearTimeout(timer);
} }
@ -179,6 +181,18 @@ export default function LivestreamChatLayout(props: Props) {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [commentsLength]); // (Just respond to 'commentsLength' updates and nothing else) }, [commentsLength]); // (Just respond to 'commentsLength' updates and nothing else)
// Restore Scroll Pos after mobile input opens keyboard and avoid scroll height conflicts
React.useEffect(() => {
if (keyboardOpened) {
const timer = setTimeout(() => {
restoreScrollPos();
setKeyboardOpened(false);
}, 300);
return () => clearTimeout(timer);
}
}, [keyboardOpened, restoreScrollPos]);
// Stop spinner for resolving superchats // Stop spinner for resolving superchats
React.useEffect(() => { React.useEffect(() => {
if (resolvingSuperChats) { if (resolvingSuperChats) {
@ -359,7 +373,14 @@ export default function LivestreamChatLayout(props: Props) {
) : null} ) : null}
<div className="livestream__comment-create"> <div className="livestream__comment-create">
<CommentCreate isLivestream bottom embed={embed} uri={uri} onDoneReplying={restoreScrollPos} /> <CommentCreate
isLivestream
bottom
embed={embed}
uri={uri}
onDoneReplying={restoreScrollPos}
onSlimInputClose={!scrolledPastRecent && isMobile ? () => setKeyboardOpened(true) : undefined}
/>
</div> </div>
</div> </div>
</div> </div>

View file

@ -7,6 +7,7 @@ import LivestreamLayout from 'component/livestreamLayout';
import moment from 'moment'; import moment from 'moment';
import Page from 'component/page'; import Page from 'component/page';
import React from 'react'; import React from 'react';
import { useIsMobile } from 'effects/use-screensize';
const LivestreamChatLayout = lazyImport(() => import('component/livestreamChatLayout' /* webpackChunkName: "chat" */)); const LivestreamChatLayout = lazyImport(() => import('component/livestreamChatLayout' /* webpackChunkName: "chat" */));
@ -41,6 +42,8 @@ export default function LivestreamPage(props: Props) {
doUserSetReferrer, doUserSetReferrer,
} = props; } = props;
const isMobile = useIsMobile();
const [activeStreamUri, setActiveStreamUri] = React.useState(false); const [activeStreamUri, setActiveStreamUri] = React.useState(false);
const [showLivestream, setShowLivestream] = React.useState(false); const [showLivestream, setShowLivestream] = React.useState(false);
const [showScheduledInfo, setShowScheduledInfo] = React.useState(false); const [showScheduledInfo, setShowScheduledInfo] = React.useState(false);
@ -145,8 +148,10 @@ export default function LivestreamPage(props: Props) {
// This can be removed when we start using the app video player, not a LIVESTREAM iframe // This can be removed when we start using the app video player, not a LIVESTREAM iframe
doSetPlayingUri({ uri: null }); doSetPlayingUri({ uri: null });
return () => doSetPlayingUri({ uri: null }); return () => {
}, [doSetPlayingUri]); if (isMobile) doSetPlayingUri({ uri: null });
};
}, [doSetPlayingUri, isMobile]);
return ( return (
<Page <Page