diff --git a/ui/component/livestreamChatLayout/view.jsx b/ui/component/livestreamChatLayout/view.jsx index 43b69df73..0011e3812 100644 --- a/ui/component/livestreamChatLayout/view.jsx +++ b/ui/component/livestreamChatLayout/view.jsx @@ -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> diff --git a/ui/component/livestreamComment/view.jsx b/ui/component/livestreamComment/view.jsx index c6ad7531e..2a697059f 100644 --- a/ui/component/livestreamComment/view.jsx +++ b/ui/component/livestreamComment/view.jsx @@ -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"> diff --git a/ui/component/livestreamComments/view.jsx b/ui/component/livestreamComments/view.jsx index 2cb3e5344..66be1202e 100644 --- a/ui/component/livestreamComments/view.jsx +++ b/ui/component/livestreamComments/view.jsx @@ -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>