mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-12 03:58:56 +01:00
Use Stable interface for Browse screens (#7544)
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
package eu.kanade.presentation.browse
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.provider.Settings
|
||||
import android.util.DisplayMetrics
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.background
|
||||
@@ -32,7 +35,6 @@ import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -51,6 +53,7 @@ import eu.kanade.presentation.browse.components.ExtensionIcon
|
||||
import eu.kanade.presentation.components.DIVIDER_ALPHA
|
||||
import eu.kanade.presentation.components.Divider
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.components.LoadingScreen
|
||||
import eu.kanade.presentation.components.PreferenceRow
|
||||
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||
import eu.kanade.presentation.util.horizontalPadding
|
||||
@@ -66,65 +69,68 @@ fun ExtensionDetailsScreen(
|
||||
nestedScrollInterop: NestedScrollConnection,
|
||||
presenter: ExtensionDetailsPresenter,
|
||||
onClickUninstall: () -> Unit,
|
||||
onClickAppInfo: () -> Unit,
|
||||
onClickSourcePreferences: (sourceId: Long) -> Unit,
|
||||
onClickSource: (sourceId: Long) -> Unit,
|
||||
) {
|
||||
val extension = presenter.extension
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.extension == null -> EmptyScreen(textResource = R.string.empty_screen)
|
||||
else -> {
|
||||
val context = LocalContext.current
|
||||
val extension = presenter.extension
|
||||
var showNsfwWarning by remember { mutableStateOf(false) }
|
||||
|
||||
if (extension == null) {
|
||||
EmptyScreen(textResource = R.string.empty_screen)
|
||||
return
|
||||
}
|
||||
|
||||
val sources by presenter.sourcesState.collectAsState()
|
||||
|
||||
var showNsfwWarning by remember { mutableStateOf(false) }
|
||||
|
||||
ScrollbarLazyColumn(
|
||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
||||
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
|
||||
) {
|
||||
when {
|
||||
extension.isUnofficial ->
|
||||
item {
|
||||
WarningBanner(R.string.unofficial_extension_message)
|
||||
ScrollbarLazyColumn(
|
||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
||||
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
|
||||
) {
|
||||
when {
|
||||
extension.isUnofficial ->
|
||||
item {
|
||||
WarningBanner(R.string.unofficial_extension_message)
|
||||
}
|
||||
extension.isObsolete ->
|
||||
item {
|
||||
WarningBanner(R.string.obsolete_extension_message)
|
||||
}
|
||||
}
|
||||
extension.isObsolete ->
|
||||
|
||||
item {
|
||||
WarningBanner(R.string.obsolete_extension_message)
|
||||
DetailsHeader(
|
||||
extension = extension,
|
||||
onClickUninstall = onClickUninstall,
|
||||
onClickAppInfo = {
|
||||
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
||||
data = Uri.fromParts("package", extension.pkgName, null)
|
||||
context.startActivity(this)
|
||||
}
|
||||
},
|
||||
onClickAgeRating = {
|
||||
showNsfwWarning = true
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
DetailsHeader(
|
||||
extension = extension,
|
||||
onClickUninstall = onClickUninstall,
|
||||
onClickAppInfo = onClickAppInfo,
|
||||
onClickAgeRating = {
|
||||
showNsfwWarning = true
|
||||
},
|
||||
)
|
||||
items(
|
||||
items = presenter.sources,
|
||||
key = { it.source.id },
|
||||
) { source ->
|
||||
SourceSwitchPreference(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
source = source,
|
||||
onClickSourcePreferences = onClickSourcePreferences,
|
||||
onClickSource = onClickSource,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (showNsfwWarning) {
|
||||
NsfwWarningDialog(
|
||||
onClickConfirm = {
|
||||
showNsfwWarning = false
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
items(
|
||||
items = sources,
|
||||
key = { it.source.id },
|
||||
) { source ->
|
||||
SourceSwitchPreference(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
source = source,
|
||||
onClickSourcePreferences = onClickSourcePreferences,
|
||||
onClickSource = onClickSource,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (showNsfwWarning) {
|
||||
NsfwWarningDialog(
|
||||
onClickConfirm = {
|
||||
showNsfwWarning = false
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package eu.kanade.presentation.browse
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionSourceItem
|
||||
|
||||
@Stable
|
||||
interface ExtensionDetailsState {
|
||||
val isLoading: Boolean
|
||||
val extension: Extension.Installed?
|
||||
val sources: List<ExtensionSourceItem>
|
||||
}
|
||||
|
||||
fun ExtensionDetailsState(): ExtensionDetailsState {
|
||||
return ExtensionDetailsStateImpl()
|
||||
}
|
||||
|
||||
class ExtensionDetailsStateImpl : ExtensionDetailsState {
|
||||
override var isLoading: Boolean by mutableStateOf(true)
|
||||
override var extension: Extension.Installed? by mutableStateOf(null)
|
||||
override var sources: List<ExtensionSourceItem> by mutableStateOf(emptyList())
|
||||
}
|
||||
@@ -5,10 +5,8 @@ import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
@@ -19,47 +17,52 @@ import eu.kanade.presentation.components.LoadingScreen
|
||||
import eu.kanade.presentation.components.PreferenceRow
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionFilterPresenter
|
||||
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionFilterState
|
||||
import eu.kanade.tachiyomi.ui.browse.extension.FilterUiModel
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
||||
@Composable
|
||||
fun ExtensionFilterScreen(
|
||||
nestedScrollInterop: NestedScrollConnection,
|
||||
presenter: ExtensionFilterPresenter,
|
||||
onClickLang: (String) -> Unit,
|
||||
) {
|
||||
val state by presenter.state.collectAsState()
|
||||
|
||||
when (state) {
|
||||
is ExtensionFilterState.Loading -> LoadingScreen()
|
||||
is ExtensionFilterState.Error -> Text(text = (state as ExtensionFilterState.Error).error.message!!)
|
||||
is ExtensionFilterState.Success ->
|
||||
val context = LocalContext.current
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.isEmpty -> EmptyScreen(textResource = R.string.empty_screen)
|
||||
else -> {
|
||||
SourceFilterContent(
|
||||
nestedScrollInterop = nestedScrollInterop,
|
||||
items = (state as ExtensionFilterState.Success).models,
|
||||
onClickLang = onClickLang,
|
||||
state = presenter,
|
||||
onClickLang = {
|
||||
presenter.toggleLanguage(it)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
presenter.events.collectLatest {
|
||||
when (it) {
|
||||
ExtensionFilterPresenter.Event.FailedFetchingLanguages -> {
|
||||
context.toast(R.string.internal_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SourceFilterContent(
|
||||
nestedScrollInterop: NestedScrollConnection,
|
||||
items: List<FilterUiModel>,
|
||||
state: ExtensionFilterState,
|
||||
onClickLang: (String) -> Unit,
|
||||
) {
|
||||
if (items.isEmpty()) {
|
||||
EmptyScreen(textResource = R.string.empty_screen)
|
||||
return
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
||||
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
|
||||
) {
|
||||
items(
|
||||
items = items,
|
||||
items = state.items,
|
||||
) { model ->
|
||||
ExtensionFilterItem(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
@@ -0,0 +1,25 @@
|
||||
package eu.kanade.presentation.browse
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import eu.kanade.tachiyomi.ui.browse.extension.FilterUiModel
|
||||
|
||||
@Stable
|
||||
interface ExtensionFilterState {
|
||||
val isLoading: Boolean
|
||||
val items: List<FilterUiModel>
|
||||
val isEmpty: Boolean
|
||||
}
|
||||
|
||||
fun ExtensionFilterState(): ExtensionFilterState {
|
||||
return ExtensionFilterStateImpl()
|
||||
}
|
||||
|
||||
class ExtensionFilterStateImpl : ExtensionFilterState {
|
||||
override var isLoading: Boolean by mutableStateOf(true)
|
||||
override var items: List<FilterUiModel> by mutableStateOf(emptyList())
|
||||
override val isEmpty: Boolean by derivedStateOf { items.isEmpty() }
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -40,7 +39,9 @@ import com.google.accompanist.swiperefresh.SwipeRefresh
|
||||
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
||||
import eu.kanade.presentation.browse.components.BaseBrowseItem
|
||||
import eu.kanade.presentation.browse.components.ExtensionIcon
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.components.FastScrollLazyColumn
|
||||
import eu.kanade.presentation.components.LoadingScreen
|
||||
import eu.kanade.presentation.components.SwipeRefreshIndicator
|
||||
import eu.kanade.presentation.theme.header
|
||||
import eu.kanade.presentation.util.horizontalPadding
|
||||
@@ -49,7 +50,6 @@ import eu.kanade.presentation.util.topPaddingValues
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.extension.model.InstallStep
|
||||
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionState
|
||||
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionUiModel
|
||||
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsPresenter
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
@@ -69,19 +69,18 @@ fun ExtensionScreen(
|
||||
onRefresh: () -> Unit,
|
||||
onLaunched: () -> Unit,
|
||||
) {
|
||||
val state by presenter.state.collectAsState()
|
||||
val isRefreshing = presenter.isRefreshing
|
||||
|
||||
SwipeRefresh(
|
||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
||||
state = rememberSwipeRefreshState(isRefreshing),
|
||||
state = rememberSwipeRefreshState(presenter.isRefreshing),
|
||||
indicator = { s, trigger -> SwipeRefreshIndicator(s, trigger) },
|
||||
onRefresh = onRefresh,
|
||||
) {
|
||||
when (state) {
|
||||
is ExtensionState.Initialized -> {
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.isEmpty -> EmptyScreen(R.string.empty_screen)
|
||||
else -> {
|
||||
ExtensionContent(
|
||||
items = (state as ExtensionState.Initialized).list,
|
||||
state = presenter,
|
||||
onLongClickItem = onLongClickItem,
|
||||
onClickItemCancel = onClickItemCancel,
|
||||
onInstallExtension = onInstallExtension,
|
||||
@@ -93,14 +92,13 @@ fun ExtensionScreen(
|
||||
onLaunched = onLaunched,
|
||||
)
|
||||
}
|
||||
ExtensionState.Uninitialized -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ExtensionContent(
|
||||
items: List<ExtensionUiModel>,
|
||||
state: ExtensionsState,
|
||||
onLongClickItem: (Extension) -> Unit,
|
||||
onClickItemCancel: (Extension) -> Unit,
|
||||
onInstallExtension: (Extension.Available) -> Unit,
|
||||
@@ -117,7 +115,7 @@ fun ExtensionContent(
|
||||
contentPadding = WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
||||
) {
|
||||
items(
|
||||
items = items,
|
||||
items = state.items,
|
||||
key = {
|
||||
when (it) {
|
||||
is ExtensionUiModel.Header.Resource -> it.textRes
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
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.tachiyomi.ui.browse.extension.ExtensionUiModel
|
||||
|
||||
interface ExtensionsState {
|
||||
val isLoading: Boolean
|
||||
val isRefreshing: Boolean
|
||||
val items: List<ExtensionUiModel>
|
||||
val isEmpty: Boolean
|
||||
}
|
||||
|
||||
fun ExtensionState(): ExtensionsState {
|
||||
return ExtensionsStateImpl()
|
||||
}
|
||||
|
||||
class ExtensionsStateImpl : ExtensionsState {
|
||||
override var isLoading: Boolean by mutableStateOf(true)
|
||||
override var isRefreshing: Boolean by mutableStateOf(false)
|
||||
override var items: List<ExtensionUiModel> by mutableStateOf(emptyList())
|
||||
override val isEmpty: Boolean by derivedStateOf { items.isEmpty() }
|
||||
}
|
||||
@@ -4,61 +4,66 @@ import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import eu.kanade.domain.manga.model.Manga
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.components.LoadingScreen
|
||||
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||
import eu.kanade.presentation.manga.components.BaseMangaListItem
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaState
|
||||
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrationMangaPresenter
|
||||
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
|
||||
|
||||
@Composable
|
||||
fun MigrateMangaScreen(
|
||||
nestedScrollInterop: NestedScrollConnection,
|
||||
presenter: MigrationMangaPresenter,
|
||||
presenter: MigrateMangaPresenter,
|
||||
onClickItem: (Manga) -> Unit,
|
||||
onClickCover: (Manga) -> Unit,
|
||||
) {
|
||||
val state by presenter.state.collectAsState()
|
||||
|
||||
when (state) {
|
||||
MigrateMangaState.Loading -> LoadingScreen()
|
||||
is MigrateMangaState.Error -> Text(text = (state as MigrateMangaState.Error).error.message!!)
|
||||
is MigrateMangaState.Success -> {
|
||||
val context = LocalContext.current
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.isEmpty -> EmptyScreen(textResource = R.string.empty_screen)
|
||||
else -> {
|
||||
MigrateMangaContent(
|
||||
nestedScrollInterop = nestedScrollInterop,
|
||||
list = (state as MigrateMangaState.Success).list,
|
||||
state = presenter,
|
||||
onClickItem = onClickItem,
|
||||
onClickCover = onClickCover,
|
||||
)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
presenter.events.collectLatest { event ->
|
||||
when (event) {
|
||||
Event.FailedFetchingFavorites -> {
|
||||
context.toast(R.string.internal_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MigrateMangaContent(
|
||||
nestedScrollInterop: NestedScrollConnection,
|
||||
list: List<Manga>,
|
||||
state: MigrateMangaState,
|
||||
onClickItem: (Manga) -> Unit,
|
||||
onClickCover: (Manga) -> Unit,
|
||||
) {
|
||||
if (list.isEmpty()) {
|
||||
EmptyScreen(textResource = R.string.empty_screen)
|
||||
return
|
||||
}
|
||||
ScrollbarLazyColumn(
|
||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
||||
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
|
||||
) {
|
||||
items(list) { manga ->
|
||||
items(state.items) { manga ->
|
||||
MigrateMangaItem(
|
||||
manga = manga,
|
||||
onClickItem = onClickItem,
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
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() }
|
||||
}
|
||||
@@ -11,12 +11,12 @@ import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -32,27 +32,29 @@ import eu.kanade.presentation.util.horizontalPadding
|
||||
import eu.kanade.presentation.util.plus
|
||||
import eu.kanade.presentation.util.topPaddingValues
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrateSourceState
|
||||
import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrationSourcesPresenter
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||
|
||||
@Composable
|
||||
fun MigrateSourceScreen(
|
||||
nestedScrollInterop: NestedScrollConnection,
|
||||
presenter: MigrationSourcesPresenter,
|
||||
onClickItem: (Source) -> Unit,
|
||||
onLongClickItem: (Source) -> Unit,
|
||||
) {
|
||||
val state by presenter.state.collectAsState()
|
||||
when (state) {
|
||||
is MigrateSourceState.Loading -> LoadingScreen()
|
||||
is MigrateSourceState.Error -> Text(text = (state as MigrateSourceState.Error).error.message!!)
|
||||
is MigrateSourceState.Success ->
|
||||
val context = LocalContext.current
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.isEmpty -> EmptyScreen(textResource = R.string.information_empty_library)
|
||||
else ->
|
||||
MigrateSourceList(
|
||||
nestedScrollInterop = nestedScrollInterop,
|
||||
list = (state as MigrateSourceState.Success).sources,
|
||||
list = presenter.items,
|
||||
onClickItem = onClickItem,
|
||||
onLongClickItem = onLongClickItem,
|
||||
onLongClickItem = { source ->
|
||||
val sourceId = source.id.toString()
|
||||
context.copyToClipboard(sourceId, sourceId)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -64,11 +66,6 @@ fun MigrateSourceList(
|
||||
onClickItem: (Source) -> Unit,
|
||||
onLongClickItem: (Source) -> Unit,
|
||||
) {
|
||||
if (list.isEmpty()) {
|
||||
EmptyScreen(textResource = R.string.information_empty_library)
|
||||
return
|
||||
}
|
||||
|
||||
ScrollbarLazyColumn(
|
||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
||||
contentPadding = WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
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.source.model.Source
|
||||
|
||||
interface MigrateSourceState {
|
||||
val isLoading: Boolean
|
||||
val items: List<Pair<Source, Long>>
|
||||
val isEmpty: Boolean
|
||||
}
|
||||
|
||||
fun MigrateSourceState(): MigrateSourceState {
|
||||
return MigrateSourceStateImpl()
|
||||
}
|
||||
|
||||
class MigrateSourceStateImpl : MigrateSourceState {
|
||||
override var isLoading: Boolean by mutableStateOf(true)
|
||||
override var items: List<Pair<Source, Long>> by mutableStateOf(emptyList())
|
||||
override val isEmpty: Boolean by derivedStateOf { items.isEmpty() }
|
||||
}
|
||||
@@ -6,9 +6,8 @@ import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||
@@ -22,9 +21,10 @@ import eu.kanade.presentation.components.PreferenceRow
|
||||
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.browse.source.FilterUiModel
|
||||
import eu.kanade.tachiyomi.ui.browse.source.SourceFilterState
|
||||
import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterPresenter
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
||||
@Composable
|
||||
fun SourcesFilterScreen(
|
||||
@@ -33,39 +33,43 @@ fun SourcesFilterScreen(
|
||||
onClickLang: (String) -> Unit,
|
||||
onClickSource: (Source) -> Unit,
|
||||
) {
|
||||
val state by presenter.state.collectAsState()
|
||||
|
||||
when (state) {
|
||||
is SourceFilterState.Loading -> LoadingScreen()
|
||||
is SourceFilterState.Error -> Text(text = (state as SourceFilterState.Error).error.message!!)
|
||||
is SourceFilterState.Success ->
|
||||
val context = LocalContext.current
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.isEmpty -> EmptyScreen(textResource = R.string.source_filter_empty_screen)
|
||||
else -> {
|
||||
SourcesFilterContent(
|
||||
nestedScrollInterop = nestedScrollInterop,
|
||||
items = (state as SourceFilterState.Success).models,
|
||||
state = presenter,
|
||||
onClickLang = onClickLang,
|
||||
onClickSource = onClickSource,
|
||||
)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
presenter.events.collectLatest { event ->
|
||||
when (event) {
|
||||
SourcesFilterPresenter.Event.FailedFetchingLanguages -> {
|
||||
context.toast(R.string.internal_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SourcesFilterContent(
|
||||
nestedScrollInterop: NestedScrollConnection,
|
||||
items: List<FilterUiModel>,
|
||||
state: SourcesFilterState,
|
||||
onClickLang: (String) -> Unit,
|
||||
onClickSource: (Source) -> Unit,
|
||||
) {
|
||||
if (items.isEmpty()) {
|
||||
EmptyScreen(textResource = R.string.source_filter_empty_screen)
|
||||
return
|
||||
}
|
||||
|
||||
ScrollbarLazyColumn(
|
||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
||||
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
|
||||
) {
|
||||
items(
|
||||
items = items,
|
||||
items = state.items,
|
||||
contentType = {
|
||||
when (it) {
|
||||
is FilterUiModel.Header -> "header"
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
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.tachiyomi.ui.browse.source.FilterUiModel
|
||||
|
||||
interface SourcesFilterState {
|
||||
val isLoading: Boolean
|
||||
val items: List<FilterUiModel>
|
||||
val isEmpty: Boolean
|
||||
}
|
||||
|
||||
fun SourcesFilterState(): SourcesFilterState {
|
||||
return SourcesFilterStateImpl()
|
||||
}
|
||||
|
||||
class SourcesFilterStateImpl : SourcesFilterState {
|
||||
override var isLoading: Boolean by mutableStateOf(true)
|
||||
override var items: List<FilterUiModel> by mutableStateOf(emptyList())
|
||||
override val isEmpty: Boolean by derivedStateOf { items.isEmpty() }
|
||||
}
|
||||
@@ -19,10 +19,8 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||
@@ -42,9 +40,11 @@ import eu.kanade.presentation.util.plus
|
||||
import eu.kanade.presentation.util.topPaddingValues
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.ui.browse.source.SourceState
|
||||
import eu.kanade.tachiyomi.ui.browse.source.SourcesPresenter
|
||||
import eu.kanade.tachiyomi.ui.browse.source.SourcesPresenter.Dialog
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
||||
@Composable
|
||||
fun SourcesScreen(
|
||||
@@ -55,44 +55,47 @@ fun SourcesScreen(
|
||||
onClickLatest: (Source) -> Unit,
|
||||
onClickPin: (Source) -> Unit,
|
||||
) {
|
||||
val state by presenter.state.collectAsState()
|
||||
|
||||
when (state) {
|
||||
is SourceState.Loading -> LoadingScreen()
|
||||
is SourceState.Error -> Text(text = (state as SourceState.Error).error.message!!)
|
||||
is SourceState.Success -> SourceList(
|
||||
nestedScrollConnection = nestedScrollInterop,
|
||||
list = (state as SourceState.Success).uiModels,
|
||||
onClickItem = onClickItem,
|
||||
onClickDisable = onClickDisable,
|
||||
onClickLatest = onClickLatest,
|
||||
onClickPin = onClickPin,
|
||||
)
|
||||
val context = LocalContext.current
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.isEmpty -> EmptyScreen(R.string.source_empty_screen)
|
||||
else -> {
|
||||
SourceList(
|
||||
nestedScrollConnection = nestedScrollInterop,
|
||||
state = presenter,
|
||||
onClickItem = onClickItem,
|
||||
onClickDisable = onClickDisable,
|
||||
onClickLatest = onClickLatest,
|
||||
onClickPin = onClickPin,
|
||||
)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
presenter.events.collectLatest { event ->
|
||||
when (event) {
|
||||
SourcesPresenter.Event.FailedFetchingSources -> {
|
||||
context.toast(R.string.internal_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SourceList(
|
||||
nestedScrollConnection: NestedScrollConnection,
|
||||
list: List<SourceUiModel>,
|
||||
state: SourcesState,
|
||||
onClickItem: (Source) -> Unit,
|
||||
onClickDisable: (Source) -> Unit,
|
||||
onClickLatest: (Source) -> Unit,
|
||||
onClickPin: (Source) -> Unit,
|
||||
) {
|
||||
if (list.isEmpty()) {
|
||||
EmptyScreen(textResource = R.string.source_empty_screen)
|
||||
return
|
||||
}
|
||||
|
||||
var sourceState by remember { mutableStateOf<Source?>(null) }
|
||||
|
||||
ScrollbarLazyColumn(
|
||||
modifier = Modifier.nestedScroll(nestedScrollConnection),
|
||||
contentPadding = WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
||||
) {
|
||||
items(
|
||||
items = list,
|
||||
items = state.items,
|
||||
contentType = {
|
||||
when (it) {
|
||||
is SourceUiModel.Header -> "header"
|
||||
@@ -117,7 +120,7 @@ fun SourceList(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
source = model.source,
|
||||
onClickItem = onClickItem,
|
||||
onLongClickItem = { sourceState = it },
|
||||
onLongClickItem = { state.dialog = Dialog(it) },
|
||||
onClickLatest = onClickLatest,
|
||||
onClickPin = onClickPin,
|
||||
)
|
||||
@@ -125,18 +128,19 @@ fun SourceList(
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceState != null) {
|
||||
if (state.dialog != null) {
|
||||
val source = state.dialog!!.source
|
||||
SourceOptionsDialog(
|
||||
source = sourceState!!,
|
||||
source = source,
|
||||
onClickPin = {
|
||||
onClickPin(sourceState!!)
|
||||
sourceState = null
|
||||
onClickPin(source)
|
||||
state.dialog = null
|
||||
},
|
||||
onClickDisable = {
|
||||
onClickDisable(sourceState!!)
|
||||
sourceState = null
|
||||
onClickDisable(source)
|
||||
state.dialog = null
|
||||
},
|
||||
onDismiss = { sourceState = null },
|
||||
onDismiss = { state.dialog = null },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package eu.kanade.presentation.browse
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import eu.kanade.tachiyomi.ui.browse.source.SourcesPresenter
|
||||
|
||||
@Stable
|
||||
interface SourcesState {
|
||||
var dialog: SourcesPresenter.Dialog?
|
||||
val isLoading: Boolean
|
||||
val items: List<SourceUiModel>
|
||||
val isEmpty: Boolean
|
||||
}
|
||||
|
||||
fun SourcesState(): SourcesState {
|
||||
return SourcesStateImpl()
|
||||
}
|
||||
|
||||
class SourcesStateImpl : SourcesState {
|
||||
override var dialog: SourcesPresenter.Dialog? by mutableStateOf(null)
|
||||
override var isLoading: Boolean by mutableStateOf(true)
|
||||
override var items: List<SourceUiModel> by mutableStateOf(emptyList())
|
||||
override val isEmpty: Boolean by derivedStateOf { items.isEmpty() }
|
||||
}
|
||||
Reference in New Issue
Block a user