refactor: pull out ClawAppBar

Also change consumers of ClawBackStack to take delegating references
This commit is contained in:
Harsh Shandilya 2025-05-25 18:28:40 +05:30
parent beb1943ee6
commit 9dce06ff80
4 changed files with 90 additions and 59 deletions

View file

@ -0,0 +1,75 @@
/*
* Copyright © Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.claw.android.ui.decorations
import android.app.Activity
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Tune
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
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.Settings
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ClawAppBar(
activity: Activity?,
isTopLevel: Boolean,
navigateTo: (Destination) -> Unit,
popBackStack: () -> Destination?,
modifier: Modifier = Modifier,
) {
TopAppBar(
modifier = modifier.shadow(8.dp),
navigationIcon = {
if (!isTopLevel) {
IconButton(onClick = { if (popBackStack() == null) activity?.finish() }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Go back to previous screen",
)
}
} else {
Icon(
painter = painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = "The app icon for Claw",
modifier = Modifier.size(48.dp),
)
}
},
title = {
if (isTopLevel) {
Text(text = stringResource(R.string.app_name), fontWeight = FontWeight.Bold)
}
},
actions = {
if (isTopLevel) {
IconButton(onClick = { navigateTo(Search) }) {
Icon(imageVector = Icons.Filled.Search, contentDescription = "Search posts")
}
IconButton(onClick = { navigateTo(Settings) }) {
Icon(imageVector = Icons.Filled.Tune, contentDescription = "Settings")
}
}
},
)
}

View file

@ -31,7 +31,6 @@ 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.ClawBackStack
import dev.msfjarvis.claw.android.ui.navigation.Destination 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 +39,9 @@ const val AnimationDuration = 100
@Composable @Composable
fun ClawNavigationBar( fun ClawNavigationBar(
backStack: ClawBackStack<Destination>,
items: ImmutableList<NavigationItem>, items: ImmutableList<NavigationItem>,
currentDestination: Destination?,
navigateTo: (Destination) -> Unit,
isVisible: Boolean, isVisible: Boolean,
hazeState: HazeState, hazeState: HazeState,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@ -83,7 +83,6 @@ fun ClawNavigationBar(
containerColor = containerColor =
if (HazeDefaults.blurEnabled()) Color.Transparent else MaterialTheme.colorScheme.surface, if (HazeDefaults.blurEnabled()) Color.Transparent else MaterialTheme.colorScheme.surface,
) { ) {
val currentDestination = backStack.firstOrNull()
items.forEach { navItem -> items.forEach { navItem ->
val isSelected = currentDestination == navItem.destination val isSelected = currentDestination == navItem.destination
NavigationBarItem( NavigationBarItem(
@ -101,7 +100,7 @@ fun ClawNavigationBar(
if (isSelected) { if (isSelected) {
navItem.listStateResetCallback() navItem.listStateResetCallback()
} else { } else {
backStack.add(navItem.destination) navigateTo(navItem.destination)
} }
}, },
modifier = Modifier.testTag(navItem.label.uppercase()), modifier = Modifier.testTag(navItem.label.uppercase()),

View file

@ -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.ClawBackStack
import dev.msfjarvis.claw.android.ui.navigation.Destination import dev.msfjarvis.claw.android.ui.navigation.Destination
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
@Composable @Composable
fun ClawNavigationRail( fun ClawNavigationRail(
backStack: ClawBackStack<Destination>,
items: ImmutableList<NavigationItem>, items: ImmutableList<NavigationItem>,
currentDestination: Destination?,
navigateTo: (Destination) -> Unit,
isVisible: Boolean, isVisible: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
@ -49,7 +49,6 @@ fun ClawNavigationRail(
modifier = Modifier, modifier = Modifier,
) { ) {
NavigationRail(modifier = modifier) { NavigationRail(modifier = modifier) {
val currentDestination = backStack.firstOrNull()
Spacer(Modifier.weight(1f)) Spacer(Modifier.weight(1f))
items.forEach { navItem -> items.forEach { navItem ->
val isSelected = currentDestination == navItem.destination val isSelected = currentDestination == navItem.destination
@ -68,7 +67,7 @@ fun ClawNavigationRail(
if (isSelected) { if (isSelected) {
navItem.listStateResetCallback() navItem.listStateResetCallback()
} else { } else {
backStack.add(navItem.destination) navigateTo(navItem.destination)
} }
}, },
modifier = Modifier.testTag(navItem.label.uppercase()), modifier = Modifier.testTag(navItem.label.uppercase()),

View file

@ -15,20 +15,11 @@ import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.togetherWith import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Tune
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.windowsizeclass.WindowSizeClass import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@ -36,14 +27,9 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation3.runtime.entry import androidx.navigation3.runtime.entry
import androidx.navigation3.runtime.entryProvider import androidx.navigation3.runtime.entryProvider
@ -54,8 +40,8 @@ import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
import dev.chrisbanes.haze.HazeState import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.hazeSource import dev.chrisbanes.haze.hazeSource
import dev.msfjarvis.claw.android.MainActivity import dev.msfjarvis.claw.android.MainActivity
import dev.msfjarvis.claw.android.R
import dev.msfjarvis.claw.android.ui.PostActions import dev.msfjarvis.claw.android.ui.PostActions
import dev.msfjarvis.claw.android.ui.decorations.ClawAppBar
import dev.msfjarvis.claw.android.ui.decorations.ClawNavigationBar import dev.msfjarvis.claw.android.ui.decorations.ClawNavigationBar
import dev.msfjarvis.claw.android.ui.decorations.NavigationItem import dev.msfjarvis.claw.android.ui.decorations.NavigationItem
import dev.msfjarvis.claw.android.ui.lists.DatabasePosts import dev.msfjarvis.claw.android.ui.lists.DatabasePosts
@ -138,48 +124,20 @@ fun Nav3Screen(
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar( ClawAppBar(
modifier = Modifier.shadow(8.dp), activity = activity,
navigationIcon = { isTopLevel = clawBackStack.isOnTopLevelDestination(),
if (!(clawBackStack.isOnTopLevelDestination())) { navigateTo = { clawBackStack.add(it) },
IconButton( popBackStack = { clawBackStack.removeLastOrNull() },
onClick = { if (clawBackStack.removeLastOrNull() == null) activity?.finish() }
) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Go back to previous screen",
)
}
} else {
Icon(
painter = painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = "The app icon for Claw",
modifier = Modifier.size(48.dp),
)
}
},
title = {
if (clawBackStack.isOnTopLevelDestination()) {
Text(text = stringResource(R.string.app_name), fontWeight = FontWeight.Bold)
}
},
actions = {
if (clawBackStack.isOnTopLevelDestination()) {
IconButton(onClick = { clawBackStack.add(Search) }) {
Icon(imageVector = Icons.Filled.Search, contentDescription = "Search posts")
}
IconButton(onClick = { clawBackStack.add(Settings) }) {
Icon(imageVector = Icons.Filled.Tune, contentDescription = "Settings")
}
}
},
) )
}, },
bottomBar = { bottomBar = {
val currentDestination = clawBackStack.firstOrNull()
AnimatedVisibility(visible = navigationType == ClawNavigationType.BOTTOM_NAVIGATION) { AnimatedVisibility(visible = navigationType == ClawNavigationType.BOTTOM_NAVIGATION) {
ClawNavigationBar( ClawNavigationBar(
clawBackStack,
items = navItems, items = navItems,
currentDestination = currentDestination,
navigateTo = { clawBackStack.add(it) },
isVisible = clawBackStack.isOnTopLevelDestination(), isVisible = clawBackStack.isOnTopLevelDestination(),
hazeState = hazeState, hazeState = hazeState,
) )