Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions app/src/main/java/com/cornellappdev/score/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
package com.cornellappdev.score

import android.os.Build
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import com.cornellappdev.score.nav.root.RootNavigation
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
Expand Down
46 changes: 46 additions & 0 deletions app/src/main/java/com/cornellappdev/score/nav/ScoreNavHost.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.cornellappdev.score.nav

import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.cornellappdev.score.nav.root.ScoreScreens
import com.cornellappdev.score.nav.root.ScoreScreens.Home
import com.cornellappdev.score.screen.GameDetailsScreen
import com.cornellappdev.score.screen.HomeScreen
import com.cornellappdev.score.screen.PastGamesScreen

@Composable
fun ScoreNavHost(navController: NavHostController) {
// This ViewModelStoreOwner is used to scope the past and home screen view models to the root
// screen instead of their individual tabs. This way the view models are not reconstructed
// everytime you switch tabs.
val mainScreenViewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current)

NavHost(
navController = navController,
startDestination = Home
) {
composable<Home> {
CompositionLocalProvider(LocalViewModelStoreOwner provides mainScreenViewModelStoreOwner) {
HomeScreen(navigateToGameDetails = {
navController.navigate(ScoreScreens.GameDetailsPage(""))
})
}
}
composable<ScoreScreens.ScoresScreen> {
CompositionLocalProvider(LocalViewModelStoreOwner provides mainScreenViewModelStoreOwner) {
PastGamesScreen(navigateToGameDetails = {
navController.navigate(ScoreScreens.GameDetailsPage(""))
})
}
}
composable<ScoreScreens.GameDetailsPage> {
GameDetailsScreen(onBackArrow = {
navController.navigateUp()
})
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.cornellappdev.score.nav

import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.NavBackStackEntry
import com.cornellappdev.score.nav.root.ScoreScreens
import com.cornellappdev.score.nav.root.tabs
import com.cornellappdev.score.nav.root.toScreen
import com.cornellappdev.score.theme.CrimsonPrimary
import com.cornellappdev.score.theme.GrayPrimary
import com.cornellappdev.score.theme.Style.bodyMedium
import com.cornellappdev.score.theme.White

@Composable
fun ScoreNavigationBar(
navigateToScreen: (ScoreScreens) -> Unit,
navBackStackEntry: NavBackStackEntry?,
modifier: Modifier = Modifier,
) {
NavigationBar(modifier = modifier, containerColor = White) {
tabs.map { item ->
val isSelected = item.screen == navBackStackEntry?.toScreen()

NavigationBarItem(
selected = isSelected,
onClick = { navigateToScreen(item.screen) },
icon = {
Icon(
painter = painterResource(id = if (isSelected) item.selectedIcon else item.unselectedIcon),
contentDescription = null,
tint = Color.Unspecified
)
},
label = {
Text(
text = item.label,
style = bodyMedium,
color = if (isSelected) {
CrimsonPrimary
} else {
GrayPrimary
}
)
}
)
}
}
}

@Preview
@Composable
private fun ScoreNavigationBarPreview() {
ScoreNavigationBar({}, null)
}
106 changes: 24 additions & 82 deletions app/src/main/java/com/cornellappdev/score/nav/root/RootNavigation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,23 @@ import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavBackStackEntry
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.toRoute
import com.cornellappdev.score.R
import com.cornellappdev.score.nav.root.ScoreRootScreens.Home.toScreen
import com.cornellappdev.score.screen.GameDetailsScreen
import com.cornellappdev.score.screen.HomeScreen
import com.cornellappdev.score.screen.PastGamesScreen
import com.cornellappdev.score.theme.CrimsonPrimary
import com.cornellappdev.score.theme.GrayPrimary
import com.cornellappdev.score.nav.ScoreNavHost
import com.cornellappdev.score.nav.ScoreNavigationBar
import com.cornellappdev.score.nav.root.ScoreScreens.GameDetailsPage
import com.cornellappdev.score.nav.root.ScoreScreens.Home
import com.cornellappdev.score.nav.root.ScoreScreens.ScoresScreen
import com.cornellappdev.score.theme.LocalInfiniteLoading
import com.cornellappdev.score.theme.Style.bodyMedium
import com.cornellappdev.score.theme.White
import kotlinx.serialization.Serializable

@Composable
Expand Down Expand Up @@ -67,93 +56,46 @@ fun RootNavigation(
}
}


Scaffold(modifier = Modifier.fillMaxSize(), bottomBar = {
if (navBackStackEntry?.toScreen() is ScoreRootScreens.GameDetailsPage) {
if (navBackStackEntry?.toScreen() is GameDetailsPage) {
return@Scaffold
}
NavigationBar(containerColor = White) {
tabs.map { item ->
val isSelected = item.screen == navBackStackEntry?.toScreen()

NavigationBarItem(
selected = isSelected,
onClick = { navController.navigate(item.screen) },
icon = {
Icon(
painter = painterResource(id = if (isSelected) item.selectedIcon else item.unselectedIcon),
contentDescription = null,
tint = Color.Unspecified
)
},
label = {
Text(
text = item.label,
style = bodyMedium,
color = if (isSelected) {
CrimsonPrimary
} else {
GrayPrimary
}
)
}
)
}
}
ScoreNavigationBar({ navController.navigate(it) }, navBackStackEntry)
}
) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) {
CompositionLocalProvider(LocalInfiniteLoading provides animatedValue) {
NavHost(
navController = navController,
startDestination = ScoreRootScreens.Home
) {
composable<ScoreRootScreens.Home> {
HomeScreen(navigateToGameDetails = {
navController.navigate(ScoreRootScreens.GameDetailsPage(it))
})
}

composable<ScoreRootScreens.GameDetailsPage> {
GameDetailsScreen(onBackArrow = {
navController.navigateUp()
})

}

composable<ScoreRootScreens.ScoresScreen> {
PastGamesScreen(navigateToGameDetails = {
navController.navigate(ScoreRootScreens.GameDetailsPage(it))
})
}
}
ScoreNavHost(navController)
}
}
}
}


@Serializable
sealed class ScoreRootScreens {
sealed class ScoreScreens {
@Serializable
data object Home : ScoreRootScreens()
data object Home : ScoreScreens()

@Serializable
data class GameDetailsPage(val gameId: String) : ScoreRootScreens()
data class GameDetailsPage(val gameId: String) : ScoreScreens()

@Serializable
data object ScoresScreen : ScoreRootScreens()

fun NavBackStackEntry.toScreen(): ScoreRootScreens? =
when (destination.route?.substringAfterLast(".")?.substringBefore("/")) {
"Home" -> toRoute<Home>()
"GameDetailsPage" -> toRoute<GameDetailsPage>()
"ScoresScreen" -> toRoute<ScoresScreen>()
else -> throw IllegalArgumentException("Invalid screen")
}
data object ScoresScreen : ScoreScreens()
}

fun NavBackStackEntry.toScreen(): ScoreScreens? =
when (destination.route?.substringAfterLast(".")?.substringBefore("/")) {
"Home" -> toRoute<Home>()
"GameDetailsPage" -> toRoute<GameDetailsPage>()
"ScoresScreen" -> toRoute<ScoresScreen>()
else -> throw IllegalArgumentException("Invalid screen")
}

data class NavItem(
val screen: ScoreRootScreens,
val screen: ScoreScreens,
val label: String,
val unselectedIcon: Int,
val selectedIcon: Int
Expand All @@ -164,12 +106,12 @@ val tabs = listOf(
label = "Schedule",
unselectedIcon = R.drawable.ic_schedule,
selectedIcon = R.drawable.ic_schedule_filled,
screen = ScoreRootScreens.Home,
screen = ScoreScreens.Home,
),
NavItem(
label = "Scores",
unselectedIcon = R.drawable.ic_scores,
selectedIcon = R.drawable.ic_scores_filled,
screen = ScoreRootScreens.ScoresScreen,
screen = ScoreScreens.ScoresScreen,
),
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class RootNavigationRepository @Inject constructor() : BaseNavigationRepository<ScoreRootScreens>()
class RootNavigationRepository @Inject constructor() : BaseNavigationRepository<ScoreScreens>()
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.cornellappdev.score.nav.root

import com.cornellappdev.score.viewmodel.BaseViewModel
import com.cornellappdev.score.util.UIEvent
import com.cornellappdev.score.viewmodel.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

Expand All @@ -13,7 +13,7 @@ class RootNavigationViewModel @Inject constructor(
) {

data class RootNavigationUiState(
val navigationEvent: UIEvent<ScoreRootScreens>? = null,
val navigationEvent: UIEvent<ScoreScreens>? = null,
)

init {
Expand Down