diff --git a/app/src/main/java/dev/msfjarvis/lobsters/ui/main/LobstersApp.kt b/app/src/main/java/dev/msfjarvis/lobsters/ui/main/LobstersApp.kt index be83cd7b..74d659e1 100644 --- a/app/src/main/java/dev/msfjarvis/lobsters/ui/main/LobstersApp.kt +++ b/app/src/main/java/dev/msfjarvis/lobsters/ui/main/LobstersApp.kt @@ -1,75 +1,114 @@ package dev.msfjarvis.lobsters.ui.main -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material.BackdropScaffold -import androidx.compose.material.BackdropScaffoldDefaults -import androidx.compose.material.BackdropValue +import androidx.compose.material.BottomNavigation +import androidx.compose.material.BottomNavigationItem +import androidx.compose.material.Scaffold import androidx.compose.material.Text -import androidx.compose.material.rememberBackdropScaffoldState import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.compose.KEY_ROUTE +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.currentBackStackEntryAsState +import androidx.navigation.compose.navigate +import androidx.navigation.compose.rememberNavController import androidx.paging.compose.collectAsLazyPagingItems -import dev.msfjarvis.lobsters.R +import dev.msfjarvis.lobsters.ui.navigation.Destination import dev.msfjarvis.lobsters.ui.posts.HottestPosts import dev.msfjarvis.lobsters.ui.posts.SavedPosts import dev.msfjarvis.lobsters.ui.viewmodel.LobstersViewModel +import dev.msfjarvis.lobsters.util.IconResource +import kotlinx.coroutines.launch @Composable fun LobstersApp() { val viewModel: LobstersViewModel = viewModel() - val scaffoldState = rememberBackdropScaffoldState(initialValue = BackdropValue.Concealed) - BackdropScaffold( - scaffoldState = scaffoldState, - gesturesEnabled = false, - appBar = { - Spacer(modifier = Modifier.height(BackdropScaffoldDefaults.PeekHeight.times(0.3f))) - Text( - text = stringResource(id = R.string.app_name), - textAlign = TextAlign.Center, - fontSize = 18.sp, - fontWeight = FontWeight.Bold, - modifier = Modifier.fillMaxWidth(), - ) - Spacer(modifier = Modifier.height(BackdropScaffoldDefaults.PeekHeight.times(0.3f))) - }, - backLayerContent = { BackgroundLayerContent(viewModel) }, - ) { - ForegroundLayerContent(viewModel) - } -} - -@Composable -fun ForegroundLayerContent( - viewModel: LobstersViewModel, -) { + val coroutineScope = rememberCoroutineScope() + val navController = rememberNavController() val hottestPosts = viewModel.posts.collectAsLazyPagingItems() - val hottestPostsListState = rememberLazyListState() - - HottestPosts( - posts = hottestPosts, - listState = hottestPostsListState, - isPostSaved = viewModel::isPostSaved, - saveAction = viewModel::toggleSave, - ) -} - -@Composable -fun BackgroundLayerContent( - viewModel: LobstersViewModel, -) { val savedPosts by viewModel.savedPosts.collectAsState() - SavedPosts( - posts = savedPosts, - saveAction = viewModel::toggleSave, - ) + val hottestPostsListState = rememberLazyListState() + + val navBackStackEntry by navController.currentBackStackEntryAsState() + val currentRoute = + navBackStackEntry?.arguments?.getString(KEY_ROUTE) ?: Destination.startDestination.route + val currentDestination = Destination.getDestinationFromRoute(currentRoute) + val navigateToDestination: (destination: Destination) -> Unit = { destination -> + navController.navigate(destination.route) { + launchSingleTop = true + popUpTo(navController.graph.startDestination) { inclusive = false } + } + } + val jumpToIndex: (Int) -> Unit = { + coroutineScope.launch { + hottestPostsListState.snapToItemIndex(it) + } + } + + Scaffold( + bottomBar = { + LobstersBottomNav( + currentDestination, + navigateToDestination, + jumpToIndex, + ) + }, + ) { innerPadding -> + NavHost(navController, startDestination = Destination.startDestination.route) { + composable(Destination.Hottest.route) { + HottestPosts( + posts = hottestPosts, + listState = hottestPostsListState, + isPostSaved = viewModel::isPostSaved, + saveAction = viewModel::toggleSave, + modifier = Modifier.padding(bottom = innerPadding.calculateBottomPadding()), + ) + } + composable(Destination.Saved.route) { + SavedPosts( + posts = savedPosts, + saveAction = viewModel::toggleSave, + modifier = Modifier.padding(bottom = innerPadding.calculateBottomPadding()), + ) + } + } + } +} + +@Composable +fun LobstersBottomNav( + currentDestination: Destination, + navigateToDestination: (destination: Destination) -> Unit, + jumpToIndex: (index: Int) -> Unit, +) { + BottomNavigation(modifier = Modifier.testTag("LobstersBottomNav")) { + Destination.values().forEach { screen -> + BottomNavigationItem( + icon = { + IconResource( + resourceId = screen.badgeRes, + contentDescription = stringResource(screen.labelRes), + ) + }, + label = { Text(stringResource(id = screen.labelRes)) }, + selected = currentDestination == screen, + alwaysShowLabels = false, + onClick = { + if (screen != currentDestination) { + navigateToDestination(screen) + } else if (screen.route == Destination.Hottest.route) { + jumpToIndex(0) + } + } + ) + } + } }