Fix scroll re-rendering issues and improve superchats

This commit is contained in:
Rafael 2021-12-28 12:51:37 -03:00 committed by Thomas Zarebczan
parent c50cc422b7
commit 1578810013
2 changed files with 37 additions and 39 deletions

View file

@ -9,6 +9,7 @@ type Props = {
className?: string, className?: string,
customAmounts?: { amountFiat: number, amountLBC: number }, customAmounts?: { amountFiat: number, amountLBC: number },
fee?: boolean, fee?: boolean,
hideTitle?: boolean,
isEstimate?: boolean, isEstimate?: boolean,
isFiat?: boolean, isFiat?: boolean,
noFormat?: boolean, noFormat?: boolean,
@ -38,6 +39,7 @@ class CreditAmount extends React.PureComponent<Props> {
className, className,
customAmounts, customAmounts,
fee, fee,
hideTitle,
isEstimate, isEstimate,
isFiat, isFiat,
noFormat, noFormat,
@ -94,7 +96,7 @@ class CreditAmount extends React.PureComponent<Props> {
return ( return (
<span <span
title={amount ? formatFullPrice(amount, 2) : ''} title={amount && !hideTitle ? formatFullPrice(amount, 2) : ''}
className={classnames(className, { className={classnames(className, {
'super-chat': superChat, 'super-chat': superChat,
'super-chat--light': superChatLight, 'super-chat--light': superChatLight,

View file

@ -27,8 +27,10 @@ type Props = {
doResolveUris: (Array<string>, boolean) => void, doResolveUris: (Array<string>, boolean) => void,
}; };
const VIEW_MODE_CHAT = 'view_chat'; const VIEW_MODES = {
const VIEW_MODE_SUPER_CHAT = 'view_superchat'; CHAT: 'chat',
SUPERCHAT: 'sc',
};
const COMMENT_SCROLL_TIMEOUT = 25; const COMMENT_SCROLL_TIMEOUT = 25;
const LARGE_SUPER_CHAT_LIST_THRESHOLD = 20; const LARGE_SUPER_CHAT_LIST_THRESHOLD = 20;
@ -50,14 +52,14 @@ export default function LivestreamComments(props: Props) {
let superChatsFiatAmount, superChatsLBCAmount, superChatsTotalAmount, hasSuperChats; let superChatsFiatAmount, superChatsLBCAmount, superChatsTotalAmount, hasSuperChats;
const commentsRef = React.createRef(); const commentsRef = React.createRef();
const [viewMode, setViewMode] = React.useState(VIEW_MODE_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);
const [resolvingSuperChat, setResolvingSuperChat] = React.useState(false); const [resolvingSuperChat, setResolvingSuperChat] = React.useState(false);
const claimId = claim && claim.claim_id; const claimId = claim && claim.claim_id;
const commentsLength = commentsByChronologicalOrder && commentsByChronologicalOrder.length; const commentsLength = commentsByChronologicalOrder && commentsByChronologicalOrder.length;
const commentsToDisplay = viewMode === VIEW_MODE_CHAT ? commentsByChronologicalOrder : superChatsByTipAmount; const commentsToDisplay = viewMode === VIEW_MODES.CHAT ? commentsByChronologicalOrder : superChatsByTipAmount;
const stickerSuperChats = const stickerSuperChats =
superChatsByTipAmount && superChatsByTipAmount.filter(({ comment }) => Boolean(parseSticker(comment))); superChatsByTipAmount && superChatsByTipAmount.filter(({ comment }) => Boolean(parseSticker(comment)));
@ -101,7 +103,7 @@ export default function LivestreamComments(props: Props) {
// Register scroll handler (TODO: Should throttle/debounce) // Register scroll handler (TODO: Should throttle/debounce)
React.useEffect(() => { React.useEffect(() => {
function handleScroll() { function handleScroll() {
if (discussionElement) { if (discussionElement && viewMode === VIEW_MODES.CHAT) {
const scrollTop = discussionElement.scrollTop; const scrollTop = discussionElement.scrollTop;
if (scrollTop !== scrollPos) { if (scrollTop !== scrollPos) {
setScrollPos(scrollTop); setScrollPos(scrollTop);
@ -109,11 +111,11 @@ export default function LivestreamComments(props: Props) {
} }
} }
if (discussionElement) { if (discussionElement && viewMode === VIEW_MODES.CHAT) {
discussionElement.addEventListener('scroll', handleScroll); discussionElement.addEventListener('scroll', handleScroll);
return () => discussionElement.removeEventListener('scroll', handleScroll); return () => discussionElement.removeEventListener('scroll', handleScroll);
} }
}, [discussionElement, scrollPos]); }, [discussionElement, scrollPos, viewMode]);
// Retain scrollPos=0 when receiving new messages. // Retain scrollPos=0 when receiving new messages.
React.useEffect(() => { React.useEffect(() => {
@ -201,12 +203,10 @@ export default function LivestreamComments(props: Props) {
<div className="recommended-content__toggles"> <div className="recommended-content__toggles">
{/* the superchats in chronological order button */} {/* the superchats in chronological order button */}
<Button <Button
className={classnames('button-toggle', { className={classnames('button-toggle', { 'button-toggle--active': viewMode === VIEW_MODES.CHAT })}
'button-toggle--active': viewMode === VIEW_MODE_CHAT,
})}
label={__('Chat')} label={__('Chat')}
onClick={() => { onClick={() => {
setViewMode(VIEW_MODE_CHAT); setViewMode(VIEW_MODES.CHAT);
const livestreamCommentsDiv = document.getElementsByClassName('livestream__comments')[0]; const livestreamCommentsDiv = document.getElementsByClassName('livestream__comments')[0];
livestreamCommentsDiv.scrollTop = livestreamCommentsDiv.scrollHeight; livestreamCommentsDiv.scrollTop = livestreamCommentsDiv.scrollHeight;
}} }}
@ -214,9 +214,7 @@ export default function LivestreamComments(props: Props) {
{/* the button to show superchats listed by most to least support amount */} {/* the button to show superchats listed by most to least support amount */}
<Button <Button
className={classnames('button-toggle', { className={classnames('button-toggle', { 'button-toggle--active': viewMode === VIEW_MODES.SUPERCHAT })}
'button-toggle--active': viewMode === VIEW_MODE_SUPER_CHAT,
})}
label={ label={
<> <>
<CreditAmount amount={superChatsLBCAmount || 0} size={8} /> / <CreditAmount amount={superChatsLBCAmount || 0} size={8} /> /
@ -225,7 +223,7 @@ export default function LivestreamComments(props: Props) {
} }
onClick={() => { onClick={() => {
resolveSuperChat(); resolveSuperChat();
setViewMode(VIEW_MODE_SUPER_CHAT); setViewMode(VIEW_MODES.SUPERCHAT);
}} }}
/> />
</div> </div>
@ -238,21 +236,19 @@ export default function LivestreamComments(props: Props) {
</div> </div>
)} )}
<div ref={commentsRef} className="livestream__comments-wrapper"> <div ref={commentsRef} className="livestream__comments-wrapper">
{viewMode === VIEW_MODE_CHAT && superChatsByTipAmount && hasSuperChats && ( {viewMode === VIEW_MODES.CHAT && superChatsByTipAmount && hasSuperChats && (
<div className="livestream-superchats__wrapper"> <div className="livestream-superchats__wrapper">
<div className="livestream-superchats__inner"> <div className="livestream-superchats__inner">
{superChatTopTen.map((superChat: Comment) => { {superChatTopTen.map((superChat: Comment) => {
const { comment, comment_id, channel_url, support_amount, is_fiat } = superChat;
const isSticker = stickerSuperChats && stickerSuperChats.includes(superChat); const isSticker = stickerSuperChats && stickerSuperChats.includes(superChat);
const stickerImg = <OptimizedImage src={getStickerUrl(comment)} waitLoad loading="lazy" />;
const SuperChatWrapper = !isSticker
? ({ children }) => <Tooltip title={superChat.comment}>{children}</Tooltip>
: ({ children }) => <>{children}</>;
return ( return (
<SuperChatWrapper key={superChat.comment_id}> <Tooltip title={isSticker ? stickerImg : comment} key={comment_id}>
<div className="livestream-superchat"> <div className="livestream-superchat">
<div className="livestream-superchat__thumbnail"> <div className="livestream-superchat__thumbnail">
<ChannelThumbnail uri={superChat.channel_url} xsmall /> <ChannelThumbnail uri={channel_url} xsmall />
</div> </div>
<div <div
@ -262,41 +258,41 @@ export default function LivestreamComments(props: Props) {
})} })}
> >
<div className="livestream-superchat__info--user"> <div className="livestream-superchat__info--user">
<UriIndicator uri={superChat.channel_url} link /> <UriIndicator uri={channel_url} link />
<CreditAmount <CreditAmount
hideTitle
size={10} size={10}
className="livestream-superchat__amount-large" className="livestream-superchat__amount-large"
amount={superChat.support_amount} amount={support_amount}
isFiat={superChat.is_fiat} isFiat={is_fiat}
/> />
</div> </div>
{stickerSuperChats.includes(superChat) && getStickerUrl(superChat.comment) && (
<div className="livestream-superchat__info--image"> {isSticker && <div className="livestream-superchat__info--image">{stickerImg}</div>}
<OptimizedImage src={getStickerUrl(superChat.comment)} waitLoad loading="lazy" />
</div>
)}
</div> </div>
</div> </div>
</SuperChatWrapper> </Tooltip>
); );
})} })}
{showMoreSuperChatsButton && ( {showMoreSuperChatsButton && (
<Button <Button
title={__('Show More...')} title={__('Show More...')}
label={__('Show More')}
button="inverse" button="inverse"
className="close-button" className="close-button"
onClick={() => { onClick={() => {
resolveSuperChat(); resolveSuperChat();
setViewMode(VIEW_MODE_SUPER_CHAT); setViewMode(VIEW_MODES.SUPERCHAT);
}} }}
icon={ICONS.MORE} iconRight={ICONS.MORE}
/> />
)} )}
</div> </div>
</div> </div>
)} )}
{pinnedComment && showPinned && viewMode === VIEW_MODE_CHAT && ( {pinnedComment && showPinned && viewMode === VIEW_MODES.CHAT && (
<div className="livestream-pinned__wrapper"> <div className="livestream-pinned__wrapper">
<LivestreamComment <LivestreamComment
key={pinnedComment.comment_id} key={pinnedComment.comment_id}
@ -324,7 +320,7 @@ export default function LivestreamComments(props: Props) {
{/* top to bottom comment display */} {/* top to bottom comment display */}
{!fetchingComments && commentsByChronologicalOrder.length > 0 ? ( {!fetchingComments && commentsByChronologicalOrder.length > 0 ? (
<div className="livestream__comments"> <div className="livestream__comments">
{viewMode === VIEW_MODE_CHAT && {viewMode === VIEW_MODES.CHAT &&
commentsToDisplay.map((comment) => ( commentsToDisplay.map((comment) => (
<LivestreamComment <LivestreamComment
key={comment.comment_id} key={comment.comment_id}
@ -340,13 +336,13 @@ export default function LivestreamComments(props: Props) {
/> />
))} ))}
{viewMode === VIEW_MODE_SUPER_CHAT && resolvingSuperChat && ( {viewMode === VIEW_MODES.SUPERCHAT && resolvingSuperChat && (
<div className="main--empty"> <div className="main--empty">
<Spinner /> <Spinner />
</div> </div>
)} )}
{viewMode === VIEW_MODE_SUPER_CHAT && {viewMode === VIEW_MODES.SUPERCHAT &&
!resolvingSuperChat && !resolvingSuperChat &&
superChatsReversed && superChatsReversed &&
superChatsReversed.map((comment) => ( superChatsReversed.map((comment) => (
@ -368,7 +364,7 @@ export default function LivestreamComments(props: Props) {
<div className="main--empty" style={{ flex: 1 }} /> <div className="main--empty" style={{ flex: 1 }} />
)} )}
{scrollPos < 0 && viewMode === VIEW_MODE_CHAT && ( {scrollPos < 0 && viewMode === VIEW_MODES.CHAT && (
<Button <Button
button="secondary" button="secondary"
className="livestream__comments__scroll-to-recent" className="livestream__comments__scroll-to-recent"