pl-fe: migrate user index to tanstack query

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2025-07-10 13:42:22 +02:00
parent 79b71b2a8f
commit 0c302c42bf
2 changed files with 28 additions and 45 deletions

View File

@ -1,14 +1,13 @@
import debounce from 'lodash/debounce';
import React, { useCallback, useEffect } from 'react';
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useSearchParams } from 'react-router-dom-v5-compat';
import { expandUserIndex, fetchUserIndex, setUserIndexQuery } from 'pl-fe/actions/admin';
import ScrollableList from 'pl-fe/components/scrollable-list';
import Column from 'pl-fe/components/ui/column';
import Input from 'pl-fe/components/ui/input';
import AccountContainer from 'pl-fe/containers/account-container';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import { useAdminAccounts } from 'pl-fe/queries/admin/use-accounts';
import { SearchInput } from '../search/search';
const messages = defineMessages({
heading: { id: 'column.admin.users', defaultMessage: 'Users' },
@ -16,51 +15,32 @@ const messages = defineMessages({
searchPlaceholder: { id: 'admin.user_index.search_input_placeholder', defaultMessage: 'Who are you looking for?' },
});
const UserIndexPage: React.FC = () => {
const dispatch = useAppDispatch();
const [params] = useSearchParams();
const query = params.get('q') || '';
const intl = useIntl();
const { isLoading, items, total, query, next } = useAppSelector((state) => state.admin_user_index);
const handleLoadMore = () => {
if (!isLoading) dispatch(expandUserIndex());
};
const updateQuery = useCallback(debounce(() => {
dispatch(fetchUserIndex());
}, 900, { leading: true }), []);
const handleQueryChange: React.ChangeEventHandler<HTMLInputElement> = e => {
dispatch(setUserIndexQuery(e.target.value));
updateQuery();
};
useEffect(() => {
updateQuery();
}, []);
const hasMore = (total === undefined || items.length < total) && !!next;
const showLoading = isLoading && !items.length;
const { data: accountIds, isPending, isFetching, hasNextPage, fetchNextPage } = useAdminAccounts({
origin: 'local',
status: 'active',
username: query,
});
return (
<Column label={intl.formatMessage(messages.heading)}>
<Input
value={query}
onChange={handleQueryChange}
placeholder={intl.formatMessage(messages.searchPlaceholder)}
/>
<SearchInput />
<ScrollableList
scrollKey='userIndex'
hasMore={hasMore}
isLoading={isLoading}
showLoading={showLoading}
onLoadMore={handleLoadMore}
hasMore={hasNextPage}
isLoading={isFetching}
showLoading={isPending}
onLoadMore={() => fetchNextPage({ cancelRefetch: false })}
emptyMessage={intl.formatMessage(messages.empty)}
className='mt-4'
itemClassName='pb-4'
>
{items.map(id =>
{(accountIds || []).map(id =>
<AccountContainer key={id} id={id} withDate />,
)}
</ScrollableList>

View File

@ -39,7 +39,11 @@ const messages = defineMessages({
links: { id: 'search_results.links', defaultMessage: 'News' },
});
const SearchInput = () => {
interface ISearchInput {
placeholder?: string;
}
const SearchInput: React.FC<ISearchInput> = ({ placeholder }) => {
const [params, setParams] = useSearchParams();
const [value, setValue] = useState(params.get('q') || '');
@ -82,13 +86,13 @@ const SearchInput = () => {
<div
className='sticky top-[76px] z-10 w-full bg-white/90 backdrop-blur black:bg-black/80 dark:bg-primary-900/90'
>
<label htmlFor='search' className='sr-only'>{intl.formatMessage(messages.placeholder)}</label>
<label htmlFor='search' className='sr-only'>{placeholder || intl.formatMessage(messages.placeholder)}</label>
<div className='relative'>
<Input
type='text'
id='search'
placeholder={intl.formatMessage(messages.placeholder)}
placeholder={placeholder || intl.formatMessage(messages.placeholder)}
value={value}
onChange={handleChange}
onKeyDown={handleKeyDown}
@ -115,7 +119,6 @@ const SearchInput = () => {
className='size-4 text-gray-600'
/>
)}
</div>
</div>
</div>
@ -375,4 +378,4 @@ const SearchPage = () => {
);
};
export { SearchPage as default };
export { SearchInput, SearchPage as default };