From 93e0311984ef2219146ab4729b9c8665ddfca25e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 4 Oct 2024 00:15:31 +0200 Subject: [PATCH] pl-fe: further actually improve virtual scrolling behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../pl-fe/src/components/scrollable-list.tsx | 116 +++++++++--------- packages/pl-fe/src/components/status-list.tsx | 5 +- .../features/chats/components/chat-list.tsx | 1 - .../chats/components/chat-message-list.tsx | 1 - .../chats/components/chat-search/results.tsx | 1 - .../steps/suggested-accounts-step.tsx | 1 - .../src/features/status/components/thread.tsx | 21 ++-- .../modals/familiar-followers-modal.tsx | 1 - .../ui/components/modals/favourites-modal.tsx | 1 - .../ui/components/modals/media-modal.tsx | 2 +- .../ui/components/modals/mentions-modal.tsx | 1 - .../ui/components/modals/reactions-modal.tsx | 1 - .../ui/components/modals/reblogs-modal.tsx | 1 - 13 files changed, 74 insertions(+), 79 deletions(-) diff --git a/packages/pl-fe/src/components/scrollable-list.tsx b/packages/pl-fe/src/components/scrollable-list.tsx index f826678bf..b019f1ecb 100644 --- a/packages/pl-fe/src/components/scrollable-list.tsx +++ b/packages/pl-fe/src/components/scrollable-list.tsx @@ -8,16 +8,7 @@ import { useSettings } from 'pl-fe/hooks'; import LoadMore from './load-more'; import { Card, Spinner } from './ui'; -type IScrollableListWindowScroll = { - /** Whether to use the window to scroll the content instead of the container. */ - useWindowScroll?: true; -} | { - /** Whether to use the window to scroll the content instead of the container. */ - useWindowScroll: false; - parentRef: React.RefObject; -}; - -interface IScrollableList { +interface IScrollableListBase { /** Pagination callback when the end of the list is reached. */ onLoadMore?: () => void; /** Whether the data is currently being fetched. */ @@ -42,8 +33,6 @@ interface IScrollableList { placeholderComponent?: React.ComponentType | React.NamedExoticComponent; /** Number of placeholders to render while loading. */ placeholderCount?: number; - /** Extra class names on the parent element. */ - className?: string; /** Extra class names on the list element. */ listClassName?: string; /** Class names on each item container. */ @@ -52,8 +41,6 @@ interface IScrollableList { loadMoreClassName?: string; /** `id` attribute on the parent element. */ id?: string; - /** CSS styles on the parent element. */ - style?: React.CSSProperties; /** Initial item index to scroll to. */ initialIndex?: number; /** Estimated size for items. */ @@ -62,8 +49,20 @@ interface IScrollableList { alignToBottom?: boolean; } -const ScrollableList = React.forwardRef, IScrollableList & IScrollableListWindowScroll>(({ - prepend = null, +interface IScrollableListWithContainer extends IScrollableListBase { + /** Extra class names on the container element. */ + className?: string; + /** CSS styles on the container element. */ + style?: React.CSSProperties; +} + +interface IScrollableListWithoutContainer extends IScrollableListBase { + parentRef: React.RefObject; +} + +type IScrollableList = IScrollableListWithContainer | IScrollableListWithoutContainer; + +const ScrollableList = React.forwardRef, IScrollableList>(({ prepend = null, alwaysPrepend, children, isLoading, @@ -72,7 +71,6 @@ const ScrollableList = React.forwardRef, IScrollableList & showLoading, onScroll, onLoadMore, - className, listClassName, itemClassName, loadMoreClassName, @@ -81,15 +79,12 @@ const ScrollableList = React.forwardRef, IScrollableList & placeholderComponent: Placeholder, placeholderCount = 0, initialIndex, - style = {}, estimatedSize = 300, alignToBottom, ...props }, ref) => { const { autoloadMore } = useSettings(); - const parentRef = React.useRef(null); - /** Normalized children. */ const elements = Array.from(children || []); @@ -97,11 +92,11 @@ const ScrollableList = React.forwardRef, IScrollableList & const data = showPlaceholder ? Array(placeholderCount).fill('') : elements; - const virtualizer = props.useWindowScroll === false ? useVirtualizer({ + const virtualizer = 'parentRef' in props ? useVirtualizer({ count: data.length + (hasMore ? 1 : 0), overscan: 3, estimateSize: () => estimatedSize, - getScrollElement: () => props.parentRef.current || parentRef.current, + getScrollElement: () => props.parentRef.current, }) : useWindowVirtualizer({ count: data.length + (hasMore ? 1 : 0), overscan: 3, @@ -170,44 +165,55 @@ const ScrollableList = React.forwardRef, IScrollableList & const virtualItems = virtualizer.getVirtualItems(); + const body = ( +
+ {(!showLoading || showPlaceholder) && data.length ? ( + <> + {prepend} + {virtualItems.map((item) => ( +
+ {renderItem(item.index)} +
+ ))} + + ) : renderEmpty()} +
+ ); + + if ('parentRef' in props) return body; + return (
-
- {(!showLoading || showPlaceholder) && data.length ? ( - <> - {prepend} - {virtualItems.map((item) => ( -
- {renderItem(item.index)} -
- ))} - - ) : renderEmpty()} -
+ {body}
); }); -export { type IScrollableList, ScrollableList as default }; +export { + type IScrollableList, + type IScrollableListWithContainer, + type IScrollableListWithoutContainer, + ScrollableList as default, +}; diff --git a/packages/pl-fe/src/components/status-list.tsx b/packages/pl-fe/src/components/status-list.tsx index e44d5936b..de5da2065 100644 --- a/packages/pl-fe/src/components/status-list.tsx +++ b/packages/pl-fe/src/components/status-list.tsx @@ -4,7 +4,7 @@ import React, { useCallback } from 'react'; import { FormattedMessage } from 'react-intl'; import LoadGap from 'pl-fe/components/load-gap'; -import ScrollableList from 'pl-fe/components/scrollable-list'; +import ScrollableList, { type IScrollableListWithContainer } from 'pl-fe/components/scrollable-list'; import StatusContainer from 'pl-fe/containers/status-container'; import FeedSuggestions from 'pl-fe/features/feed-suggestions/feed-suggestions'; import PlaceholderStatus from 'pl-fe/features/placeholder/components/placeholder-status'; @@ -14,9 +14,8 @@ import { usePlFeConfig } from 'pl-fe/hooks'; import { Stack, Text } from './ui'; import type { OrderedSet as ImmutableOrderedSet } from 'immutable'; -import type { IScrollableList } from 'pl-fe/components/scrollable-list'; -interface IStatusList extends Omit { +interface IStatusList extends Omit { /** Unique key to preserve the scroll position when navigating back. */ scrollKey: string; /** List of status IDs to display. */ diff --git a/packages/pl-fe/src/features/chats/components/chat-list.tsx b/packages/pl-fe/src/features/chats/components/chat-list.tsx index d6f83feb3..2810ad44a 100644 --- a/packages/pl-fe/src/features/chats/components/chat-list.tsx +++ b/packages/pl-fe/src/features/chats/components/chat-list.tsx @@ -59,7 +59,6 @@ const ChatList: React.FC = ({ onClickChat, parentRef, topOffset }) => hasMore={hasNextPage} onLoadMore={handleLoadMore} estimatedSize={64} - useWindowScroll={false} parentRef={parentRef} loadMoreClassName='mx-4 mb-4' > diff --git a/packages/pl-fe/src/features/chats/components/chat-message-list.tsx b/packages/pl-fe/src/features/chats/components/chat-message-list.tsx index 1cc1e148b..964fb938b 100644 --- a/packages/pl-fe/src/features/chats/components/chat-message-list.tsx +++ b/packages/pl-fe/src/features/chats/components/chat-message-list.tsx @@ -196,7 +196,6 @@ const ChatMessageList: React.FC = ({ chat }) => { isLoading={isFetching} showLoading={isFetching && !isFetchingNextPage} onLoadMore={handleStartReached} - useWindowScroll={false} parentRef={parentRef} > {cachedChatMessages.map((chatMessage, index) => { diff --git a/packages/pl-fe/src/features/chats/components/chat-search/results.tsx b/packages/pl-fe/src/features/chats/components/chat-search/results.tsx index f78dd890c..e53f7c286 100644 --- a/packages/pl-fe/src/features/chats/components/chat-search/results.tsx +++ b/packages/pl-fe/src/features/chats/components/chat-search/results.tsx @@ -61,7 +61,6 @@ const Results = ({ accountSearchResult, onSelect, parentRef }: IResults) => { isLoading={isFetching} hasMore={hasNextPage} onLoadMore={handleLoadMore} - useWindowScroll={false} parentRef={parentRef} > {(accounts || []).map((chat) => renderAccount(chat))} diff --git a/packages/pl-fe/src/features/onboarding/steps/suggested-accounts-step.tsx b/packages/pl-fe/src/features/onboarding/steps/suggested-accounts-step.tsx index c0a89012d..02681b36b 100644 --- a/packages/pl-fe/src/features/onboarding/steps/suggested-accounts-step.tsx +++ b/packages/pl-fe/src/features/onboarding/steps/suggested-accounts-step.tsx @@ -21,7 +21,6 @@ const SuggestedAccountsStep = ({ onNext }: { onNext: () => void }) => { {data.map((suggestion) => ( diff --git a/packages/pl-fe/src/features/status/components/thread.tsx b/packages/pl-fe/src/features/status/components/thread.tsx index 70e956a44..6a6b0c558 100644 --- a/packages/pl-fe/src/features/status/components/thread.tsx +++ b/packages/pl-fe/src/features/status/components/thread.tsx @@ -78,14 +78,14 @@ const getDescendantsIds = createSelector([ interface IThread { status: SelectedStatus; withMedia?: boolean; - useWindowScroll?: boolean; + isModal?: boolean; itemClassName?: string; } const Thread: React.FC = ({ itemClassName, status, - useWindowScroll = true, + isModal, withMedia = true, }) => { const dispatch = useAppDispatch(); @@ -114,7 +114,7 @@ const Thread: React.FC = ({ }); let initialIndex = ancestorsIds.size; - if (!useWindowScroll && initialIndex !== 0) initialIndex = ancestorsIds.size + 1; + if (isModal && initialIndex !== 0) initialIndex = ancestorsIds.size + 1; const node = useRef(null); const statusRef = useRef(null); @@ -234,7 +234,7 @@ const Thread: React.FC = ({ }; const _selectChild = (index: number) => { - if (!useWindowScroll) index = index + 1; + if (isModal) index = index + 1; const selector = `[data-index="${index}"] .focusable`; const element = node.current?.querySelector(selector); @@ -341,7 +341,7 @@ const Thread: React.FC = ({ @@ -356,7 +356,7 @@ const Thread: React.FC = ({ const children: JSX.Element[] = []; - if (!useWindowScroll) { + if (isModal) { // Add padding to the top of the Thread (for Media Modal) children.push(
); } @@ -376,8 +376,8 @@ const Thread: React.FC = ({ space={2} className={ clsx({ - 'h-full': !useWindowScroll, - 'mt-2': useWindowScroll, + 'h-full': isModal, + 'mt-2': !isModal, }) } > @@ -391,7 +391,7 @@ const Thread: React.FC = ({ ref={node} className={ clsx('bg-white black:bg-black dark:bg-primary-900', { - 'h-full overflow-auto': !useWindowScroll, + 'h-full overflow-auto': isModal, }) } > @@ -403,10 +403,9 @@ const Thread: React.FC = ({ itemClassName={itemClassName} listClassName={ clsx({ - 'h-full': !useWindowScroll, + 'h-full': isModal, }) } - useWindowScroll={useWindowScroll} parentRef={node} > {children} diff --git a/packages/pl-fe/src/features/ui/components/modals/familiar-followers-modal.tsx b/packages/pl-fe/src/features/ui/components/modals/familiar-followers-modal.tsx index 0596f88c2..87b006cf3 100644 --- a/packages/pl-fe/src/features/ui/components/modals/familiar-followers-modal.tsx +++ b/packages/pl-fe/src/features/ui/components/modals/familiar-followers-modal.tsx @@ -38,7 +38,6 @@ const FamiliarFollowersModal: React.FC {familiarFollowerIds.map(id => diff --git a/packages/pl-fe/src/features/ui/components/modals/favourites-modal.tsx b/packages/pl-fe/src/features/ui/components/modals/favourites-modal.tsx index 5cca93b98..a3f6a3241 100644 --- a/packages/pl-fe/src/features/ui/components/modals/favourites-modal.tsx +++ b/packages/pl-fe/src/features/ui/components/modals/favourites-modal.tsx @@ -54,7 +54,6 @@ const FavouritesModal: React.FC = ({ onCl onLoadMore={handleLoadMore} hasMore={!!next} estimatedSize={42} - useWindowScroll={false} parentRef={modalRef} > {accountIds.map(id => diff --git a/packages/pl-fe/src/features/ui/components/modals/media-modal.tsx b/packages/pl-fe/src/features/ui/components/modals/media-modal.tsx index d99aba5ee..481877630 100644 --- a/packages/pl-fe/src/features/ui/components/modals/media-modal.tsx +++ b/packages/pl-fe/src/features/ui/components/modals/media-modal.tsx @@ -337,8 +337,8 @@ const MediaModal: React.FC = (props) => {
)} diff --git a/packages/pl-fe/src/features/ui/components/modals/mentions-modal.tsx b/packages/pl-fe/src/features/ui/components/modals/mentions-modal.tsx index 6ab36dc96..ba559f169 100644 --- a/packages/pl-fe/src/features/ui/components/modals/mentions-modal.tsx +++ b/packages/pl-fe/src/features/ui/components/modals/mentions-modal.tsx @@ -46,7 +46,6 @@ const MentionsModal: React.FC = ({ onClose, listClassName='max-w-full' itemClassName='pb-3' estimatedSize={42} - useWindowScroll={false} parentRef={modalRef} > {accountIds.map(id => diff --git a/packages/pl-fe/src/features/ui/components/modals/reactions-modal.tsx b/packages/pl-fe/src/features/ui/components/modals/reactions-modal.tsx index da5a30c42..07d06894f 100644 --- a/packages/pl-fe/src/features/ui/components/modals/reactions-modal.tsx +++ b/packages/pl-fe/src/features/ui/components/modals/reactions-modal.tsx @@ -95,7 +95,6 @@ const ReactionsModal: React.FC = ({ onClos itemClassName='pb-3' style={{ height: 'calc(80vh - 88px)' }} estimatedSize={42} - useWindowScroll={false} parentRef={modalRef} > {accounts.map((account) => diff --git a/packages/pl-fe/src/features/ui/components/modals/reblogs-modal.tsx b/packages/pl-fe/src/features/ui/components/modals/reblogs-modal.tsx index e0514763f..6a8120c58 100644 --- a/packages/pl-fe/src/features/ui/components/modals/reblogs-modal.tsx +++ b/packages/pl-fe/src/features/ui/components/modals/reblogs-modal.tsx @@ -56,7 +56,6 @@ const ReblogsModal: React.FC = ({ onClose, s onLoadMore={handleLoadMore} hasMore={!!next} estimatedSize={42} - useWindowScroll={false} parentRef={modalRef} > {accountIds.map((id) =>