Fix chat scroll

This commit is contained in:
Rafael 2022-02-04 22:44:54 -03:00 committed by Thomas Zarebczan
parent 6a9b9247ce
commit 25182c7dcf
3 changed files with 45 additions and 30 deletions

View file

@ -74,15 +74,9 @@ export default function LivestreamChatLayout(props: Props) {
const allCommentsElem = document.querySelectorAll('.livestream__comment');
const lastCommentElem = allCommentsElem && allCommentsElem[allCommentsElem.length - 1];
const minScrollPos =
discussionElement && lastCommentElem && discussionElement.scrollHeight - lastCommentElem.offsetHeight;
discussionElement && lastCommentElem && discussionElement.scrollHeight - lastCommentElem.offsetHeight * 2;
const minOffset = discussionElement && minScrollPos && discussionElement.scrollHeight - minScrollPos;
const restoreScrollPos = React.useCallback(() => {
if (discussionElement) discussionElement.scrollTop = !isMobile ? 0 : discussionElement.scrollHeight;
}, [discussionElement, isMobile]);
const commentsRef = React.createRef();
const [viewMode, setViewMode] = React.useState(VIEW_MODES.CHAT);
const [scrollPos, setScrollPos] = React.useState(0);
const [showPinned, setShowPinned] = React.useState(true);
@ -91,6 +85,7 @@ export default function LivestreamChatLayout(props: Props) {
const [chatHidden, setChatHidden] = React.useState(false);
const [didInitialScroll, setDidInitialScroll] = React.useState(false);
const [bottomScrollTop, setBottomScrollTop] = React.useState(0);
const [inputDrawerOpen, setInputDrawerOpen] = React.useState(false);
const recentScrollPos = isMobile ? (bottomScrollTop > 0 && minOffset ? bottomScrollTop - minOffset : 0) : 0;
const claimId = claim && claim.claim_id;
@ -98,6 +93,18 @@ export default function LivestreamChatLayout(props: Props) {
const commentsLength = commentsToDisplay && commentsToDisplay.length;
const pinnedComment = pinnedComments.length > 0 ? pinnedComments[0] : null;
const { superChatsChannelUrls, superChatsFiatAmount, superChatsLBCAmount } = getTipValues(superChatsByAmount);
const hasRecentComments = Boolean(
scrollPos && (!isMobile || recentScrollPos) && scrollPos < recentScrollPos && viewMode === VIEW_MODES.CHAT
);
const restoreScrollPos = React.useCallback(() => {
if (discussionElement) {
discussionElement.scrollTop = !isMobile ? 0 : discussionElement.scrollHeight;
setBottomScrollTop(discussionElement.scrollTop);
}
}, [discussionElement, isMobile]);
const commentsRef = React.createRef();
function toggleSuperChat() {
if (superChatsChannelUrls && superChatsChannelUrls.length > 0) {
@ -129,14 +136,14 @@ export default function LivestreamChatLayout(props: Props) {
isMobile &&
discussionElement &&
viewMode === VIEW_MODES.CHAT &&
!didInitialScroll &&
(!didInitialScroll || bottomScrollTop === 0) &&
discussionElement.scrollTop < discussionElement.scrollHeight
) {
discussionElement.scrollTop = discussionElement.scrollHeight;
setDidInitialScroll(true);
setBottomScrollTop(discussionElement.scrollTop);
}
}, [didInitialScroll, discussionElement, isMobile, viewMode]);
}, [bottomScrollTop, didInitialScroll, discussionElement, isMobile, viewMode]);
// Register scroll handler (TODO: Should throttle/debounce)
React.useEffect(() => {
@ -334,10 +341,15 @@ export default function LivestreamChatLayout(props: Props) {
<Spinner />
</div>
) : (
<LivestreamComments uri={uri} commentsToDisplay={commentsToDisplay} isMobile={isMobile} />
<LivestreamComments
uri={uri}
commentsToDisplay={commentsToDisplay}
isMobile={isMobile}
restoreScrollPos={!hasRecentComments && !inputDrawerOpen && restoreScrollPos}
/>
)}
{scrollPos && (!isMobile || recentScrollPos) && scrollPos < recentScrollPos && viewMode === VIEW_MODES.CHAT ? (
{hasRecentComments ? (
<Button
button="secondary"
className="livestream-comments__scroll-to-recent"
@ -354,13 +366,10 @@ export default function LivestreamChatLayout(props: Props) {
embed={embed}
uri={uri}
onDoneReplying={restoreScrollPos}
onSlimInputClick={
scrollPos &&
recentScrollPos &&
scrollPos >= recentScrollPos &&
viewMode === VIEW_MODES.CHAT &&
restoreScrollPos
}
onSlimInputClick={() => {
restoreScrollPos();
setInputDrawerOpen(!inputDrawerOpen);
}}
/>
</div>
</div>

View file

@ -28,10 +28,21 @@ type Props = {
stakedLevel: number,
isMobile?: boolean,
handleDismissPin?: () => void,
restoreScrollPos?: () => void,
};
export default function LivestreamComment(props: Props) {
const { comment, forceUpdate, uri, claim, myChannelIds, stakedLevel, isMobile, handleDismissPin } = props;
const {
comment,
forceUpdate,
uri,
claim,
myChannelIds,
stakedLevel,
isMobile,
handleDismissPin,
restoreScrollPos,
} = props;
const {
channel_url: authorUri,
@ -46,8 +57,6 @@ export default function LivestreamComment(props: Props) {
timestamp,
} = comment;
const commentRef = React.useRef();
const [hasUserMention, setUserMention] = React.useState(false);
const isStreamer = claim && claim.signing_channel && claim.signing_channel.permanent_url === authorUri;
@ -56,10 +65,6 @@ export default function LivestreamComment(props: Props) {
const isSticker = Boolean(stickerUrlFromMessage);
const timePosted = timestamp * 1000;
const commentIsMine = comment.channel_id && isMyComment(comment.channel_id);
const discussionElement = document.querySelector('.livestream__comments--mobile');
const currentComment = commentRef && commentRef.current;
const minScrollPos =
discussionElement && currentComment && discussionElement.scrollHeight - currentComment.offsetHeight;
// todo: implement comment_list --mine in SDK so redux can grab with selectCommentIsMine
function isMyComment(channelId: string) {
@ -69,10 +74,10 @@ export default function LivestreamComment(props: Props) {
// For every new <LivestreamComment /> component that is rendered on mobile view,
// keep the scroll at the bottom (newest)
React.useEffect(() => {
if (isMobile && discussionElement && minScrollPos && discussionElement.scrollTop >= minScrollPos) {
discussionElement.scrollTop = discussionElement.scrollHeight;
if (isMobile && restoreScrollPos) {
restoreScrollPos();
}
}, [discussionElement, isMobile, minScrollPos]);
}, [isMobile, restoreScrollPos]);
return (
<li
@ -82,7 +87,6 @@ export default function LivestreamComment(props: Props) {
'livestream__comment--mentioned': hasUserMention,
'livestream__comment--mobile': isMobile,
})}
ref={commentRef}
>
{supportAmount > 0 && (
<div className="livestreamComment__superchatBanner">

View file

@ -12,10 +12,11 @@ type Props = {
fetchingComments: boolean,
uri: string,
isMobile?: boolean,
restoreScrollPos?: () => void,
};
export default function LivestreamComments(props: Props) {
const { commentsToDisplay, fetchingComments, uri, isMobile } = props;
const { commentsToDisplay, fetchingComments, uri, isMobile, restoreScrollPos } = props;
const [forceUpdate, setForceUpdate] = React.useState(0);
@ -56,6 +57,7 @@ export default function LivestreamComments(props: Props) {
uri={uri}
forceUpdate={forceUpdate}
isMobile
restoreScrollPos={restoreScrollPos}
/>
))}
</div>