Optionally hide library manga when browsing source (#665)

This commit is contained in:
schroda
2024-03-23 23:44:46 +01:00
committed by GitHub
parent 66d6c95dfd
commit 6186cb0ea3
5 changed files with 71 additions and 21 deletions

View File

@@ -811,6 +811,7 @@
}, },
"label": { "label": {
"dark_theme": "Dark theme", "dark_theme": "Dark theme",
"hide_library_entries": "Hide entries already in library",
"language_description": "Feel free to translate the project on", "language_description": "Feel free to translate the project on",
"manga_item_width": "Manga item width", "manga_item_width": "Manga item width",
"show_nsfw": "Show NSFW", "show_nsfw": "Show NSFW",

View File

@@ -38,6 +38,9 @@ export const getDefaultSettings = (): MetadataServerSettings => ({
includeChapters: true, includeChapters: true,
includeCategories: true, includeCategories: true,
deleteChapters: true, deleteChapters: true,
// browse
hideLibraryEntries: false,
}); });
export const convertSettingsToMetadata = ( export const convertSettingsToMetadata = (

View File

@@ -34,6 +34,7 @@ import {
GetSourceMangasFetchMutationVariables, GetSourceMangasFetchMutationVariables,
} from '@/lib/graphql/generated/graphql.ts'; } from '@/lib/graphql/generated/graphql.ts';
import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext.tsx'; import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext.tsx';
import { useMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts';
const ContentTypeMenu = styled('div')(({ theme }) => ({ const ContentTypeMenu = styled('div')(({ theme }) => ({
display: 'flex', display: 'flex',
@@ -107,12 +108,13 @@ const useSourceManga = (
searchTerm: string | null | undefined, searchTerm: string | null | undefined,
filters: IPos[], filters: IPos[],
initialPages: number, initialPages: number,
hideLibraryEntries: boolean,
): [ ): [
AbortableApolloUseMutationPaginatedResponse<GetSourceMangasFetchMutation, GetSourceMangasFetchMutationVariables>[0], AbortableApolloUseMutationPaginatedResponse<GetSourceMangasFetchMutation, GetSourceMangasFetchMutationVariables>[0],
AbortableApolloUseMutationPaginatedResponse< AbortableApolloUseMutationPaginatedResponse<
GetSourceMangasFetchMutation, GetSourceMangasFetchMutation,
GetSourceMangasFetchMutationVariables GetSourceMangasFetchMutationVariables
>[1][number], >[1][number] & { filteredOutAllItemsOfFetchedPage: boolean },
] => { ] => {
let result: AbortableApolloUseMutationPaginatedResponse< let result: AbortableApolloUseMutationPaginatedResponse<
GetSourceMangasFetchMutation, GetSourceMangasFetchMutation,
@@ -158,17 +160,32 @@ const useSourceManga = (
const pages = result[1]!; const pages = result[1]!;
const lastLoadedPageIndex = pages.findLastIndex((page) => !!page.data?.fetchSourceManga); const lastLoadedPageIndex = pages.findLastIndex((page) => !!page.data?.fetchSourceManga);
const lastLoadedPage = pages[lastLoadedPageIndex]; const lastLoadedPage = pages[lastLoadedPageIndex];
const items = useMemo(
() =>
(pages ?? [])
.map((page) => page.data?.fetchSourceManga.mangas ?? [])
.reduce((prevList, list) => [...prevList, ...list], []),
[pages],
);
const uniqueItems = useMemo(() => getUniqueMangas(items), [items]);
if (!uniqueItems.length) { const isPageLoading = pages.slice(-1)[0].isLoading;
return [result[0] as any, result[1][result[1].length - 1]]; let filteredOutAllItemsOfFetchedPage = !isPageLoading;
const items = useMemo(() => {
type FetchItemsResult = GetSourceMangasFetchMutation['fetchSourceManga']['mangas'];
let allItems: FetchItemsResult = [];
pages.forEach((page, index) => {
const pageItems = page.data?.fetchSourceManga.mangas ?? ([] as FetchItemsResult);
const uniqueItems = getUniqueMangas([...allItems, ...pageItems]);
const uniquePageItems = pageItems.filter(
(pageItem) => !!uniqueItems.find((uniqueItem) => uniqueItem.id === pageItem.id),
);
const nonLibraryItems = uniquePageItems.filter((item) => !hideLibraryEntries || !item.inLibrary);
// const nonLibraryItems = uniquePageItems;
const isLastPage = !isPageLoading && pages.length === index + 1;
filteredOutAllItemsOfFetchedPage = isLastPage && !nonLibraryItems.length && !!pageItems.length;
allItems = [...allItems, ...nonLibraryItems];
});
return allItems;
}, [pages, hideLibraryEntries]);
if (lastLoadedPageIndex === -1) {
return [result[0], { ...result[1][result[1].length - 1], filteredOutAllItemsOfFetchedPage }];
} }
return [ return [
@@ -183,9 +200,10 @@ const useSourceManga = (
pages.length > lastLoadedPageIndex + 1 pages.length > lastLoadedPageIndex + 1
? false ? false
: lastLoadedPage!.data!.fetchSourceManga.hasNextPage, : lastLoadedPage!.data!.fetchSourceManga.hasNextPage,
mangas: uniqueItems, mangas: items,
}, },
}, },
filteredOutAllItemsOfFetchedPage,
}, },
]; ];
}; };
@@ -217,6 +235,10 @@ export function SourceMangas() {
setIsFirstRender(false); setIsFirstRender(false);
}, []); }, []);
const {
settings: { hideLibraryEntries },
} = useMetadataServerSettings();
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);
@@ -224,13 +246,9 @@ export function SourceMangas() {
const searchTerm = useDebounce(query, 1000); const searchTerm = useDebounce(query, 1000);
const [resetScrollPosition, setResetScrollPosition] = useState(false); const [resetScrollPosition, setResetScrollPosition] = useState(false);
const [contentType, setContentType] = useState(currentLocationContentType); const [contentType, setContentType] = useState(currentLocationContentType);
const [loadPage, { data, isLoading, size: lastPageNum, abortRequest }] = useSourceManga( const [loadPage, { data, isLoading: loading, size: lastPageNum, abortRequest, filteredOutAllItemsOfFetchedPage }] =
sourceId, useSourceManga(sourceId, contentType, searchTerm, filtersToApply, 1, hideLibraryEntries);
contentType, const isLoading = loading || filteredOutAllItemsOfFetchedPage;
searchTerm,
filtersToApply,
1,
);
const mangas = data?.fetchSourceManga.mangas ?? []; const mangas = data?.fetchSourceManga.mangas ?? [];
const hasNextPage = data?.fetchSourceManga.hasNextPage ?? false; const hasNextPage = data?.fetchSourceManga.hasNextPage ?? false;
@@ -311,6 +329,12 @@ export function SourceMangas() {
setResetScrollPosition(true); setResetScrollPosition(true);
}, [sourceId, contentType, updateLocationFilters]); }, [sourceId, contentType, updateLocationFilters]);
useEffect(() => {
if (filteredOutAllItemsOfFetchedPage && hasNextPage && !loading) {
loadPage(lastPageNum + 1);
}
}, [filteredOutAllItemsOfFetchedPage, loading]);
useEffect(() => { useEffect(() => {
if (!clearCache) { if (!clearCache) {
return; return;

View File

@@ -16,9 +16,13 @@ import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarC
import { requestManager } from '@/lib/requests/RequestManager.ts'; import { requestManager } from '@/lib/requests/RequestManager.ts';
import { NumberSetting } from '@/components/settings/NumberSetting.tsx'; import { NumberSetting } from '@/components/settings/NumberSetting.tsx';
import { MutableListSetting } from '@/components/settings/MutableListSetting.tsx'; import { MutableListSetting } from '@/components/settings/MutableListSetting.tsx';
import { ServerSettings as GqlServerSettings } from '@/typings.ts'; import { MetadataBrowseSettings, ServerSettings as GqlServerSettings } from '@/typings.ts';
import { TextSetting } from '@/components/settings/text/TextSetting.tsx'; import { TextSetting } from '@/components/settings/text/TextSetting.tsx';
import { useLocalStorage } from '@/util/useLocalStorage.tsx'; import { useLocalStorage } from '@/util/useLocalStorage.tsx';
import {
createUpdateMetadataServerSettings,
useMetadataServerSettings,
} from '@/lib/metadata/metadataServerSettings.ts';
type ExtensionsSettings = Pick<GqlServerSettings, 'maxSourcesInParallel' | 'localSourcePath' | 'extensionRepos'>; type ExtensionsSettings = Pick<GqlServerSettings, 'maxSourcesInParallel' | 'localSourcePath' | 'extensionRepos'>;
@@ -52,8 +56,21 @@ export const BrowseSettings = () => {
mutateSettings({ variables: { input: { settings: { [setting]: value } } } }); mutateSettings({ variables: { input: { settings: { [setting]: value } } } });
}; };
const {
settings: { hideLibraryEntries },
} = useMetadataServerSettings();
const updateMetadataServerSettings = createUpdateMetadataServerSettings<keyof MetadataBrowseSettings>();
return ( return (
<List> <List>
<ListItem>
<ListItemText primary={t('settings.label.hide_library_entries')} />
<Switch
edge="end"
checked={hideLibraryEntries}
onChange={() => updateMetadataServerSettings('hideLibraryEntries', !hideLibraryEntries)}
/>
</ListItem>
<ListItem> <ListItem>
<ListItemText <ListItemText
primary={t('settings.label.show_nsfw')} primary={t('settings.label.show_nsfw')}

View File

@@ -241,10 +241,15 @@ export type MetadataMigrationSettings = {
deleteChapters: boolean; deleteChapters: boolean;
}; };
export type MetadataBrowseSettings = {
hideLibraryEntries: boolean;
};
export type MetadataServerSettings = MetadataDownloadSettings & export type MetadataServerSettings = MetadataDownloadSettings &
MetadataLibrarySettings & MetadataLibrarySettings &
MetadataClientSettings & MetadataClientSettings &
MetadataMigrationSettings; MetadataMigrationSettings &
MetadataBrowseSettings;
export interface ISearchSettings { export interface ISearchSettings {
ignoreFilters: boolean; ignoreFilters: boolean;