Refactor ChatSearch and add various states

This commit is contained in:
Justin 2022-09-13 11:55:13 -04:00
parent 0952fe6dae
commit 0ae515ef18
6 changed files with 112 additions and 38 deletions

View File

@ -15,7 +15,7 @@ import useAccountSearch from 'soapbox/queries/search';
import ChatList from '../chat-list';
import ChatPaneHeader from '../chat-pane-header';
import ChatSearch from '../chat-search';
import ChatSearch from '../chat-search/chat-search';
import ChatWindow from '../chat-window';
import { Pane } from '../ui';
@ -44,10 +44,11 @@ const ChatPane = () => {
const unreadCount = sumBy(chats, (chat) => chat.unread);
const hasSearchValue = Number(value?.length) > 0;
console.log('hasSearchValue', hasSearchValue);
const handleClickChat = (chat: IChat) => setChat(chat);
const handleClickChat = (chat: IChat) => {
setChat(chat);
setValue(undefined);
};
const clearValue = () => {
if (hasSearchValue) {
@ -133,7 +134,10 @@ const ChatPane = () => {
unreadCount={unreadCount}
isOpen={isOpen}
onToggle={toggleChatPane}
secondaryAction={() => setSearching(true)}
secondaryAction={() => {
setSearching(true);
setValue(undefined);
}}
secondaryActionIcon={require('@tabler/icons/edit.svg')}
/>

View File

@ -4,7 +4,7 @@ import React from 'react';
import { __stub } from 'soapbox/api';
import { ChatProvider } from 'soapbox/contexts/chat-context';
import { render, screen, waitFor } from '../../../../jest/test-helpers';
import { render, screen, waitFor } from '../../../../../jest/test-helpers';
import ChatSearch from '../chat-search';
const renderComponent = () => render(
@ -28,7 +28,7 @@ describe('<ChatSearch />', () => {
});
describe('when the pane is open', () => {
beforeEach(async() => {
beforeEach(async () => {
renderComponent();
await userEvent.click(screen.getByTestId('icon-button'));
});
@ -50,7 +50,7 @@ describe('<ChatSearch />', () => {
});
});
it('renders accounts', async() => {
it('renders accounts', async () => {
renderComponent();
const user = userEvent.setup();

View File

@ -0,0 +1,14 @@
import React from 'react';
import { Stack, Text } from 'soapbox/components/ui';
const Blankslate = () => (
<Stack justifyContent='center' alignItems='center' space={2} className='h-full w-2/3 mx-auto'>
<Text weight='bold' size='lg' align='center'>Search followers</Text>
<Text theme='muted' align='center'>
You can start a conversation with anyone that follows you.
</Text>
</Stack>
);
export default Blankslate;

View File

@ -3,16 +3,19 @@ import { AxiosError } from 'axios';
import React, { useState } from 'react';
import snackbar from 'soapbox/actions/snackbar';
import { Avatar, HStack, Icon, Input, Stack, Text } from 'soapbox/components/ui';
import VerificationBadge from 'soapbox/components/verification_badge';
import { HStack, Icon, Input, Stack, Text } from 'soapbox/components/ui';
import { useChatContext } from 'soapbox/contexts/chat-context';
import { useAppDispatch, useDebounce } from 'soapbox/hooks';
import { useChats } from 'soapbox/queries/chats';
import { queryClient } from 'soapbox/queries/client';
import useAccountSearch from 'soapbox/queries/search';
import ChatPaneHeader from './chat-pane-header';
import { Pane } from './ui';
import ChatPaneHeader from '../chat-pane-header';
import { Pane } from '../ui';
import Blankslate from './blankslate';
import EmptyResultsBlankslate from './empty-results-blankslate';
import Results from './results';
const ChatSearch = () => {
const debounce = useDebounce;
@ -24,9 +27,10 @@ const ChatSearch = () => {
const [value, setValue] = useState<string>();
const debouncedValue = debounce(value as string, 300);
const { data: accounts } = useAccountSearch(debouncedValue);
const { data: accounts, isFetching } = useAccountSearch(debouncedValue);
const hasSearchValue = value && value.length > 0;
const hasSearchValue = debouncedValue && debouncedValue.length > 0;
const hasSearchResults = (accounts || []).length > 0;
const handleClickOnSearchResult = useMutation((accountId: string) => {
return getOrCreateChatByAccountId(accountId);
@ -41,6 +45,24 @@ const ChatSearch = () => {
},
});
const renderBody = () => {
if (hasSearchResults) {
return (
<Results
accounts={accounts}
onSelect={(id) => {
handleClickOnSearchResult.mutate(id);
clearValue();
}}
/>
);
} else if (hasSearchValue && !hasSearchResults && !isFetching) {
return <EmptyResultsBlankslate />;
} else {
return <Blankslate />;
}
};
const clearValue = () => {
if (hasSearchValue) {
setValue('');
@ -93,30 +115,7 @@ const ChatSearch = () => {
</div>
<Stack className='overflow-y-scroll flex-grow h-full' space={2}>
{(accounts || []).map((account: any) => (
<button
key={account.id}
type='button'
className='px-4 py-2 w-full flex flex-col hover:bg-gray-100 dark:hover:bg-gray-800'
onClick={() => {
handleClickOnSearchResult.mutate(account.id);
clearValue();
}}
data-testid='account'
>
<HStack alignItems='center' space={2}>
<Avatar src={account.avatar} size={40} />
<Stack alignItems='start'>
<div className='flex items-center space-x-1 flex-grow'>
<Text weight='bold' size='sm' truncate>{account.display_name}</Text>
{account.verified && <VerificationBadge />}
</div>
<Text size='sm' weight='medium' theme='muted' truncate>@{account.acct}</Text>
</Stack>
</HStack>
</button>
))}
{renderBody()}
</Stack>
</Stack>
) : null}

View File

@ -0,0 +1,14 @@
import React from 'react';
import { Stack, Text } from 'soapbox/components/ui';
const EmptyResultsBlankslate = () => (
<Stack justifyContent='center' alignItems='center' space={2} className='h-full w-2/3 mx-auto'>
<Text weight='bold' size='lg' align='center'>No matches found</Text>
<Text theme='muted' align='center'>
Try searching for another name.
</Text>
</Stack>
);
export default EmptyResultsBlankslate;

View File

@ -0,0 +1,43 @@
import React from 'react';
import { Avatar, HStack, Stack, Text } from 'soapbox/components/ui';
import VerificationBadge from 'soapbox/components/verification_badge';
interface IResults {
accounts: {
display_name: string
acct: string
id: string
avatar: string
verified: boolean
}[]
onSelect(id: string): void
}
const Results = ({ accounts, onSelect }: IResults) => (
<>
{(accounts || []).map((account: any) => (
<button
key={account.id}
type='button'
className='px-4 py-2 w-full flex flex-col hover:bg-gray-100 dark:hover:bg-gray-800'
onClick={() => onSelect(account.id)}
data-testid='account'
>
<HStack alignItems='center' space={2}>
<Avatar src={account.avatar} size={40} />
<Stack alignItems='start'>
<div className='flex items-center space-x-1 flex-grow'>
<Text weight='bold' size='sm' truncate>{account.display_name}</Text>
{account.verified && <VerificationBadge />}
</div>
<Text size='sm' weight='medium' theme='muted' truncate>@{account.acct}</Text>
</Stack>
</HStack>
</button>
))}
</>
);
export default Results;