Эх сурвалжийг харах

Use Voyager on Migrate Manga screen (#8611)

Andreas 2 жил өмнө
parent
commit
a8c5780963

+ 13 - 30
app/src/main/java/eu/kanade/presentation/browse/MigrateMangaScreen.kt

@@ -4,31 +4,24 @@ import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.lazy.items
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
 import eu.kanade.domain.manga.model.Manga
 import eu.kanade.presentation.components.AppBar
 import eu.kanade.presentation.components.EmptyScreen
 import eu.kanade.presentation.components.FastScrollLazyColumn
-import eu.kanade.presentation.components.LoadingScreen
 import eu.kanade.presentation.components.Scaffold
 import eu.kanade.presentation.manga.components.BaseMangaListItem
 import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaPresenter
-import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaPresenter.Event
-import eu.kanade.tachiyomi.util.system.toast
-import kotlinx.coroutines.flow.collectLatest
+import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaState
 
 @Composable
 fun MigrateMangaScreen(
     navigateUp: () -> Unit,
     title: String?,
-    presenter: MigrateMangaPresenter,
+    state: MigrateMangaState,
     onClickItem: (Manga) -> Unit,
     onClickCover: (Manga) -> Unit,
 ) {
-    val context = LocalContext.current
     Scaffold(
         topBar = { scrollBehavior ->
             AppBar(
@@ -38,30 +31,20 @@ fun MigrateMangaScreen(
             )
         },
     ) { contentPadding ->
-        when {
-            presenter.isLoading -> LoadingScreen()
-            presenter.isEmpty -> EmptyScreen(
+        if (state.isEmpty) {
+            EmptyScreen(
                 textResource = R.string.empty_screen,
                 modifier = Modifier.padding(contentPadding),
             )
-            else -> {
-                MigrateMangaContent(
-                    contentPadding = contentPadding,
-                    state = presenter,
-                    onClickItem = onClickItem,
-                    onClickCover = onClickCover,
-                )
-            }
-        }
-    }
-    LaunchedEffect(Unit) {
-        presenter.events.collectLatest { event ->
-            when (event) {
-                Event.FailedFetchingFavorites -> {
-                    context.toast(R.string.internal_error)
-                }
-            }
+            return@Scaffold
         }
+
+        MigrateMangaContent(
+            contentPadding = contentPadding,
+            state = state,
+            onClickItem = onClickItem,
+            onClickCover = onClickCover,
+        )
     }
 }
 
@@ -75,7 +58,7 @@ private fun MigrateMangaContent(
     FastScrollLazyColumn(
         contentPadding = contentPadding,
     ) {
-        items(state.items) { manga ->
+        items(state.titles) { manga ->
             MigrateMangaItem(
                 manga = manga,
                 onClickItem = onClickItem,

+ 0 - 23
app/src/main/java/eu/kanade/presentation/browse/MigrateMangaState.kt

@@ -1,23 +0,0 @@
-package eu.kanade.presentation.browse
-
-import androidx.compose.runtime.derivedStateOf
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import eu.kanade.domain.manga.model.Manga
-
-interface MigrateMangaState {
-    val isLoading: Boolean
-    val items: List<Manga>
-    val isEmpty: Boolean
-}
-
-fun MigrationMangaState(): MigrateMangaState {
-    return MigrateMangaStateImpl()
-}
-
-class MigrateMangaStateImpl : MigrateMangaState {
-    override var isLoading: Boolean by mutableStateOf(true)
-    override var items: List<Manga> by mutableStateOf(emptyList())
-    override val isEmpty: Boolean by derivedStateOf { items.isEmpty() }
-}

+ 0 - 51
app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaPresenter.kt

@@ -1,51 +0,0 @@
-package eu.kanade.tachiyomi.ui.browse.migration.manga
-
-import android.os.Bundle
-import eu.kanade.domain.manga.interactor.GetFavorites
-import eu.kanade.presentation.browse.MigrateMangaState
-import eu.kanade.presentation.browse.MigrateMangaStateImpl
-import eu.kanade.presentation.browse.MigrationMangaState
-import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
-import eu.kanade.tachiyomi.util.lang.launchIO
-import eu.kanade.tachiyomi.util.system.logcat
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.flow.catch
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.receiveAsFlow
-import logcat.LogPriority
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
-
-class MigrateMangaPresenter(
-    private val sourceId: Long,
-    private val state: MigrateMangaStateImpl = MigrationMangaState() as MigrateMangaStateImpl,
-    private val getFavorites: GetFavorites = Injekt.get(),
-) : BasePresenter<MigrationMangaController>(), MigrateMangaState by state {
-
-    private val _events = Channel<Event>(Int.MAX_VALUE)
-    val events = _events.receiveAsFlow()
-
-    override fun onCreate(savedState: Bundle?) {
-        super.onCreate(savedState)
-        presenterScope.launchIO {
-            getFavorites
-                .subscribe(sourceId)
-                .catch {
-                    logcat(LogPriority.ERROR, it)
-                    _events.send(Event.FailedFetchingFavorites)
-                }
-                .map { list ->
-                    list.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.title })
-                }
-                .collectLatest { sortedList ->
-                    state.isLoading = false
-                    state.items = sortedList
-                }
-        }
-    }
-
-    sealed class Event {
-        object FailedFetchingFavorites : Event()
-    }
-}

+ 0 - 51
app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrationMangaController.kt

@@ -1,51 +0,0 @@
-package eu.kanade.tachiyomi.ui.browse.migration.manga
-
-import android.os.Bundle
-import androidx.compose.runtime.Composable
-import androidx.core.os.bundleOf
-import eu.kanade.presentation.browse.MigrateMangaScreen
-import eu.kanade.tachiyomi.ui.base.controller.FullComposeController
-import eu.kanade.tachiyomi.ui.base.controller.pushController
-import eu.kanade.tachiyomi.ui.browse.migration.search.SearchController
-import eu.kanade.tachiyomi.ui.manga.MangaController
-
-class MigrationMangaController : FullComposeController<MigrateMangaPresenter> {
-
-    constructor(sourceId: Long, sourceName: String?) : super(
-        bundleOf(
-            SOURCE_ID_EXTRA to sourceId,
-            SOURCE_NAME_EXTRA to sourceName,
-        ),
-    )
-
-    @Suppress("unused")
-    constructor(bundle: Bundle) : this(
-        bundle.getLong(SOURCE_ID_EXTRA),
-        bundle.getString(SOURCE_NAME_EXTRA),
-    )
-
-    private val sourceId: Long = args.getLong(SOURCE_ID_EXTRA)
-    private val sourceName: String? = args.getString(SOURCE_NAME_EXTRA)
-
-    override fun createPresenter() = MigrateMangaPresenter(sourceId)
-
-    @Composable
-    override fun ComposeContent() {
-        MigrateMangaScreen(
-            navigateUp = router::popCurrentController,
-            title = sourceName,
-            presenter = presenter,
-            onClickItem = {
-                router.pushController(SearchController(it.id))
-            },
-            onClickCover = {
-                router.pushController(MangaController(it.id))
-            },
-        )
-    }
-
-    companion object {
-        const val SOURCE_ID_EXTRA = "source_id_extra"
-        const val SOURCE_NAME_EXTRA = "source_name_extra"
-    }
-}

+ 66 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrationMangaScreen.kt

@@ -0,0 +1,66 @@
+package eu.kanade.tachiyomi.ui.browse.migration.manga
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.platform.LocalContext
+import cafe.adriel.voyager.core.model.rememberScreenModel
+import cafe.adriel.voyager.core.screen.Screen
+import cafe.adriel.voyager.navigator.LocalNavigator
+import cafe.adriel.voyager.navigator.currentOrThrow
+import eu.kanade.presentation.browse.MigrateMangaScreen
+import eu.kanade.presentation.components.LoadingScreen
+import eu.kanade.presentation.util.LocalRouter
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.ui.base.controller.pushController
+import eu.kanade.tachiyomi.ui.browse.migration.search.SearchController
+import eu.kanade.tachiyomi.ui.manga.MangaScreen
+import eu.kanade.tachiyomi.util.system.toast
+import kotlinx.coroutines.flow.collectLatest
+
+data class MigrationMangaScreen(
+    private val sourceId: Long,
+) : Screen {
+
+    @Composable
+    override fun Content() {
+        val context = LocalContext.current
+        val navigator = LocalNavigator.currentOrThrow
+        val router = LocalRouter.currentOrThrow
+        val screenModel = rememberScreenModel { MigrationMangaScreenModel(sourceId) }
+
+        val state by screenModel.state.collectAsState()
+
+        if (state.isLoading) {
+            LoadingScreen()
+            return
+        }
+
+        MigrateMangaScreen(
+            navigateUp = navigator::pop,
+            title = state.source!!.name,
+            state = state,
+            onClickItem = {
+                router.pushController(SearchController(it.id))
+            },
+            onClickCover = {
+                navigator.push(MangaScreen(it.id))
+            },
+        )
+
+        LaunchedEffect(Unit) {
+            screenModel.events.collectLatest { event ->
+                when (event) {
+                    MigrationMangaEvent.FailedFetchingFavorites -> {
+                        context.toast(R.string.internal_error)
+                    }
+                    MigrationMangaEvent.FailedGettingSource -> {
+                        context.toast(R.string.loader_not_implemented_error)
+                        router.popCurrentController()
+                    }
+                }
+            }
+        }
+    }
+}

+ 77 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrationMangaScreenModel.kt

@@ -0,0 +1,77 @@
+package eu.kanade.tachiyomi.ui.browse.migration.manga
+
+import androidx.compose.runtime.Immutable
+import cafe.adriel.voyager.core.model.StateScreenModel
+import cafe.adriel.voyager.core.model.coroutineScope
+import eu.kanade.domain.manga.interactor.GetFavorites
+import eu.kanade.domain.manga.model.Manga
+import eu.kanade.tachiyomi.source.Source
+import eu.kanade.tachiyomi.source.SourceManager
+import eu.kanade.tachiyomi.util.system.logcat
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+import logcat.LogPriority
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
+
+class MigrationMangaScreenModel(
+    private val sourceId: Long,
+    private val sourceManager: SourceManager = Injekt.get(),
+    private val getFavorites: GetFavorites = Injekt.get(),
+) : StateScreenModel<MigrateMangaState>(MigrateMangaState()) {
+
+    private val _events: Channel<MigrationMangaEvent> = Channel()
+    val events: Flow<MigrationMangaEvent> = _events.receiveAsFlow()
+
+    init {
+        coroutineScope.launch {
+            mutableState.update { state ->
+                val source = sourceManager.get(sourceId)
+                if (source == null) {
+                    _events.send(MigrationMangaEvent.FailedGettingSource)
+                }
+                state.copy(source = source)
+            }
+
+            getFavorites.subscribe(sourceId)
+                .catch {
+                    logcat(LogPriority.ERROR, it)
+                    _events.send(MigrationMangaEvent.FailedFetchingFavorites)
+                    mutableState.update { it.copy(titleList = emptyList()) }
+                }
+                .map {
+                    it.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.title })
+                }
+                .collectLatest { list ->
+                    mutableState.update { it.copy(titleList = list) }
+                }
+        }
+    }
+}
+
+sealed class MigrationMangaEvent {
+    object FailedGettingSource : MigrationMangaEvent()
+    object FailedFetchingFavorites : MigrationMangaEvent()
+}
+
+@Immutable
+data class MigrateMangaState(
+    val source: Source? = null,
+    private val titleList: List<Manga>? = null,
+) {
+
+    val titles: List<Manga>
+        get() = titleList ?: emptyList()
+
+    val isLoading: Boolean
+        get() = source == null || titleList == null
+
+    val isEmpty: Boolean
+        get() = titles.isEmpty()
+}

+ 4 - 10
app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceTab.kt

@@ -9,19 +9,18 @@ import androidx.compose.ui.platform.LocalUriHandler
 import androidx.compose.ui.res.stringResource
 import cafe.adriel.voyager.core.model.rememberScreenModel
 import cafe.adriel.voyager.core.screen.Screen
+import cafe.adriel.voyager.navigator.LocalNavigator
 import cafe.adriel.voyager.navigator.currentOrThrow
 import eu.kanade.presentation.browse.MigrateSourceScreen
 import eu.kanade.presentation.components.AppBar
 import eu.kanade.presentation.components.TabContent
-import eu.kanade.presentation.util.LocalRouter
 import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.ui.base.controller.pushController
-import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrationMangaController
+import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrationMangaScreen
 
 @Composable
 fun Screen.migrateSourceTab(): TabContent {
     val uriHandler = LocalUriHandler.current
-    val router = LocalRouter.currentOrThrow
+    val navigator = LocalNavigator.currentOrThrow
     val screenModel = rememberScreenModel { MigrateSourceScreenModel() }
     val state by screenModel.state.collectAsState()
 
@@ -41,12 +40,7 @@ fun Screen.migrateSourceTab(): TabContent {
                 state = state,
                 contentPadding = contentPadding,
                 onClickItem = { source ->
-                    router.pushController(
-                        MigrationMangaController(
-                            source.id,
-                            source.name,
-                        ),
-                    )
+                    navigator.push(MigrationMangaScreen(source.id))
                 },
                 onToggleSortingDirection = screenModel::toggleSortingDirection,
                 onToggleSortingMode = screenModel::toggleSortingMode,