sforkowany z mirror/soapbox
Add infinite scroll to ChatList
rodzic
e384d1f40d
commit
01167af69e
|
@ -1,6 +1,6 @@
|
||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation } from '@tanstack/react-query';
|
||||||
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||||
import React, { MutableRefObject, useRef, useState } from 'react';
|
import React, { MutableRefObject, useEffect, useRef, useState } from 'react';
|
||||||
import { useIntl, defineMessages } from 'react-intl';
|
import { useIntl, defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -56,7 +56,6 @@ const ChatBox: React.FC<IChatBox> = ({ chat, onSetInputRef, autosize, inputRef }
|
||||||
|
|
||||||
const isSubmitDisabled = content.length === 0 && !attachment;
|
const isSubmitDisabled = content.length === 0 && !attachment;
|
||||||
|
|
||||||
// TODO: needs last_read_id param
|
|
||||||
const markAsRead = useMutation(() => markChatAsRead(), {
|
const markAsRead = useMutation(() => markChatAsRead(), {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries(['chats']);
|
queryClient.invalidateQueries(['chats']);
|
||||||
|
@ -216,12 +215,10 @@ const ChatBox: React.FC<IChatBox> = ({ chat, onSetInputRef, autosize, inputRef }
|
||||||
// );
|
// );
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!chatMessageIds) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack className='overflow-hidden flex flex-grow' onMouseOver={handleMouseOver}>
|
<Stack className='overflow-hidden flex flex-grow' onMouseOver={handleMouseOver}>
|
||||||
<div className='flex-grow h-full overflow-hidden flex justify-center'>
|
<div className='flex-grow h-full overflow-hidden flex justify-center'>
|
||||||
<ChatMessageList chatMessageIds={chatMessageIds} chat={chat} autosize />
|
<ChatMessageList chat={chat} autosize />
|
||||||
</div >
|
</div >
|
||||||
|
|
||||||
<div className='mt-auto p-4 shadow-3xl'>
|
<div className='mt-auto p-4 shadow-3xl'>
|
||||||
|
|
|
@ -19,18 +19,15 @@ interface IChatList {
|
||||||
const ChatList: React.FC<IChatList> = ({ onClickChat, useWindowScroll = false }) => {
|
const ChatList: React.FC<IChatList> = ({ onClickChat, useWindowScroll = false }) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const { chatsQuery: { data: chats, isFetching } } = useChats();
|
const { chatsQuery: { data: chats, isFetching, hasNextPage, fetchNextPage } } = useChats();
|
||||||
|
|
||||||
const isEmpty = chats?.length === 0;
|
const isEmpty = chats?.length === 0;
|
||||||
|
|
||||||
// const handleLoadMore = useCallback(() => {
|
const handleLoadMore = () => {
|
||||||
// if (hasMore && !isLoading) {
|
if (hasNextPage && !isFetching) {
|
||||||
// dispatch(expandChats());
|
fetchNextPage();
|
||||||
// }
|
}
|
||||||
// }, [dispatch, hasMore, isLoading]);
|
};
|
||||||
|
|
||||||
const handleLoadMore = () => console.log('load more');
|
|
||||||
|
|
||||||
|
|
||||||
const handleRefresh = () => {
|
const handleRefresh = () => {
|
||||||
return dispatch(fetchChats()) as any;
|
return dispatch(fetchChats()) as any;
|
||||||
|
@ -62,7 +59,7 @@ const ChatList: React.FC<IChatList> = ({ onClickChat, useWindowScroll = false })
|
||||||
)}
|
)}
|
||||||
components={{
|
components={{
|
||||||
ScrollSeekPlaceholder: () => <PlaceholderChat />,
|
ScrollSeekPlaceholder: () => <PlaceholderChat />,
|
||||||
// Footer: () => hasMore ? <PlaceholderChat /> : null,
|
// Footer: () => hasNextPage ? <Spinner withText={false} /> : null,
|
||||||
EmptyPlaceholder: renderEmpty,
|
EmptyPlaceholder: renderEmpty,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,27 +1,20 @@
|
||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation } from '@tanstack/react-query';
|
||||||
import classNames from 'clsx';
|
import classNames from 'clsx';
|
||||||
import {
|
import { List as ImmutableList } from 'immutable';
|
||||||
Map as ImmutableMap,
|
|
||||||
List as ImmutableList,
|
|
||||||
OrderedSet as ImmutableOrderedSet,
|
|
||||||
} from 'immutable';
|
|
||||||
import escape from 'lodash/escape';
|
import escape from 'lodash/escape';
|
||||||
import throttle from 'lodash/throttle';
|
import throttle from 'lodash/throttle';
|
||||||
import React, { useState, useEffect, useRef, useLayoutEffect, useMemo } from 'react';
|
import React, { useState, useEffect, useRef, useLayoutEffect } from 'react';
|
||||||
import { useIntl, defineMessages } from 'react-intl';
|
import { useIntl, defineMessages } from 'react-intl';
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
|
|
||||||
import { fetchChatMessages, deleteChatMessage } from 'soapbox/actions/chats';
|
|
||||||
import { openModal } from 'soapbox/actions/modals';
|
import { openModal } from 'soapbox/actions/modals';
|
||||||
import { initReport, initReportById } from 'soapbox/actions/reports';
|
import { initReportById } from 'soapbox/actions/reports';
|
||||||
import { Avatar, Button, Divider, HStack, IconButton, Spinner, Stack, Text } from 'soapbox/components/ui';
|
import { Avatar, Divider, HStack, Spinner, Stack, Text } from 'soapbox/components/ui';
|
||||||
import DropdownMenuContainer from 'soapbox/containers/dropdown_menu_container';
|
import DropdownMenuContainer from 'soapbox/containers/dropdown_menu_container';
|
||||||
import { useChatContext } from 'soapbox/contexts/chat-context';
|
// import emojify from 'soapbox/features/emoji/emoji';
|
||||||
import emojify from 'soapbox/features/emoji/emoji';
|
|
||||||
import PlaceholderChatMessage from 'soapbox/features/placeholder/components/placeholder-chat-message';
|
import PlaceholderChatMessage from 'soapbox/features/placeholder/components/placeholder-chat-message';
|
||||||
import Bundle from 'soapbox/features/ui/components/bundle';
|
import Bundle from 'soapbox/features/ui/components/bundle';
|
||||||
import { MediaGallery } from 'soapbox/features/ui/util/async-components';
|
import { MediaGallery } from 'soapbox/features/ui/util/async-components';
|
||||||
import { useAppSelector, useAppDispatch, useRefEventHandler, useOwnAccount } from 'soapbox/hooks';
|
import { useAppSelector, useAppDispatch, useOwnAccount } from 'soapbox/hooks';
|
||||||
import { IChat, IChatMessage, useChat, useChatMessages } from 'soapbox/queries/chats';
|
import { IChat, IChatMessage, useChat, useChatMessages } from 'soapbox/queries/chats';
|
||||||
import { queryClient } from 'soapbox/queries/client';
|
import { queryClient } from 'soapbox/queries/client';
|
||||||
import { onlyEmoji } from 'soapbox/utils/rich_content';
|
import { onlyEmoji } from 'soapbox/utils/rich_content';
|
||||||
|
@ -58,27 +51,15 @@ const timeChange = (prev: IChatMessage, curr: IChatMessage): TimeFormat | null =
|
||||||
// return map.set(`:${emoji.get('shortcode')}:`, emoji);
|
// return map.set(`:${emoji.get('shortcode')}:`, emoji);
|
||||||
// }, ImmutableMap());
|
// }, ImmutableMap());
|
||||||
|
|
||||||
const getChatMessages = createSelector(
|
|
||||||
[(chatMessages: ImmutableMap<string, ChatMessageEntity>, chatMessageIds: ImmutableOrderedSet<string>) => (
|
|
||||||
chatMessageIds.reduce((acc, curr) => {
|
|
||||||
const chatMessage = chatMessages.get(curr);
|
|
||||||
return chatMessage ? acc.push(chatMessage) : acc;
|
|
||||||
}, ImmutableList<ChatMessageEntity>())
|
|
||||||
)],
|
|
||||||
chatMessages => chatMessages,
|
|
||||||
);
|
|
||||||
|
|
||||||
interface IChatMessageList {
|
interface IChatMessageList {
|
||||||
/** Chat the messages are being rendered from. */
|
/** Chat the messages are being rendered from. */
|
||||||
chat: IChat,
|
chat: IChat,
|
||||||
/** Message IDs to render. */
|
|
||||||
chatMessageIds: ImmutableOrderedSet<string>,
|
|
||||||
/** Whether to make the chatbox fill the height of the screen. */
|
/** Whether to make the chatbox fill the height of the screen. */
|
||||||
autosize?: boolean,
|
autosize?: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Scrollable list of chat messages. */
|
/** Scrollable list of chat messages. */
|
||||||
const ChatMessageList: React.FC<IChatMessageList> = ({ chat, chatMessageIds, autosize }) => {
|
const ChatMessageList: React.FC<IChatMessageList> = ({ chat, autosize }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const account = useOwnAccount();
|
const account = useOwnAccount();
|
||||||
|
@ -86,9 +67,7 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, chatMessageIds, aut
|
||||||
const [initialLoad, setInitialLoad] = useState(true);
|
const [initialLoad, setInitialLoad] = useState(true);
|
||||||
const [scrollPosition, setScrollPosition] = useState(0);
|
const [scrollPosition, setScrollPosition] = useState(0);
|
||||||
|
|
||||||
const { needsAcceptance } = useChatContext();
|
const { deleteChatMessage, markChatAsRead } = useChat(chat.id);
|
||||||
|
|
||||||
const { deleteChatMessage, acceptChat, deleteChat } = useChat(chat.id);
|
|
||||||
const { data: chatMessages, isLoading, isFetching, isFetched, fetchNextPage, isFetchingNextPage, isPlaceholderData } = useChatMessages(chat.id);
|
const { data: chatMessages, isLoading, isFetching, isFetched, fetchNextPage, isFetchingNextPage, isPlaceholderData } = useChatMessages(chat.id);
|
||||||
const formattedChatMessages = chatMessages || [];
|
const formattedChatMessages = chatMessages || [];
|
||||||
|
|
||||||
|
@ -99,9 +78,6 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, chatMessageIds, aut
|
||||||
const lastComputedScroll = useRef<number | undefined>(undefined);
|
const lastComputedScroll = useRef<number | undefined>(undefined);
|
||||||
const scrollBottom = useRef<number | undefined>(undefined);
|
const scrollBottom = useRef<number | undefined>(undefined);
|
||||||
|
|
||||||
const initialCount = useMemo(() => formattedChatMessages.length, []);
|
|
||||||
|
|
||||||
|
|
||||||
const handleDeleteMessage = useMutation((chatMessageId: string) => deleteChatMessage(chatMessageId), {
|
const handleDeleteMessage = useMutation((chatMessageId: string) => deleteChatMessage(chatMessageId), {
|
||||||
onSettled: () => {
|
onSettled: () => {
|
||||||
queryClient.invalidateQueries(['chats', 'messages', chat.id]);
|
queryClient.invalidateQueries(['chats', 'messages', chat.id]);
|
||||||
|
@ -152,8 +128,6 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, chatMessageIds, aut
|
||||||
|
|
||||||
const restoreScrollPosition = () => {
|
const restoreScrollPosition = () => {
|
||||||
if (node.current && scrollBottom.current) {
|
if (node.current && scrollBottom.current) {
|
||||||
console.log('bottom', scrollBottom.current);
|
|
||||||
|
|
||||||
lastComputedScroll.current = node.current.scrollHeight - scrollBottom.current;
|
lastComputedScroll.current = node.current.scrollHeight - scrollBottom.current;
|
||||||
node.current.scrollTop = lastComputedScroll.current;
|
node.current.scrollTop = lastComputedScroll.current;
|
||||||
}
|
}
|
||||||
|
@ -227,7 +201,7 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, chatMessageIds, aut
|
||||||
// return emojify(formatted, emojiMap.toJS());
|
// return emojify(formatted, emojiMap.toJS());
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderDivider = (key: React.Key, text: string) => <Divider text={text} textSize='sm' />;
|
const renderDivider = (key: React.Key, text: string) => <Divider key={key} text={text} textSize='sm' />;
|
||||||
|
|
||||||
const handleReportUser = (userId: string) => {
|
const handleReportUser = (userId: string) => {
|
||||||
return () => {
|
return () => {
|
||||||
|
@ -370,14 +344,14 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, chatMessageIds, aut
|
||||||
// }
|
// }
|
||||||
}, [formattedChatMessages.length]);
|
}, [formattedChatMessages.length]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
markChatAsRead();
|
||||||
|
}, [formattedChatMessages.length]);
|
||||||
|
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// scrollToBottom();
|
// scrollToBottom();
|
||||||
// }, [messagesEnd.current]);
|
// }, [messagesEnd.current]);
|
||||||
|
|
||||||
// History added.
|
|
||||||
const lastChatId = Number(chatMessages && chatMessages[0]?.id);
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Restore scroll bar position when loading old messages.
|
// Restore scroll bar position when loading old messages.
|
||||||
if (!initialLoad) {
|
if (!initialLoad) {
|
||||||
|
@ -400,6 +374,12 @@ const ChatMessageList: React.FC<IChatMessageList> = ({ chat, chatMessageIds, aut
|
||||||
<ChatMessageListIntro />
|
<ChatMessageListIntro />
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
{isFetchingNextPage ? (
|
||||||
|
<div className='flex items-center justify-center'>
|
||||||
|
<Spinner size={30} withText={false} />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<div className='flex-grow flex flex-col justify-end space-y-4'>
|
<div className='flex-grow flex flex-col justify-end space-y-4'>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -9,7 +9,8 @@ import { Avatar, HStack, Icon, Input, Stack, Text } from 'soapbox/components/ui'
|
||||||
import VerificationBadge from 'soapbox/components/verification_badge';
|
import VerificationBadge from 'soapbox/components/verification_badge';
|
||||||
import { useChatContext } from 'soapbox/contexts/chat-context';
|
import { useChatContext } from 'soapbox/contexts/chat-context';
|
||||||
import { useAppDispatch, useDebounce } from 'soapbox/hooks';
|
import { useAppDispatch, useDebounce } from 'soapbox/hooks';
|
||||||
import { useChats } from 'soapbox/queries/chats';
|
import { IChat, useChats } from 'soapbox/queries/chats';
|
||||||
|
import { queryClient } from 'soapbox/queries/client';
|
||||||
import useAccountSearch from 'soapbox/queries/search';
|
import useAccountSearch from 'soapbox/queries/search';
|
||||||
|
|
||||||
import ChatList from '../chat-list';
|
import ChatList from '../chat-list';
|
||||||
|
@ -48,9 +49,11 @@ const ChatPane = () => {
|
||||||
},
|
},
|
||||||
onSuccess: (response) => {
|
onSuccess: (response) => {
|
||||||
setChat(response.data);
|
setChat(response.data);
|
||||||
|
queryClient.invalidateQueries(['chats']);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleClickChat = (chat: IChat) => setChat(chat);
|
||||||
|
|
||||||
const clearValue = () => {
|
const clearValue = () => {
|
||||||
if (hasSearchValue) {
|
if (hasSearchValue) {
|
||||||
|
@ -66,7 +69,7 @@ const ChatPane = () => {
|
||||||
<button
|
<button
|
||||||
key={account.id}
|
key={account.id}
|
||||||
type='button'
|
type='button'
|
||||||
className='px-4 py-2 w-full flex flex-col hover:bg-gray-100'
|
className='px-4 py-2 w-full flex flex-col hover:bg-gray-100 dark:hover:bg-gray-800'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleClickOnSearchResult.mutate(account.id);
|
handleClickOnSearchResult.mutate(account.id);
|
||||||
clearValue();
|
clearValue();
|
||||||
|
@ -88,7 +91,7 @@ const ChatPane = () => {
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return <ChatList onClickChat={(chat) => setChat(chat)} useWindowScroll={false} />;
|
return <ChatList onClickChat={handleClickChat} useWindowScroll={false} />;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import RelativeTimestamp from 'soapbox/components/relative_timestamp';
|
||||||
import { Avatar, HStack, Stack, Text } from 'soapbox/components/ui';
|
import { Avatar, HStack, Stack, Text } from 'soapbox/components/ui';
|
||||||
import VerificationBadge from 'soapbox/components/verification_badge';
|
import VerificationBadge from 'soapbox/components/verification_badge';
|
||||||
|
|
||||||
|
@ -11,6 +12,11 @@ interface IChatInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Chat: React.FC<IChatInterface> = ({ chat, onClick }) => {
|
const Chat: React.FC<IChatInterface> = ({ chat, onClick }) => {
|
||||||
|
// Temporary: remove once bad Staging data is removed.
|
||||||
|
if (!chat.account) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={chat.id}
|
key={chat.id}
|
||||||
|
@ -18,17 +24,34 @@ const Chat: React.FC<IChatInterface> = ({ chat, onClick }) => {
|
||||||
onClick={() => onClick(chat)}
|
onClick={() => onClick(chat)}
|
||||||
className='px-4 py-2 w-full flex flex-col hover:bg-gray-100 dark:hover:bg-gray-800'
|
className='px-4 py-2 w-full flex flex-col hover:bg-gray-100 dark:hover:bg-gray-800'
|
||||||
>
|
>
|
||||||
|
<HStack alignItems='center' justifyContent='between' space={2} className='w-full'>
|
||||||
<HStack alignItems='center' space={2}>
|
<HStack alignItems='center' space={2}>
|
||||||
<Avatar src={chat.account?.avatar} size={40} />
|
<Avatar src={chat.account?.avatar} size={40} />
|
||||||
|
|
||||||
<Stack alignItems='start'>
|
<Stack alignItems='start'>
|
||||||
<div className='flex items-center space-x-1 flex-grow'>
|
<div className='flex items-center space-x-1 flex-grow'>
|
||||||
<Text weight='bold' size='sm' truncate>{chat.account?.display_name}</Text>
|
<Text weight='bold' size='sm' truncate>{chat.account?.display_name || `@${chat.account.username}`}</Text>
|
||||||
{chat.account?.verified && <VerificationBadge />}
|
{chat.account?.verified && <VerificationBadge />}
|
||||||
</div>
|
</div>
|
||||||
<Text size='sm' weight='medium' theme='muted' truncate>@{chat.account?.acct}</Text>
|
|
||||||
|
{chat.last_message?.content && (
|
||||||
|
<Text size='sm' weight='medium' theme='muted' truncate className='max-w-[200px]'>
|
||||||
|
{chat.last_message?.content}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|
||||||
|
{chat.last_message && (
|
||||||
|
<HStack alignItems='center' space={2}>
|
||||||
|
{chat.last_message.unread && (
|
||||||
|
<div className='w-2 h-2 rounded-full bg-secondary-500' />
|
||||||
|
)}
|
||||||
|
|
||||||
|
<RelativeTimestamp timestamp={chat.last_message.created_at} size='sm' />
|
||||||
|
</HStack>
|
||||||
|
)}
|
||||||
|
</HStack>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { randomIntFromInterval } from '../utils';
|
||||||
import PlaceholderAvatar from './placeholder_avatar';
|
import PlaceholderAvatar from './placeholder_avatar';
|
||||||
|
|
||||||
/** Fake chat to display while data is loading. */
|
/** Fake chat to display while data is loading. */
|
||||||
const PlaceholderChat = ({ isMyMessage = false }: { isMyMessage?: boolean }) => {
|
const PlaceholderChatMessage = ({ isMyMessage = false }: { isMyMessage?: boolean }) => {
|
||||||
const messageLength = randomIntFromInterval(160, 220);
|
const messageLength = randomIntFromInterval(160, 220);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -55,7 +55,7 @@ const PlaceholderChat = ({ isMyMessage = false }: { isMyMessage?: boolean }) =>
|
||||||
'order-2': !isMyMessage,
|
'order-2': !isMyMessage,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div style={{ width: 50, height: 12 }} className='rounded-full bg-primary-50 dark:bg-primary-800' />
|
<span style={{ width: 50, height: 12 }} className='rounded-full bg-primary-50 dark:bg-primary-800 block' />
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<div className={classNames({ 'order-1': !isMyMessage })}>
|
<div className={classNames({ 'order-1': !isMyMessage })}>
|
||||||
|
@ -66,4 +66,4 @@ const PlaceholderChat = ({ isMyMessage = false }: { isMyMessage?: boolean }) =>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PlaceholderChat;
|
export default PlaceholderChatMessage;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useInfiniteQuery, useMutation, useQuery } from '@tanstack/react-query';
|
import { useInfiniteQuery, useMutation } from '@tanstack/react-query';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import snackbar from 'soapbox/actions/snackbar';
|
import snackbar from 'soapbox/actions/snackbar';
|
||||||
|
@ -11,7 +11,15 @@ export interface IChat {
|
||||||
id: string
|
id: string
|
||||||
unread: number
|
unread: number
|
||||||
created_by_account: string
|
created_by_account: string
|
||||||
last_message: null | string
|
last_message: null | {
|
||||||
|
account_id: string
|
||||||
|
chat_id: string
|
||||||
|
content: string
|
||||||
|
created_at: string
|
||||||
|
discarded_at: string | null
|
||||||
|
id: string
|
||||||
|
unread: boolean
|
||||||
|
}
|
||||||
created_at: Date
|
created_at: Date
|
||||||
updated_at: Date
|
updated_at: Date
|
||||||
accepted: boolean
|
accepted: boolean
|
||||||
|
@ -76,18 +84,48 @@ const useChatMessages = (chatId: string) => {
|
||||||
data,
|
data,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const useChats = () => {
|
const useChats = () => {
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
|
|
||||||
const getChats = async() => {
|
const getChats = async(pageParam?: any): Promise<{ result: IChat[], maxId: string, hasMore: boolean }> => {
|
||||||
const { data } = await api.get('/api/v1/pleroma/chats');
|
const { data, headers } = await api.get('/api/v1/pleroma/chats', {
|
||||||
return data;
|
params: {
|
||||||
|
max_id: pageParam?.maxId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const hasMore = !!headers.link;
|
||||||
|
const nextMaxId = data[data.length - 1]?.id;
|
||||||
|
|
||||||
|
return {
|
||||||
|
result: data,
|
||||||
|
maxId: nextMaxId,
|
||||||
|
hasMore,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const chatsQuery = useQuery<IChat[]>(['chats'], getChats, {
|
const queryInfo = useInfiniteQuery(['chats'], ({ pageParam }) => getChats(pageParam), {
|
||||||
placeholderData: [],
|
keepPreviousData: true,
|
||||||
|
getNextPageParam: (config) => {
|
||||||
|
if (config.hasMore) {
|
||||||
|
return { maxId: config.maxId };
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const data = queryInfo.data?.pages.reduce<IChat[]>(
|
||||||
|
(prev: IChat[], curr) => [...prev, ...curr.result],
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const chatsQuery = {
|
||||||
|
...queryInfo,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
|
||||||
const getOrCreateChatByAccountId = (accountId: string) => api.post<IChat>(`/api/v1/pleroma/chats/by-account-id/${accountId}`);
|
const getOrCreateChatByAccountId = (accountId: string) => api.post<IChat>(`/api/v1/pleroma/chats/by-account-id/${accountId}`);
|
||||||
|
|
||||||
return { chatsQuery, getOrCreateChatByAccountId };
|
return { chatsQuery, getOrCreateChatByAccountId };
|
||||||
|
|
|
@ -29,8 +29,6 @@ const play = (audio: HTMLAudioElement): void => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('playing');
|
|
||||||
|
|
||||||
audio.play();
|
audio.play();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue