Fix chat scroll
This commit is contained in:
parent
6a9b9247ce
commit
25182c7dcf
3 changed files with 45 additions and 30 deletions
|
@ -74,15 +74,9 @@ export default function LivestreamChatLayout(props: Props) {
|
||||||
const allCommentsElem = document.querySelectorAll('.livestream__comment');
|
const allCommentsElem = document.querySelectorAll('.livestream__comment');
|
||||||
const lastCommentElem = allCommentsElem && allCommentsElem[allCommentsElem.length - 1];
|
const lastCommentElem = allCommentsElem && allCommentsElem[allCommentsElem.length - 1];
|
||||||
const minScrollPos =
|
const minScrollPos =
|
||||||
discussionElement && lastCommentElem && discussionElement.scrollHeight - lastCommentElem.offsetHeight;
|
discussionElement && lastCommentElem && discussionElement.scrollHeight - lastCommentElem.offsetHeight * 2;
|
||||||
const minOffset = discussionElement && minScrollPos && discussionElement.scrollHeight - minScrollPos;
|
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 [viewMode, setViewMode] = React.useState(VIEW_MODES.CHAT);
|
||||||
const [scrollPos, setScrollPos] = React.useState(0);
|
const [scrollPos, setScrollPos] = React.useState(0);
|
||||||
const [showPinned, setShowPinned] = React.useState(true);
|
const [showPinned, setShowPinned] = React.useState(true);
|
||||||
|
@ -91,6 +85,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 [bottomScrollTop, setBottomScrollTop] = React.useState(0);
|
const [bottomScrollTop, setBottomScrollTop] = React.useState(0);
|
||||||
|
const [inputDrawerOpen, setInputDrawerOpen] = React.useState(false);
|
||||||
|
|
||||||
const recentScrollPos = isMobile ? (bottomScrollTop > 0 && minOffset ? bottomScrollTop - minOffset : 0) : 0;
|
const recentScrollPos = isMobile ? (bottomScrollTop > 0 && minOffset ? bottomScrollTop - minOffset : 0) : 0;
|
||||||
const claimId = claim && claim.claim_id;
|
const claimId = claim && claim.claim_id;
|
||||||
|
@ -98,6 +93,18 @@ export default function LivestreamChatLayout(props: Props) {
|
||||||
const commentsLength = commentsToDisplay && commentsToDisplay.length;
|
const commentsLength = commentsToDisplay && commentsToDisplay.length;
|
||||||
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 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() {
|
function toggleSuperChat() {
|
||||||
if (superChatsChannelUrls && superChatsChannelUrls.length > 0) {
|
if (superChatsChannelUrls && superChatsChannelUrls.length > 0) {
|
||||||
|
@ -129,14 +136,14 @@ export default function LivestreamChatLayout(props: Props) {
|
||||||
isMobile &&
|
isMobile &&
|
||||||
discussionElement &&
|
discussionElement &&
|
||||||
viewMode === VIEW_MODES.CHAT &&
|
viewMode === VIEW_MODES.CHAT &&
|
||||||
!didInitialScroll &&
|
(!didInitialScroll || bottomScrollTop === 0) &&
|
||||||
discussionElement.scrollTop < discussionElement.scrollHeight
|
discussionElement.scrollTop < discussionElement.scrollHeight
|
||||||
) {
|
) {
|
||||||
discussionElement.scrollTop = discussionElement.scrollHeight;
|
discussionElement.scrollTop = discussionElement.scrollHeight;
|
||||||
setDidInitialScroll(true);
|
setDidInitialScroll(true);
|
||||||
setBottomScrollTop(discussionElement.scrollTop);
|
setBottomScrollTop(discussionElement.scrollTop);
|
||||||
}
|
}
|
||||||
}, [didInitialScroll, discussionElement, isMobile, viewMode]);
|
}, [bottomScrollTop, didInitialScroll, discussionElement, isMobile, viewMode]);
|
||||||
|
|
||||||
// Register scroll handler (TODO: Should throttle/debounce)
|
// Register scroll handler (TODO: Should throttle/debounce)
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
@ -334,10 +341,15 @@ export default function LivestreamChatLayout(props: Props) {
|
||||||
<Spinner />
|
<Spinner />
|
||||||
</div>
|
</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
|
||||||
button="secondary"
|
button="secondary"
|
||||||
className="livestream-comments__scroll-to-recent"
|
className="livestream-comments__scroll-to-recent"
|
||||||
|
@ -354,13 +366,10 @@ export default function LivestreamChatLayout(props: Props) {
|
||||||
embed={embed}
|
embed={embed}
|
||||||
uri={uri}
|
uri={uri}
|
||||||
onDoneReplying={restoreScrollPos}
|
onDoneReplying={restoreScrollPos}
|
||||||
onSlimInputClick={
|
onSlimInputClick={() => {
|
||||||
scrollPos &&
|
restoreScrollPos();
|
||||||
recentScrollPos &&
|
setInputDrawerOpen(!inputDrawerOpen);
|
||||||
scrollPos >= recentScrollPos &&
|
}}
|
||||||
viewMode === VIEW_MODES.CHAT &&
|
|
||||||
restoreScrollPos
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -28,10 +28,21 @@ type Props = {
|
||||||
stakedLevel: number,
|
stakedLevel: number,
|
||||||
isMobile?: boolean,
|
isMobile?: boolean,
|
||||||
handleDismissPin?: () => void,
|
handleDismissPin?: () => void,
|
||||||
|
restoreScrollPos?: () => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function LivestreamComment(props: Props) {
|
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 {
|
const {
|
||||||
channel_url: authorUri,
|
channel_url: authorUri,
|
||||||
|
@ -46,8 +57,6 @@ export default function LivestreamComment(props: Props) {
|
||||||
timestamp,
|
timestamp,
|
||||||
} = comment;
|
} = comment;
|
||||||
|
|
||||||
const commentRef = React.useRef();
|
|
||||||
|
|
||||||
const [hasUserMention, setUserMention] = React.useState(false);
|
const [hasUserMention, setUserMention] = React.useState(false);
|
||||||
|
|
||||||
const isStreamer = claim && claim.signing_channel && claim.signing_channel.permanent_url === authorUri;
|
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 isSticker = Boolean(stickerUrlFromMessage);
|
||||||
const timePosted = timestamp * 1000;
|
const timePosted = timestamp * 1000;
|
||||||
const commentIsMine = comment.channel_id && isMyComment(comment.channel_id);
|
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
|
// todo: implement comment_list --mine in SDK so redux can grab with selectCommentIsMine
|
||||||
function isMyComment(channelId: string) {
|
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,
|
// For every new <LivestreamComment /> component that is rendered on mobile view,
|
||||||
// keep the scroll at the bottom (newest)
|
// keep the scroll at the bottom (newest)
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (isMobile && discussionElement && minScrollPos && discussionElement.scrollTop >= minScrollPos) {
|
if (isMobile && restoreScrollPos) {
|
||||||
discussionElement.scrollTop = discussionElement.scrollHeight;
|
restoreScrollPos();
|
||||||
}
|
}
|
||||||
}, [discussionElement, isMobile, minScrollPos]);
|
}, [isMobile, restoreScrollPos]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
|
@ -82,7 +87,6 @@ export default function LivestreamComment(props: Props) {
|
||||||
'livestream__comment--mentioned': hasUserMention,
|
'livestream__comment--mentioned': hasUserMention,
|
||||||
'livestream__comment--mobile': isMobile,
|
'livestream__comment--mobile': isMobile,
|
||||||
})}
|
})}
|
||||||
ref={commentRef}
|
|
||||||
>
|
>
|
||||||
{supportAmount > 0 && (
|
{supportAmount > 0 && (
|
||||||
<div className="livestreamComment__superchatBanner">
|
<div className="livestreamComment__superchatBanner">
|
||||||
|
|
|
@ -12,10 +12,11 @@ type Props = {
|
||||||
fetchingComments: boolean,
|
fetchingComments: boolean,
|
||||||
uri: string,
|
uri: string,
|
||||||
isMobile?: boolean,
|
isMobile?: boolean,
|
||||||
|
restoreScrollPos?: () => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function LivestreamComments(props: Props) {
|
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);
|
const [forceUpdate, setForceUpdate] = React.useState(0);
|
||||||
|
|
||||||
|
@ -56,6 +57,7 @@ export default function LivestreamComments(props: Props) {
|
||||||
uri={uri}
|
uri={uri}
|
||||||
forceUpdate={forceUpdate}
|
forceUpdate={forceUpdate}
|
||||||
isMobile
|
isMobile
|
||||||
|
restoreScrollPos={restoreScrollPos}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue