Add "status" library filter

This commit is contained in:
schroda
2024-09-15 00:48:32 +02:00
parent 009da03236
commit 1708d5daff
8 changed files with 60 additions and 16 deletions

View File

@@ -704,6 +704,15 @@
"reload_from_source": "Reload data from source", "reload_from_source": "Reload data from source",
"status": "Status" "status": "Status"
}, },
"status": {
"cancelled": "Cancelled",
"completed": "Completed",
"finished": "Finished",
"hiatus": "Hiatus",
"licensed": "Licensed",
"ongoing": "Ongoing",
"unknown": "$t(global.label.unknown)"
},
"title_one": "Manga", "title_one": "Manga",
"title_other": "Manga" "title_other": "Manga"
}, },

View File

@@ -34,6 +34,7 @@ export const DefaultLibraryOptions: LibraryOptions = {
unread: undefined, unread: undefined,
hasDuplicateChapters: undefined, hasDuplicateChapters: undefined,
tracker: {}, tracker: {},
status: {} as LibraryOptions['status'],
showTabSize: false, showTabSize: false,
}; };

View File

@@ -19,8 +19,9 @@ import { GridLayout, useLibraryOptionsContext } from '@/components/context/Libra
import { OptionsTabs } from '@/components/molecules/OptionsTabs'; import { OptionsTabs } from '@/components/molecules/OptionsTabs';
import { requestManager } from '@/lib/requests/RequestManager.ts'; import { requestManager } from '@/lib/requests/RequestManager.ts';
import { Trackers } from '@/lib/data/Trackers.ts'; import { Trackers } from '@/lib/data/Trackers.ts';
import { GetTrackersSettingsQuery } from '@/lib/graphql/generated/graphql.ts'; import { GetTrackersSettingsQuery, MangaStatus } from '@/lib/graphql/generated/graphql.ts';
import { GET_TRACKERS_SETTINGS } from '@/lib/graphql/queries/TrackerQuery.ts'; import { GET_TRACKERS_SETTINGS } from '@/lib/graphql/queries/TrackerQuery.ts';
import { statusToTranslationKey } from '@/lib/data/Mangas.ts';
const TITLES: { [key in 'filter' | 'sort' | 'display']: TranslationKey } = { const TITLES: { [key in 'filter' | 'sort' | 'display']: TranslationKey } = {
filter: 'global.label.filter', filter: 'global.label.filter',
@@ -83,6 +84,19 @@ export const LibraryOptionsPanel: React.FC<IProps> = ({ open, onClose }) => {
checked={options.hasDuplicateChapters} checked={options.hasDuplicateChapters}
onChange={(c) => handleFilterChange('hasDuplicateChapters', c)} onChange={(c) => handleFilterChange('hasDuplicateChapters', c)}
/> />
<FormLabel sx={{ mt: 2 }}>{t('manga.label.status')}</FormLabel>
{Object.values(MangaStatus).map((status) => (
<ThreeStateCheckboxInput
label={t(statusToTranslationKey[status])}
checked={options.status[status]}
onChange={(checked) =>
handleFilterChange('status', {
...options.status,
[status]: checked,
})
}
/>
))}
<FormLabel sx={{ mt: 2 }}>{t('global.filter.label.tracked')}</FormLabel> <FormLabel sx={{ mt: 2 }}>{t('global.filter.label.tracked')}</FormLabel>
{loggedInTrackers.map((tracker) => ( {loggedInTrackers.map((tracker) => (
<ThreeStateCheckboxInput <ThreeStateCheckboxInput

View File

@@ -90,26 +90,31 @@ const trackerFilter = (trackFilters: LibraryOptions['tracker'], manga: TMangaTra
() => !isTrackerBound, () => !isTrackerBound,
); );
}) })
.every((matchesFilter) => matchesFilter); .every(Boolean);
type TMangaFilterOptions = { type TMangaStatusFilter = Pick<MangaType, 'status'>;
unread: NullAndUndefined<boolean>; const statusFilter = (statusFilters: LibraryOptions['status'], manga: TMangaStatusFilter): boolean =>
downloaded: NullAndUndefined<boolean>; Object.entries(statusFilters)
bookmarked: NullAndUndefined<boolean>; .map(([status, statusFilterState]) => triStateFilterBoolean(statusFilterState, status === manga.status))
hasDuplicateChapters: NullAndUndefined<boolean>; .every(Boolean);
tracker: LibraryOptions['tracker'];
}; type TMangaFilterOptions = Pick<
LibraryOptions,
'unread' | 'downloaded' | 'bookmarked' | 'hasDuplicateChapters' | 'tracker' | 'status'
>;
type TMangaFilter = Pick<MangaType, 'downloadCount' | 'unreadCount' | 'bookmarkCount' | 'hasDuplicateChapters'> & type TMangaFilter = Pick<MangaType, 'downloadCount' | 'unreadCount' | 'bookmarkCount' | 'hasDuplicateChapters'> &
TMangaTrackerFilter; TMangaTrackerFilter &
TMangaStatusFilter;
const filterManga = ( const filterManga = (
manga: TMangaFilter, manga: TMangaFilter,
{ unread, downloaded, bookmarked, hasDuplicateChapters, tracker }: TMangaFilterOptions, { unread, downloaded, bookmarked, hasDuplicateChapters, tracker, status }: TMangaFilterOptions,
): boolean => ): boolean =>
triStateFilterNumber(downloaded, manga.downloadCount) && triStateFilterNumber(downloaded, manga.downloadCount) &&
triStateFilterNumber(unread, manga.unreadCount) && triStateFilterNumber(unread, manga.unreadCount) &&
triStateFilterNumber(bookmarked, manga.bookmarkCount) && triStateFilterNumber(bookmarked, manga.bookmarkCount) &&
triStateFilterBoolean(hasDuplicateChapters, manga.hasDuplicateChapters) && triStateFilterBoolean(hasDuplicateChapters, manga.hasDuplicateChapters) &&
trackerFilter(tracker, manga); trackerFilter(tracker, manga) &&
statusFilter(status, manga);
type TMangasFilter = TMangaQueryFilter & TMangaFilter; type TMangasFilter = TMangaQueryFilter & TMangaFilter;
const filterMangas = <Manga extends TMangasFilter>( const filterMangas = <Manga extends TMangasFilter>(
@@ -183,7 +188,7 @@ export const useGetVisibleLibraryMangas = <Manga extends MangaIdInfo & TMangasFi
} => { } => {
const [query] = useQueryParam('query', StringParam); const [query] = useQueryParam('query', StringParam);
const { options } = useLibraryOptionsContext(); const { options } = useLibraryOptionsContext();
const { unread, downloaded, bookmarked, tracker, hasDuplicateChapters } = options; const { unread, downloaded, bookmarked, tracker, hasDuplicateChapters, status } = options;
const { settings } = useMetadataServerSettings(); const { settings } = useMetadataServerSettings();
const filteredMangas = useMemo( const filteredMangas = useMemo(
@@ -194,6 +199,7 @@ export const useGetVisibleLibraryMangas = <Manga extends MangaIdInfo & TMangasFi
bookmarked, bookmarked,
hasDuplicateChapters, hasDuplicateChapters,
tracker, tracker,
status,
ignoreFilters: settings.ignoreFilters, ignoreFilters: settings.ignoreFilters,
}), }),
[mangas, query, unread, downloaded, bookmarked, hasDuplicateChapters, tracker, settings.ignoreFilters], [mangas, query, unread, downloaded, bookmarked, hasDuplicateChapters, tracker, settings.ignoreFilters],

View File

@@ -19,6 +19,7 @@ import {
GetMangaToMigrateToFetchMutation, GetMangaToMigrateToFetchMutation,
MangaBaseFieldsFragment, MangaBaseFieldsFragment,
MangaReaderFieldsFragment, MangaReaderFieldsFragment,
MangaStatus,
MangaType, MangaType,
TrackRecordType, TrackRecordType,
UpdateMangaCategoriesPatchInput, UpdateMangaCategoriesPatchInput,
@@ -39,6 +40,16 @@ export type MangaAction =
| 'migrate' | 'migrate'
| 'track'; | 'track';
export const statusToTranslationKey: Record<MangaStatus, TranslationKey> = {
[MangaStatus.Cancelled]: 'manga.status.cancelled',
[MangaStatus.Completed]: 'manga.status.completed',
[MangaStatus.Licensed]: 'manga.status.licensed',
[MangaStatus.Ongoing]: 'manga.status.ongoing',
[MangaStatus.OnHiatus]: 'manga.status.hiatus',
[MangaStatus.PublishingFinished]: 'manga.status.finished',
[MangaStatus.Unknown]: 'manga.status.unknown',
};
export const actionToTranslationKey: { export const actionToTranslationKey: {
[key in MangaAction]: { [key in MangaAction]: {
action: { action: {

View File

@@ -96,6 +96,7 @@ export const MANGA_LIBRARY_FIELDS = gql`
genre genre
lastFetchedAt lastFetchedAt
inLibraryAt inLibraryAt
status
artist artist
author author

View File

@@ -2751,7 +2751,7 @@ export type MangaChapterNodeFieldsFragment = { __typename?: 'MangaType', firstUn
export type MangaReaderFieldsFragment = { __typename?: 'MangaType', id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, meta: Array<{ __typename?: 'MangaMetaType', key: string, value: string }>, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number } }; export type MangaReaderFieldsFragment = { __typename?: 'MangaType', id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, meta: Array<{ __typename?: 'MangaMetaType', key: string, value: string }>, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number } };
export type MangaLibraryFieldsFragment = { __typename?: 'MangaType', genre: Array<string>, lastFetchedAt?: string | null, inLibraryAt: string, artist?: string | null, author?: string | null, description?: string | null, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, source?: { __typename?: 'SourceType', id: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null }; export type MangaLibraryFieldsFragment = { __typename?: 'MangaType', genre: Array<string>, lastFetchedAt?: string | null, inLibraryAt: string, status: MangaStatus, artist?: string | null, author?: string | null, description?: string | null, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, source?: { __typename?: 'SourceType', id: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null };
export type MangaScreenFieldsFragment = { __typename?: 'MangaType', artist?: string | null, author?: string | null, description?: string | null, status: MangaStatus, realUrl?: string | null, sourceId: string, genre: Array<string>, lastFetchedAt?: string | null, inLibraryAt: string, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, source?: { __typename?: 'SourceType', id: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null }; export type MangaScreenFieldsFragment = { __typename?: 'MangaType', artist?: string | null, author?: string | null, description?: string | null, status: MangaStatus, realUrl?: string | null, sourceId: string, genre: Array<string>, lastFetchedAt?: string | null, inLibraryAt: string, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, source?: { __typename?: 'SourceType', id: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null };
@@ -3274,7 +3274,7 @@ export type GetCategoryMangasQueryVariables = Exact<{
}>; }>;
export type GetCategoryMangasQuery = { __typename?: 'Query', category: { __typename?: 'CategoryType', id: number, mangas: { __typename?: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename?: 'MangaType', genre: Array<string>, lastFetchedAt?: string | null, inLibraryAt: string, artist?: string | null, author?: string | null, description?: string | null, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, source?: { __typename?: 'SourceType', id: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } } }; export type GetCategoryMangasQuery = { __typename?: 'Query', category: { __typename?: 'CategoryType', id: number, mangas: { __typename?: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename?: 'MangaType', genre: Array<string>, lastFetchedAt?: string | null, inLibraryAt: string, status: MangaStatus, artist?: string | null, author?: string | null, description?: string | null, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, source?: { __typename?: 'SourceType', id: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } } };
export type GetChaptersReaderQueryVariables = Exact<{ export type GetChaptersReaderQueryVariables = Exact<{
after?: InputMaybe<Scalars['Cursor']['input']>; after?: InputMaybe<Scalars['Cursor']['input']>;
@@ -3432,7 +3432,7 @@ export type GetMangasLibraryQueryVariables = Exact<{
}>; }>;
export type GetMangasLibraryQuery = { __typename?: 'Query', mangas: { __typename?: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename?: 'MangaType', genre: Array<string>, lastFetchedAt?: string | null, inLibraryAt: string, artist?: string | null, author?: string | null, description?: string | null, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, source?: { __typename?: 'SourceType', id: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } }; export type GetMangasLibraryQuery = { __typename?: 'Query', mangas: { __typename?: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename?: 'MangaType', genre: Array<string>, lastFetchedAt?: string | null, inLibraryAt: string, status: MangaStatus, artist?: string | null, author?: string | null, description?: string | null, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, source?: { __typename?: 'SourceType', id: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } };
export type GetMangasDuplicatesQueryVariables = Exact<{ export type GetMangasDuplicatesQueryVariables = Exact<{
after?: InputMaybe<Scalars['Cursor']['input']>; after?: InputMaybe<Scalars['Cursor']['input']>;

View File

@@ -16,6 +16,7 @@ import {
GetSourceBrowseQuery, GetSourceBrowseQuery,
GetSourceSettingsQuery, GetSourceSettingsQuery,
MangaReaderFieldsFragment, MangaReaderFieldsFragment,
MangaStatus,
MetaType, MetaType,
SourcePreferenceChangeInput, SourcePreferenceChangeInput,
TrackerType, TrackerType,
@@ -281,6 +282,7 @@ export interface LibraryOptions {
sortDesc: NullAndUndefined<boolean>; sortDesc: NullAndUndefined<boolean>;
showTabSize: boolean; showTabSize: boolean;
tracker: Record<TrackerType['id'], NullAndUndefined<boolean>>; tracker: Record<TrackerType['id'], NullAndUndefined<boolean>>;
status: Record<MangaStatus, NullAndUndefined<boolean>>;
} }
export type ServerSettings = GetServerSettingsQuery['settings']; export type ServerSettings = GetServerSettingsQuery['settings'];