From df66e180d88d5fb7bcf5a1290624dd26d539984e Mon Sep 17 00:00:00 2001
From: schroda <50052685+schroda@users.noreply.github.com>
Date: Sat, 4 May 2024 15:41:21 +0200
Subject: [PATCH] Add setting to show duplicated library entries
---
public/locales/en.json | 6 +++
src/App.tsx | 6 ++-
src/components/manga/MangaBadges.tsx | 4 +-
src/screens/settings/LibraryDuplicates.tsx | 61 ++++++++++++++++++++++
src/screens/settings/LibrarySettings.tsx | 7 +++
5 files changed, 81 insertions(+), 3 deletions(-)
create mode 100644 src/screens/settings/LibraryDuplicates.tsx
diff --git a/public/locales/en.json b/public/locales/en.json
index d4ac0b36..ce9088aa 100644
--- a/public/locales/en.json
+++ b/public/locales/en.json
@@ -505,6 +505,12 @@
"title": "Cleanup database"
}
}
+ },
+ "duplicates": {
+ "label": {
+ "description": "Show all duplicated entries in your library",
+ "title": "Duplicated entries"
+ }
}
},
"general": {
diff --git a/src/App.tsx b/src/App.tsx
index 2f3b719b..a39e441e 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -44,6 +44,7 @@ const { Migrate } = loadable(() => import('@/screens/Migrate.tsx'), lazyLoadFall
const { DeviceSetting } = loadable(() => import('@/components/settings/DeviceSetting.tsx'), lazyLoadFallback);
const { TrackingSettings } = loadable(() => import('@/screens/settings/TrackingSettings.tsx'), lazyLoadFallback);
const { TrackerOAuthLogin } = loadable(() => import('@/screens/TrackerOAuthLogin.tsx'), lazyLoadFallback);
+const { LibraryDuplicates } = loadable(() => import('@/screens/settings/LibraryDuplicates.tsx'), lazyLoadFallback);
if (process.env.NODE_ENV !== 'production') {
// Adds messages only in a dev environment
@@ -105,7 +106,10 @@ export const App: React.FC = () => (
} />
} />
} />
- } />
+
+ } />
+ } />
+
} />
} />
} />
diff --git a/src/components/manga/MangaBadges.tsx b/src/components/manga/MangaBadges.tsx
index 75dd65fa..d8fc308a 100644
--- a/src/components/manga/MangaBadges.tsx
+++ b/src/components/manga/MangaBadges.tsx
@@ -74,10 +74,10 @@ export const MangaBadges = ({
{t('manga.button.in_library')}
)}
- {showUnreadBadge && (unread ?? 0) > 0 && (
+ {showUnreadBadge && mode === 'default' && (unread ?? 0) > 0 && (
{unread}
)}
- {showDownloadBadge && (downloadCount ?? 0) > 0 && (
+ {showDownloadBadge && mode === 'default' && (downloadCount ?? 0) > 0 && (
{
+ const { t } = useTranslation();
+
+ const [gridLayout, setGridLayout] = useLocalStorage('migrateGridLayout', GridLayout.List);
+
+ const { setTitle, setAction } = useContext(NavBarContext);
+ useEffect(() => {
+ setTitle(t('library.settings.advanced.duplicates.label.title'));
+ setAction();
+
+ return () => {
+ setTitle('');
+ setAction(null);
+ };
+ }, [t, gridLayout]);
+
+ const { data, loading, error, refetch } = requestManager.useGetMangas({ condition: { inLibrary: true } });
+
+ const duplicatedMangas = useMemo(() => {
+ const libraryMangas: TManga[] = data?.mangas.nodes ?? [];
+
+ const titleToMangas = Object.groupBy(libraryMangas, ({ title }) => title);
+
+ return Object.entries(titleToMangas)
+ .filter(([, mangasWithTitle]) => (mangasWithTitle?.length ?? 0) > 1)
+ .map(([, mangasWithTitle]) => mangasWithTitle ?? [])
+ .flat();
+ }, [data?.mangas.nodes]);
+
+ return (
+ {}}
+ message={error ? t('manga.error.label.request_failure') : t('library.error.label.empty')}
+ messageExtra={error?.message}
+ isLoading={loading}
+ retry={error ? () => refetch().catch(defaultPromiseErrorHandler('LibraryDuplicates::refetch')) : undefined}
+ gridLayout={gridLayout}
+ />
+ );
+};
diff --git a/src/screens/settings/LibrarySettings.tsx b/src/screens/settings/LibrarySettings.tsx
index 3571af9b..25d22db4 100644
--- a/src/screens/settings/LibrarySettings.tsx
+++ b/src/screens/settings/LibrarySettings.tsx
@@ -28,6 +28,7 @@ import { Mangas } from '@/lib/data/Mangas.ts';
import { EmptyViewAbsoluteCentered } from '@/components/util/EmptyViewAbsoluteCentered.tsx';
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder.tsx';
+import { ListItemLink } from '@/components/util/ListItemLink.tsx';
const removeNonLibraryMangasFromCategories = async (): Promise => {
try {
@@ -170,6 +171,12 @@ export function LibrarySettings() {
secondary={t('library.settings.advanced.database.cleanup.label.description')}
/>
+
+
+
);