Browse Source

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
Felix Kaiser 3 years ago
parent
commit
71ddb16574

+ 15 - 0
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)

+ 28 - 2
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()
         }
     }
 

+ 4 - 0
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> {

+ 2 - 0
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>