mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-12 03:58:56 +01:00
Initial conversion of browse tabs to full Compose
TODO: - Global search should launch a controller with the search textfield focused. This is pending a Compose rewrite of that screen. - Better migrate sort UI - Extensions search
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
package eu.kanade.presentation.browse
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.TabRow
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.google.accompanist.pager.HorizontalPager
|
||||
import com.google.accompanist.pager.rememberPagerState
|
||||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.components.AppBarActions
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.presentation.components.TabIndicator
|
||||
import eu.kanade.presentation.components.TabText
|
||||
import eu.kanade.tachiyomi.R
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun BrowseScreen(
|
||||
startIndex: Int? = null,
|
||||
tabs: List<BrowseTab>,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val state = rememberPagerState()
|
||||
|
||||
LaunchedEffect(startIndex) {
|
||||
if (startIndex != null) {
|
||||
state.scrollToPage(startIndex)
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
topBar = {
|
||||
AppBar(
|
||||
title = stringResource(R.string.browse),
|
||||
actions = {
|
||||
AppBarActions(tabs[state.currentPage].actions)
|
||||
},
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
Column(modifier = Modifier.padding(paddingValues)) {
|
||||
TabRow(
|
||||
selectedTabIndex = state.currentPage,
|
||||
indicator = { TabIndicator(it[state.currentPage]) },
|
||||
) {
|
||||
tabs.forEachIndexed { index, tab ->
|
||||
Tab(
|
||||
selected = state.currentPage == index,
|
||||
onClick = { scope.launch { state.animateScrollToPage(index) } },
|
||||
text = {
|
||||
TabText(stringResource(tab.titleRes), tab.badgeNumber, state.currentPage == index)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalPager(
|
||||
count = tabs.size,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = state,
|
||||
verticalAlignment = Alignment.Top,
|
||||
) { page ->
|
||||
tabs[page].content()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class BrowseTab(
|
||||
@StringRes val titleRes: Int,
|
||||
val badgeNumber: Int? = null,
|
||||
val actions: List<AppBar.Action> = emptyList(),
|
||||
val content: @Composable () -> Unit,
|
||||
)
|
||||
@@ -22,15 +22,12 @@ 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.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
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
|
||||
@@ -57,7 +54,6 @@ import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
|
||||
@Composable
|
||||
fun ExtensionScreen(
|
||||
nestedScrollInterop: NestedScrollConnection,
|
||||
presenter: ExtensionsPresenter,
|
||||
onLongClickItem: (Extension) -> Unit,
|
||||
onClickItemCancel: (Extension) -> Unit,
|
||||
@@ -68,10 +64,8 @@ fun ExtensionScreen(
|
||||
onOpenExtension: (Extension.Installed) -> Unit,
|
||||
onClickUpdateAll: () -> Unit,
|
||||
onRefresh: () -> Unit,
|
||||
onLaunched: () -> Unit,
|
||||
) {
|
||||
SwipeRefresh(
|
||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
||||
state = rememberSwipeRefreshState(presenter.isRefreshing),
|
||||
indicator = { s, trigger -> SwipeRefreshIndicator(s, trigger) },
|
||||
onRefresh = onRefresh,
|
||||
@@ -90,7 +84,6 @@ fun ExtensionScreen(
|
||||
onTrustExtension = onTrustExtension,
|
||||
onOpenExtension = onOpenExtension,
|
||||
onClickUpdateAll = onClickUpdateAll,
|
||||
onLaunched = onLaunched,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -108,7 +101,6 @@ fun ExtensionContent(
|
||||
onTrustExtension: (Extension.Untrusted) -> Unit,
|
||||
onOpenExtension: (Extension.Installed) -> Unit,
|
||||
onClickUpdateAll: () -> Unit,
|
||||
onLaunched: () -> Unit,
|
||||
) {
|
||||
var trustState by remember { mutableStateOf<Extension.Untrusted?>(null) }
|
||||
|
||||
@@ -187,9 +179,6 @@ fun ExtensionContent(
|
||||
}
|
||||
},
|
||||
)
|
||||
LaunchedEffect(Unit) {
|
||||
onLaunched()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ interface ExtensionsState {
|
||||
val isLoading: Boolean
|
||||
val isRefreshing: Boolean
|
||||
val items: List<ExtensionUiModel>
|
||||
val updates: Int
|
||||
val isEmpty: Boolean
|
||||
}
|
||||
|
||||
@@ -21,5 +22,6 @@ 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() }
|
||||
}
|
||||
|
||||
@@ -8,17 +8,17 @@ import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
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
|
||||
import eu.kanade.domain.source.interactor.SetMigrateSorting
|
||||
import eu.kanade.domain.source.model.Source
|
||||
import eu.kanade.presentation.browse.components.BaseSourceItem
|
||||
import eu.kanade.presentation.browse.components.SourceIcon
|
||||
@@ -39,7 +39,6 @@ import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||
|
||||
@Composable
|
||||
fun MigrateSourceScreen(
|
||||
nestedScrollInterop: NestedScrollConnection,
|
||||
presenter: MigrationSourcesPresenter,
|
||||
onClickItem: (Source) -> Unit,
|
||||
) {
|
||||
@@ -49,28 +48,44 @@ fun MigrateSourceScreen(
|
||||
presenter.isEmpty -> EmptyScreen(textResource = R.string.information_empty_library)
|
||||
else ->
|
||||
MigrateSourceList(
|
||||
nestedScrollInterop = nestedScrollInterop,
|
||||
list = presenter.items,
|
||||
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() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MigrateSourceList(
|
||||
nestedScrollInterop: NestedScrollConnection,
|
||||
list: List<Pair<Source, Long>>,
|
||||
onClickItem: (Source) -> Unit,
|
||||
onLongClickItem: (Source) -> Unit,
|
||||
sortingMode: SetMigrateSorting.Mode,
|
||||
onToggleSortingMode: () -> Unit,
|
||||
sortingDirection: SetMigrateSorting.Direction,
|
||||
onToggleSortingDirection: () -> Unit,
|
||||
) {
|
||||
ScrollbarLazyColumn(
|
||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
||||
contentPadding = bottomNavPaddingValues + WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
||||
) {
|
||||
stickyHeader {
|
||||
Row {
|
||||
Button(onClick = onToggleSortingMode) {
|
||||
Text(sortingMode.toString())
|
||||
}
|
||||
Button(onClick = onToggleSortingDirection) {
|
||||
Text(sortingDirection.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item(key = "title") {
|
||||
Text(
|
||||
text = stringResource(R.string.migration_selection_prompt),
|
||||
|
||||
@@ -4,12 +4,15 @@ 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 {
|
||||
@@ -20,4 +23,6 @@ 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)
|
||||
}
|
||||
|
||||
@@ -21,8 +21,6 @@ import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
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 androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -40,14 +38,12 @@ import eu.kanade.presentation.util.topPaddingValues
|
||||
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.util.system.LocaleHelper
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
||||
@Composable
|
||||
fun SourcesScreen(
|
||||
nestedScrollInterop: NestedScrollConnection,
|
||||
presenter: SourcesPresenter,
|
||||
onClickItem: (Source) -> Unit,
|
||||
onClickDisable: (Source) -> Unit,
|
||||
@@ -60,7 +56,6 @@ fun SourcesScreen(
|
||||
presenter.isEmpty -> EmptyScreen(R.string.source_empty_screen)
|
||||
else -> {
|
||||
SourceList(
|
||||
nestedScrollConnection = nestedScrollInterop,
|
||||
state = presenter,
|
||||
onClickItem = onClickItem,
|
||||
onClickDisable = onClickDisable,
|
||||
@@ -82,7 +77,6 @@ fun SourcesScreen(
|
||||
|
||||
@Composable
|
||||
fun SourceList(
|
||||
nestedScrollConnection: NestedScrollConnection,
|
||||
state: SourcesState,
|
||||
onClickItem: (Source) -> Unit,
|
||||
onClickDisable: (Source) -> Unit,
|
||||
@@ -90,7 +84,6 @@ fun SourceList(
|
||||
onClickPin: (Source) -> Unit,
|
||||
) {
|
||||
ScrollbarLazyColumn(
|
||||
modifier = Modifier.nestedScroll(nestedScrollConnection),
|
||||
contentPadding = bottomNavPaddingValues + WindowInsets.navigationBars.asPaddingValues() + topPaddingValues,
|
||||
) {
|
||||
items(
|
||||
@@ -119,7 +112,7 @@ fun SourceList(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
source = model.source,
|
||||
onClickItem = onClickItem,
|
||||
onLongClickItem = { state.dialog = Dialog(it) },
|
||||
onLongClickItem = { state.dialog = SourcesPresenter.Dialog(it) },
|
||||
onClickLatest = onClickLatest,
|
||||
onClickPin = onClickPin,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user