feat: move search to top bar, replace with Stats tab in bottom nav

- Bottom tabs: Home / Stats / Settings (was Home / Search / Settings)
- Search icon back in feed top bar
- Stats screen: no back button, tab-style fade transitions
- Removed Stats link from Settings screen
This commit is contained in:
Paweł Orzech 2026-03-19 14:57:44 +01:00
parent c91ccd0afb
commit 3da3e97e77
No known key found for this signature in database
4 changed files with 19 additions and 91 deletions

View file

@ -249,6 +249,9 @@ fun FeedScreen(
}
},
actions = {
IconButton(onClick = { viewModel.activateSearch() }) {
Icon(Icons.Default.Search, contentDescription = "Search")
}
SortButton(
currentSort = sortOrder,
onSortSelected = { viewModel.setSortOrder(it) }

View file

@ -10,8 +10,8 @@ import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.BarChart
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.*
import androidx.compose.runtime.*
@ -56,12 +56,12 @@ data class BottomNavItem(
val bottomNavItems = listOf(
BottomNavItem(Routes.FEED, "Home", Icons.Default.Home),
BottomNavItem(Routes.SEARCH, "Search", Icons.Default.Search),
BottomNavItem(Routes.STATS, "Stats", Icons.Default.BarChart),
BottomNavItem(Routes.SETTINGS, "Settings", Icons.Default.Settings)
)
/** Routes where the bottom navigation bar should be visible */
private val bottomBarRoutes = setOf(Routes.FEED, Routes.SEARCH, Routes.SETTINGS)
private val bottomBarRoutes = setOf(Routes.FEED, Routes.STATS, Routes.SETTINGS)
@Composable
fun SwooshNavGraph(
@ -93,27 +93,15 @@ fun SwooshNavGraph(
NavigationBarItem(
selected = selected,
onClick = {
if (item.route == Routes.SEARCH) {
// Navigate to feed with search mode active
feedViewModel.activateSearch()
navController.navigate(Routes.SEARCH) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
} else {
if (item.route == Routes.FEED) {
feedViewModel.deactivateSearch()
}
navController.navigate(item.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
if (item.route == Routes.FEED) {
feedViewModel.deactivateSearch()
}
navController.navigate(item.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
},
icon = {
@ -189,37 +177,6 @@ fun SwooshNavGraph(
)
}
composable(
Routes.SEARCH,
enterTransition = { fadeIn(tween(200)) },
exitTransition = { fadeOut(tween(150)) },
popEnterTransition = { fadeIn(tween(200)) },
popExitTransition = { fadeOut(tween(150)) }
) {
// Ensure search is active when this tab is shown
LaunchedEffect(Unit) {
feedViewModel.activateSearch()
}
FeedScreen(
viewModel = feedViewModel,
onPostClick = { post ->
selectedPost = post
navController.navigate(Routes.DETAIL)
},
onCompose = {
editPost = null
navController.navigate(Routes.COMPOSER)
},
onEditPost = { post ->
editPost = post
navController.navigate(Routes.COMPOSER)
},
onAddAccount = {
navController.navigate(Routes.ADD_ACCOUNT)
}
)
}
composable(
Routes.COMPOSER,
enterTransition = { slideInVertically(initialOffsetY = { it }, animationSpec = tween(250)) + fadeIn(tween(200)) },
@ -298,23 +255,18 @@ fun SwooshNavGraph(
navController.navigate(Routes.SETUP) {
popUpTo(0) { inclusive = true }
}
},
onStatsClick = {
navController.navigate(Routes.STATS)
}
)
}
composable(
Routes.STATS,
enterTransition = { slideInHorizontally(initialOffsetX = { it }, animationSpec = tween(250)) },
enterTransition = { fadeIn(tween(200)) },
exitTransition = { fadeOut(tween(150)) },
popEnterTransition = { fadeIn(tween(200)) },
popExitTransition = { slideOutHorizontally(targetOffsetX = { it }, animationSpec = tween(200)) }
popExitTransition = { fadeOut(tween(150)) }
) {
StatsScreen(
onBack = { navController.popBackStack() }
)
StatsScreen()
}
composable(

View file

@ -7,7 +7,6 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.BarChart
import androidx.compose.material.icons.filled.BrightnessAuto
import androidx.compose.material.icons.filled.DarkMode
import androidx.compose.material.icons.filled.LightMode
@ -31,8 +30,7 @@ import com.swoosh.microblog.ui.theme.ThemeViewModel
fun SettingsScreen(
onBack: () -> Unit,
onLogout: () -> Unit,
themeViewModel: ThemeViewModel? = null,
onStatsClick: () -> Unit = {}
themeViewModel: ThemeViewModel? = null
) {
val context = LocalContext.current
val accountManager = remember { AccountManager(context) }
@ -130,24 +128,6 @@ fun SettingsScreen(
HorizontalDivider()
Spacer(modifier = Modifier.height(16.dp))
// Writing Statistics button
FilledTonalButton(
onClick = onStatsClick,
modifier = Modifier.fillMaxWidth()
) {
Icon(
Icons.Default.BarChart,
contentDescription = null,
modifier = Modifier.size(18.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text("Writing Statistics")
}
Spacer(modifier = Modifier.height(16.dp))
HorizontalDivider()
Spacer(modifier = Modifier.height(16.dp))
// Disconnect current account
OutlinedButton(
onClick = { showDisconnectDialog = true },

View file

@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.Article
import androidx.compose.material.icons.filled.Create
import androidx.compose.material.icons.filled.Schedule
@ -29,7 +28,6 @@ import kotlinx.coroutines.delay
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun StatsScreen(
onBack: () -> Unit,
viewModel: StatsViewModel = viewModel()
) {
val state by viewModel.uiState.collectAsStateWithLifecycle()
@ -73,12 +71,7 @@ fun StatsScreen(
Scaffold(
topBar = {
TopAppBar(
title = { Text("Writing Statistics") },
navigationIcon = {
IconButton(onClick = onBack) {
Icon(Icons.AutoMirrored.Filled.ArrowBack, "Back")
}
}
title = { Text("Statistics") }
)
}
) { padding ->