From 71ddb1657440d54178f10fd62e3d0c66ee95a5a0 Mon Sep 17 00:00:00 2001
From: Felix Kaiser <30923667+foxscore@users.noreply.github.com>
Date: Sat, 12 Feb 2022 17:00:57 +0100
Subject: [PATCH] Detect identical mangas when adding to library (#6579)

* added duplicate manga check

When adding a manga to your library, the app will go through each manga previously added and compare their names. If a match is detected, it will prompt the user and ask for confirmation. On this prompt there is also an option to view the other manga.

* added german translations for newly added strings

* Revert "added german translations for newly added strings"

This reverts commit 71ada620671651daeeb2546aecd02400a4bc86bc.

* changed `AlertDialog.Builder` to `MaterialAlertDialogBuilder`

* using SQL query instead of filtering entire library with Kotlin
---
 .../data/database/queries/MangaQueries.kt     | 15 ++++++++++
 .../tachiyomi/ui/manga/MangaController.kt     | 30 +++++++++++++++++--
 .../tachiyomi/ui/manga/MangaPresenter.kt      |  4 +++
 app/src/main/res/values/strings.xml           |  2 ++
 4 files changed, 49 insertions(+), 2 deletions(-)

diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt
index 78e6c5a8d..5d5f65c36 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt
@@ -34,6 +34,21 @@ interface MangaQueries : DbProvider {
         .withGetResolver(LibraryMangaGetResolver.INSTANCE)
         .prepare()
 
+    fun getDuplicateLibraryManga(manga: Manga) = db.get()
+        .`object`(Manga::class.java)
+        .withQuery(
+            Query.builder()
+                .table(MangaTable.TABLE)
+                .where("${MangaTable.COL_FAVORITE} = 1 AND LOWER(${MangaTable.COL_TITLE}) = ? AND ${MangaTable.COL_SOURCE} != ?")
+                .whereArgs(
+                    manga.title.lowercase(),
+                    manga.source,
+                )
+                .limit(1)
+                .build()
+        )
+        .prepare()
+
     fun getFavoriteMangas(sortByTitle: Boolean = true): PreparedGetListOfObjects<Manga> {
         var queryBuilder = Query.builder()
             .table(MangaTable.TABLE)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt
index 66f06fdf4..c35dbb0eb 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt
@@ -19,7 +19,6 @@ import androidx.core.os.bundleOf
 import androidx.core.view.ViewCompat
 import androidx.core.view.WindowInsetsCompat
 import androidx.core.view.doOnLayout
-import androidx.core.view.isVisible
 import androidx.core.view.updateLayoutParams
 import androidx.recyclerview.widget.ConcatAdapter
 import androidx.recyclerview.widget.LinearLayoutManager
@@ -30,6 +29,7 @@ import coil.request.ImageRequest
 import com.bluelinelabs.conductor.Controller
 import com.bluelinelabs.conductor.ControllerChangeHandler
 import com.bluelinelabs.conductor.ControllerChangeType
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
 import com.google.android.material.snackbar.Snackbar
 import dev.chrisbanes.insetter.applyInsetter
@@ -145,6 +145,7 @@ class MangaController :
 
     private val preferences: PreferencesHelper by injectLazy()
     private val coverCache: CoverCache by injectLazy()
+    private val sourceManager: SourceManager by injectLazy()
 
     private var mangaInfoAdapter: MangaInfoHeaderAdapter? = null
     private var chaptersHeaderAdapter: MangaChaptersHeaderAdapter? = null
@@ -525,7 +526,32 @@ class MangaController :
             activity?.toast(activity?.getString(R.string.manga_removed_library))
             activity?.invalidateOptionsMenu()
         } else {
-            addToLibrary(manga)
+            val duplicateManga = presenter.getDuplicateLibraryManga(manga)
+            if (duplicateManga != null) {
+                showAddDuplicateDialog(
+                    manga,
+                    duplicateManga,
+                )
+            } else {
+                addToLibrary(manga)
+            }
+        }
+    }
+
+    private fun showAddDuplicateDialog(newManga: Manga, libraryManga: Manga) {
+        activity?.let {
+            val source = sourceManager.getOrStub(libraryManga.source)
+            MaterialAlertDialogBuilder(it).apply {
+                setMessage(activity?.getString(R.string.confirm_manga_add_duplicate, source.name))
+                setPositiveButton(activity?.getString(R.string.action_add)) { _, _, ->
+                    addToLibrary(newManga)
+                }
+                setNegativeButton(activity?.getString(R.string.action_cancel)) { _, _, -> }
+                setNeutralButton(activity?.getString(R.string.action_show_manga)) { _, _, ->
+                    router.pushController(MangaController(libraryManga).withFadeTransaction())
+                }
+                setCancelable(true)
+            }.create().show()
         }
     }
 
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt
index beccee6b9..c2a2579a0 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt
@@ -174,6 +174,10 @@ class MangaPresenter(
         fetchTrackers()
     }
 
+    fun getDuplicateLibraryManga(manga: Manga): Manga? {
+        return db.getDuplicateLibraryManga(manga).executeAsBlocking()
+    }
+
     // Manga info - start
 
     private fun getMangaObservable(): Observable<Manga> {
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4ed26f542..11656c49f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -81,6 +81,7 @@
     <string name="action_start">Start</string>
     <string name="action_resume">Resume</string>
     <string name="action_open_in_browser">Open in browser</string>
+    <string name="action_show_manga">Show manga</string>
     <!-- Do not translate "WebView" -->
     <string name="action_open_in_web_view">Open in WebView</string>
     <string name="action_web_view" translatable="false">WebView</string>
@@ -571,6 +572,7 @@
     <string name="in_library">In library</string>
     <string name="remove_from_library">Remove from library</string>
     <string name="manga_info_full_title_label">Title</string>
+    <string name="confirm_manga_add_duplicate">You have an entry in your library with the same name but from a different source (%1$s).\n\nDo you still wish to continue?</string>
     <string name="manga_added_library">Added to library</string>
     <string name="manga_removed_library">Removed from library</string>
     <string name="manga_info_expand">More</string>