Use upstream scaffold (#3834)

This commit is contained in:
AntsyLich
2026-08-22 08:22:45 +06:00
committed by GitHub
parent 9ec479266b
commit a2c24ff042
6 changed files with 146 additions and 566 deletions
@@ -2,22 +2,18 @@ package eu.kanade.tachiyomi.ui.home
import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.material3.Badge
import androidx.compose.material3.BadgedBox
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.NavigationRailItem
import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteDefaults
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteItem
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteType
import androidx.compose.material3.adaptive.navigationsuite.rememberNavigationSuiteScaffoldState
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
@@ -29,6 +25,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
@@ -52,9 +49,6 @@ import mihon.app.di.appGraph
import soup.compose.material.motion.animation.materialFadeThroughIn
import soup.compose.material.motion.animation.materialFadeThroughOut
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.NavigationBar
import tachiyomi.presentation.core.components.material.NavigationRail
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.pluralStringResource
object HomeScreen : Screen() {
@@ -86,52 +80,48 @@ object HomeScreen : Screen() {
) { tabNavigator ->
// Provide usable navigator to content screen
CompositionLocalProvider(LocalNavigator provides navigator) {
Scaffold(
startBar = {
if (isTabletUi()) {
NavigationRail {
TABS.fastForEach {
NavigationRailItem(it)
}
}
val tabletUi = isTabletUi()
val navigationSuiteType = if (tabletUi) {
NavigationSuiteType.NavigationRail
} else {
NavigationSuiteType.NavigationBar
}
val navigationSuiteState = rememberNavigationSuiteScaffoldState()
LaunchedEffect(navigationSuiteState, tabletUi) {
if (tabletUi) navigationSuiteState.show()
showBottomNavEvent.receiveAsFlow().collectLatest { show ->
if (tabletUi || show) {
navigationSuiteState.show()
} else {
navigationSuiteState.hide()
}
}
}
NavigationSuiteScaffold(
navigationSuiteType = navigationSuiteType,
state = navigationSuiteState,
navigationSuiteColors = NavigationSuiteDefaults.colors(
navigationRailContainerColor = MaterialTheme.colorScheme
.surfaceColorAtElevation(3.dp),
),
navigationItemVerticalArrangement = Arrangement.Center,
navigationItems = {
TABS.fastForEach { NavigationSuiteItem(it, navigationSuiteType) }
},
bottomBar = {
if (!isTabletUi()) {
val bottomNavVisible by produceState(initialValue = true) {
showBottomNavEvent.receiveAsFlow().collectLatest { value = it }
}
AnimatedVisibility(
visible = bottomNavVisible,
enter = expandVertically(),
exit = shrinkVertically(),
) {
NavigationBar {
TABS.fastForEach {
NavigationBarItem(it)
}
}
}
}
},
contentWindowInsets = WindowInsets(0),
) { contentPadding ->
Box(
modifier = Modifier
.padding(contentPadding)
.consumeWindowInsets(contentPadding),
) {
AnimatedContent(
targetState = tabNavigator.current,
transitionSpec = {
materialFadeThroughIn(
initialScale = 1f,
durationMillis = TabFadeDuration,
) togetherWith materialFadeThroughOut(durationMillis = TabFadeDuration)
},
label = "tabContent",
) {
AnimatedContent(
targetState = tabNavigator.current,
transitionSpec = {
materialFadeThroughIn(initialScale = 1f, durationMillis = TabFadeDuration) togetherWith
materialFadeThroughOut(durationMillis = TabFadeDuration)
},
label = "tabContent",
) {
tabNavigator.saveableState(key = "currentTab", it) {
it.Content()
}
tabNavigator.saveableState(key = "currentTab", it) {
it.Content()
}
}
}
@@ -176,12 +166,16 @@ object HomeScreen : Screen() {
}
@Composable
private fun RowScope.NavigationBarItem(tab: eu.kanade.presentation.util.Tab) {
private fun NavigationSuiteItem(
tab: eu.kanade.presentation.util.Tab,
navigationSuiteType: NavigationSuiteType,
) {
val tabNavigator = LocalTabNavigator.current
val navigator = LocalNavigator.currentOrThrow
val scope = rememberCoroutineScope()
val selected = tabNavigator.current::class == tab::class
NavigationBarItem(
NavigationSuiteItem(
navigationSuiteType = navigationSuiteType,
selected = selected,
onClick = {
if (!selected) {
@@ -190,7 +184,12 @@ object HomeScreen : Screen() {
scope.launch { tab.onReselect(navigator) }
}
},
icon = { NavigationIconItem(tab) },
icon = {
Icon(
painter = tab.options.icon!!,
contentDescription = tab.options.title,
)
},
label = {
Text(
text = tab.options.title,
@@ -199,93 +198,59 @@ object HomeScreen : Screen() {
overflow = TextOverflow.Ellipsis,
)
},
alwaysShowLabel = true,
badge = tabBadge(tab),
)
}
@Composable
fun NavigationRailItem(tab: eu.kanade.presentation.util.Tab) {
val tabNavigator = LocalTabNavigator.current
val navigator = LocalNavigator.currentOrThrow
val scope = rememberCoroutineScope()
val selected = tabNavigator.current::class == tab::class
NavigationRailItem(
selected = selected,
onClick = {
if (!selected) {
tabNavigator.current = tab
} else {
scope.launch { tab.onReselect(navigator) }
}
},
icon = { NavigationIconItem(tab) },
label = {
Text(
text = tab.options.title,
style = MaterialTheme.typography.labelLarge,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
},
alwaysShowLabel = true,
)
}
@Composable
private fun NavigationIconItem(tab: eu.kanade.presentation.util.Tab) {
private fun tabBadge(tab: eu.kanade.presentation.util.Tab): (@Composable () -> Unit)? {
val context = LocalContext.current
BadgedBox(
badge = {
when {
tab is UpdatesTab -> {
val count by produceState(initialValue = 0) {
val pref = context.appGraph.libraryPreferences
combine(
pref.newShowUpdatesCount.changes(),
pref.newUpdatesCount.changes(),
) { show, count -> if (show) count else 0 }
.collectLatest { value = it }
}
if (count > 0) {
Badge {
val desc = pluralStringResource(
MR.plurals.notification_chapters_generic,
count = count,
count,
)
Text(
text = count.toString(),
modifier = Modifier.semantics { contentDescription = desc },
)
}
}
}
BrowseTab::class.isInstance(tab) -> {
val count by produceState(initialValue = 0) {
context.appGraph.sourcePreferences.extensionUpdatesCount.changes()
.collectLatest { value = it }
}
if (count > 0) {
Badge {
val desc = pluralStringResource(
MR.plurals.update_check_notification_ext_updates,
count = count,
count,
)
Text(
text = count.toString(),
modifier = Modifier.semantics { contentDescription = desc },
)
}
}
val count by produceState(initialValue = 0, tab) {
val graph = context.appGraph
when (tab) {
is UpdatesTab -> {
combine(
graph.libraryPreferences.newShowUpdatesCount.changes(),
graph.libraryPreferences.newUpdatesCount.changes(),
) { show, count ->
if (show) count else 0
}
.collectLatest { value = it }
}
},
) {
Icon(
painter = tab.options.icon!!,
contentDescription = tab.options.title,
)
is BrowseTab -> {
graph.sourcePreferences.extensionUpdatesCount.changes()
.collectLatest { value = it }
}
else -> value = 0
}
}
if (count <= 0) return null
return {
Badge {
val desc = when (tab) {
is UpdatesTab -> pluralStringResource(
MR.plurals.notification_chapters_generic,
count = count,
count,
)
is BrowseTab -> pluralStringResource(
MR.plurals.update_check_notification_ext_updates,
count = count,
count,
)
else -> null
}
Text(
text = count.toString(),
modifier = Modifier.semantics {
if (desc != null) contentDescription = desc
},
)
}
}
}