diff --git a/app/soapbox/actions/streaming.ts b/app/soapbox/actions/streaming.ts index e4434d237..5ec405464 100644 --- a/app/soapbox/actions/streaming.ts +++ b/app/soapbox/actions/streaming.ts @@ -1,6 +1,6 @@ import { getSettings } from 'soapbox/actions/settings'; import messages from 'soapbox/locales/messages'; -import { queryClient } from 'soapbox/queries/client'; +import { updatePageItem, appendPageItem } from 'soapbox/utils/queries'; import { play, soundCache } from 'soapbox/utils/sounds'; import { connectStream } from '../stream'; @@ -24,8 +24,6 @@ import { processTimelineUpdate, } from './timelines'; -import type { InfiniteData } from '@tanstack/react-query'; -import type { PaginatedResult } from 'soapbox/queries/chats'; import type { AppDispatch, RootState } from 'soapbox/store'; import type { APIEntity, Chat, ChatMessage } from 'soapbox/types/entities'; @@ -56,24 +54,10 @@ interface ChatPayload extends Omit { const updateChat = (payload: ChatPayload) => { const { last_message: lastMessage } = payload; - queryClient.setQueriesData>>(['chats', 'search'], (data) => { - if (data) { - const pages = data.pages.map(page => { - const result = page.result.map(chat => chat.id === payload.id ? payload as any : chat); - return { ...page, result }; - }); - return { ...data, pages }; - } - }); + updatePageItem(['chats', 'search'], payload as any, (o, n) => o.id === n.id); if (lastMessage) { - queryClient.setQueryData>>(['chats', 'messages', payload.id], (data) => { - if (data) { - const pages = [...data.pages]; - pages[0] = { ...pages[0], result: [...pages[0].result, lastMessage] }; - return { ...data, pages }; - } - }); + appendPageItem(['chats', 'messages', payload.id], lastMessage); } }; diff --git a/app/soapbox/features/chats/components/chat-list-item.tsx b/app/soapbox/features/chats/components/chat-list-item.tsx index 0453baaf8..20e3f1f0e 100644 --- a/app/soapbox/features/chats/components/chat-list-item.tsx +++ b/app/soapbox/features/chats/components/chat-list-item.tsx @@ -98,7 +98,7 @@ const ChatListItem: React.FC = ({ chat, chatSilence, onC weight='medium' theme='muted' truncate - className='w-full truncate-child pointer-events-none' + className='w-full h-5 truncate-child pointer-events-none' data-testid='chat-last-message' dangerouslySetInnerHTML={{ __html: chat.last_message?.content }} /> diff --git a/app/soapbox/features/chats/components/chat-page/chat-page.tsx b/app/soapbox/features/chats/components/chat-page/chat-page.tsx index 74a2e01b4..062aa5b18 100644 --- a/app/soapbox/features/chats/components/chat-page/chat-page.tsx +++ b/app/soapbox/features/chats/components/chat-page/chat-page.tsx @@ -21,7 +21,10 @@ const ChatPage = () => { const { top } = containerRef.current.getBoundingClientRect(); const fullHeight = document.body.offsetHeight; - setHeight(fullHeight - top); + // On mobile, account for bottom navigation. + const offset = document.body.clientWidth < 976 ? -61 : 0; + + setHeight(fullHeight - top + offset); }; useEffect(() => { diff --git a/app/soapbox/features/chats/components/chat-page/components/chat-page-sidebar.tsx b/app/soapbox/features/chats/components/chat-page/components/chat-page-sidebar.tsx index a3acb2ceb..fec90dc40 100644 --- a/app/soapbox/features/chats/components/chat-page/components/chat-page-sidebar.tsx +++ b/app/soapbox/features/chats/components/chat-page/components/chat-page-sidebar.tsx @@ -1,62 +1,55 @@ -import { useMutation } from '@tanstack/react-query'; -import { AxiosError } from 'axios'; -import React from 'react'; +import React, { useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import snackbar from 'soapbox/actions/snackbar'; -import AccountSearch from 'soapbox/components/account_search'; -import { CardTitle, Stack } from 'soapbox/components/ui'; +import { CardTitle, HStack, IconButton, Stack } from 'soapbox/components/ui'; import { useChatContext } from 'soapbox/contexts/chat-context'; -import { useAppDispatch } from 'soapbox/hooks'; -import { IChat, useChats } from 'soapbox/queries/chats'; -import { queryClient } from 'soapbox/queries/client'; +import { useDebounce, useFeatures } from 'soapbox/hooks'; +import { IChat } from 'soapbox/queries/chats'; import ChatList from '../../chat-list'; +import ChatSearchInput from '../../chat-search-input'; const messages = defineMessages({ title: { id: 'column.chats', defaultMessage: 'Messages' }, - searchPlaceholder: { id: 'chats.search_placeholder', defaultMessage: 'Start a chat with…' }, }); const ChatPageSidebar = () => { - const dispatch = useAppDispatch(); const intl = useIntl(); + const features = useFeatures(); + const [search, setSearch] = useState(''); const { setChat } = useChatContext(); - const { getOrCreateChatByAccountId } = useChats(); - const handleSuggestion = (accountId: string) => { - handleClickOnSearchResult.mutate(accountId); - }; - - const handleClickOnSearchResult = useMutation((accountId: string) => { - return getOrCreateChatByAccountId(accountId); - }, { - onError: (error: AxiosError) => { - const data = error.response?.data as any; - dispatch(snackbar.error(data?.error)); - }, - onSuccess: (response) => { - setChat(response.data); - queryClient.invalidateQueries(['chats', 'search']); - }, - }); + const debouncedSearch = useDebounce(search, 300); const handleClickChat = (chat: IChat) => setChat(chat); return ( - + + - + + + + {features.chatsSearch && ( + setSearch(e.target.value)} + onClear={() => setSearch('')} + /> + )} - + ); diff --git a/app/soapbox/features/chats/components/chat-pane/chat-pane.tsx b/app/soapbox/features/chats/components/chat-pane/chat-pane.tsx index 6efb92f66..9ab8252e0 100644 --- a/app/soapbox/features/chats/components/chat-pane/chat-pane.tsx +++ b/app/soapbox/features/chats/components/chat-pane/chat-pane.tsx @@ -1,14 +1,14 @@ import sumBy from 'lodash/sumBy'; import React, { useState } from 'react'; -import { defineMessages, useIntl } from 'react-intl'; -import { Icon, Input, Stack } from 'soapbox/components/ui'; +import { Stack } from 'soapbox/components/ui'; import { useChatContext } from 'soapbox/contexts/chat-context'; -import { useDebounce } from 'soapbox/hooks'; +import { useDebounce, useFeatures } from 'soapbox/hooks'; import { IChat, useChats } from 'soapbox/queries/chats'; import ChatList from '../chat-list'; import ChatPaneHeader from '../chat-pane-header'; +import ChatSearchInput from '../chat-search-input'; import ChatSearch from '../chat-search/chat-search'; import EmptyResultsBlankslate from '../chat-search/empty-results-blankslate'; import ChatWindow from '../chat-window'; @@ -16,12 +16,8 @@ import { Pane } from '../ui'; import Blankslate from './blankslate'; -const messages = defineMessages({ - searchPlaceholder: { id: 'chats.search_placeholder', defaultMessage: 'Search inbox' }, -}); - const ChatPane = () => { - const intl = useIntl(); + const features = useFeatures(); const debounce = useDebounce; const [value, setValue] = useState(); @@ -49,26 +45,15 @@ const ChatPane = () => { if (hasSearchValue || Number(chats?.length) > 0) { return ( -
- setValue(event.target.value)} - isSearch - append={ - - } - /> -
+ {features.chatsSearch && ( +
+ setValue(event.target.value)} + onClear={clearValue} + /> +
+ )} {Number(chats?.length) > 0 ? ( , + /** Callback when the input is cleared. */ + onClear: React.MouseEventHandler, +} + +/** Search input for filtering chats. */ +const ChatSearchInput: React.FC = ({ value, onChange, onClear }) => { + const intl = useIntl(); + + return ( + +