96 lines
2.9 KiB
TypeScript
96 lines
2.9 KiB
TypeScript
import { Map as ImmutableMap } from 'immutable';
|
|
import React, { useCallback } from 'react';
|
|
import { defineMessages, useIntl } from 'react-intl';
|
|
import { useDispatch } from 'react-redux';
|
|
import { Virtuoso } from 'react-virtuoso';
|
|
import { createSelector } from 'reselect';
|
|
|
|
import { fetchChats, expandChats } from 'soapbox/actions/chats';
|
|
import PullToRefresh from 'soapbox/components/pull-to-refresh';
|
|
import { Card, Text } from 'soapbox/components/ui';
|
|
import PlaceholderChat from 'soapbox/features/placeholder/components/placeholder_chat';
|
|
import { useAppSelector } from 'soapbox/hooks';
|
|
|
|
import Chat from './chat';
|
|
|
|
const messages = defineMessages({
|
|
emptyMessage: { id: 'chat_panels.main_window.empty', defaultMessage: 'No chats found. To start a chat, visit a user\'s profile' },
|
|
});
|
|
|
|
const getSortedChatIds = (chats: ImmutableMap<string, any>) => (
|
|
chats
|
|
.toList()
|
|
.sort(chatDateComparator)
|
|
.map(chat => chat.id)
|
|
);
|
|
|
|
const chatDateComparator = (chatA: { updated_at: string }, chatB: { updated_at: string }) => {
|
|
// Sort most recently updated chats at the top
|
|
const a = new Date(chatA.updated_at);
|
|
const b = new Date(chatB.updated_at);
|
|
|
|
if (a === b) return 0;
|
|
if (a > b) return -1;
|
|
if (a < b) return 1;
|
|
return 0;
|
|
};
|
|
|
|
const sortedChatIdsSelector = createSelector(
|
|
[getSortedChatIds],
|
|
chats => chats,
|
|
);
|
|
|
|
interface IChatList {
|
|
onClickChat: (chat: any) => void,
|
|
useWindowScroll?: boolean,
|
|
}
|
|
|
|
const ChatList: React.FC<IChatList> = ({ onClickChat, useWindowScroll = false }) => {
|
|
const dispatch = useDispatch();
|
|
const intl = useIntl();
|
|
|
|
const chatIds = useAppSelector(state => sortedChatIdsSelector(state.chats.items));
|
|
const hasMore = useAppSelector(state => !!state.chats.next);
|
|
const isLoading = useAppSelector(state => state.chats.isLoading);
|
|
|
|
const isEmpty = chatIds.size === 0;
|
|
|
|
const handleLoadMore = useCallback(() => {
|
|
if (hasMore && !isLoading) {
|
|
dispatch(expandChats());
|
|
}
|
|
}, [dispatch, hasMore, isLoading]);
|
|
|
|
const handleRefresh = () => {
|
|
return dispatch(fetchChats()) as any;
|
|
};
|
|
|
|
const renderEmpty = () => isLoading ? <PlaceholderChat /> : (
|
|
<Card className='mt-2' variant='rounded' size='lg'>
|
|
<Text>{intl.formatMessage(messages.emptyMessage)}</Text>
|
|
</Card>
|
|
);
|
|
|
|
return (
|
|
<PullToRefresh onRefresh={handleRefresh}>
|
|
{isEmpty ? renderEmpty() : (
|
|
<Virtuoso
|
|
className='chat-list'
|
|
useWindowScroll={useWindowScroll}
|
|
data={chatIds.toArray()}
|
|
endReached={handleLoadMore}
|
|
itemContent={(_index, chatId) => (
|
|
<Chat chatId={chatId} onClick={onClickChat} />
|
|
)}
|
|
components={{
|
|
ScrollSeekPlaceholder: () => <PlaceholderChat />,
|
|
Footer: () => hasMore ? <PlaceholderChat /> : null,
|
|
EmptyPlaceholder: renderEmpty,
|
|
}}
|
|
/>
|
|
)}
|
|
</PullToRefresh>
|
|
);
|
|
};
|
|
|
|
export default ChatList;
|