mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 16:18:55 +01:00 
			
		
		
		
	Rework Duplicate Dialog and Allow Migration (#492)
* (Mostly) Working Manga screen migration via duplicate dialog * Fully working migrate from Browse Search * Small tweaks for Antsy * Update app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt * Update app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
This commit is contained in:
		@@ -1,16 +1,33 @@
 | 
			
		||||
package eu.kanade.presentation.manga
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.clickable
 | 
			
		||||
import androidx.compose.foundation.layout.Arrangement
 | 
			
		||||
import androidx.compose.foundation.layout.FlowRow
 | 
			
		||||
import androidx.compose.foundation.layout.Column
 | 
			
		||||
import androidx.compose.foundation.layout.PaddingValues
 | 
			
		||||
import androidx.compose.foundation.layout.Row
 | 
			
		||||
import androidx.compose.foundation.layout.Spacer
 | 
			
		||||
import androidx.compose.material3.AlertDialog
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxWidth
 | 
			
		||||
import androidx.compose.foundation.layout.height
 | 
			
		||||
import androidx.compose.foundation.layout.padding
 | 
			
		||||
import androidx.compose.foundation.layout.sizeIn
 | 
			
		||||
import androidx.compose.material.icons.Icons
 | 
			
		||||
import androidx.compose.material.icons.outlined.Add
 | 
			
		||||
import androidx.compose.material.icons.outlined.Book
 | 
			
		||||
import androidx.compose.material.icons.outlined.SwapVert
 | 
			
		||||
import androidx.compose.material3.HorizontalDivider
 | 
			
		||||
import androidx.compose.material3.MaterialTheme
 | 
			
		||||
import androidx.compose.material3.OutlinedButton
 | 
			
		||||
import androidx.compose.material3.Text
 | 
			
		||||
import androidx.compose.material3.TextButton
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.ui.Alignment
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import androidx.compose.ui.unit.sp
 | 
			
		||||
import eu.kanade.presentation.components.AdaptiveSheet
 | 
			
		||||
import eu.kanade.presentation.components.TabbedDialogPaddings
 | 
			
		||||
import eu.kanade.presentation.more.settings.LocalPreferenceMinHeight
 | 
			
		||||
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
 | 
			
		||||
import tachiyomi.i18n.MR
 | 
			
		||||
import tachiyomi.presentation.core.components.material.padding
 | 
			
		||||
import tachiyomi.presentation.core.i18n.stringResource
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
@@ -18,42 +35,92 @@ fun DuplicateMangaDialog(
 | 
			
		||||
    onDismissRequest: () -> Unit,
 | 
			
		||||
    onConfirm: () -> Unit,
 | 
			
		||||
    onOpenManga: () -> Unit,
 | 
			
		||||
    onMigrate: () -> Unit,
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
) {
 | 
			
		||||
    AlertDialog(
 | 
			
		||||
    val minHeight = LocalPreferenceMinHeight.current
 | 
			
		||||
 | 
			
		||||
    AdaptiveSheet(
 | 
			
		||||
        modifier = modifier,
 | 
			
		||||
        onDismissRequest = onDismissRequest,
 | 
			
		||||
        title = {
 | 
			
		||||
            Text(text = stringResource(MR.strings.are_you_sure))
 | 
			
		||||
        },
 | 
			
		||||
        text = {
 | 
			
		||||
            Text(text = stringResource(MR.strings.confirm_add_duplicate_manga))
 | 
			
		||||
        },
 | 
			
		||||
        confirmButton = {
 | 
			
		||||
            FlowRow(
 | 
			
		||||
                horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall),
 | 
			
		||||
    ) {
 | 
			
		||||
        Column(
 | 
			
		||||
            modifier = Modifier
 | 
			
		||||
                .padding(
 | 
			
		||||
                    vertical = TabbedDialogPaddings.Vertical,
 | 
			
		||||
                    horizontal = TabbedDialogPaddings.Horizontal,
 | 
			
		||||
                )
 | 
			
		||||
                .fillMaxWidth(),
 | 
			
		||||
        ) {
 | 
			
		||||
            Text(
 | 
			
		||||
                modifier = Modifier.padding(TitlePadding),
 | 
			
		||||
                text = stringResource(MR.strings.are_you_sure),
 | 
			
		||||
                style = MaterialTheme.typography.headlineMedium,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            Text(
 | 
			
		||||
                text = stringResource(MR.strings.confirm_add_duplicate_manga),
 | 
			
		||||
                style = MaterialTheme.typography.bodyMedium,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            Spacer(Modifier.height(PaddingSize))
 | 
			
		||||
 | 
			
		||||
            TextPreferenceWidget(
 | 
			
		||||
                title = stringResource(MR.strings.action_show_manga),
 | 
			
		||||
                icon = Icons.Outlined.Book,
 | 
			
		||||
                onPreferenceClick = {
 | 
			
		||||
                    onDismissRequest()
 | 
			
		||||
                    onOpenManga()
 | 
			
		||||
                },
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            HorizontalDivider()
 | 
			
		||||
 | 
			
		||||
            TextPreferenceWidget(
 | 
			
		||||
                title = stringResource(MR.strings.action_migrate_duplicate),
 | 
			
		||||
                icon = Icons.Outlined.SwapVert,
 | 
			
		||||
                onPreferenceClick = {
 | 
			
		||||
                    onDismissRequest()
 | 
			
		||||
                    onMigrate()
 | 
			
		||||
                },
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            HorizontalDivider()
 | 
			
		||||
 | 
			
		||||
            TextPreferenceWidget(
 | 
			
		||||
                title = stringResource(MR.strings.action_add_anyway),
 | 
			
		||||
                icon = Icons.Outlined.Add,
 | 
			
		||||
                onPreferenceClick = {
 | 
			
		||||
                    onDismissRequest()
 | 
			
		||||
                    onConfirm()
 | 
			
		||||
                },
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            Row(
 | 
			
		||||
                modifier = Modifier
 | 
			
		||||
                    .sizeIn(minHeight = minHeight)
 | 
			
		||||
                    .clickable { onDismissRequest.invoke() }
 | 
			
		||||
                    .padding(ButtonPadding)
 | 
			
		||||
                    .fillMaxWidth(),
 | 
			
		||||
                verticalAlignment = Alignment.CenterVertically,
 | 
			
		||||
                horizontalArrangement = Arrangement.Center,
 | 
			
		||||
            ) {
 | 
			
		||||
                TextButton(
 | 
			
		||||
                    onClick = {
 | 
			
		||||
                        onDismissRequest()
 | 
			
		||||
                        onOpenManga()
 | 
			
		||||
                    },
 | 
			
		||||
                ) {
 | 
			
		||||
                    Text(text = stringResource(MR.strings.action_show_manga))
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Spacer(modifier = Modifier.weight(1f))
 | 
			
		||||
 | 
			
		||||
                TextButton(onClick = onDismissRequest) {
 | 
			
		||||
                    Text(text = stringResource(MR.strings.action_cancel))
 | 
			
		||||
                }
 | 
			
		||||
                TextButton(
 | 
			
		||||
                    onClick = {
 | 
			
		||||
                        onDismissRequest()
 | 
			
		||||
                        onConfirm()
 | 
			
		||||
                    },
 | 
			
		||||
                ) {
 | 
			
		||||
                    Text(text = stringResource(MR.strings.action_add))
 | 
			
		||||
                OutlinedButton(onClick = onDismissRequest, modifier = Modifier.fillMaxWidth()) {
 | 
			
		||||
                    Text(
 | 
			
		||||
                        modifier = Modifier
 | 
			
		||||
                            .padding(vertical = 8.dp),
 | 
			
		||||
                        text = stringResource(MR.strings.action_cancel),
 | 
			
		||||
                        color = MaterialTheme.colorScheme.primary,
 | 
			
		||||
                        style = MaterialTheme.typography.titleLarge,
 | 
			
		||||
                        fontSize = 16.sp,
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
    )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private val PaddingSize = 16.dp
 | 
			
		||||
 | 
			
		||||
private val ButtonPadding = PaddingValues(top = 16.dp, bottom = 16.dp)
 | 
			
		||||
private val TitlePadding = PaddingValues(bottom = 16.dp, top = 8.dp)
 | 
			
		||||
 
 | 
			
		||||
@@ -83,7 +83,7 @@ data class SourceSearchScreen(
 | 
			
		||||
        ) { paddingValues ->
 | 
			
		||||
            val pagingFlow by screenModel.mangaPagerFlowFlow.collectAsState()
 | 
			
		||||
            val openMigrateDialog: (Manga) -> Unit = {
 | 
			
		||||
                screenModel.setDialog(BrowseSourceScreenModel.Dialog.Migrate(it))
 | 
			
		||||
                screenModel.setDialog(BrowseSourceScreenModel.Dialog.Migrate(newManga = it, oldManga = oldManga))
 | 
			
		||||
            }
 | 
			
		||||
            BrowseSourceContent(
 | 
			
		||||
                source = screenModel.source,
 | 
			
		||||
 
 | 
			
		||||
@@ -47,6 +47,8 @@ import eu.kanade.presentation.util.Screen
 | 
			
		||||
import eu.kanade.tachiyomi.source.CatalogueSource
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.HttpSource
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.extension.details.SourcePreferencesScreen
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateDialog
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateDialogScreenModel
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing
 | 
			
		||||
import eu.kanade.tachiyomi.ui.category.CategoryScreen
 | 
			
		||||
import eu.kanade.tachiyomi.ui.manga.MangaScreen
 | 
			
		||||
@@ -252,6 +254,22 @@ data class BrowseSourceScreen(
 | 
			
		||||
                    onDismissRequest = onDismissRequest,
 | 
			
		||||
                    onConfirm = { screenModel.addFavorite(dialog.manga) },
 | 
			
		||||
                    onOpenManga = { navigator.push(MangaScreen(dialog.duplicate.id)) },
 | 
			
		||||
                    onMigrate = {
 | 
			
		||||
                        screenModel.setDialog(BrowseSourceScreenModel.Dialog.Migrate(dialog.manga, dialog.duplicate))
 | 
			
		||||
                    },
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            is BrowseSourceScreenModel.Dialog.Migrate -> {
 | 
			
		||||
                MigrateDialog(
 | 
			
		||||
                    oldManga = dialog.oldManga,
 | 
			
		||||
                    newManga = dialog.newManga,
 | 
			
		||||
                    screenModel = MigrateDialogScreenModel(),
 | 
			
		||||
                    onDismissRequest = onDismissRequest,
 | 
			
		||||
                    onClickTitle = { navigator.push(MangaScreen(dialog.oldManga.id)) },
 | 
			
		||||
                    onPopScreen = {
 | 
			
		||||
                        onDismissRequest()
 | 
			
		||||
                    },
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            is BrowseSourceScreenModel.Dialog.RemoveManga -> {
 | 
			
		||||
@@ -274,7 +292,6 @@ data class BrowseSourceScreen(
 | 
			
		||||
                    },
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            is BrowseSourceScreenModel.Dialog.Migrate -> {}
 | 
			
		||||
            else -> {}
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -345,7 +345,7 @@ class BrowseSourceScreenModel(
 | 
			
		||||
            val manga: Manga,
 | 
			
		||||
            val initialSelection: ImmutableList<CheckboxState.State<Category>>,
 | 
			
		||||
        ) : Dialog
 | 
			
		||||
        data class Migrate(val newManga: Manga) : Dialog
 | 
			
		||||
        data class Migrate(val newManga: Manga, val oldManga: Manga) : Dialog
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Immutable
 | 
			
		||||
 
 | 
			
		||||
@@ -41,6 +41,8 @@ import eu.kanade.presentation.util.isTabletUi
 | 
			
		||||
import eu.kanade.tachiyomi.source.Source
 | 
			
		||||
import eu.kanade.tachiyomi.source.isLocalOrStub
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.HttpSource
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateDialog
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateDialogScreenModel
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateSearchScreen
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen
 | 
			
		||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchScreen
 | 
			
		||||
@@ -191,11 +193,28 @@ class MangaScreen(
 | 
			
		||||
                    },
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            is MangaScreenModel.Dialog.DuplicateManga -> DuplicateMangaDialog(
 | 
			
		||||
                onDismissRequest = onDismissRequest,
 | 
			
		||||
                onConfirm = { screenModel.toggleFavorite(onRemoved = {}, checkDuplicate = false) },
 | 
			
		||||
                onOpenManga = { navigator.push(MangaScreen(dialog.duplicate.id)) },
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            is MangaScreenModel.Dialog.DuplicateManga -> {
 | 
			
		||||
                DuplicateMangaDialog(
 | 
			
		||||
                    onDismissRequest = onDismissRequest,
 | 
			
		||||
                    onConfirm = { screenModel.toggleFavorite(onRemoved = {}, checkDuplicate = false) },
 | 
			
		||||
                    onOpenManga = { navigator.push(MangaScreen(dialog.duplicate.id)) },
 | 
			
		||||
                    onMigrate = {
 | 
			
		||||
                        screenModel.showMigrateDialog(dialog.duplicate)
 | 
			
		||||
                    },
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            is MangaScreenModel.Dialog.Migrate -> {
 | 
			
		||||
                MigrateDialog(
 | 
			
		||||
                    oldManga = dialog.oldManga,
 | 
			
		||||
                    newManga = dialog.newManga,
 | 
			
		||||
                    screenModel = MigrateDialogScreenModel(),
 | 
			
		||||
                    onDismissRequest = onDismissRequest,
 | 
			
		||||
                    onClickTitle = { navigator.push(MangaScreen(dialog.oldManga.id)) },
 | 
			
		||||
                    onPopScreen = { navigator.replace(MangaScreen(dialog.newManga.id)) },
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            MangaScreenModel.Dialog.SettingsSheet -> ChapterSettingsDialog(
 | 
			
		||||
                onDismissRequest = onDismissRequest,
 | 
			
		||||
                manga = successState.manga,
 | 
			
		||||
 
 | 
			
		||||
@@ -1003,6 +1003,7 @@ class MangaScreenModel(
 | 
			
		||||
        ) : Dialog
 | 
			
		||||
        data class DeleteChapters(val chapters: List<Chapter>) : Dialog
 | 
			
		||||
        data class DuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog
 | 
			
		||||
        data class Migrate(val newManga: Manga, val oldManga: Manga) : Dialog
 | 
			
		||||
        data class SetFetchInterval(val manga: Manga) : Dialog
 | 
			
		||||
        data object SettingsSheet : Dialog
 | 
			
		||||
        data object TrackSheet : Dialog
 | 
			
		||||
@@ -1029,6 +1030,11 @@ class MangaScreenModel(
 | 
			
		||||
        updateSuccessState { it.copy(dialog = Dialog.FullCover) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun showMigrateDialog(duplicate: Manga) {
 | 
			
		||||
        val manga = successState?.manga ?: return
 | 
			
		||||
        updateSuccessState { it.copy(dialog = Dialog.Migrate(newManga = manga, oldManga = duplicate)) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setExcludedScanlators(excludedScanlators: Set<String>) {
 | 
			
		||||
        screenModelScope.launchIO {
 | 
			
		||||
            setExcludedScanlators.await(mangaId, excludedScanlators)
 | 
			
		||||
 
 | 
			
		||||
@@ -160,6 +160,8 @@
 | 
			
		||||
    <string name="action_webview_refresh">Refresh</string>
 | 
			
		||||
    <string name="action_start_downloading_now">Start downloading now</string>
 | 
			
		||||
    <string name="action_not_now">Not now</string>
 | 
			
		||||
    <string name="action_add_anyway">Add anyway</string>
 | 
			
		||||
    <string name="action_migrate_duplicate">Migrate existing entry</string>
 | 
			
		||||
 | 
			
		||||
    <!-- Operations -->
 | 
			
		||||
    <string name="loading">Loading…</string>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user