Apply filters when searching in SourceMangas (#545)

This commit is contained in:
schroda
2024-01-11 02:57:56 +01:00
committed by GitHub
parent 89e8472ebc
commit 955cc682fd

View File

@@ -73,7 +73,6 @@ export enum SourceContentType {
POPULAR, POPULAR,
LATEST, LATEST,
SEARCH, SEARCH,
FILTER,
} }
interface IPos { interface IPos {
@@ -86,8 +85,7 @@ interface IPos {
const SOURCE_CONTENT_TYPE_TO_ERROR_MSG_KEY: { [contentType in SourceContentType]: TranslationKey } = { const SOURCE_CONTENT_TYPE_TO_ERROR_MSG_KEY: { [contentType in SourceContentType]: TranslationKey } = {
[SourceContentType.POPULAR]: 'manga.error.label.no_mangas_found', [SourceContentType.POPULAR]: 'manga.error.label.no_mangas_found',
[SourceContentType.LATEST]: 'manga.error.label.no_mangas_found', [SourceContentType.LATEST]: 'manga.error.label.no_mangas_found',
[SourceContentType.FILTER]: 'manga.error.label.no_matches', [SourceContentType.SEARCH]: 'manga.error.label.no_matches',
[SourceContentType.SEARCH]: 'manga.error.label.no_mangas_found',
}; };
const getUniqueMangas = (mangas: TPartialManga[]): TPartialManga[] => { const getUniqueMangas = (mangas: TPartialManga[]): TPartialManga[] => {
@@ -128,12 +126,9 @@ const useSourceManga = (
result = requestManager.useGetSourceLatestMangas(sourceId, initialPages); result = requestManager.useGetSourceLatestMangas(sourceId, initialPages);
break; break;
case SourceContentType.SEARCH: case SourceContentType.SEARCH:
result = requestManager.useSourceSearch(sourceId, searchTerm ?? '', undefined, initialPages);
break;
case SourceContentType.FILTER:
result = requestManager.useSourceSearch( result = requestManager.useSourceSearch(
sourceId, sourceId,
undefined, searchTerm ?? '',
filters.map((filter) => { filters.map((filter) => {
const { position, state, group } = filter; const { position, state, group } = filter;
@@ -205,6 +200,7 @@ export function SourceMangas() {
const { sourceId } = useParams<{ sourceId: string }>(); const { sourceId } = useParams<{ sourceId: string }>();
const navigate = useNavigate(); const navigate = useNavigate();
const { search } = useLocation();
const { const {
contentType: currentLocationContentType = SourceContentType.POPULAR, contentType: currentLocationContentType = SourceContentType.POPULAR,
filtersToApply: currentLocationFiltersToApply = [], filtersToApply: currentLocationFiltersToApply = [],
@@ -213,10 +209,17 @@ export function SourceMangas() {
contentType: SourceContentType; contentType: SourceContentType;
filtersToApply: IPos[]; filtersToApply: IPos[];
clearCache: boolean; clearCache: boolean;
search: string;
}>().state ?? {}; }>().state ?? {};
useSetDefaultBackTo('sources'); useSetDefaultBackTo('sources');
const [isFirstRender, setIsFirstRender] = useState(true);
useEffect(() => {
setIsFirstRender(false);
}, []);
const { options } = useLibraryOptionsContext(); const { options } = useLibraryOptionsContext();
const [query] = useQueryParam('query', StringParam); const [query] = useQueryParam('query', StringParam);
const [dialogFiltersToApply, setDialogFiltersToApply] = useState<IPos[]>(currentLocationFiltersToApply); const [dialogFiltersToApply, setDialogFiltersToApply] = useState<IPos[]>(currentLocationFiltersToApply);
@@ -250,9 +253,20 @@ export function SourceMangas() {
) : undefined; ) : undefined;
const updateContentType = useCallback( const updateContentType = useCallback(
(newContentType: SourceContentType, updateLocationState: boolean = true) => { (
newContentType: SourceContentType,
{ updateLocationState = true, search: newSearch }: { updateLocationState?: boolean; search?: string } = {},
) => {
if (updateLocationState) { if (updateLocationState) {
navigate('', { replace: true, state: { contentType: newContentType } }); navigate(
{ pathname: '', search: newSearch },
{
replace: true,
state: {
contentType: newContentType,
},
},
);
} }
setContentType(newContentType); setContentType(newContentType);
@@ -263,22 +277,26 @@ export function SourceMangas() {
const updateLocationFilters = useCallback( const updateLocationFilters = useCallback(
(updatedFilters: IPos[]) => { (updatedFilters: IPos[]) => {
if (contentType === SourceContentType.FILTER) { if (contentType === SourceContentType.SEARCH) {
navigate('', { replace: true, state: { contentType, filtersToApply: updatedFilters } }); navigate(
{ pathname: '', search },
{
replace: true,
state: {
contentType,
filtersToApply: updatedFilters,
},
},
);
} }
}, },
[contentType], [contentType, search],
); );
const isSearchTermAvailable = searchTerm && query?.length; const isSearchTermAvailable = searchTerm && query?.length;
const setSearchContentType = isSearchTermAvailable && contentType !== SourceContentType.SEARCH; const setSearchContentType = isSearchTermAvailable && contentType !== SourceContentType.SEARCH;
if (setSearchContentType) { if (setSearchContentType) {
updateContentType(SourceContentType.SEARCH, false); updateContentType(SourceContentType.SEARCH, { search });
}
const closeSearch = !query?.length && contentType === SourceContentType.SEARCH;
if (closeSearch) {
updateContentType(currentLocationContentType, false);
} }
const loadMore = useCallback(() => { const loadMore = useCallback(() => {
@@ -294,7 +312,7 @@ export function SourceMangas() {
setFiltersToApply([]); setFiltersToApply([]);
updateLocationFilters([]); updateLocationFilters([]);
setResetScrollPosition(true); setResetScrollPosition(true);
}, [sourceId, contentType]); }, [sourceId, contentType, updateLocationFilters]);
useEffect(() => { useEffect(() => {
if (!clearCache) { if (!clearCache) {
@@ -315,17 +333,16 @@ export function SourceMangas() {
useEffect( useEffect(
() => () => { () => () => {
if (contentType !== SourceContentType.SEARCH) { if (contentType !== SourceContentType.SEARCH || isFirstRender) {
return; return;
} }
// INFO: // INFO:
// with strict mode + dev mode the first request will be aborted. due to using SWR there won't be an // with strict mode + dev mode the first request will be aborted. due to using SWR there won't be an
// immediate second request since it's the same key. instead the "second" request will be the error handling of SWR // immediate second request since it's the same key. instead the "second" request will be the error handling of SWR
abortRequest(new Error(`SourceMangas(${sourceId}): search string changed`)); abortRequest(new Error(`SourceMangas(${sourceId}): search string changed`));
setResetScrollPosition(true); setResetScrollPosition(true);
}, },
[searchTerm, contentType], [searchTerm],
); );
useEffect(() => { useEffect(() => {
@@ -381,9 +398,9 @@ export function SourceMangas() {
</ContentTypeButton> </ContentTypeButton>
) : null} ) : null}
<ContentTypeButton <ContentTypeButton
variant={contentType === SourceContentType.FILTER ? 'contained' : 'outlined'} variant={contentType === SourceContentType.SEARCH ? 'contained' : 'outlined'}
startIcon={<FilterListIcon />} startIcon={<FilterListIcon />}
onClick={() => updateContentType(SourceContentType.FILTER)} onClick={() => updateContentType(SourceContentType.SEARCH)}
> >
{t('global.button.filter')} {t('global.button.filter')}
</ContentTypeButton> </ContentTypeButton>
@@ -398,7 +415,7 @@ export function SourceMangas() {
isLoading={isLoading} isLoading={isLoading}
gridLayout={options.SourcegridLayout} gridLayout={options.SourcegridLayout}
/> />
{contentType === SourceContentType.FILTER && ( {contentType === SourceContentType.SEARCH && (
<SourceOptions <SourceOptions
sourceFilter={filters} sourceFilter={filters}
updateFilterValue={setDialogFiltersToApply} updateFilterValue={setDialogFiltersToApply}