ChatList: use Virtuoso

This commit is contained in:
Alex Gleason 2022-04-15 15:19:34 -05:00
parent 712b4c5a42
commit 05068460e0
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
4 changed files with 51 additions and 41 deletions

View file

@ -61,14 +61,14 @@ export function fetchChatsV2() {
export function fetchChats() { export function fetchChats() {
return (dispatch, getState) => { return (dispatch, getState) => {
const state = getState(); const state = getState();
const instance = state.get('instance'); const { instance } = state;
const features = getFeatures(instance); const features = getFeatures(instance);
dispatch({ type: CHATS_FETCH_REQUEST }); dispatch({ type: CHATS_FETCH_REQUEST });
if (features.chatsV2) { if (features.chatsV2) {
dispatch(fetchChatsV2()); return dispatch(fetchChatsV2());
} else { } else {
dispatch(fetchChatsV1()); return dispatch(fetchChatsV1());
} }
}; };
} }

View file

@ -1,12 +1,13 @@
import { Map as ImmutableMap } from 'immutable'; import { Map as ImmutableMap } from 'immutable';
import { debounce } from 'lodash'; import React, { useCallback } from 'react';
import React from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import { Virtuoso } from 'react-virtuoso';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { expandChats } from 'soapbox/actions/chats'; import { fetchChats, expandChats } from 'soapbox/actions/chats';
import ScrollableList from 'soapbox/components/scrollable_list'; import PullToRefresh from 'soapbox/components/pull-to-refresh';
import { Text } from 'soapbox/components/ui';
import PlaceholderChat from 'soapbox/features/placeholder/components/placeholder_chat'; import PlaceholderChat from 'soapbox/features/placeholder/components/placeholder_chat';
import { useAppSelector } from 'soapbox/hooks'; import { useAppSelector } from 'soapbox/hooks';
@ -16,10 +17,6 @@ const messages = defineMessages({
emptyMessage: { id: 'chat_panels.main_window.empty', defaultMessage: 'No chats found. To start a chat, visit a user\'s profile' }, emptyMessage: { id: 'chat_panels.main_window.empty', defaultMessage: 'No chats found. To start a chat, visit a user\'s profile' },
}); });
const handleLoadMore = debounce((dispatch) => {
dispatch(expandChats());
}, 300, { leading: true });
const getSortedChatIds = (chats: ImmutableMap<string, any>) => ( const getSortedChatIds = (chats: ImmutableMap<string, any>) => (
chats chats
.toList() .toList()
@ -45,10 +42,10 @@ const sortedChatIdsSelector = createSelector(
interface IChatList { interface IChatList {
onClickChat: (chat: any) => void, onClickChat: (chat: any) => void,
onRefresh: () => void, useWindowScroll?: boolean,
} }
const ChatList: React.FC<IChatList> = ({ onClickChat, onRefresh }) => { const ChatList: React.FC<IChatList> = ({ onClickChat, useWindowScroll = false }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const intl = useIntl(); const intl = useIntl();
@ -56,28 +53,41 @@ const ChatList: React.FC<IChatList> = ({ onClickChat, onRefresh }) => {
const hasMore = useAppSelector(state => !!state.chats.get('next')); const hasMore = useAppSelector(state => !!state.chats.get('next'));
const isLoading = useAppSelector(state => state.chats.get('isLoading')); const isLoading = useAppSelector(state => state.chats.get('isLoading'));
const handleLoadMore = useCallback(() => {
if (hasMore && !isLoading) {
dispatch(expandChats());
}
}, [dispatch, hasMore, isLoading]);
const handleRefresh = () => {
return dispatch(fetchChats()) as any;
};
return ( return (
<ScrollableList <PullToRefresh onRefresh={handleRefresh}>
<Virtuoso
className='chat-list' className='chat-list'
scrollKey='awaiting-approval' useWindowScroll={useWindowScroll}
emptyMessage={intl.formatMessage(messages.emptyMessage)} data={chatIds.toArray()}
hasMore={hasMore} endReached={handleLoadMore}
isLoading={isLoading} itemContent={(_index, chatId) => (
showLoading={isLoading && chatIds.size === 0} <Chat chatId={chatId} onClick={onClickChat} />
onLoadMore={() => handleLoadMore(dispatch)} )}
onRefresh={onRefresh} components={{
placeholderComponent={PlaceholderChat} ScrollSeekPlaceholder: () => <PlaceholderChat />,
placeholderCount={20} Footer: () => hasMore ? <PlaceholderChat /> : null,
> EmptyPlaceholder: () => {
{chatIds.map((chatId: string) => ( if (isLoading) {
<div key={chatId} className='chat-list-item'> return (
<Chat <>{Array(20).fill(<PlaceholderChat />)}</>
chatId={chatId} );
onClick={onClickChat} } else {
return <Text>{intl.formatMessage(messages.emptyMessage)}</Text>;
}
},
}}
/> />
</div> </PullToRefresh>
))}
</ScrollableList>
); );
}; };

View file

@ -3,7 +3,7 @@ import { defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { fetchChats, launchChat } from 'soapbox/actions/chats'; import { launchChat } from 'soapbox/actions/chats';
import AccountSearch from 'soapbox/components/account_search'; import AccountSearch from 'soapbox/components/account_search';
import AudioToggle from 'soapbox/features/chats/components/audio_toggle'; import AudioToggle from 'soapbox/features/chats/components/audio_toggle';
@ -29,10 +29,6 @@ const ChatIndex: React.FC = () => {
history.push(`/chats/${chat.id}`); history.push(`/chats/${chat.id}`);
}; };
const handleRefresh = () => {
return dispatch(fetchChats());
};
return ( return (
<Column label={intl.formatMessage(messages.title)}> <Column label={intl.formatMessage(messages.title)}>
<div className='column__switch'> <div className='column__switch'>
@ -46,7 +42,7 @@ const ChatIndex: React.FC = () => {
<ChatList <ChatList
onClickChat={handleClickChat} onClickChat={handleClickChat}
onRefresh={handleRefresh} useWindowScroll
/> />
</Column> </Column>
); );

View file

@ -90,12 +90,16 @@
&__content { &__content {
@apply flex flex-1 flex-col overflow-hidden bg-white dark:bg-slate-800; @apply flex flex-1 flex-col overflow-hidden bg-white dark:bg-slate-800;
> div {
@apply max-h-full;
}
.chat-box { .chat-box {
@apply flex flex-1 flex-col overflow-hidden; @apply flex flex-1 flex-col overflow-hidden;
} }
.chat-list { .chat-list {
@apply overflow-y-auto; @apply overflow-y-auto max-h-full;
} }
} }