Refactor BottomNavBar visibility and add Daily Review feature

This commit is contained in:
jonasgaudian
2026-02-18 00:32:22 +01:00
parent 4b572f8773
commit 4cd014957f
4 changed files with 38 additions and 17 deletions

View File

@@ -253,19 +253,23 @@ fun TranslatorApp(
val currentDestination = navBackStackEntry?.destination val currentDestination = navBackStackEntry?.destination
val selectedScreen = Screen.fromDestination(currentDestination) val selectedScreen = Screen.fromDestination(currentDestination)
@Suppress("HardCodedStringLiteral") val isBottomBarHidden = currentDestination?.hierarchy?.any { destination -> @Suppress("HardCodedStringLiteral")
val currentRoute = currentDestination?.route
val isHiddenByHierarchy = currentDestination?.hierarchy?.any { destination ->
destination.route in setOf( destination.route in setOf(
Screen.Translation.route, Screen.Translation.route,
Screen.Dictionary.route, Screen.Dictionary.route,
Screen.Exercises.route, Screen.Exercises.route,
Screen.Settings.route Screen.Settings.route
) )
} == true || currentDestination?.route in setOf( } == true
"start_exercise", val isBottomBarHidden = isHiddenByHierarchy || currentRoute in setOf(
"new_word", "new_word",
"new_word_review", "new_word_review",
"vocabulary_detail/{itemId}" "vocabulary_detail/{itemId}",
) "daily_review"
) || currentRoute?.startsWith("start_exercise") == true
|| currentRoute?.startsWith("vocabulary_exercise") == true
val showBottomNavLabels by settingsViewModel.showBottomNavLabels.collectAsStateWithLifecycle(initialValue = false) val showBottomNavLabels by settingsViewModel.showBottomNavLabels.collectAsStateWithLifecycle(initialValue = false)
BottomNavigationBar( BottomNavigationBar(

View File

@@ -29,10 +29,10 @@ import eu.gaudian.translator.view.exercises.MainExerciseScreen
import eu.gaudian.translator.view.exercises.StartExerciseScreen import eu.gaudian.translator.view.exercises.StartExerciseScreen
import eu.gaudian.translator.view.exercises.YouTubeBrowserScreen import eu.gaudian.translator.view.exercises.YouTubeBrowserScreen
import eu.gaudian.translator.view.exercises.YouTubeExerciseScreen import eu.gaudian.translator.view.exercises.YouTubeExerciseScreen
import eu.gaudian.translator.view.home.DailyReviewScreen
import eu.gaudian.translator.view.home.HomeScreen import eu.gaudian.translator.view.home.HomeScreen
import eu.gaudian.translator.view.library.LibraryScreen import eu.gaudian.translator.view.library.LibraryScreen
import eu.gaudian.translator.view.settings.DictionaryOptionsScreen import eu.gaudian.translator.view.settings.DictionaryOptionsScreen
import eu.gaudian.translator.view.settings.SettingsRoutes
import eu.gaudian.translator.view.settings.TranslationSettingsScreen import eu.gaudian.translator.view.settings.TranslationSettingsScreen
import eu.gaudian.translator.view.settings.settingsGraph import eu.gaudian.translator.view.settings.settingsGraph
import eu.gaudian.translator.view.stats.StatsScreen import eu.gaudian.translator.view.stats.StatsScreen
@@ -53,6 +53,7 @@ import eu.gaudian.translator.view.vocabulary.VocabularySortingScreen
private const val TRANSITION_DURATION = 300 private const val TRANSITION_DURATION = 300
object NavigationRoutes { object NavigationRoutes {
const val DAILY_REVIEW = "daily_review"
const val NEW_WORD = "new_word" const val NEW_WORD = "new_word"
const val NEW_WORD_REVIEW = "new_word_review" const val NEW_WORD_REVIEW = "new_word_review"
const val VOCABULARY_DETAIL = "vocabulary_detail" const val VOCABULARY_DETAIL = "vocabulary_detail"
@@ -68,6 +69,7 @@ object NavigationRoutes {
const val STATS_VOCABULARY_LIST = "stats/vocabulary_list" const val STATS_VOCABULARY_LIST = "stats/vocabulary_list"
} }
@OptIn(androidx.compose.animation.ExperimentalAnimationApi::class)
@Composable @Composable
fun AppNavHost( fun AppNavHost(
navController: NavHostController, navController: NavHostController,
@@ -81,15 +83,23 @@ fun AppNavHost(
Screen.Home.route, Screen.Home.route,
Screen.Library.route, Screen.Library.route,
Screen.Stats.route, Screen.Stats.route,
Screen.Translation.route,
Screen.Dictionary.route,
Screen.Exercises.route,
SettingsRoutes.LIST
) )
// Helper to check if a route is a top-level tab // Helper to check if a route is a top-level tab
// Note: Routes can be "main_home", "main_library" etc. but mainTabRoutes contains parent routes
fun isTabTransition(initial: String?, target: String?): Boolean { fun isTabTransition(initial: String?, target: String?): Boolean {
return mainTabRoutes.contains(initial) && mainTabRoutes.contains(target) if (initial == null || target == null) return false
// Check if either the direct route OR a "main_*" variant is in mainTabRoutes
val initialIsTab = mainTabRoutes.contains(initial) ||
mainTabRoutes.any { route ->
initial == "main_${route}" || initial.startsWith("${route}_")
}
val targetIsTab = mainTabRoutes.contains(target) ||
mainTabRoutes.any { route ->
target == "main_${route}" || target.startsWith("${route}_")
}
return initialIsTab && targetIsTab
} }
NavHost( NavHost(
@@ -146,6 +156,10 @@ fun AppNavHost(
HomeScreen(navController = navController) HomeScreen(navController = navController)
} }
composable(NavigationRoutes.DAILY_REVIEW) {
DailyReviewScreen(navController = navController)
}
composable(NavigationRoutes.NEW_WORD) { composable(NavigationRoutes.NEW_WORD) {
NewWordScreen(navController = navController) NewWordScreen(navController = navController)
} }

View File

@@ -62,6 +62,7 @@ fun HomeScreen(
val streak by viewModel.streak.collectAsState() val streak by viewModel.streak.collectAsState()
val dailyGoal by viewModel.dailyGoal.collectAsState() val dailyGoal by viewModel.dailyGoal.collectAsState()
val todayCompletedCount by viewModel.todayCompletedCount.collectAsState() val todayCompletedCount by viewModel.todayCompletedCount.collectAsState()
val dueTodayCount by viewModel.dueTodayCount.collectAsState()
// Calculate daily goal progress // Calculate daily goal progress
val progress = if (dailyGoal > 0) { val progress = if (dailyGoal > 0) {
@@ -95,13 +96,12 @@ fun HomeScreen(
) )
} }
item { item {
//TODO replace with actual implementation
@Suppress("HardCodedStringLiteral")
ActionCard( ActionCard(
title = "Daily Review", title = stringResource(R.string.label_daily_review),
subtitle = "42 words need attention", subtitle = stringResource(R.string.desc_daily_review_due, dueTodayCount),
icon = Icons.Default.Psychology, icon = Icons.Default.Psychology,
contentColor = MaterialTheme.colorScheme.onPrimary contentColor = MaterialTheme.colorScheme.onPrimary,
onClick = { navController.navigate(NavigationRoutes.DAILY_REVIEW) }
) )
} }
item { item {

View File

@@ -1088,6 +1088,9 @@
<string name="label_2d_days">%1$d Days</string> <string name="label_2d_days">%1$d Days</string>
<string name="label_current_streak">Current Streak</string> <string name="label_current_streak">Current Streak</string>
<string name="label_daily_goal">Daily Goal</string> <string name="label_daily_goal">Daily Goal</string>
<string name="label_daily_review">Daily Review</string>
<string name="desc_daily_review_due">%1$d words need attention</string>
<string name="text_daily_review_placeholder">Daily review screen - implementation pending</string>
<string name="text_desc_no_activity_data_available">No activity data available</string> <string name="text_desc_no_activity_data_available">No activity data available</string>
<string name="label_see_history">See History</string> <string name="label_see_history">See History</string>
<string name="label_weekly_progress">Weekly Progress</string> <string name="label_weekly_progress">Weekly Progress</string>