From ecc3599a85d9ed91d508775c66b0d15c758ad833 Mon Sep 17 00:00:00 2001 From: saltrafael <76502841+saltrafael@users.noreply.github.com> Date: Mon, 14 Feb 2022 16:28:25 -0300 Subject: [PATCH] Improve scrolling behavior, fix tips sorting (#863) --- ui/component/claimLink/view.jsx | 4 +- ui/component/livestreamChatLayout/index.js | 11 +- ui/component/livestreamChatLayout/view.jsx | 213 ++++++++++----------- ui/component/livestreamComments/index.js | 17 +- ui/component/livestreamComments/view.jsx | 50 +++-- 5 files changed, 163 insertions(+), 132 deletions(-) diff --git a/ui/component/claimLink/view.jsx b/ui/component/claimLink/view.jsx index 1596e1487d1..909fe9c6560 100644 --- a/ui/component/claimLink/view.jsx +++ b/ui/component/claimLink/view.jsx @@ -14,7 +14,7 @@ type Props = { children: React.Node, description: ?string, isResolvingUri: boolean, - doResolveUri: (string) => void, + doResolveUri: (string, boolean) => void, playingUri: ?PlayingUri, parentCommentId?: string, isMarkdownPost?: boolean, @@ -43,7 +43,7 @@ class ClaimLink extends React.Component { const { isResolvingUri, doResolveUri, claim, uri } = props; if (!isResolvingUri && claim === undefined && uri) { - doResolveUri(uri); + doResolveUri(uri, true); } }; diff --git a/ui/component/livestreamChatLayout/index.js b/ui/component/livestreamChatLayout/index.js index c109d173fe5..2cc6f40150b 100644 --- a/ui/component/livestreamChatLayout/index.js +++ b/ui/component/livestreamChatLayout/index.js @@ -6,7 +6,6 @@ import { doCommentList, doSuperChatList } from 'redux/actions/comments'; import { selectTopLevelCommentsForUri, selectSuperChatsForUri, - selectSuperChatTotalAmountForUri, selectPinnedCommentsForUri, } from 'redux/selectors/comments'; import { selectThemePath } from 'redux/selectors/settings'; @@ -14,19 +13,21 @@ import LivestreamChatLayout from './view'; const select = (state, props) => { const { uri } = props; + const claim = selectClaimForUri(state, uri); return { - claim: selectClaimForUri(state, uri), + claimId: claim && claim.claim_id, comments: selectTopLevelCommentsForUri(state, uri, MAX_LIVESTREAM_COMMENTS), pinnedComments: selectPinnedCommentsForUri(state, uri), superChats: selectSuperChatsForUri(state, uri), - superChatsTotalAmount: selectSuperChatTotalAmountForUri(state, uri), theme: selectThemePath(state), }; }; -export default connect(select, { +const perform = { doCommentList, doSuperChatList, doResolveUris, -})(LivestreamChatLayout); +}; + +export default connect(select, perform)(LivestreamChatLayout); diff --git a/ui/component/livestreamChatLayout/view.jsx b/ui/component/livestreamChatLayout/view.jsx index 13096265af9..15f02f2f859 100644 --- a/ui/component/livestreamChatLayout/view.jsx +++ b/ui/component/livestreamChatLayout/view.jsx @@ -1,8 +1,6 @@ // @flow import 'scss/component/_livestream-chat.scss'; -// $FlowFixMe -import { Global } from '@emotion/react'; // $FlowFixMe import { grey } from '@mui/material/colors'; @@ -17,39 +15,38 @@ import LivestreamComments from 'component/livestreamComments'; import LivestreamSuperchats from './livestream-superchats'; import LivestreamMenu from './livestream-menu'; import React from 'react'; -import Spinner from 'component/spinner'; import Yrbl from 'component/yrbl'; import { getTipValues } from 'util/livestream'; import Slide from '@mui/material/Slide'; -const VIEW_MODES = { +export const VIEW_MODES = { CHAT: 'chat', SUPERCHAT: 'sc', }; const COMMENT_SCROLL_TIMEOUT = 25; -const LARGE_SUPER_CHAT_LIST_THRESHOLD = 20; type Props = { - claim: ?StreamClaim, - comments: Array, embed?: boolean, isPopoutWindow?: boolean, - pinnedComments: Array, - superChats: Array, uri: string, hideHeader?: boolean, superchatsHidden?: boolean, customViewMode?: string, - theme: string, setCustomViewMode?: (any) => void, - doCommentList: (string, string, number, number) => void, - doResolveUris: (Array, boolean) => void, - doSuperChatList: (string) => void, + // redux + claimId?: string, + comments: Array, + pinnedComments: Array, + superChats: Array, + theme: string, + doCommentList: (uri: string, parentId: string, page: number, pageSize: number) => void, + doResolveUris: (uris: Array, cache: boolean) => void, + doSuperChatList: (uri: string) => void, }; export default function LivestreamChatLayout(props: Props) { const { - claim, + claimId, comments: commentsByChronologicalOrder, embed, isPopoutWindow, @@ -83,14 +80,21 @@ export default function LivestreamChatLayout(props: Props) { const [didInitialScroll, setDidInitialScroll] = React.useState(false); const [minScrollHeight, setMinScrollHeight] = React.useState(0); const [keyboardOpened, setKeyboardOpened] = React.useState(false); + const [superchatsAmount, setSuperchatsAmount] = React.useState(false); + const [chatElement, setChatElement] = React.useState(); - const claimId = claim && claim.claim_id; - const commentsToDisplay = viewMode === VIEW_MODES.CHAT ? commentsByChronologicalOrder : superChatsByAmount; + const superChatsByChronologicalOrder = + superChatsByAmount && superChatsByAmount.sort((a, b) => b.timestamp - a.timestamp); + const commentsToDisplay = + viewMode === VIEW_MODES.CHAT ? commentsByChronologicalOrder : superChatsByChronologicalOrder; const commentsLength = commentsToDisplay && commentsToDisplay.length; const pinnedComment = pinnedComments.length > 0 ? pinnedComments[0] : null; - const { superChatsChannelUrls, superChatsFiatAmount, superChatsLBCAmount } = getTipValues(superChatsByAmount); + const { superChatsChannelUrls, superChatsFiatAmount, superChatsLBCAmount } = getTipValues( + superChatsByChronologicalOrder + ); const scrolledPastRecent = Boolean( - viewMode === VIEW_MODES.CHAT && !isMobile ? scrollPos < 0 : scrollPos < minScrollHeight + (viewMode !== VIEW_MODES.SUPERCHAT || !resolvingSuperChats) && + (!isMobile ? scrollPos < 0 : scrollPos < minScrollHeight) ); const restoreScrollPos = React.useCallback(() => { @@ -107,16 +111,26 @@ export default function LivestreamChatLayout(props: Props) { } }, [discussionElement, isMobile, lastCommentElem, minScrollHeight]); - const commentsRef = React.createRef(); + function toggleClick(toggleMode: string) { + if (toggleMode === VIEW_MODES.SUPERCHAT) { + toggleSuperChat(); + } else { + setViewMode(VIEW_MODES.CHAT); + } + + if (discussionElement) { + discussionElement.scrollTop = 0; + } + } function toggleSuperChat() { - if (superChatsChannelUrls && superChatsChannelUrls.length > 0) { - doResolveUris(superChatsChannelUrls, true); + const hasNewSuperchats = !superchatsAmount || superChatsChannelUrls.length !== superchatsAmount; - if (superChatsByAmount.length > LARGE_SUPER_CHAT_LIST_THRESHOLD) { - setResolvingSuperChats(true); - } + if (superChatsChannelUrls && hasNewSuperchats) { + setSuperchatsAmount(superChatsChannelUrls.length); + doResolveUris(superChatsChannelUrls, false); } + setViewMode(VIEW_MODES.SUPERCHAT); if (setCustomViewMode) setCustomViewMode(VIEW_MODES.SUPERCHAT); } @@ -135,17 +149,21 @@ export default function LivestreamChatLayout(props: Props) { }, [claimId, uri, doCommentList, doSuperChatList]); React.useEffect(() => { - if (isMobile && viewMode === VIEW_MODES.CHAT && !didInitialScroll) { + if (isMobile && !didInitialScroll) { restoreScrollPos(); setDidInitialScroll(true); } }, [didInitialScroll, isMobile, restoreScrollPos, viewMode]); + React.useEffect(() => { + if (discussionElement && !openedPopoutWindow) setChatElement(discussionElement); + }, [discussionElement, openedPopoutWindow]); + // Register scroll handler (TODO: Should throttle/debounce) React.useEffect(() => { function handleScroll() { - if (discussionElement) { - const scrollTop = discussionElement.scrollTop; + if (chatElement) { + const scrollTop = chatElement.scrollTop; if (scrollTop !== scrollPos) { setScrollPos(scrollTop); @@ -153,11 +171,12 @@ export default function LivestreamChatLayout(props: Props) { } } - if (discussionElement) { - discussionElement.addEventListener('scroll', handleScroll); - return () => discussionElement.removeEventListener('scroll', handleScroll); + if (chatElement) { + chatElement.addEventListener('scroll', handleScroll); + + return () => chatElement.removeEventListener('scroll', handleScroll); } - }, [discussionElement, scrollPos]); + }, [chatElement, scrollPos]); // Retain scrollPos=0 when receiving new messages. React.useEffect(() => { @@ -193,45 +212,7 @@ export default function LivestreamChatLayout(props: Props) { } }, [keyboardOpened, restoreScrollPos]); - // Stop spinner for resolving superchats - React.useEffect(() => { - if (resolvingSuperChats) { - // The real solution to the sluggishness is to fix the claim store/selectors - // and to paginate the long superchat list. This serves as a band-aid, - // showing a spinner while we batch-resolve. The duration is just a rough - // estimate -- the lag will handle the remaining time. - const timer = setTimeout(() => { - setResolvingSuperChats(false); - // Scroll to the top: - if (discussionElement) { - const divHeight = discussionElement.scrollHeight; - discussionElement.scrollTop = divHeight * -1; - } - }, 1000); - return () => clearTimeout(timer); - } - }, [discussionElement, resolvingSuperChats]); - - if (!claim) return null; - - const chatContentToggle = (toggleMode: string, label: any) => ( -