Use Voyager on Browse tab (#8605)
(cherry picked from commit f4ac754d02)
# Conflicts:
# app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt
# app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt
# app/src/main/java/eu/kanade/presentation/browse/SourcesState.kt
# app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseController.kt
# app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowsePresenter.kt
# app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceTab.kt
# app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesScreenModel.kt
# app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesTab.kt
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
package eu.kanade.presentation.browse
|
||||
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.components.AppBarActions
|
||||
@@ -9,6 +12,7 @@ import eu.kanade.presentation.components.TabContent
|
||||
|
||||
@Composable
|
||||
fun BrowseTabWrapper(tab: TabContent) {
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
Scaffold(
|
||||
topBar = { scrollBehavior ->
|
||||
AppBar(
|
||||
@@ -19,7 +23,8 @@ fun BrowseTabWrapper(tab: TabContent) {
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||
) { paddingValues ->
|
||||
tab.content(paddingValues)
|
||||
tab.content(paddingValues, snackbarHostState)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,13 +53,13 @@ import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.extension.model.InstallStep
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionUiModel
|
||||
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsPresenter
|
||||
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsState
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import exh.source.anyIs
|
||||
|
||||
@Composable
|
||||
fun ExtensionScreen(
|
||||
presenter: ExtensionsPresenter,
|
||||
state: ExtensionsState,
|
||||
contentPadding: PaddingValues,
|
||||
onLongClickItem: (Extension) -> Unit,
|
||||
onClickItemCancel: (Extension) -> Unit,
|
||||
@@ -72,19 +72,19 @@ fun ExtensionScreen(
|
||||
onRefresh: () -> Unit,
|
||||
) {
|
||||
SwipeRefresh(
|
||||
refreshing = presenter.isRefreshing,
|
||||
refreshing = state.isRefreshing,
|
||||
onRefresh = onRefresh,
|
||||
enabled = !presenter.isLoading,
|
||||
enabled = !state.isLoading,
|
||||
) {
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.isEmpty -> EmptyScreen(
|
||||
state.isLoading -> LoadingScreen(modifier = Modifier.padding(contentPadding))
|
||||
state.isEmpty -> EmptyScreen(
|
||||
textResource = R.string.empty_screen,
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
)
|
||||
else -> {
|
||||
ExtensionContent(
|
||||
state = presenter,
|
||||
state = state,
|
||||
contentPadding = contentPadding,
|
||||
onLongClickItem = onLongClickItem,
|
||||
onClickItemCancel = onClickItemCancel,
|
||||
|
||||
@@ -1,27 +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.tachiyomi.ui.browse.extension.ExtensionUiModel
|
||||
|
||||
interface ExtensionsState {
|
||||
val isLoading: Boolean
|
||||
val isRefreshing: Boolean
|
||||
val items: List<ExtensionUiModel>
|
||||
val updates: Int
|
||||
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 var updates: Int by mutableStateOf(0)
|
||||
override val isEmpty: Boolean by derivedStateOf { items.isEmpty() }
|
||||
}
|
||||
@@ -53,7 +53,7 @@ import eu.kanade.presentation.util.plus
|
||||
import eu.kanade.presentation.util.topSmallPaddingValues
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.ui.browse.feed.FeedPresenter
|
||||
import eu.kanade.tachiyomi.ui.browse.feed.FeedScreenState
|
||||
import exh.savedsearches.models.FeedSavedSearch
|
||||
import exh.savedsearches.models.SavedSearch
|
||||
import eu.kanade.domain.manga.model.MangaCover as MangaCoverData
|
||||
@@ -69,97 +69,39 @@ data class FeedItemUI(
|
||||
|
||||
@Composable
|
||||
fun FeedScreen(
|
||||
presenter: FeedPresenter,
|
||||
state: FeedScreenState,
|
||||
contentPadding: PaddingValues,
|
||||
onClickAdd: (CatalogueSource) -> Unit,
|
||||
onClickCreate: (CatalogueSource, SavedSearch?) -> Unit,
|
||||
onClickSavedSearch: (SavedSearch, CatalogueSource) -> Unit,
|
||||
onClickSource: (CatalogueSource) -> Unit,
|
||||
onClickDelete: (FeedSavedSearch) -> Unit,
|
||||
onClickDeleteConfirm: (FeedSavedSearch) -> Unit,
|
||||
onClickManga: (Manga) -> Unit,
|
||||
getMangaState: @Composable (Manga, CatalogueSource?) -> State<Manga>,
|
||||
) {
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.isEmpty -> EmptyScreen(
|
||||
state.isLoading -> LoadingScreen()
|
||||
state.isEmpty -> EmptyScreen(
|
||||
textResource = R.string.feed_tab_empty,
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
)
|
||||
else -> {
|
||||
FeedList(
|
||||
state = presenter,
|
||||
contentPadding = contentPadding,
|
||||
getMangaState = { item, source -> presenter.getManga(item, source) },
|
||||
onClickSavedSearch = onClickSavedSearch,
|
||||
onClickSource = onClickSource,
|
||||
onClickDelete = onClickDelete,
|
||||
onClickManga = onClickManga,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
when (val dialog = presenter.dialog) {
|
||||
is FeedPresenter.Dialog.AddFeed -> {
|
||||
FeedAddDialog(
|
||||
sources = dialog.options,
|
||||
onDismiss = { presenter.dialog = null },
|
||||
onClickAdd = {
|
||||
presenter.dialog = null
|
||||
onClickAdd(it ?: return@FeedAddDialog)
|
||||
},
|
||||
)
|
||||
}
|
||||
is FeedPresenter.Dialog.AddFeedSearch -> {
|
||||
FeedAddSearchDialog(
|
||||
source = dialog.source,
|
||||
savedSearches = dialog.options,
|
||||
onDismiss = { presenter.dialog = null },
|
||||
onClickAdd = { source, savedSearch ->
|
||||
presenter.dialog = null
|
||||
onClickCreate(source, savedSearch)
|
||||
},
|
||||
)
|
||||
}
|
||||
is FeedPresenter.Dialog.DeleteFeed -> {
|
||||
FeedDeleteConfirmDialog(
|
||||
feed = dialog.feed,
|
||||
onDismiss = { presenter.dialog = null },
|
||||
onClickDeleteConfirm = {
|
||||
presenter.dialog = null
|
||||
onClickDeleteConfirm(it)
|
||||
},
|
||||
)
|
||||
}
|
||||
null -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FeedList(
|
||||
state: FeedState,
|
||||
contentPadding: PaddingValues,
|
||||
getMangaState: @Composable ((Manga, CatalogueSource?) -> State<Manga>),
|
||||
onClickSavedSearch: (SavedSearch, CatalogueSource) -> Unit,
|
||||
onClickSource: (CatalogueSource) -> Unit,
|
||||
onClickDelete: (FeedSavedSearch) -> Unit,
|
||||
onClickManga: (Manga) -> Unit,
|
||||
) {
|
||||
ScrollbarLazyColumn(
|
||||
contentPadding = contentPadding + topSmallPaddingValues,
|
||||
) {
|
||||
items(
|
||||
state.items.orEmpty(),
|
||||
key = { it.feed.id },
|
||||
) { item ->
|
||||
FeedItem(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
item = item,
|
||||
getMangaState = { getMangaState(it, item.source) },
|
||||
onClickSavedSearch = onClickSavedSearch,
|
||||
onClickSource = onClickSource,
|
||||
onClickDelete = onClickDelete,
|
||||
onClickManga = onClickManga,
|
||||
)
|
||||
ScrollbarLazyColumn(
|
||||
contentPadding = contentPadding + topSmallPaddingValues,
|
||||
) {
|
||||
items(
|
||||
state.items.orEmpty(),
|
||||
key = { it.feed.id },
|
||||
) { item ->
|
||||
FeedItem(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
item = item,
|
||||
getMangaState = { getMangaState(it, item.source) },
|
||||
onClickSavedSearch = onClickSavedSearch,
|
||||
onClickSource = onClickSource,
|
||||
onClickDelete = onClickDelete,
|
||||
onClickManga = onClickManga,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
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.ui.browse.feed.FeedPresenter
|
||||
|
||||
@Stable
|
||||
interface FeedState {
|
||||
var dialog: FeedPresenter.Dialog?
|
||||
val isLoading: Boolean
|
||||
val isEmpty: Boolean
|
||||
val items: List<FeedItemUI>?
|
||||
}
|
||||
|
||||
fun FeedState(): FeedState {
|
||||
return FeedStateImpl()
|
||||
}
|
||||
|
||||
class FeedStateImpl : FeedState {
|
||||
override var dialog: FeedPresenter.Dialog? by mutableStateOf(null)
|
||||
override var isLoading: Boolean by mutableStateOf(true)
|
||||
override var isEmpty: Boolean by mutableStateOf(false)
|
||||
override var items: List<FeedItemUI>? by mutableStateOf(null)
|
||||
}
|
||||
@@ -41,38 +41,40 @@ import eu.kanade.presentation.util.plus
|
||||
import eu.kanade.presentation.util.secondaryItemAlpha
|
||||
import eu.kanade.presentation.util.topSmallPaddingValues
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrationSourcesPresenter
|
||||
import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrateSourceState
|
||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||
|
||||
@Composable
|
||||
fun MigrateSourceScreen(
|
||||
presenter: MigrationSourcesPresenter,
|
||||
state: MigrateSourceState,
|
||||
contentPadding: PaddingValues,
|
||||
onClickItem: (Source) -> Unit,
|
||||
onToggleSortingDirection: () -> Unit,
|
||||
onToggleSortingMode: () -> Unit,
|
||||
// SY -->
|
||||
onClickAll: (Source) -> Unit,
|
||||
// SY <--
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.isEmpty -> EmptyScreen(
|
||||
state.isLoading -> LoadingScreen(modifier = Modifier.padding(contentPadding))
|
||||
state.isEmpty -> EmptyScreen(
|
||||
textResource = R.string.information_empty_library,
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
)
|
||||
else ->
|
||||
MigrateSourceList(
|
||||
list = presenter.items,
|
||||
list = state.items,
|
||||
contentPadding = contentPadding,
|
||||
onClickItem = onClickItem,
|
||||
onLongClickItem = { source ->
|
||||
val sourceId = source.id.toString()
|
||||
context.copyToClipboard(sourceId, sourceId)
|
||||
},
|
||||
sortingMode = presenter.sortingMode,
|
||||
onToggleSortingMode = { presenter.toggleSortingMode() },
|
||||
sortingDirection = presenter.sortingDirection,
|
||||
onToggleSortingDirection = { presenter.toggleSortingDirection() },
|
||||
sortingMode = state.sortingMode,
|
||||
onToggleSortingMode = onToggleSortingMode,
|
||||
sortingDirection = state.sortingDirection,
|
||||
onToggleSortingDirection = onToggleSortingDirection,
|
||||
// SY -->
|
||||
onClickAll = onClickAll,
|
||||
// SY <--
|
||||
|
||||
@@ -1,28 +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.source.interactor.SetMigrateSorting
|
||||
import eu.kanade.domain.source.model.Source
|
||||
|
||||
interface MigrateSourceState {
|
||||
val isLoading: Boolean
|
||||
val items: List<Pair<Source, Long>>
|
||||
val isEmpty: Boolean
|
||||
val sortingMode: SetMigrateSorting.Mode
|
||||
val sortingDirection: SetMigrateSorting.Direction
|
||||
}
|
||||
|
||||
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() }
|
||||
override var sortingMode: SetMigrateSorting.Mode by mutableStateOf(SetMigrateSorting.Mode.ALPHABETICAL)
|
||||
override var sortingDirection: SetMigrateSorting.Direction by mutableStateOf(SetMigrateSorting.Direction.ASCENDING)
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -40,153 +39,70 @@ import eu.kanade.presentation.util.plus
|
||||
import eu.kanade.presentation.util.topSmallPaddingValues
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.ui.browse.source.SourcesPresenter
|
||||
import eu.kanade.tachiyomi.ui.browse.source.SourcesPresenter.Dialog
|
||||
import eu.kanade.tachiyomi.ui.browse.source.SourcesState
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
||||
@Composable
|
||||
fun SourcesScreen(
|
||||
presenter: SourcesPresenter,
|
||||
state: SourcesState,
|
||||
contentPadding: PaddingValues,
|
||||
onClickItem: (Source, String) -> Unit,
|
||||
onClickDisable: (Source) -> Unit,
|
||||
onClickPin: (Source) -> Unit,
|
||||
// SY -->
|
||||
onClickSetCategories: (Source, List<String>) -> Unit,
|
||||
onClickToggleDataSaver: (Source) -> Unit,
|
||||
// SY <--
|
||||
onLongClickItem: (Source) -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
when {
|
||||
presenter.isLoading -> LoadingScreen()
|
||||
presenter.isEmpty -> EmptyScreen(
|
||||
state.isLoading -> LoadingScreen(modifier = Modifier.padding(contentPadding))
|
||||
state.isEmpty -> EmptyScreen(
|
||||
textResource = R.string.source_empty_screen,
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
)
|
||||
else -> {
|
||||
SourceList(
|
||||
state = presenter,
|
||||
contentPadding = contentPadding,
|
||||
onClickItem = onClickItem,
|
||||
onClickDisable = onClickDisable,
|
||||
onClickPin = onClickPin,
|
||||
// SY -->
|
||||
onClickSetCategories = onClickSetCategories,
|
||||
onClickToggleDataSaver = onClickToggleDataSaver,
|
||||
// SY <--
|
||||
)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
presenter.events.collectLatest { event ->
|
||||
when (event) {
|
||||
SourcesPresenter.Event.FailedFetchingSources -> {
|
||||
context.toast(R.string.internal_error)
|
||||
ScrollbarLazyColumn(
|
||||
contentPadding = contentPadding + topSmallPaddingValues,
|
||||
) {
|
||||
items(
|
||||
items = state.items,
|
||||
contentType = {
|
||||
when (it) {
|
||||
is SourceUiModel.Header -> "header"
|
||||
is SourceUiModel.Item -> "item"
|
||||
}
|
||||
},
|
||||
key = {
|
||||
when (it) {
|
||||
is SourceUiModel.Header -> it.hashCode()
|
||||
is SourceUiModel.Item -> "source-${it.source.key()}"
|
||||
}
|
||||
},
|
||||
) { model ->
|
||||
when (model) {
|
||||
is SourceUiModel.Header -> {
|
||||
SourceHeader(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
language = model.language,
|
||||
// SY -->
|
||||
isCategory = model.isCategory,
|
||||
// SY <--
|
||||
)
|
||||
}
|
||||
is SourceUiModel.Item -> SourceItem(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
source = model.source,
|
||||
// SY -->
|
||||
showLatest = state.showLatest,
|
||||
showPin = state.showPin,
|
||||
// SY <--
|
||||
onClickItem = onClickItem,
|
||||
onLongClickItem = onLongClickItem,
|
||||
onClickPin = onClickPin,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SourceList(
|
||||
state: SourcesState,
|
||||
contentPadding: PaddingValues,
|
||||
onClickItem: (Source, String) -> Unit,
|
||||
onClickDisable: (Source) -> Unit,
|
||||
onClickPin: (Source) -> Unit,
|
||||
// SY -->
|
||||
onClickSetCategories: (Source, List<String>) -> Unit,
|
||||
onClickToggleDataSaver: (Source) -> Unit,
|
||||
// SY <--
|
||||
) {
|
||||
ScrollbarLazyColumn(
|
||||
contentPadding = contentPadding + topSmallPaddingValues,
|
||||
) {
|
||||
items(
|
||||
items = state.items,
|
||||
contentType = {
|
||||
when (it) {
|
||||
is SourceUiModel.Header -> "header"
|
||||
is SourceUiModel.Item -> "item"
|
||||
}
|
||||
},
|
||||
key = {
|
||||
when (it) {
|
||||
is SourceUiModel.Header -> it.hashCode()
|
||||
is SourceUiModel.Item -> "source-${it.source.key()}"
|
||||
}
|
||||
},
|
||||
) { model ->
|
||||
when (model) {
|
||||
is SourceUiModel.Header -> {
|
||||
SourceHeader(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
language = model.language,
|
||||
// SY -->
|
||||
isCategory = model.isCategory,
|
||||
// SY <--
|
||||
)
|
||||
}
|
||||
is SourceUiModel.Item -> SourceItem(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
source = model.source,
|
||||
// SY -->
|
||||
showLatest = state.showLatest,
|
||||
showPin = state.showPin,
|
||||
// SY <--
|
||||
onClickItem = onClickItem,
|
||||
// SY -->
|
||||
onLongClickItem = { state.dialog = Dialog.SourceLongClick(it) },
|
||||
// SY <--
|
||||
onClickPin = onClickPin,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SY -->
|
||||
when (val dialog = state.dialog) {
|
||||
is Dialog.SourceCategories -> {
|
||||
SourceCategoriesDialog(
|
||||
source = dialog.source,
|
||||
categories = state.categories,
|
||||
onClickCategories = { source, newCategories ->
|
||||
onClickSetCategories(source, newCategories)
|
||||
state.dialog = null
|
||||
},
|
||||
onDismiss = { state.dialog = null },
|
||||
)
|
||||
}
|
||||
is Dialog.SourceLongClick -> {
|
||||
val source = dialog.source
|
||||
SourceOptionsDialog(
|
||||
source = source,
|
||||
onClickPin = {
|
||||
onClickPin(source)
|
||||
state.dialog = null
|
||||
},
|
||||
onClickDisable = {
|
||||
onClickDisable(source)
|
||||
state.dialog = null
|
||||
},
|
||||
onClickSetCategories = {
|
||||
state.dialog = Dialog.SourceCategories(source)
|
||||
},
|
||||
onClickToggleDataSaver = {
|
||||
onClickToggleDataSaver(source)
|
||||
state.dialog = null
|
||||
},
|
||||
onDismiss = { state.dialog = null },
|
||||
)
|
||||
}
|
||||
null -> Unit
|
||||
}
|
||||
// SY <--
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SourceHeader(
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -268,7 +184,7 @@ private fun SourcePinButton(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SourceOptionsDialog(
|
||||
fun SourceOptionsDialog(
|
||||
source: Source,
|
||||
onClickPin: () -> Unit,
|
||||
onClickDisable: () -> Unit,
|
||||
@@ -338,7 +254,7 @@ sealed class SourceUiModel {
|
||||
fun SourceCategoriesDialog(
|
||||
source: Source?,
|
||||
categories: List<String>,
|
||||
onClickCategories: (Source, List<String>) -> Unit,
|
||||
onClickCategories: (List<String>) -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
source ?: return
|
||||
@@ -379,7 +295,7 @@ fun SourceCategoriesDialog(
|
||||
},
|
||||
onDismissRequest = onDismiss,
|
||||
confirmButton = {
|
||||
TextButton(onClick = { onClickCategories(source, newCategories.toList()) }) {
|
||||
TextButton(onClick = { onClickCategories(newCategories.toList()) }) {
|
||||
Text(text = stringResource(android.R.string.ok))
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
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
|
||||
|
||||
// SY -->
|
||||
val showPin: Boolean
|
||||
val showLatest: Boolean
|
||||
val categories: List<String>
|
||||
// SY <--
|
||||
}
|
||||
|
||||
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() }
|
||||
|
||||
// SY -->
|
||||
override var showLatest: Boolean by mutableStateOf(true)
|
||||
override var showPin: Boolean by mutableStateOf(true)
|
||||
override var categories: List<String> by mutableStateOf(emptyList())
|
||||
// SY <--
|
||||
}
|
||||
@@ -8,10 +8,13 @@ import androidx.compose.foundation.layout.calculateStartPadding
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.TabRow
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -32,6 +35,7 @@ fun TabbedScreen(
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val state = rememberPagerState()
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
|
||||
LaunchedEffect(startIndex) {
|
||||
if (startIndex != null) {
|
||||
@@ -52,6 +56,7 @@ fun TabbedScreen(
|
||||
actions = { AppBarActions(tab.actions) },
|
||||
)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||
) { contentPadding ->
|
||||
Column(
|
||||
modifier = Modifier.padding(
|
||||
@@ -86,6 +91,7 @@ fun TabbedScreen(
|
||||
TachiyomiBottomNavigationView.withBottomNavPadding(
|
||||
PaddingValues(bottom = contentPadding.calculateBottomPadding()),
|
||||
),
|
||||
snackbarHostState,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -97,5 +103,5 @@ data class TabContent(
|
||||
val badgeNumber: Int? = null,
|
||||
val searchEnabled: Boolean = false,
|
||||
val actions: List<AppBar.Action> = emptyList(),
|
||||
val content: @Composable (contentPadding: PaddingValues) -> Unit,
|
||||
val content: @Composable (contentPadding: PaddingValues, snackbarHostState: SnackbarHostState) -> Unit,
|
||||
)
|
||||
|
||||
+2
-12
@@ -1,6 +1,5 @@
|
||||
package eu.kanade.presentation.more.settings.screen
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@@ -22,7 +21,6 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -37,7 +35,6 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.net.toUri
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.domain.backup.service.BackupPreferences
|
||||
import eu.kanade.presentation.components.Divider
|
||||
@@ -52,6 +49,7 @@ import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
|
||||
import eu.kanade.tachiyomi.data.backup.BackupFileValidator
|
||||
import eu.kanade.tachiyomi.data.backup.BackupRestoreService
|
||||
import eu.kanade.tachiyomi.data.backup.models.Backup
|
||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
@@ -70,7 +68,7 @@ object SettingsBackupScreen : SearchableSettings {
|
||||
override fun getPreferences(): List<Preference> {
|
||||
val backupPreferences = Injekt.get<BackupPreferences>()
|
||||
|
||||
RequestStoragePermission()
|
||||
DiskUtil.RequestStoragePermission()
|
||||
|
||||
return listOf(
|
||||
getCreateBackupPref(),
|
||||
@@ -79,14 +77,6 @@ object SettingsBackupScreen : SearchableSettings {
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RequestStoragePermission() {
|
||||
val permissionState = rememberPermissionState(permission = Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
LaunchedEffect(Unit) {
|
||||
permissionState.launchPermissionRequest()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun getCreateBackupPref(): Preference.PreferenceItem.TextPreference {
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
Reference in New Issue
Block a user