pl-fe: move ChatList to @tanstack/virtual, some fixes
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
1727dc4e1b
commit
aadd9439aa
14 changed files with 108 additions and 101 deletions
|
@ -8,6 +8,15 @@ 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<HTMLElement>;
|
||||
};
|
||||
|
||||
interface IScrollableList {
|
||||
/** Pagination callback when the end of the list is reached. */
|
||||
onLoadMore?: () => void;
|
||||
|
@ -27,10 +36,8 @@ interface IScrollableList {
|
|||
emptyMessageCard?: boolean;
|
||||
/** Scrollable content. */
|
||||
children: Iterable<React.ReactNode>;
|
||||
/** Callback when the list is scrolled to the top. */
|
||||
onScrollToTop?: () => void;
|
||||
/** Callback when the list is scrolled. */
|
||||
onScroll?: () => void;
|
||||
onScroll?: (startIndex?: number, endIndex?: number) => void;
|
||||
/** Placeholder component to render while loading. */
|
||||
placeholderComponent?: React.ComponentType | React.NamedExoticComponent;
|
||||
/** Number of placeholders to render while loading. */
|
||||
|
@ -47,15 +54,13 @@ interface IScrollableList {
|
|||
id?: string;
|
||||
/** CSS styles on the parent element. */
|
||||
style?: React.CSSProperties;
|
||||
/** Whether to use the window to scroll the content instead of the container. */
|
||||
useWindowScroll?: boolean;
|
||||
/** Initial item index to scroll to. */
|
||||
initialIndex?: number;
|
||||
/** Estimated size for items */
|
||||
estimatedSize?: number;
|
||||
}
|
||||
|
||||
const ScrollableList = React.forwardRef<Virtualizer<any, any>, IScrollableList>(({
|
||||
const ScrollableList = React.forwardRef<Virtualizer<any, any>, IScrollableList & IScrollableListWindowScroll>(({
|
||||
prepend = null,
|
||||
alwaysPrepend,
|
||||
children,
|
||||
|
@ -64,7 +69,6 @@ const ScrollableList = React.forwardRef<Virtualizer<any, any>, IScrollableList>(
|
|||
emptyMessageCard = true,
|
||||
showLoading,
|
||||
onScroll,
|
||||
onScrollToTop,
|
||||
onLoadMore,
|
||||
className,
|
||||
listClassName,
|
||||
|
@ -76,8 +80,8 @@ const ScrollableList = React.forwardRef<Virtualizer<any, any>, IScrollableList>(
|
|||
placeholderCount = 0,
|
||||
initialIndex = 0,
|
||||
style = {},
|
||||
useWindowScroll = true,
|
||||
estimatedSize = 300,
|
||||
...props
|
||||
}, ref) => {
|
||||
const { autoloadMore } = useSettings();
|
||||
|
||||
|
@ -90,15 +94,15 @@ const ScrollableList = React.forwardRef<Virtualizer<any, any>, IScrollableList>(
|
|||
|
||||
const data = showPlaceholder ? Array(placeholderCount).fill('') : elements;
|
||||
|
||||
const virtualizer = useWindowScroll ? useWindowVirtualizer({
|
||||
const virtualizer = props.useWindowScroll === false ? useVirtualizer({
|
||||
count: data.length + (hasMore ? 1 : 0),
|
||||
overscan: 3,
|
||||
estimateSize: () => estimatedSize,
|
||||
}) : useVirtualizer({
|
||||
getScrollElement: () => props.parentRef.current || parentRef.current,
|
||||
}) : useWindowVirtualizer({
|
||||
count: data.length + (hasMore ? 1 : 0),
|
||||
overscan: 3,
|
||||
estimateSize: () => estimatedSize,
|
||||
getScrollElement: () => parentRef.current,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -114,10 +118,8 @@ const ScrollableList = React.forwardRef<Virtualizer<any, any>, IScrollableList>(
|
|||
}, [showLoading, initialIndex]);
|
||||
|
||||
useEffect(() => {
|
||||
if (range?.startIndex === 0) {
|
||||
onScrollToTop?.();
|
||||
} else onScroll?.();
|
||||
}, [range?.startIndex === 0]);
|
||||
onScroll?.(range?.startIndex, range?.endIndex);
|
||||
}, [range?.startIndex, range?.endIndex]);
|
||||
|
||||
useEffect(() => {
|
||||
if (onLoadMore && range?.endIndex === data.length && !showLoading && autoloadMore && hasMore) {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import clsx from 'clsx';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { Virtuoso } from 'react-virtuoso';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import PullToRefresh from 'pl-fe/components/pull-to-refresh';
|
||||
import { Spinner, Stack } from 'pl-fe/components/ui';
|
||||
import ScrollableList from 'pl-fe/components/scrollable-list';
|
||||
import { Stack } from 'pl-fe/components/ui';
|
||||
import PlaceholderChat from 'pl-fe/features/placeholder/components/placeholder-chat';
|
||||
import { useChats } from 'pl-fe/queries/chats';
|
||||
|
||||
|
@ -11,12 +11,11 @@ import ChatListItem from './chat-list-item';
|
|||
|
||||
interface IChatList {
|
||||
onClickChat: (chat: any) => void;
|
||||
useWindowScroll?: boolean;
|
||||
parentRef: React.RefObject<HTMLElement>;
|
||||
topOffset: number;
|
||||
}
|
||||
|
||||
const ChatList: React.FC<IChatList> = ({ onClickChat, useWindowScroll = false }) => {
|
||||
const chatListRef = useRef(null);
|
||||
|
||||
const ChatList: React.FC<IChatList> = ({ onClickChat, parentRef, topOffset }) => {
|
||||
const { chatsQuery: { data: chats, isFetching, hasNextPage, fetchNextPage, refetch } } = useChats();
|
||||
|
||||
const [isNearBottom, setNearBottom] = useState<boolean>(false);
|
||||
|
@ -45,43 +44,48 @@ const ChatList: React.FC<IChatList> = ({ onClickChat, useWindowScroll = false })
|
|||
};
|
||||
|
||||
return (
|
||||
<div className='relative h-full'>
|
||||
<PullToRefresh onRefresh={handleRefresh}>
|
||||
<Virtuoso
|
||||
ref={chatListRef}
|
||||
atTopStateChange={(atTop) => setNearTop(atTop)}
|
||||
atBottomStateChange={(atBottom) => setNearBottom(atBottom)}
|
||||
useWindowScroll={useWindowScroll}
|
||||
data={chats}
|
||||
endReached={handleLoadMore}
|
||||
itemContent={(_index, chat) => (
|
||||
<div className='px-2'>
|
||||
<ChatListItem chat={chat} onClick={onClickChat} />
|
||||
</div>
|
||||
)}
|
||||
components={{
|
||||
ScrollSeekPlaceholder: () => <PlaceholderChat />,
|
||||
Footer: () => hasNextPage ? <Spinner withText={false} /> : null,
|
||||
EmptyPlaceholder: renderEmpty,
|
||||
}}
|
||||
/>
|
||||
</PullToRefresh>
|
||||
<>
|
||||
<div className='relative h-full'>
|
||||
<PullToRefresh onRefresh={handleRefresh}>
|
||||
<ScrollableList
|
||||
onScroll={(top, bottom) => {
|
||||
setNearTop(top === 0);
|
||||
setNearBottom(bottom === chats?.length);
|
||||
}}
|
||||
itemClassName='px-2'
|
||||
emptyMessage={renderEmpty()}
|
||||
placeholderComponent={PlaceholderChat}
|
||||
placeholderCount={3}
|
||||
hasMore={hasNextPage}
|
||||
onLoadMore={handleLoadMore}
|
||||
estimatedSize={64}
|
||||
useWindowScroll={false}
|
||||
parentRef={parentRef}
|
||||
loadMoreClassName='mx-4 mb-4'
|
||||
>
|
||||
{(chats || []).map(chat => (
|
||||
<ChatListItem key={chat.id} chat={chat} onClick={onClickChat} />
|
||||
))}
|
||||
</ScrollableList>
|
||||
</PullToRefresh>
|
||||
|
||||
<>
|
||||
<div
|
||||
className={clsx('pointer-events-none absolute inset-x-0 top-0 flex justify-center rounded-t-lg bg-gradient-to-b from-white to-transparent pb-12 pt-8 transition-opacity duration-500 dark:from-gray-900', {
|
||||
'opacity-0': isNearTop,
|
||||
'opacity-100 black:opacity-50': !isNearTop,
|
||||
})}
|
||||
/>
|
||||
<div
|
||||
className={clsx('pointer-events-none absolute inset-x-0 bottom-0 flex justify-center rounded-b-lg bg-gradient-to-t from-white to-transparent pb-8 pt-12 transition-opacity duration-500 dark:from-gray-900', {
|
||||
'opacity-0': isNearBottom,
|
||||
'opacity-100 black:opacity-50': !isNearBottom,
|
||||
})}
|
||||
/>
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={clsx('pointer-events-none absolute inset-x-0 flex justify-center rounded-t-lg bg-gradient-to-b from-white to-transparent pb-12 pt-8 transition-opacity duration-500 black:from-black dark:from-gray-900', {
|
||||
'opacity-0': isNearTop,
|
||||
'opacity-100 black:opacity-50': !isNearTop,
|
||||
})}
|
||||
style={{
|
||||
top: topOffset,
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className={clsx('pointer-events-none absolute inset-x-0 bottom-0 flex justify-center rounded-b-lg bg-gradient-to-t from-white to-transparent pb-8 pt-12 transition-opacity duration-500 black:from-black dark:from-gray-900', {
|
||||
'opacity-0': isNearBottom,
|
||||
'opacity-100 black:opacity-50': !isNearBottom,
|
||||
})}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { VirtuosoMockContext } from 'react-virtuoso';
|
||||
|
||||
import { __stub } from 'pl-fe/api';
|
||||
import { ChatContext } from 'pl-fe/contexts/chat-context';
|
||||
|
@ -62,11 +61,11 @@ const store = rootState
|
|||
.set('instance', buildInstance({ version: '3.4.1 (compatible; TruthSocial 1.0.0+unreleased)' }));
|
||||
|
||||
const renderComponentWithChatContext = () => render(
|
||||
<VirtuosoMockContext.Provider value={{ viewportHeight: 300, itemHeight: 100 }}>
|
||||
<ChatContext.Provider value={{ chat }}>
|
||||
<ChatMessageList chat={chat} />
|
||||
</ChatContext.Provider>
|
||||
</VirtuosoMockContext.Provider>,
|
||||
// <VirtuosoMockContext.Provider value={{ viewportHeight: 300, itemHeight: 100 }}>
|
||||
<ChatContext.Provider value={{ chat }}>
|
||||
<ChatMessageList chat={chat} />
|
||||
</ChatContext.Provider>,
|
||||
// </VirtuosoMockContext.Provider>,
|
||||
undefined,
|
||||
store,
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
|
@ -15,6 +15,7 @@ const messages = defineMessages({
|
|||
const ChatPageSidebar = () => {
|
||||
const intl = useIntl();
|
||||
const history = useHistory();
|
||||
const listRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleClickChat = (chat: Chat) => {
|
||||
history.push(`/chats/${chat.id}`);
|
||||
|
@ -29,7 +30,7 @@ const ChatPageSidebar = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<Stack space={4} className='h-full'>
|
||||
<Stack space={4} className='h-full relative'>
|
||||
<Stack space={4} className='px-4 pt-6'>
|
||||
<HStack alignItems='center' justifyContent='between'>
|
||||
<CardTitle title={intl.formatMessage(messages.title)} />
|
||||
|
@ -50,8 +51,8 @@ const ChatPageSidebar = () => {
|
|||
</HStack>
|
||||
</Stack>
|
||||
|
||||
<Stack className='h-full grow'>
|
||||
<ChatList onClickChat={handleClickChat} />
|
||||
<Stack className='h-full grow overflow-auto' ref={listRef}>
|
||||
<ChatList onClickChat={handleClickChat} parentRef={listRef} topOffset={68} />
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Stack } from 'pl-fe/components/ui';
|
||||
|
@ -19,6 +19,8 @@ import Blankslate from './blankslate';
|
|||
import type { Chat } from 'pl-api';
|
||||
|
||||
const ChatPane = () => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { unreadChatsCount } = useStatContext();
|
||||
|
||||
const { screen, changeScreen, isOpen, toggleChatPane } = useChatContext();
|
||||
|
@ -31,11 +33,9 @@ const ChatPane = () => {
|
|||
const renderBody = () => {
|
||||
if (Number(chats?.length) > 0 || isLoading) {
|
||||
return (
|
||||
<Stack space={4} className='h-full grow'>
|
||||
<Stack space={4} className='h-full grow overflow-auto' ref={ref}>
|
||||
{(Number(chats?.length) > 0 || isLoading) ? (
|
||||
<ChatList
|
||||
onClickChat={handleClickChat}
|
||||
/>
|
||||
<ChatList onClickChat={handleClickChat} parentRef={ref} topOffset={64} />
|
||||
) : (
|
||||
<EmptyResultsBlankslate />
|
||||
)}
|
||||
|
|
|
@ -48,7 +48,7 @@ const LandingTimeline = () => {
|
|||
{timelineEnabled ? (
|
||||
<PullToRefresh onRefresh={handleRefresh}>
|
||||
<Timeline
|
||||
className='black:p-0 black:sm:p-4'
|
||||
listClassName='black:p-0 black:sm:p-4'
|
||||
loadMoreClassName='black:sm:mx-4'
|
||||
scrollKey={`${timelineId}_timeline`}
|
||||
timelineId={timelineId}
|
||||
|
|
|
@ -56,12 +56,8 @@ const Notifications = () => {
|
|||
dispatch(expandNotifications({ maxId: last && last.id }));
|
||||
}, 300, { leading: true }), [notifications]);
|
||||
|
||||
const handleScrollToTop = useCallback(debounce(() => {
|
||||
dispatch(scrollTopNotifications(true));
|
||||
}, 100), []);
|
||||
|
||||
const handleScroll = useCallback(debounce(() => {
|
||||
dispatch(scrollTopNotifications(false));
|
||||
const handleScroll = useCallback(debounce((startIndex?: number) => {
|
||||
dispatch(scrollTopNotifications(startIndex === 0));
|
||||
}, 100), []);
|
||||
|
||||
const handleMoveUp = (id: string) => {
|
||||
|
@ -93,7 +89,6 @@ const Notifications = () => {
|
|||
|
||||
return () => {
|
||||
handleLoadOlder.cancel();
|
||||
handleScrollToTop.cancel();
|
||||
handleScroll.cancel();
|
||||
dispatch(scrollTopNotifications(false));
|
||||
};
|
||||
|
@ -135,7 +130,6 @@ const Notifications = () => {
|
|||
placeholderComponent={PlaceholderNotification}
|
||||
placeholderCount={20}
|
||||
onLoadMore={handleLoadOlder}
|
||||
onScrollToTop={handleScrollToTop}
|
||||
onScroll={handleScroll}
|
||||
listClassName={clsx('divide-y divide-solid divide-gray-200 black:divide-gray-800 dark:divide-primary-800', {
|
||||
'animate-pulse': notifications.size === 0,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { BigCard } from 'pl-fe/components/big-card';
|
||||
|
@ -8,6 +8,7 @@ import AccountContainer from 'pl-fe/containers/account-container';
|
|||
import { useOnboardingSuggestions } from 'pl-fe/queries/suggestions';
|
||||
|
||||
const SuggestedAccountsStep = ({ onNext }: { onNext: () => void }) => {
|
||||
const parentRef = useRef<HTMLDivElement>(null);
|
||||
const { data, isFetching } = useOnboardingSuggestions();
|
||||
|
||||
const renderSuggestions = () => {
|
||||
|
@ -16,11 +17,12 @@ const SuggestedAccountsStep = ({ onNext }: { onNext: () => void }) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='flex flex-col sm:pb-10 sm:pt-4'>
|
||||
<div className='flex flex-col sm:pb-10 sm:pt-4' ref={parentRef}>
|
||||
<ScrollableList
|
||||
isLoading={isFetching}
|
||||
useWindowScroll={false}
|
||||
style={{ height: 320 }}
|
||||
useWindowScroll={false}
|
||||
parentRef={parentRef}
|
||||
>
|
||||
{data.map((suggestion) => (
|
||||
<div key={suggestion.account.id} className='py-2'>
|
||||
|
|
|
@ -400,13 +400,14 @@ const Thread: React.FC<IThread> = ({
|
|||
ref={virtualizer}
|
||||
placeholderComponent={() => <PlaceholderStatus variant='slim' />}
|
||||
initialIndex={initialIndex}
|
||||
useWindowScroll={useWindowScroll}
|
||||
itemClassName={itemClassName}
|
||||
listClassName={
|
||||
clsx({
|
||||
'h-full': !useWindowScroll,
|
||||
})
|
||||
}
|
||||
useWindowScroll={useWindowScroll}
|
||||
parentRef={node}
|
||||
>
|
||||
{children}
|
||||
</ScrollableList>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
import React from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import ScrollableList from 'pl-fe/components/scrollable-list';
|
||||
|
@ -17,6 +17,7 @@ interface FamiliarFollowersModalProps {
|
|||
}
|
||||
|
||||
const FamiliarFollowersModal: React.FC<BaseModalProps & FamiliarFollowersModalProps> = ({ accountId, onClose }) => {
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
const account = useAppSelector(state => getAccount(state, accountId));
|
||||
const familiarFollowerIds: ImmutableOrderedSet<string> = useAppSelector(state => state.user_lists.familiar_followers.get(accountId)?.items || ImmutableOrderedSet());
|
||||
|
||||
|
@ -36,8 +37,9 @@ const FamiliarFollowersModal: React.FC<BaseModalProps & FamiliarFollowersModalPr
|
|||
emptyMessage={emptyMessage}
|
||||
itemClassName='pb-3'
|
||||
style={{ height: 'calc(80vh - 88px)' }}
|
||||
useWindowScroll={false}
|
||||
estimatedSize={42}
|
||||
useWindowScroll={false}
|
||||
parentRef={modalRef}
|
||||
>
|
||||
{familiarFollowerIds.map(id =>
|
||||
<AccountContainer key={id} id={id} />,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { fetchFavourites, expandFavourites } from 'pl-fe/actions/interactions';
|
||||
|
@ -14,6 +14,7 @@ interface FavouritesModalProps {
|
|||
}
|
||||
|
||||
const FavouritesModal: React.FC<BaseModalProps & FavouritesModalProps> = ({ onClose, statusId }) => {
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const accountIds = useAppSelector((state) => state.user_lists.favourited_by.get(statusId)?.items);
|
||||
|
@ -50,10 +51,11 @@ const FavouritesModal: React.FC<BaseModalProps & FavouritesModalProps> = ({ onCl
|
|||
listClassName='max-w-full'
|
||||
itemClassName='pb-3'
|
||||
style={{ height: 'calc(80vh - 88px)' }}
|
||||
useWindowScroll={false}
|
||||
onLoadMore={handleLoadMore}
|
||||
hasMore={!!next}
|
||||
estimatedSize={42}
|
||||
useWindowScroll={false}
|
||||
parentRef={modalRef}
|
||||
>
|
||||
{accountIds.map(id =>
|
||||
<AccountContainer key={id} id={id} />,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import clsx from 'clsx';
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { fetchReactions } from 'pl-fe/actions/interactions';
|
||||
|
@ -28,6 +28,7 @@ interface ReactionsModalProps {
|
|||
}
|
||||
|
||||
const ReactionsModal: React.FC<BaseModalProps & ReactionsModalProps> = ({ onClose, statusId, reaction: initialReaction }) => {
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
const [reaction, setReaction] = useState(initialReaction);
|
||||
|
@ -93,8 +94,9 @@ const ReactionsModal: React.FC<BaseModalProps & ReactionsModalProps> = ({ onClos
|
|||
listClassName='max-w-full'
|
||||
itemClassName='pb-3'
|
||||
style={{ height: 'calc(80vh - 88px)' }}
|
||||
useWindowScroll={false}
|
||||
estimatedSize={42}
|
||||
useWindowScroll={false}
|
||||
parentRef={modalRef}
|
||||
>
|
||||
{accounts.map((account) =>
|
||||
<AccountContainer key={`${account.id}-${account.reaction}`} id={account.id} emoji={account.reaction} emojiUrl={account.reactionUrl} />,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { fetchReblogs, expandReblogs } from 'pl-fe/actions/interactions';
|
||||
|
@ -19,6 +19,7 @@ const ReblogsModal: React.FC<BaseModalProps & ReblogsModalProps> = ({ onClose, s
|
|||
const intl = useIntl();
|
||||
const accountIds = useAppSelector((state) => state.user_lists.reblogged_by.get(statusId)?.items);
|
||||
const next = useAppSelector((state) => state.user_lists.reblogged_by.get(statusId)?.next);
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const fetchData = () => {
|
||||
dispatch(fetchReblogs(statusId));
|
||||
|
@ -52,10 +53,11 @@ const ReblogsModal: React.FC<BaseModalProps & ReblogsModalProps> = ({ onClose, s
|
|||
listClassName='max-w-full'
|
||||
itemClassName='pb-3'
|
||||
style={{ height: 'calc(80vh - 88px)' }}
|
||||
useWindowScroll={false}
|
||||
onLoadMore={handleLoadMore}
|
||||
hasMore={!!next}
|
||||
estimatedSize={42}
|
||||
useWindowScroll={false}
|
||||
parentRef={modalRef}
|
||||
>
|
||||
{accountIds.map((id) =>
|
||||
<AccountContainer key={id} id={id} />,
|
||||
|
@ -68,6 +70,7 @@ const ReblogsModal: React.FC<BaseModalProps & ReblogsModalProps> = ({ onClose, s
|
|||
<Modal
|
||||
title={<FormattedMessage id='column.reblogs' defaultMessage='Reposts' />}
|
||||
onClose={onClickClose}
|
||||
ref={modalRef}
|
||||
>
|
||||
{body}
|
||||
</Modal>
|
||||
|
|
|
@ -44,12 +44,8 @@ const Timeline: React.FC<ITimeline> = ({
|
|||
dispatch(dequeueTimeline(timelineId, onLoadMore));
|
||||
}, []);
|
||||
|
||||
const handleScrollToTop = useCallback(debounce(() => {
|
||||
dispatch(scrollTopTimeline(timelineId, true));
|
||||
}, 100), [timelineId]);
|
||||
|
||||
const handleScroll = useCallback(debounce(() => {
|
||||
dispatch(scrollTopTimeline(timelineId, false));
|
||||
const handleScroll = useCallback(debounce((startIndex?: number) => {
|
||||
dispatch(scrollTopTimeline(timelineId, startIndex === 0));
|
||||
}, 100), [timelineId]);
|
||||
|
||||
return (
|
||||
|
@ -65,7 +61,6 @@ const Timeline: React.FC<ITimeline> = ({
|
|||
|
||||
<StatusList
|
||||
timelineId={timelineId}
|
||||
onScrollToTop={handleScrollToTop}
|
||||
onScroll={handleScroll}
|
||||
lastStatusId={lastStatusId}
|
||||
statusIds={statusIds}
|
||||
|
|
Loading…
Reference in a new issue