Improve library search
Simplify searching by making matching logic less strict. - do not require full match - additionally compare by removing all non letter and number chars
This commit is contained in:
@@ -13,6 +13,7 @@ import { useLibraryOptionsContext } from '@/components/context/LibraryOptionsCon
|
|||||||
import { useMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts';
|
import { useMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts';
|
||||||
import { ChapterType, MangaType, TrackRecordType } from '@/lib/graphql/generated/graphql.ts';
|
import { ChapterType, MangaType, TrackRecordType } from '@/lib/graphql/generated/graphql.ts';
|
||||||
import { MangaIdInfo } from '@/lib/data/Mangas.ts';
|
import { MangaIdInfo } from '@/lib/data/Mangas.ts';
|
||||||
|
import { baseCleanup, enhancedCleanup } from '@/lib/data/Strings.ts';
|
||||||
|
|
||||||
const triStateFilter = (
|
const triStateFilter = (
|
||||||
triState: NullAndUndefined<boolean>,
|
triState: NullAndUndefined<boolean>,
|
||||||
@@ -46,14 +47,27 @@ const triStateFilterBoolean = (triState: NullAndUndefined<boolean>, status?: boo
|
|||||||
type TMangaQueryFilter = Pick<MangaType, 'title'>;
|
type TMangaQueryFilter = Pick<MangaType, 'title'>;
|
||||||
const queryFilter = (query: NullAndUndefined<string>, { title }: TMangaQueryFilter): boolean => {
|
const queryFilter = (query: NullAndUndefined<string>, { title }: TMangaQueryFilter): boolean => {
|
||||||
if (!query) return true;
|
if (!query) return true;
|
||||||
return title.toLowerCase().includes(query.toLowerCase());
|
|
||||||
|
const cleanupQuery = baseCleanup(query);
|
||||||
|
|
||||||
|
return baseCleanup(title).includes(cleanupQuery) || enhancedCleanup(title).includes(cleanupQuery);
|
||||||
};
|
};
|
||||||
|
|
||||||
type TMangaQueryGenreFilter = Pick<MangaType, 'genre'>;
|
type TMangaQueryGenreFilter = Pick<MangaType, 'genre'>;
|
||||||
const queryGenreFilter = (query: NullAndUndefined<string>, { genre }: TMangaQueryGenreFilter): boolean => {
|
const queryGenreFilter = (query: NullAndUndefined<string>, { genre: mangaGenres }: TMangaQueryGenreFilter): boolean => {
|
||||||
if (!query) return true;
|
if (!query) return true;
|
||||||
const queries = query.split(',').map((str) => str.toLowerCase().trim());
|
|
||||||
return queries.every((element) => genre.map((el) => el.toLowerCase()).includes(element));
|
const genreQueries = query.split(',').map(baseCleanup);
|
||||||
|
const genres = mangaGenres
|
||||||
|
.map((genre) => {
|
||||||
|
const baseCleanedUpGenre = baseCleanup(genre);
|
||||||
|
const fullyCleanedUpGenre = enhancedCleanup(baseCleanedUpGenre);
|
||||||
|
|
||||||
|
return `${baseCleanedUpGenre}, ${fullyCleanedUpGenre}`;
|
||||||
|
})
|
||||||
|
.join(', ');
|
||||||
|
|
||||||
|
return genreQueries.every((genreQuery) => genres.includes(genreQuery));
|
||||||
};
|
};
|
||||||
|
|
||||||
type TMangaTrackerFilter = { trackRecords: { nodes: Pick<TrackRecordType, 'id' | 'trackerId'>[] } };
|
type TMangaTrackerFilter = { trackRecords: { nodes: Pick<TrackRecordType, 'id' | 'trackerId'>[] } };
|
||||||
|
|||||||
11
src/lib/data/Strings.ts
Normal file
11
src/lib/data/Strings.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const baseCleanup = (str: string) => str.toLowerCase().trim();
|
||||||
|
|
||||||
|
export const enhancedCleanup = (str: string): string => baseCleanup(str).replace(/[^a-zA-Z0-9]/g, '');
|
||||||
@@ -35,17 +35,12 @@ import { GET_MANGAS_DUPLICATES } from '@/lib/graphql/queries/MangaQuery.ts';
|
|||||||
import { BaseMangaGrid } from '@/components/source/BaseMangaGrid.tsx';
|
import { BaseMangaGrid } from '@/components/source/BaseMangaGrid.tsx';
|
||||||
import { IMangaGridProps } from '@/components/MangaGrid.tsx';
|
import { IMangaGridProps } from '@/components/MangaGrid.tsx';
|
||||||
import { StyledGroupItemWrapper } from '@/components/virtuoso/StyledGroupItemWrapper.tsx';
|
import { StyledGroupItemWrapper } from '@/components/virtuoso/StyledGroupItemWrapper.tsx';
|
||||||
|
import { enhancedCleanup } from '@/lib/data/Strings.ts';
|
||||||
const cleanupTitle = (title: string): string =>
|
|
||||||
title
|
|
||||||
.toLowerCase()
|
|
||||||
.trim()
|
|
||||||
.replace(/[^a-zA-Z0-9]/g, '');
|
|
||||||
|
|
||||||
const findDuplicatesByTitle = <Manga extends Pick<MangaType, 'title'>>(
|
const findDuplicatesByTitle = <Manga extends Pick<MangaType, 'title'>>(
|
||||||
libraryMangas: Manga[],
|
libraryMangas: Manga[],
|
||||||
): Record<string, Manga[]> => {
|
): Record<string, Manga[]> => {
|
||||||
const titleToMangas = Object.groupBy(libraryMangas, ({ title }) => cleanupTitle(title));
|
const titleToMangas = Object.groupBy(libraryMangas, ({ title }) => enhancedCleanup(title));
|
||||||
|
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
Object.entries(titleToMangas)
|
Object.entries(titleToMangas)
|
||||||
@@ -62,7 +57,7 @@ const findDuplicatesByTitleAndAlternativeTitles = <Manga extends TMangaDuplicate
|
|||||||
const titleToAlternativeTitleMatches: Record<string, Set<Manga>> = {};
|
const titleToAlternativeTitleMatches: Record<string, Set<Manga>> = {};
|
||||||
|
|
||||||
libraryMangas.forEach((mangaToCheck) => {
|
libraryMangas.forEach((mangaToCheck) => {
|
||||||
const titleToCheck = cleanupTitle(mangaToCheck.title);
|
const titleToCheck = enhancedCleanup(mangaToCheck.title);
|
||||||
|
|
||||||
titleToMangas[titleToCheck] ??= new Set();
|
titleToMangas[titleToCheck] ??= new Set();
|
||||||
titleToMangas[titleToCheck].add(mangaToCheck);
|
titleToMangas[titleToCheck].add(mangaToCheck);
|
||||||
@@ -76,8 +71,8 @@ const findDuplicatesByTitleAndAlternativeTitles = <Manga extends TMangaDuplicate
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const doesTitleMatch = cleanupTitle(libraryManga.title) === titleToCheck;
|
const doesTitleMatch = enhancedCleanup(libraryManga.title) === titleToCheck;
|
||||||
const doesAlternativeTitleMatch = cleanupTitle(libraryManga?.description ?? '').includes(titleToCheck);
|
const doesAlternativeTitleMatch = enhancedCleanup(libraryManga?.description ?? '').includes(titleToCheck);
|
||||||
|
|
||||||
const isDuplicate = doesTitleMatch || doesAlternativeTitleMatch;
|
const isDuplicate = doesTitleMatch || doesAlternativeTitleMatch;
|
||||||
if (!isDuplicate) {
|
if (!isDuplicate) {
|
||||||
|
|||||||
Reference in New Issue
Block a user