mirror of
https://github.com/msfjarvis/compose-lobsters
synced 2025-08-14 22:17:03 +05:30
refactor: eliminate Destination indirection
This commit is contained in:
parent
9dce06ff80
commit
f64d4ab9a1
7 changed files with 34 additions and 40 deletions
|
@ -17,7 +17,6 @@ plugins {
|
||||||
id("dev.msfjarvis.claw.kotlin-kapt")
|
id("dev.msfjarvis.claw.kotlin-kapt")
|
||||||
id("dev.msfjarvis.claw.sentry")
|
id("dev.msfjarvis.claw.sentry")
|
||||||
id("dev.msfjarvis.claw.versioning-plugin")
|
id("dev.msfjarvis.claw.versioning-plugin")
|
||||||
id("kotlin-parcelize")
|
|
||||||
alias(libs.plugins.aboutlibraries)
|
alias(libs.plugins.aboutlibraries)
|
||||||
alias(libs.plugins.android.junit5)
|
alias(libs.plugins.android.junit5)
|
||||||
alias(libs.plugins.anvil)
|
alias(libs.plugins.anvil)
|
||||||
|
|
|
@ -24,8 +24,8 @@ import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.navigation3.runtime.NavKey
|
||||||
import dev.msfjarvis.claw.android.R
|
import dev.msfjarvis.claw.android.R
|
||||||
import dev.msfjarvis.claw.android.ui.navigation.Destination
|
|
||||||
import dev.msfjarvis.claw.android.ui.navigation.Search
|
import dev.msfjarvis.claw.android.ui.navigation.Search
|
||||||
import dev.msfjarvis.claw.android.ui.navigation.Settings
|
import dev.msfjarvis.claw.android.ui.navigation.Settings
|
||||||
|
|
||||||
|
@ -34,8 +34,8 @@ import dev.msfjarvis.claw.android.ui.navigation.Settings
|
||||||
fun ClawAppBar(
|
fun ClawAppBar(
|
||||||
activity: Activity?,
|
activity: Activity?,
|
||||||
isTopLevel: Boolean,
|
isTopLevel: Boolean,
|
||||||
navigateTo: (Destination) -> Unit,
|
navigateTo: (NavKey) -> Unit,
|
||||||
popBackStack: () -> Destination?,
|
popBackStack: () -> NavKey?,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
|
|
|
@ -26,12 +26,12 @@ import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.testTag
|
import androidx.compose.ui.platform.testTag
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.navigation3.runtime.NavKey
|
||||||
import dev.chrisbanes.haze.HazeDefaults
|
import dev.chrisbanes.haze.HazeDefaults
|
||||||
import dev.chrisbanes.haze.HazeState
|
import dev.chrisbanes.haze.HazeState
|
||||||
import dev.chrisbanes.haze.HazeStyle
|
import dev.chrisbanes.haze.HazeStyle
|
||||||
import dev.chrisbanes.haze.hazeEffect
|
import dev.chrisbanes.haze.hazeEffect
|
||||||
import dev.msfjarvis.claw.android.ui.navigation.AppDestinations
|
import dev.msfjarvis.claw.android.ui.navigation.AppDestinations
|
||||||
import dev.msfjarvis.claw.android.ui.navigation.Destination
|
|
||||||
import dev.msfjarvis.claw.common.ui.FloatingNavigationBar
|
import dev.msfjarvis.claw.common.ui.FloatingNavigationBar
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
|
@ -40,8 +40,8 @@ const val AnimationDuration = 100
|
||||||
@Composable
|
@Composable
|
||||||
fun ClawNavigationBar(
|
fun ClawNavigationBar(
|
||||||
items: ImmutableList<NavigationItem>,
|
items: ImmutableList<NavigationItem>,
|
||||||
currentDestination: Destination?,
|
currentNavKey: NavKey?,
|
||||||
navigateTo: (Destination) -> Unit,
|
navigateTo: (NavKey) -> Unit,
|
||||||
isVisible: Boolean,
|
isVisible: Boolean,
|
||||||
hazeState: HazeState,
|
hazeState: HazeState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
@ -84,7 +84,7 @@ fun ClawNavigationBar(
|
||||||
if (HazeDefaults.blurEnabled()) Color.Transparent else MaterialTheme.colorScheme.surface,
|
if (HazeDefaults.blurEnabled()) Color.Transparent else MaterialTheme.colorScheme.surface,
|
||||||
) {
|
) {
|
||||||
items.forEach { navItem ->
|
items.forEach { navItem ->
|
||||||
val isSelected = currentDestination == navItem.destination
|
val isSelected = currentNavKey == navItem.navKey
|
||||||
NavigationBarItem(
|
NavigationBarItem(
|
||||||
icon = {
|
icon = {
|
||||||
Crossfade(isSelected, label = "nav-label") {
|
Crossfade(isSelected, label = "nav-label") {
|
||||||
|
@ -100,7 +100,7 @@ fun ClawNavigationBar(
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
navItem.listStateResetCallback()
|
navItem.listStateResetCallback()
|
||||||
} else {
|
} else {
|
||||||
navigateTo(navItem.destination)
|
navigateTo(navItem.navKey)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.testTag(navItem.label.uppercase()),
|
modifier = Modifier.testTag(navItem.label.uppercase()),
|
||||||
|
@ -115,7 +115,7 @@ class NavigationItem
|
||||||
private constructor(
|
private constructor(
|
||||||
val icon: ImageVector,
|
val icon: ImageVector,
|
||||||
val label: String,
|
val label: String,
|
||||||
val destination: Destination,
|
val navKey: NavKey,
|
||||||
val selectedIcon: ImageVector,
|
val selectedIcon: ImageVector,
|
||||||
val listStateResetCallback: () -> Unit,
|
val listStateResetCallback: () -> Unit,
|
||||||
) {
|
) {
|
||||||
|
@ -125,7 +125,7 @@ private constructor(
|
||||||
) : this(
|
) : this(
|
||||||
destination.icon,
|
destination.icon,
|
||||||
destination.label,
|
destination.label,
|
||||||
destination.destination,
|
destination.navKey,
|
||||||
destination.selectedIcon,
|
destination.selectedIcon,
|
||||||
listStateResetCallback,
|
listStateResetCallback,
|
||||||
) {}
|
) {}
|
||||||
|
|
|
@ -21,14 +21,14 @@ import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.testTag
|
import androidx.compose.ui.platform.testTag
|
||||||
import dev.msfjarvis.claw.android.ui.navigation.Destination
|
import androidx.navigation3.runtime.NavKey
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ClawNavigationRail(
|
fun ClawNavigationRail(
|
||||||
items: ImmutableList<NavigationItem>,
|
items: ImmutableList<NavigationItem>,
|
||||||
currentDestination: Destination?,
|
currentNavKey: NavKey?,
|
||||||
navigateTo: (Destination) -> Unit,
|
navigateTo: (NavKey) -> Unit,
|
||||||
isVisible: Boolean,
|
isVisible: Boolean,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
@ -51,7 +51,7 @@ fun ClawNavigationRail(
|
||||||
NavigationRail(modifier = modifier) {
|
NavigationRail(modifier = modifier) {
|
||||||
Spacer(Modifier.weight(1f))
|
Spacer(Modifier.weight(1f))
|
||||||
items.forEach { navItem ->
|
items.forEach { navItem ->
|
||||||
val isSelected = currentDestination == navItem.destination
|
val isSelected = currentNavKey == navItem.navKey
|
||||||
NavigationRailItem(
|
NavigationRailItem(
|
||||||
icon = {
|
icon = {
|
||||||
Crossfade(isSelected, label = "nav-label") {
|
Crossfade(isSelected, label = "nav-label") {
|
||||||
|
@ -67,7 +67,7 @@ fun ClawNavigationRail(
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
navItem.listStateResetCallback()
|
navItem.listStateResetCallback()
|
||||||
} else {
|
} else {
|
||||||
navigateTo(navItem.destination)
|
navigateTo(navItem.navKey)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.testTag(navItem.label.uppercase()),
|
modifier = Modifier.testTag(navItem.label.uppercase()),
|
||||||
|
|
|
@ -22,7 +22,7 @@ import io.github.aakira.napier.Napier
|
||||||
* to the front while lists add behind. To counter these expectations with the actual backing data
|
* to the front while lists add behind. To counter these expectations with the actual backing data
|
||||||
* structure, many APIs in this class inverse of identically named functions on [List].
|
* structure, many APIs in this class inverse of identically named functions on [List].
|
||||||
*/
|
*/
|
||||||
class ClawBackStack<T : NavKey>(private val initialDestination: T) {
|
class ClawBackStack(private val initialDestination: NavKey) {
|
||||||
/**
|
/**
|
||||||
* Marker interface for destinations that occupy the "top" level of the back stack.
|
* Marker interface for destinations that occupy the "top" level of the back stack.
|
||||||
*
|
*
|
||||||
|
@ -41,7 +41,7 @@ class ClawBackStack<T : NavKey>(private val initialDestination: T) {
|
||||||
* from getting stuck in a frustratingly long stack of top level destinations that are so easily
|
* from getting stuck in a frustratingly long stack of top level destinations that are so easily
|
||||||
* accessible that they have no reason to be on the stack.
|
* accessible that they have no reason to be on the stack.
|
||||||
*/
|
*/
|
||||||
fun add(destination: T) {
|
fun add(destination: NavKey) {
|
||||||
logCurrentState("add")
|
logCurrentState("add")
|
||||||
if (destination is TopLevelDestination) {
|
if (destination is TopLevelDestination) {
|
||||||
backStack.clear()
|
backStack.clear()
|
||||||
|
@ -59,17 +59,17 @@ class ClawBackStack<T : NavKey>(private val initialDestination: T) {
|
||||||
return (top != null && top is TopLevelDestination)
|
return (top != null && top is TopLevelDestination)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun firstOrNull(): T? {
|
fun firstOrNull(): NavKey? {
|
||||||
logCurrentState("firstOrNull")
|
logCurrentState("firstOrNull")
|
||||||
return backStack.lastOrNull()
|
return backStack.lastOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun lastOrNull(): T? {
|
fun lastOrNull(): NavKey? {
|
||||||
logCurrentState("lastOrNull")
|
logCurrentState("lastOrNull")
|
||||||
return backStack.firstOrNull()
|
return backStack.firstOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeLastOrNull(): T? {
|
fun removeLastOrNull(): NavKey? {
|
||||||
logCurrentState("removeLastOrNull")
|
logCurrentState("removeLastOrNull")
|
||||||
return backStack.removeLastOrNull()
|
return backStack.removeLastOrNull()
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
*/
|
*/
|
||||||
package dev.msfjarvis.claw.android.ui.navigation
|
package dev.msfjarvis.claw.android.ui.navigation
|
||||||
|
|
||||||
import android.os.Parcelable
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Favorite
|
import androidx.compose.material.icons.filled.Favorite
|
||||||
import androidx.compose.material.icons.filled.NewReleases
|
import androidx.compose.material.icons.filled.NewReleases
|
||||||
|
@ -17,49 +16,46 @@ import androidx.compose.material.icons.outlined.Whatshot
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.navigation3.runtime.NavKey
|
import androidx.navigation3.runtime.NavKey
|
||||||
import dev.msfjarvis.claw.android.ui.navigation.ClawBackStack.TopLevelDestination
|
import dev.msfjarvis.claw.android.ui.navigation.ClawBackStack.TopLevelDestination
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
sealed interface Destination : Parcelable, NavKey
|
@Serializable data object Hottest : NavKey, TopLevelDestination
|
||||||
|
|
||||||
@Parcelize @Serializable data object Hottest : Destination, TopLevelDestination
|
@Serializable data object Newest : NavKey, TopLevelDestination
|
||||||
|
|
||||||
@Parcelize @Serializable data object Newest : Destination, TopLevelDestination
|
@Serializable data object Saved : NavKey, TopLevelDestination
|
||||||
|
|
||||||
@Parcelize @Serializable data object Saved : Destination, TopLevelDestination
|
@Serializable data class Comments(val postId: String) : NavKey
|
||||||
|
|
||||||
@Parcelize @Serializable data class Comments(val postId: String) : Destination
|
@Serializable data class User(val username: String) : NavKey
|
||||||
|
|
||||||
@Parcelize @Serializable data class User(val username: String) : Destination
|
@Serializable data object Search : NavKey
|
||||||
|
|
||||||
@Parcelize @Serializable data object Search : Destination
|
@Serializable data object Settings : NavKey
|
||||||
|
|
||||||
@Parcelize @Serializable data object Settings : Destination
|
@Serializable data object AboutLibraries : NavKey
|
||||||
|
|
||||||
@Parcelize @Serializable data object AboutLibraries : Destination
|
|
||||||
|
|
||||||
enum class AppDestinations(
|
enum class AppDestinations(
|
||||||
val icon: ImageVector,
|
val icon: ImageVector,
|
||||||
val label: String,
|
val label: String,
|
||||||
val destination: Destination,
|
val navKey: NavKey,
|
||||||
val selectedIcon: ImageVector,
|
val selectedIcon: ImageVector,
|
||||||
) {
|
) {
|
||||||
HOTTEST(
|
HOTTEST(
|
||||||
icon = Icons.Outlined.Whatshot,
|
icon = Icons.Outlined.Whatshot,
|
||||||
label = "Hottest",
|
label = "Hottest",
|
||||||
destination = Hottest,
|
navKey = Hottest,
|
||||||
selectedIcon = Icons.Filled.Whatshot,
|
selectedIcon = Icons.Filled.Whatshot,
|
||||||
),
|
),
|
||||||
NEWEST(
|
NEWEST(
|
||||||
icon = Icons.Outlined.NewReleases,
|
icon = Icons.Outlined.NewReleases,
|
||||||
label = "Newest",
|
label = "Newest",
|
||||||
destination = Newest,
|
navKey = Newest,
|
||||||
selectedIcon = Icons.Filled.NewReleases,
|
selectedIcon = Icons.Filled.NewReleases,
|
||||||
),
|
),
|
||||||
SAVED(
|
SAVED(
|
||||||
icon = Icons.Outlined.FavoriteBorder,
|
icon = Icons.Outlined.FavoriteBorder,
|
||||||
label = "Saved",
|
label = "Saved",
|
||||||
destination = Saved,
|
navKey = Saved,
|
||||||
selectedIcon = Icons.Filled.Favorite,
|
selectedIcon = Icons.Filled.Favorite,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,6 @@ import dev.msfjarvis.claw.android.ui.navigation.AppDestinations
|
||||||
import dev.msfjarvis.claw.android.ui.navigation.ClawBackStack
|
import dev.msfjarvis.claw.android.ui.navigation.ClawBackStack
|
||||||
import dev.msfjarvis.claw.android.ui.navigation.ClawNavigationType
|
import dev.msfjarvis.claw.android.ui.navigation.ClawNavigationType
|
||||||
import dev.msfjarvis.claw.android.ui.navigation.Comments
|
import dev.msfjarvis.claw.android.ui.navigation.Comments
|
||||||
import dev.msfjarvis.claw.android.ui.navigation.Destination
|
|
||||||
import dev.msfjarvis.claw.android.ui.navigation.Hottest
|
import dev.msfjarvis.claw.android.ui.navigation.Hottest
|
||||||
import dev.msfjarvis.claw.android.ui.navigation.Newest
|
import dev.msfjarvis.claw.android.ui.navigation.Newest
|
||||||
import dev.msfjarvis.claw.android.ui.navigation.Saved
|
import dev.msfjarvis.claw.android.ui.navigation.Saved
|
||||||
|
@ -75,7 +74,7 @@ fun Nav3Screen(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: ClawViewModel = injectedViewModel(),
|
viewModel: ClawViewModel = injectedViewModel(),
|
||||||
) {
|
) {
|
||||||
val clawBackStack = ClawBackStack<Destination>(Hottest)
|
val clawBackStack = ClawBackStack(Hottest)
|
||||||
|
|
||||||
// region Pain
|
// region Pain
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
@ -136,7 +135,7 @@ fun Nav3Screen(
|
||||||
AnimatedVisibility(visible = navigationType == ClawNavigationType.BOTTOM_NAVIGATION) {
|
AnimatedVisibility(visible = navigationType == ClawNavigationType.BOTTOM_NAVIGATION) {
|
||||||
ClawNavigationBar(
|
ClawNavigationBar(
|
||||||
items = navItems,
|
items = navItems,
|
||||||
currentDestination = currentDestination,
|
currentNavKey = currentDestination,
|
||||||
navigateTo = { clawBackStack.add(it) },
|
navigateTo = { clawBackStack.add(it) },
|
||||||
isVisible = clawBackStack.isOnTopLevelDestination(),
|
isVisible = clawBackStack.isOnTopLevelDestination(),
|
||||||
hazeState = hazeState,
|
hazeState = hazeState,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue