Refactor the dictionary and corrector navigation by promoting the Corrector to a top-level destination and removing the tabbed MainDictionaryScreen.
This commit is contained in:
@@ -82,19 +82,14 @@ val LocalConnectionConfigured = compositionLocalOf { true }
|
|||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
|
|
||||||
private val settingsViewModel: SettingsViewModel by viewModels()
|
private val settingsViewModel: SettingsViewModel by viewModels()
|
||||||
private var isReady = false
|
private var isReady = false
|
||||||
private var isUiLoaded = false
|
private var isUiLoaded = false
|
||||||
private var isInitializing = true
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
installSplashScreen().apply {
|
installSplashScreen().apply {
|
||||||
// The splash screen will now correctly wait until isReady is true
|
|
||||||
setKeepOnScreenCondition { !isReady }
|
setKeepOnScreenCondition { !isReady }
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
@@ -104,28 +99,22 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
|
|
||||||
// Show UI immediately and load data in background
|
|
||||||
setContent {
|
setContent {
|
||||||
AppTheme(settingsViewModel = settingsViewModel) {
|
AppTheme(settingsViewModel = settingsViewModel) {
|
||||||
TranslatorApp(settingsViewModel = settingsViewModel)
|
TranslatorApp(settingsViewModel = settingsViewModel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark UI as loaded immediately after setContent
|
|
||||||
isUiLoaded = true
|
isUiLoaded = true
|
||||||
|
|
||||||
// Start initialization in background without blocking UI
|
|
||||||
initializeData()
|
initializeData()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initializeData() {
|
private fun initializeData() {
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
// Get repositories from the Application instance (lazy initialization)
|
|
||||||
val myApp = application as MyApplication
|
val myApp = application as MyApplication
|
||||||
val languageRepository = myApp.languageRepository
|
val languageRepository = myApp.languageRepository
|
||||||
val apiRepository = myApp.apiRepository
|
val apiRepository = myApp.apiRepository
|
||||||
|
|
||||||
// Perform initialization in parallel where possible
|
|
||||||
val languageJob = launch {
|
val languageJob = launch {
|
||||||
languageRepository.initializeDefaultLanguages()
|
languageRepository.initializeDefaultLanguages()
|
||||||
languageRepository.initializeAllLanguages()
|
languageRepository.initializeAllLanguages()
|
||||||
@@ -135,13 +124,10 @@ class MainActivity : ComponentActivity() {
|
|||||||
apiRepository.initialInit()
|
apiRepository.initialInit()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for both to complete
|
|
||||||
languageJob.join()
|
languageJob.join()
|
||||||
apiJob.join()
|
apiJob.join()
|
||||||
|
|
||||||
// Signal readiness after all work is done.
|
|
||||||
isReady = true
|
isReady = true
|
||||||
isInitializing = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,10 +135,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
@Suppress("AssignedValueIsNeverRead")
|
@Suppress("AssignedValueIsNeverRead")
|
||||||
@SuppressLint("LocalContextResourcesRead")
|
@SuppressLint("LocalContextResourcesRead")
|
||||||
@Composable
|
@Composable
|
||||||
fun TranslatorApp(
|
fun TranslatorApp(settingsViewModel: SettingsViewModel) {
|
||||||
settingsViewModel: SettingsViewModel
|
|
||||||
) {
|
|
||||||
|
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
val statusViewModel: StatusViewModel = hiltViewModel(activity)
|
val statusViewModel: StatusViewModel = hiltViewModel(activity)
|
||||||
val statusMessageService = StatusMessageService
|
val statusMessageService = StatusMessageService
|
||||||
@@ -179,7 +162,6 @@ fun TranslatorApp(
|
|||||||
showExitDialog = true
|
showExitDialog = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (showExitDialog) {
|
if (showExitDialog) {
|
||||||
AppAlertDialog(
|
AppAlertDialog(
|
||||||
onDismissRequest = { showExitDialog = false },
|
onDismissRequest = { showExitDialog = false },
|
||||||
@@ -188,7 +170,6 @@ fun TranslatorApp(
|
|||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(onClick = {
|
TextButton(onClick = {
|
||||||
showExitDialog = false
|
showExitDialog = false
|
||||||
// Minimize the app similar to default back at root behavior
|
|
||||||
activity.moveTaskToBack(true)
|
activity.moveTaskToBack(true)
|
||||||
}) {
|
}) {
|
||||||
Text(stringResource(R.string.quit))
|
Text(stringResource(R.string.quit))
|
||||||
@@ -202,7 +183,6 @@ fun TranslatorApp(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for app updates and show "What's New" dialog if needed
|
|
||||||
var showWhatsNewDialog by remember { mutableStateOf(false) }
|
var showWhatsNewDialog by remember { mutableStateOf(false) }
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val changelogEntries = context.resources.getStringArray(R.array.changelog_entries)
|
val changelogEntries = context.resources.getStringArray(R.array.changelog_entries)
|
||||||
@@ -210,7 +190,6 @@ fun TranslatorApp(
|
|||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
try {
|
try {
|
||||||
// Only check for updates if the intro is completed
|
|
||||||
if (introCompleted) {
|
if (introCompleted) {
|
||||||
val currentVersion = BuildConfig.VERSION_NAME
|
val currentVersion = BuildConfig.VERSION_NAME
|
||||||
val hasSeenCurrentVersion = settingsViewModel.hasSeenCurrentVersion(currentVersion)
|
val hasSeenCurrentVersion = settingsViewModel.hasSeenCurrentVersion(currentVersion)
|
||||||
@@ -260,7 +239,8 @@ fun TranslatorApp(
|
|||||||
Screen.Translation.route,
|
Screen.Translation.route,
|
||||||
Screen.Dictionary.route,
|
Screen.Dictionary.route,
|
||||||
Screen.Exercises.route,
|
Screen.Exercises.route,
|
||||||
Screen.Settings.route
|
Screen.Settings.route,
|
||||||
|
Screen.Corrector.route
|
||||||
)
|
)
|
||||||
} == true
|
} == true
|
||||||
val isBottomBarHidden = isHiddenByHierarchy || currentRoute in setOf(
|
val isBottomBarHidden = isHiddenByHierarchy || currentRoute in setOf(
|
||||||
@@ -283,12 +263,11 @@ fun TranslatorApp(
|
|||||||
Screen.Translation,
|
Screen.Translation,
|
||||||
Screen.Dictionary,
|
Screen.Dictionary,
|
||||||
Screen.Settings,
|
Screen.Settings,
|
||||||
Screen.Exercises
|
Screen.Exercises,
|
||||||
|
Screen.Corrector
|
||||||
)
|
)
|
||||||
|
|
||||||
// Always reset the selected section to its root and clear back stack between sections
|
|
||||||
if (inSameSection) {
|
if (inSameSection) {
|
||||||
// If already within the same section, ensure we are at its graph root
|
|
||||||
navController.navigate(screen.route) {
|
navController.navigate(screen.route) {
|
||||||
popUpTo(screen.route) {
|
popUpTo(screen.route) {
|
||||||
inclusive = false
|
inclusive = false
|
||||||
@@ -303,9 +282,8 @@ fun TranslatorApp(
|
|||||||
restoreState = false
|
restoreState = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Switching sections: clear entire back stack to start to avoid back navigation results
|
|
||||||
navController.navigate(screen.route) {
|
navController.navigate(screen.route) {
|
||||||
popUpTo(0) { // Pop everything
|
popUpTo(0) {
|
||||||
inclusive = true
|
inclusive = true
|
||||||
saveState = false
|
saveState = false
|
||||||
}
|
}
|
||||||
@@ -340,8 +318,7 @@ fun TranslatorApp(
|
|||||||
statusState = statusState,
|
statusState = statusState,
|
||||||
navController = navController,
|
navController = navController,
|
||||||
onDismiss = { statusMessageService.trigger(StatusAction.HideMessageBar) },
|
onDismiss = { statusMessageService.trigger(StatusAction.HideMessageBar) },
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxWidth()
|
||||||
.fillMaxWidth()
|
|
||||||
)
|
)
|
||||||
AppNavHost(
|
AppNavHost(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
@@ -393,10 +370,8 @@ private fun AppTheme(
|
|||||||
val window = (view.context as Activity).window
|
val window = (view.context as Activity).window
|
||||||
val windowInsetsController = WindowInsetsControllerCompat(window, view)
|
val windowInsetsController = WindowInsetsControllerCompat(window, view)
|
||||||
|
|
||||||
// We must keep this for older Android version!!!
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
window.statusBarColor = colorScheme.surface.toArgb()
|
window.statusBarColor = colorScheme.surface.toArgb()
|
||||||
//Elevation must be the same as BottomNavigationBar
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
window.navigationBarColor = colorScheme.surfaceColorAtElevation(8.dp).toArgb()
|
window.navigationBarColor = colorScheme.surfaceColorAtElevation(8.dp).toArgb()
|
||||||
|
|
||||||
@@ -443,6 +418,4 @@ private fun AppTheme(
|
|||||||
content()
|
content()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,10 @@ import eu.gaudian.translator.model.VocabularyStage
|
|||||||
import eu.gaudian.translator.view.categories.CategoryDetailScreen
|
import eu.gaudian.translator.view.categories.CategoryDetailScreen
|
||||||
import eu.gaudian.translator.view.categories.CategoryListScreen
|
import eu.gaudian.translator.view.categories.CategoryListScreen
|
||||||
import eu.gaudian.translator.view.composable.Screen
|
import eu.gaudian.translator.view.composable.Screen
|
||||||
|
import eu.gaudian.translator.view.dictionary.CorrectionScreen
|
||||||
import eu.gaudian.translator.view.dictionary.DictionaryResultScreen
|
import eu.gaudian.translator.view.dictionary.DictionaryResultScreen
|
||||||
|
import eu.gaudian.translator.view.dictionary.DictionaryScreen
|
||||||
import eu.gaudian.translator.view.dictionary.EtymologyResultScreen
|
import eu.gaudian.translator.view.dictionary.EtymologyResultScreen
|
||||||
import eu.gaudian.translator.view.dictionary.MainDictionaryScreen
|
|
||||||
import eu.gaudian.translator.view.home.DailyReviewScreen
|
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
|
||||||
@@ -78,22 +79,14 @@ fun AppNavHost(
|
|||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 1. Define your Main Tab "Leaf" Routes (the actual start destinations of your graphs)
|
|
||||||
val mainTabRoutes = setOf(
|
val mainTabRoutes = setOf(
|
||||||
Screen.Home.route,
|
Screen.Home.route,
|
||||||
Screen.Library.route,
|
Screen.Library.route,
|
||||||
Screen.Stats.route,
|
Screen.Stats.route,
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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 {
|
||||||
if (initial == null || target == null) return false
|
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) ||
|
val initialIsTab = mainTabRoutes.contains(initial) ||
|
||||||
mainTabRoutes.any { route ->
|
mainTabRoutes.any { route ->
|
||||||
initial == "main_${route}" || initial.startsWith("${route}_")
|
initial == "main_${route}" || initial.startsWith("${route}_")
|
||||||
@@ -109,45 +102,33 @@ fun AppNavHost(
|
|||||||
navController = navController,
|
navController = navController,
|
||||||
startDestination = Screen.Home.route,
|
startDestination = Screen.Home.route,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
|
||||||
// ENTER TRANSITION
|
|
||||||
enterTransition = {
|
enterTransition = {
|
||||||
if (isTabTransition(initialState.destination.route, targetState.destination.route)) {
|
if (isTabTransition(initialState.destination.route, targetState.destination.route)) {
|
||||||
// Tab Switch: Just Fade In (Subtle Scale for modern feel)
|
|
||||||
fadeIn(animationSpec = tween(TRANSITION_DURATION)) +
|
fadeIn(animationSpec = tween(TRANSITION_DURATION)) +
|
||||||
scaleIn(initialScale = 0.96f, animationSpec = tween(TRANSITION_DURATION))
|
scaleIn(initialScale = 0.96f, animationSpec = tween(TRANSITION_DURATION))
|
||||||
} else {
|
} else {
|
||||||
// Detail Screen: Slide in from Right
|
|
||||||
slideInHorizontally(
|
slideInHorizontally(
|
||||||
initialOffsetX = { fullWidth -> (fullWidth * 0.1f).toInt() },
|
initialOffsetX = { fullWidth -> (fullWidth * 0.1f).toInt() },
|
||||||
animationSpec = tween(TRANSITION_DURATION, easing = FastOutSlowInEasing)
|
animationSpec = tween(TRANSITION_DURATION, easing = FastOutSlowInEasing)
|
||||||
) + fadeIn(animationSpec = tween(TRANSITION_DURATION))
|
) + fadeIn(animationSpec = tween(TRANSITION_DURATION))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// EXIT TRANSITION
|
|
||||||
exitTransition = {
|
exitTransition = {
|
||||||
if (isTabTransition(initialState.destination.route, targetState.destination.route)) {
|
if (isTabTransition(initialState.destination.route, targetState.destination.route)) {
|
||||||
// Tab Switch: Just Fade Out
|
|
||||||
fadeOut(animationSpec = tween(TRANSITION_DURATION))
|
fadeOut(animationSpec = tween(TRANSITION_DURATION))
|
||||||
} else {
|
} else {
|
||||||
// Detail Screen: Slide out to Left
|
|
||||||
slideOutHorizontally(
|
slideOutHorizontally(
|
||||||
targetOffsetX = { fullWidth -> -(fullWidth * 0.1f).toInt() },
|
targetOffsetX = { fullWidth -> -(fullWidth * 0.1f).toInt() },
|
||||||
animationSpec = tween(TRANSITION_DURATION, easing = FastOutSlowInEasing)
|
animationSpec = tween(TRANSITION_DURATION, easing = FastOutSlowInEasing)
|
||||||
) + fadeOut(animationSpec = tween(TRANSITION_DURATION))
|
) + fadeOut(animationSpec = tween(TRANSITION_DURATION))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// POP ENTER (Pressing Back) -> Always Slide back from left
|
|
||||||
popEnterTransition = {
|
popEnterTransition = {
|
||||||
slideInHorizontally(
|
slideInHorizontally(
|
||||||
initialOffsetX = { fullWidth -> -(fullWidth * 0.1f).toInt() },
|
initialOffsetX = { fullWidth -> -(fullWidth * 0.1f).toInt() },
|
||||||
animationSpec = tween(TRANSITION_DURATION, easing = FastOutSlowInEasing)
|
animationSpec = tween(TRANSITION_DURATION, easing = FastOutSlowInEasing)
|
||||||
) + fadeIn(animationSpec = tween(TRANSITION_DURATION))
|
) + fadeIn(animationSpec = tween(TRANSITION_DURATION))
|
||||||
},
|
},
|
||||||
|
|
||||||
// POP EXIT (Pressing Back) -> Always Slide away to right
|
|
||||||
popExitTransition = {
|
popExitTransition = {
|
||||||
slideOutHorizontally(
|
slideOutHorizontally(
|
||||||
targetOffsetX = { fullWidth -> (fullWidth * 0.1f).toInt() },
|
targetOffsetX = { fullWidth -> (fullWidth * 0.1f).toInt() },
|
||||||
@@ -158,23 +139,18 @@ fun AppNavHost(
|
|||||||
composable(Screen.Home.route) {
|
composable(Screen.Home.route) {
|
||||||
HomeScreen(navController = navController)
|
HomeScreen(navController = navController)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable(NavigationRoutes.DAILY_REVIEW) {
|
composable(NavigationRoutes.DAILY_REVIEW) {
|
||||||
DailyReviewScreen(navController = navController)
|
DailyReviewScreen(navController = navController)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable(NavigationRoutes.NEW_WORD) {
|
composable(NavigationRoutes.NEW_WORD) {
|
||||||
NewWordScreen(navController = navController)
|
NewWordScreen(navController = navController)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable(NavigationRoutes.NEW_WORD_REVIEW) {
|
composable(NavigationRoutes.NEW_WORD_REVIEW) {
|
||||||
NewWordReviewScreen(navController = navController)
|
NewWordReviewScreen(navController = navController)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable(NavigationRoutes.EXPLORE_PACKS) {
|
composable(NavigationRoutes.EXPLORE_PACKS) {
|
||||||
ExplorePacksScreen(navController = navController)
|
ExplorePacksScreen(navController = navController)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable(
|
composable(
|
||||||
route = "${NavigationRoutes.START_EXERCISE}?categoryId={categoryId}",
|
route = "${NavigationRoutes.START_EXERCISE}?categoryId={categoryId}",
|
||||||
arguments = listOf(
|
arguments = listOf(
|
||||||
@@ -193,7 +169,6 @@ fun AppNavHost(
|
|||||||
dueTodayOnly = false
|
dueTodayOnly = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable(NavigationRoutes.START_EXERCISE_DAILY) {
|
composable(NavigationRoutes.START_EXERCISE_DAILY) {
|
||||||
StartExerciseScreen(
|
StartExerciseScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
@@ -201,13 +176,12 @@ fun AppNavHost(
|
|||||||
dueTodayOnly = true
|
dueTodayOnly = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define all other navigation graphs at the same top level.
|
|
||||||
homeGraph(navController)
|
homeGraph(navController)
|
||||||
libraryGraph(navController)
|
libraryGraph(navController)
|
||||||
statsGraph(navController)
|
statsGraph(navController)
|
||||||
translationGraph(navController)
|
translationGraph(navController)
|
||||||
dictionaryGraph(navController)
|
dictionaryGraph(navController)
|
||||||
|
correctorGraph(navController)
|
||||||
exerciseGraph(navController)
|
exerciseGraph(navController)
|
||||||
settingsGraph(navController)
|
settingsGraph(navController)
|
||||||
}
|
}
|
||||||
@@ -233,9 +207,7 @@ fun NavGraphBuilder.libraryGraph(navController: NavHostController) {
|
|||||||
LibraryScreen(navController = navController)
|
LibraryScreen(navController = navController)
|
||||||
}
|
}
|
||||||
composable("vocabulary_sorting") {
|
composable("vocabulary_sorting") {
|
||||||
VocabularySortingScreen(
|
VocabularySortingScreen(navController = navController)
|
||||||
navController = navController
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
composable("vocabulary_detail/{itemId}") { backStackEntry ->
|
composable("vocabulary_detail/{itemId}") { backStackEntry ->
|
||||||
val itemId = backStackEntry.arguments?.getString("itemId")?.toIntOrNull()
|
val itemId = backStackEntry.arguments?.getString("itemId")?.toIntOrNull()
|
||||||
@@ -252,10 +224,7 @@ fun NavGraphBuilder.libraryGraph(navController: NavHostController) {
|
|||||||
composable("dictionary_result/{entryId}") { backStackEntry ->
|
composable("dictionary_result/{entryId}") { backStackEntry ->
|
||||||
val entryId = backStackEntry.arguments?.getString("entryId")?.toIntOrNull()
|
val entryId = backStackEntry.arguments?.getString("entryId")?.toIntOrNull()
|
||||||
if (entryId != null) {
|
if (entryId != null) {
|
||||||
DictionaryResultScreen(
|
DictionaryResultScreen(entryId = entryId, navController = navController)
|
||||||
entryId = entryId,
|
|
||||||
navController = navController,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
Text("Error: Invalid Entry ID")
|
Text("Error: Invalid Entry ID")
|
||||||
}
|
}
|
||||||
@@ -267,23 +236,16 @@ fun NavGraphBuilder.libraryGraph(navController: NavHostController) {
|
|||||||
navController = navController,
|
navController = navController,
|
||||||
showDueTodayOnly = showDueTodayOnly,
|
showDueTodayOnly = showDueTodayOnly,
|
||||||
categoryId = categoryId,
|
categoryId = categoryId,
|
||||||
onNavigateToItem = { item ->
|
onNavigateToItem = { item -> navController.navigate("vocabulary_detail/${item.id}") },
|
||||||
navController.navigate("vocabulary_detail/${item.id}")
|
|
||||||
},
|
|
||||||
onNavigateBack = { navController.popBackStack() },
|
onNavigateBack = { navController.popBackStack() },
|
||||||
enableNavigationButtons = true
|
enableNavigationButtons = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable("language_progress") {
|
composable("language_progress") {
|
||||||
LanguageJourneyScreen(
|
LanguageJourneyScreen(navController = navController)
|
||||||
navController = navController
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
composable(NavigationRoutes.STATS_VOCABULARY_HEATMAP) {
|
composable(NavigationRoutes.STATS_VOCABULARY_HEATMAP) {
|
||||||
VocabularyHeatmapScreen(
|
VocabularyHeatmapScreen(navController = navController)
|
||||||
navController = navController,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
composable("vocabulary_list/{showDueTodayOnly}/{stage}") { backStackEntry ->
|
composable("vocabulary_list/{showDueTodayOnly}/{stage}") { backStackEntry ->
|
||||||
val showDueTodayOnly = backStackEntry.arguments?.getString("showDueTodayOnly")?.toBoolean() ?: false
|
val showDueTodayOnly = backStackEntry.arguments?.getString("showDueTodayOnly")?.toBoolean() ?: false
|
||||||
@@ -291,14 +253,11 @@ fun NavGraphBuilder.libraryGraph(navController: NavHostController) {
|
|||||||
val stage = stageString?.let {
|
val stage = stageString?.let {
|
||||||
if (it.equals("null", ignoreCase = true)) null else VocabularyStage.valueOf(it)
|
if (it.equals("null", ignoreCase = true)) null else VocabularyStage.valueOf(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
AllCardsListScreen(
|
AllCardsListScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
showDueTodayOnly = showDueTodayOnly,
|
showDueTodayOnly = showDueTodayOnly,
|
||||||
stage = stage,
|
stage = stage,
|
||||||
onNavigateToItem = { item ->
|
onNavigateToItem = { item -> navController.navigate("vocabulary_detail/${item.id}") },
|
||||||
navController.navigate("vocabulary_detail/${item.id}")
|
|
||||||
},
|
|
||||||
onNavigateBack = { navController.popBackStack() },
|
onNavigateBack = { navController.popBackStack() },
|
||||||
categoryId = 0,
|
categoryId = 0,
|
||||||
enableNavigationButtons = true
|
enableNavigationButtons = true
|
||||||
@@ -311,22 +270,15 @@ fun NavGraphBuilder.libraryGraph(navController: NavHostController) {
|
|||||||
navArgument("categories") { type = NavType.StringType; nullable = true },
|
navArgument("categories") { type = NavType.StringType; nullable = true },
|
||||||
navArgument("stages") { type = NavType.StringType; nullable = true },
|
navArgument("stages") { type = NavType.StringType; nullable = true },
|
||||||
navArgument("languages") { type = NavType.StringType; nullable = true },
|
navArgument("languages") { type = NavType.StringType; nullable = true },
|
||||||
navArgument("dailyOnly") {
|
navArgument("dailyOnly") { type = NavType.BoolType; defaultValue = false }
|
||||||
type = NavType.BoolType
|
|
||||||
defaultValue = false
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
) { backStackEntry ->
|
) { backStackEntry ->
|
||||||
val arguments = backStackEntry.arguments
|
val arguments = backStackEntry.arguments
|
||||||
|
|
||||||
val dailyOnly = arguments?.getBoolean("dailyOnly") ?: false
|
val dailyOnly = arguments?.getBoolean("dailyOnly") ?: false
|
||||||
|
|
||||||
val categoryIds = arguments?.getString("categories")
|
val categoryIds = arguments?.getString("categories")
|
||||||
val stageNames = arguments?.getString("stages")
|
val stageNames = arguments?.getString("stages")
|
||||||
val languageIds = arguments?.getString("languages")
|
val languageIds = arguments?.getString("languages")
|
||||||
|
|
||||||
val dailyOnlyJson = "{\"dailyOnly\": $dailyOnly}"
|
val dailyOnlyJson = "{\"dailyOnly\": $dailyOnly}"
|
||||||
|
|
||||||
VocabularyExerciseHostScreen(
|
VocabularyExerciseHostScreen(
|
||||||
categoryIdsAsJson = categoryIds,
|
categoryIdsAsJson = categoryIds,
|
||||||
stageNamesAsJson = stageNames,
|
stageNamesAsJson = stageNames,
|
||||||
@@ -336,13 +288,7 @@ fun NavGraphBuilder.libraryGraph(navController: NavHostController) {
|
|||||||
navController = navController
|
navController = navController
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(
|
composable("vocabulary_exercise/{dailyOnly}?", arguments = listOf(navArgument("dailyOnly") { type = NavType.BoolType })) { _ ->
|
||||||
route = "vocabulary_exercise/{dailyOnly}?",
|
|
||||||
arguments = listOf(
|
|
||||||
navArgument("dailyOnly") { type = NavType.BoolType },
|
|
||||||
)
|
|
||||||
) { _ ->
|
|
||||||
|
|
||||||
VocabularyExerciseHostScreen(
|
VocabularyExerciseHostScreen(
|
||||||
categoryIdsAsJson = null,
|
categoryIdsAsJson = null,
|
||||||
stageNamesAsJson = null,
|
stageNamesAsJson = null,
|
||||||
@@ -352,34 +298,18 @@ fun NavGraphBuilder.libraryGraph(navController: NavHostController) {
|
|||||||
dailyOnlyAsJson = "{\"dailyOnly\": true}"
|
dailyOnlyAsJson = "{\"dailyOnly\": true}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(
|
composable("stage_detail/{stage}", arguments = listOf(navArgument("stage") { type = NavType.EnumType(VocabularyStage::class.java) })) { backStackEntry ->
|
||||||
"stage_detail/{stage}",
|
@Suppress("DEPRECATION")
|
||||||
arguments = listOf(
|
val stage = backStackEntry.arguments?.getSerializable("stage") as VocabularyStage
|
||||||
navArgument("stage") {
|
StageDetailScreen(navController = navController, stage = stage)
|
||||||
type = NavType.EnumType(VocabularyStage::class.java)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
{ backStackEntry ->
|
|
||||||
@Suppress("DEPRECATION") val stage = backStackEntry.arguments?.getSerializable("stage") as VocabularyStage
|
|
||||||
//NOTE: can ignore warning for now, once moved away from min SDK 28, use:
|
|
||||||
// val stage = backStackEntry.arguments?.getSerializable("stage", VocabularyStage::class.java)
|
|
||||||
StageDetailScreen(
|
|
||||||
navController = navController,
|
|
||||||
stage = stage
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
composable("category_detail/{categoryId}") { backStackEntry ->
|
composable("category_detail/{categoryId}") { backStackEntry ->
|
||||||
val categoryId = backStackEntry.arguments?.getString("categoryId")?.toIntOrNull()
|
val categoryId = backStackEntry.arguments?.getString("categoryId")?.toIntOrNull()
|
||||||
|
|
||||||
if (categoryId != null) {
|
if (categoryId != null) {
|
||||||
CategoryDetailScreen(
|
CategoryDetailScreen(
|
||||||
categoryId = categoryId,
|
categoryId = categoryId,
|
||||||
onBackClick = { navController.popBackStack() },
|
onBackClick = { navController.popBackStack() },
|
||||||
onNavigateToItem = { item ->
|
onNavigateToItem = { item -> navController.navigate("vocabulary_detail/${item.id}") },
|
||||||
navController.navigate("vocabulary_detail/${item.id}")
|
|
||||||
},
|
|
||||||
navController = navController
|
navController = navController
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -387,37 +317,22 @@ fun NavGraphBuilder.libraryGraph(navController: NavHostController) {
|
|||||||
composable("category_list_screen") {
|
composable("category_list_screen") {
|
||||||
CategoryListScreen(
|
CategoryListScreen(
|
||||||
onNavigateBack = { navController.popBackStack() },
|
onNavigateBack = { navController.popBackStack() },
|
||||||
onCategoryClicked = { categoryId ->
|
onCategoryClicked = { categoryId -> navController.navigate("category_detail/$categoryId") }
|
||||||
navController.navigate("category_detail/$categoryId")
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(
|
composable("vocabulary_sorting?mode={mode}", arguments = listOf(navArgument("mode") { type = NavType.StringType; nullable = true })) { backStackEntry ->
|
||||||
route = "vocabulary_sorting?mode={mode}", // Route now accepts an optional 'mode'
|
|
||||||
arguments = listOf(
|
|
||||||
navArgument("mode") { // Define the argument
|
|
||||||
type = NavType.StringType
|
|
||||||
nullable = true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
) { backStackEntry ->
|
|
||||||
VocabularySortingScreen(
|
VocabularySortingScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
// Pass the argument to the screen
|
|
||||||
initialFilterMode = backStackEntry.arguments?.getString("mode")
|
initialFilterMode = backStackEntry.arguments?.getString("mode")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable("no_grammar_items") {
|
composable("no_grammar_items") {
|
||||||
NoGrammarItemsScreen(
|
NoGrammarItemsScreen(navController = navController)
|
||||||
navController = navController
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun NavGraphBuilder.statsGraph(
|
fun NavGraphBuilder.statsGraph(navController: NavHostController) {
|
||||||
navController: NavHostController,
|
|
||||||
) {
|
|
||||||
navigation(
|
navigation(
|
||||||
startDestination = "main_stats",
|
startDestination = "main_stats",
|
||||||
route = Screen.Stats.route
|
route = Screen.Stats.route
|
||||||
@@ -426,9 +341,7 @@ fun NavGraphBuilder.statsGraph(
|
|||||||
StatsScreen(navController = navController)
|
StatsScreen(navController = navController)
|
||||||
}
|
}
|
||||||
composable("stats/vocabulary_sorting") {
|
composable("stats/vocabulary_sorting") {
|
||||||
VocabularySortingScreen(
|
VocabularySortingScreen(navController = navController)
|
||||||
navController = navController
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
composable("stats/vocabulary_list/{showDueTodayOnly}/{categoryId}") { backStackEntry ->
|
composable("stats/vocabulary_list/{showDueTodayOnly}/{categoryId}") { backStackEntry ->
|
||||||
val showDueTodayOnly = backStackEntry.arguments?.getString("showDueTodayOnly")?.toBoolean() ?: false
|
val showDueTodayOnly = backStackEntry.arguments?.getString("showDueTodayOnly")?.toBoolean() ?: false
|
||||||
@@ -437,22 +350,16 @@ fun NavGraphBuilder.statsGraph(
|
|||||||
navController = navController,
|
navController = navController,
|
||||||
showDueTodayOnly = showDueTodayOnly,
|
showDueTodayOnly = showDueTodayOnly,
|
||||||
categoryId = categoryId,
|
categoryId = categoryId,
|
||||||
onNavigateToItem = { item ->
|
onNavigateToItem = { item -> navController.navigate("vocabulary_detail/${item.id}") },
|
||||||
navController.navigate("vocabulary_detail/${item.id}")
|
|
||||||
},
|
|
||||||
onNavigateBack = { navController.popBackStack() },
|
onNavigateBack = { navController.popBackStack() },
|
||||||
enableNavigationButtons = true
|
enableNavigationButtons = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(NavigationRoutes.STATS_LANGUAGE_PROGRESS) {
|
composable(NavigationRoutes.STATS_LANGUAGE_PROGRESS) {
|
||||||
LanguageJourneyScreen(
|
LanguageJourneyScreen(navController = navController)
|
||||||
navController = navController
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
composable(NavigationRoutes.STATS_VOCABULARY_HEATMAP) {
|
composable(NavigationRoutes.STATS_VOCABULARY_HEATMAP) {
|
||||||
VocabularyHeatmapScreen(
|
VocabularyHeatmapScreen(navController = navController)
|
||||||
navController = navController,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
composable("stats/vocabulary_list/{showDueTodayOnly}/{stage}") { backStackEntry ->
|
composable("stats/vocabulary_list/{showDueTodayOnly}/{stage}") { backStackEntry ->
|
||||||
val showDueTodayOnly = backStackEntry.arguments?.getString("showDueTodayOnly")?.toBoolean() ?: false
|
val showDueTodayOnly = backStackEntry.arguments?.getString("showDueTodayOnly")?.toBoolean() ?: false
|
||||||
@@ -460,14 +367,11 @@ fun NavGraphBuilder.statsGraph(
|
|||||||
val stage = stageString?.let {
|
val stage = stageString?.let {
|
||||||
if (it.equals("null", ignoreCase = true)) null else VocabularyStage.valueOf(it)
|
if (it.equals("null", ignoreCase = true)) null else VocabularyStage.valueOf(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
AllCardsListScreen(
|
AllCardsListScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
showDueTodayOnly = showDueTodayOnly,
|
showDueTodayOnly = showDueTodayOnly,
|
||||||
stage = stage,
|
stage = stage,
|
||||||
onNavigateToItem = { item ->
|
onNavigateToItem = { item -> navController.navigate("vocabulary_detail/${item.id}") },
|
||||||
navController.navigate("vocabulary_detail/${item.id}")
|
|
||||||
},
|
|
||||||
onNavigateBack = { navController.popBackStack() },
|
onNavigateBack = { navController.popBackStack() },
|
||||||
categoryId = 0,
|
categoryId = 0,
|
||||||
enableNavigationButtons = true
|
enableNavigationButtons = true
|
||||||
@@ -475,14 +379,11 @@ fun NavGraphBuilder.statsGraph(
|
|||||||
}
|
}
|
||||||
composable("stats/category_detail/{categoryId}") { backStackEntry ->
|
composable("stats/category_detail/{categoryId}") { backStackEntry ->
|
||||||
val categoryId = backStackEntry.arguments?.getString("categoryId")?.toIntOrNull()
|
val categoryId = backStackEntry.arguments?.getString("categoryId")?.toIntOrNull()
|
||||||
|
|
||||||
if (categoryId != null) {
|
if (categoryId != null) {
|
||||||
CategoryDetailScreen(
|
CategoryDetailScreen(
|
||||||
categoryId = categoryId,
|
categoryId = categoryId,
|
||||||
onBackClick = { navController.popBackStack() },
|
onBackClick = { navController.popBackStack() },
|
||||||
onNavigateToItem = { item ->
|
onNavigateToItem = { item -> navController.navigate("vocabulary_detail/${item.id}") },
|
||||||
navController.navigate("vocabulary_detail/${item.id}")
|
|
||||||
},
|
|
||||||
navController = navController
|
navController = navController
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -490,29 +391,14 @@ fun NavGraphBuilder.statsGraph(
|
|||||||
composable("stats/category_list_screen") {
|
composable("stats/category_list_screen") {
|
||||||
CategoryListScreen(
|
CategoryListScreen(
|
||||||
onNavigateBack = { navController.popBackStack() },
|
onNavigateBack = { navController.popBackStack() },
|
||||||
onCategoryClicked = { categoryId ->
|
onCategoryClicked = { categoryId -> navController.navigate("stats/category_detail/$categoryId") }
|
||||||
navController.navigate("stats/category_detail/$categoryId")
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(
|
composable("stats/vocabulary_sorting?mode={mode}", arguments = listOf(navArgument("mode") { type = NavType.StringType; nullable = true })) { backStackEntry ->
|
||||||
route = "stats/vocabulary_sorting?mode={mode}",
|
VocabularySortingScreen(navController = navController, initialFilterMode = backStackEntry.arguments?.getString("mode"))
|
||||||
arguments = listOf(
|
|
||||||
navArgument("mode") {
|
|
||||||
type = NavType.StringType
|
|
||||||
nullable = true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
) { backStackEntry ->
|
|
||||||
VocabularySortingScreen(
|
|
||||||
navController = navController,
|
|
||||||
initialFilterMode = backStackEntry.arguments?.getString("mode")
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
composable("stats/no_grammar_items") {
|
composable("stats/no_grammar_items") {
|
||||||
NoGrammarItemsScreen(
|
NoGrammarItemsScreen(navController = navController)
|
||||||
navController = navController
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -539,15 +425,16 @@ fun NavGraphBuilder.dictionaryGraph(navController: NavHostController) {
|
|||||||
route = Screen.Dictionary.route
|
route = Screen.Dictionary.route
|
||||||
) {
|
) {
|
||||||
composable("main_dictionary") {
|
composable("main_dictionary") {
|
||||||
MainDictionaryScreen(navController = navController)
|
DictionaryScreen(
|
||||||
|
navController = navController,
|
||||||
|
onEntryClick = { entry -> navController.navigate("dictionary_result/${entry.id}") },
|
||||||
|
onNavigateToOptions = { navController.navigate("dictionary_options") }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
composable("dictionary_result/{entryId}") { backStackEntry ->
|
composable("dictionary_result/{entryId}") { backStackEntry ->
|
||||||
val entryId = backStackEntry.arguments?.getString("entryId")?.toIntOrNull()
|
val entryId = backStackEntry.arguments?.getString("entryId")?.toIntOrNull()
|
||||||
if (entryId != null) {
|
if (entryId != null) {
|
||||||
DictionaryResultScreen(
|
DictionaryResultScreen(entryId = entryId, navController = navController)
|
||||||
entryId = entryId,
|
|
||||||
navController = navController,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
Text("Error: Invalid Entry ID")
|
Text("Error: Invalid Entry ID")
|
||||||
}
|
}
|
||||||
@@ -558,43 +445,39 @@ fun NavGraphBuilder.dictionaryGraph(navController: NavHostController) {
|
|||||||
composable("etymology_result/{word}/{languageCode}") { backStackEntry ->
|
composable("etymology_result/{word}/{languageCode}") { backStackEntry ->
|
||||||
val word = backStackEntry.arguments?.getString("word") ?: ""
|
val word = backStackEntry.arguments?.getString("word") ?: ""
|
||||||
val languageCode = backStackEntry.arguments?.getString("languageCode")?.toIntOrNull() ?: 1
|
val languageCode = backStackEntry.arguments?.getString("languageCode")?.toIntOrNull() ?: 1
|
||||||
EtymologyResultScreen(
|
EtymologyResultScreen(navController = navController, word = word, languageCode = languageCode)
|
||||||
navController = navController,
|
|
||||||
word = word,
|
|
||||||
languageCode = languageCode
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun NavGraphBuilder.correctorGraph(navController: NavHostController) {
|
||||||
|
navigation(
|
||||||
|
startDestination = "main_corrector",
|
||||||
|
route = Screen.Corrector.route
|
||||||
|
) {
|
||||||
|
composable("main_corrector") {
|
||||||
|
CorrectionScreen(navController = navController)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(androidx.compose.animation.ExperimentalAnimationApi::class)
|
@OptIn(androidx.compose.animation.ExperimentalAnimationApi::class)
|
||||||
fun NavGraphBuilder.exerciseGraph(
|
fun NavGraphBuilder.exerciseGraph(navController: NavHostController) {
|
||||||
navController: NavHostController,
|
|
||||||
) {
|
|
||||||
navigation(
|
navigation(
|
||||||
startDestination = "main_exercise",
|
startDestination = "main_exercise",
|
||||||
route = Screen.Exercises.route
|
route = Screen.Exercises.route
|
||||||
) {
|
) {
|
||||||
composable("main_exercise") {
|
composable("main_exercise") {
|
||||||
MainExerciseScreen(
|
MainExerciseScreen(navController = navController)
|
||||||
navController = navController,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
composable("exercise_session") {
|
composable("exercise_session") {
|
||||||
ExerciseSessionScreen(
|
ExerciseSessionScreen(navController = navController)
|
||||||
navController = navController,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
composable("youtube_exercise") {
|
composable("youtube_exercise") {
|
||||||
YouTubeExerciseScreen(
|
YouTubeExerciseScreen(navController = navController)
|
||||||
navController = navController
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
composable("youtube_browse") {
|
composable("youtube_browse") {
|
||||||
YouTubeBrowserScreen(
|
YouTubeBrowserScreen(navController = navController)
|
||||||
navController = navController,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ import androidx.compose.animation.core.spring
|
|||||||
import androidx.compose.animation.slideInVertically
|
import androidx.compose.animation.slideInVertically
|
||||||
import androidx.compose.animation.slideOutVertically
|
import androidx.compose.animation.slideOutVertically
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -77,6 +78,7 @@ sealed class Screen(
|
|||||||
object Settings : Screen("settings", R.string.title_settings, AppIcons.SettingsFilled, AppIcons.SettingsOutlined)
|
object Settings : Screen("settings", R.string.title_settings, AppIcons.SettingsFilled, AppIcons.SettingsOutlined)
|
||||||
object More : Screen("more", R.string.label_more, AppIcons.MoreVert, AppIcons.MoreHorizontal)
|
object More : Screen("more", R.string.label_more, AppIcons.MoreVert, AppIcons.MoreHorizontal)
|
||||||
object Dictionary : Screen("dictionary", R.string.label_dictionary, AppIcons.DictionaryFilled, AppIcons.DictionaryOutlined)
|
object Dictionary : Screen("dictionary", R.string.label_dictionary, AppIcons.DictionaryFilled, AppIcons.DictionaryOutlined)
|
||||||
|
object Corrector : Screen("corrector", R.string.title_corrector, AppIcons.SpellCheck, AppIcons.SpellCheck)
|
||||||
object Exercises : Screen("exercises", R.string.label_exercises, AppIcons.DictionaryFilled, AppIcons.DictionaryOutlined)
|
object Exercises : Screen("exercises", R.string.label_exercises, AppIcons.DictionaryFilled, AppIcons.DictionaryOutlined)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -88,6 +90,7 @@ sealed class Screen(
|
|||||||
val items = mutableListOf<Screen>()
|
val items = mutableListOf<Screen>()
|
||||||
items.add(Translation)
|
items.add(Translation)
|
||||||
items.add(Dictionary)
|
items.add(Dictionary)
|
||||||
|
items.add(Corrector)
|
||||||
items.add(Settings)
|
items.add(Settings)
|
||||||
if (showExperimental) {
|
if (showExperimental) {
|
||||||
items.add(Exercises)
|
items.add(Exercises)
|
||||||
@@ -258,7 +261,7 @@ fun BottomNavigationBar(
|
|||||||
.background(
|
.background(
|
||||||
brush = Brush.radialGradient(
|
brush = Brush.radialGradient(
|
||||||
colors = listOf(
|
colors = listOf(
|
||||||
MaterialTheme.colorScheme.primary.copy(alpha = 0.5f),
|
MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.5f),
|
||||||
Color.Transparent
|
Color.Transparent
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@@ -271,6 +274,12 @@ fun BottomNavigationBar(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(playButtonSize)
|
.size(playButtonSize)
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
|
// CHANGED: Added a border to give the button definition
|
||||||
|
.border(
|
||||||
|
width = 4.dp, // Adjust this thickness to your liking
|
||||||
|
color = MaterialTheme.colorScheme.surfaceVariant, // Creates a nice "cutout" separation
|
||||||
|
shape = CircleShape
|
||||||
|
)
|
||||||
.background(MaterialTheme.colorScheme.primaryContainer)
|
.background(MaterialTheme.colorScheme.primaryContainer)
|
||||||
.clickable {
|
.clickable {
|
||||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||||
@@ -281,7 +290,7 @@ fun BottomNavigationBar(
|
|||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.PlayArrow,
|
imageVector = Icons.Filled.PlayArrow,
|
||||||
contentDescription = "Play",
|
contentDescription = "Play",
|
||||||
tint = Color.White,
|
tint = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||||
modifier = Modifier.size(32.dp)
|
modifier = Modifier.size(32.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,12 +60,16 @@ import androidx.compose.ui.text.input.TextFieldValue
|
|||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
|
import androidx.navigation.NavController
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.ui.theme.semanticColors
|
import eu.gaudian.translator.ui.theme.semanticColors
|
||||||
|
import eu.gaudian.translator.utils.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppButton
|
import eu.gaudian.translator.view.composable.AppButton
|
||||||
import eu.gaudian.translator.view.composable.AppIcons
|
import eu.gaudian.translator.view.composable.AppIcons
|
||||||
import eu.gaudian.translator.view.composable.AppOutlinedButton
|
import eu.gaudian.translator.view.composable.AppOutlinedButton
|
||||||
import eu.gaudian.translator.view.composable.AppSwitch
|
import eu.gaudian.translator.view.composable.AppSwitch
|
||||||
|
import eu.gaudian.translator.view.composable.AppTopAppBar
|
||||||
import eu.gaudian.translator.view.composable.DictionaryLanguageDropDown
|
import eu.gaudian.translator.view.composable.DictionaryLanguageDropDown
|
||||||
import eu.gaudian.translator.view.composable.DropdownDefaults
|
import eu.gaudian.translator.view.composable.DropdownDefaults
|
||||||
import eu.gaudian.translator.view.composable.LargeDropdownMenuItem
|
import eu.gaudian.translator.view.composable.LargeDropdownMenuItem
|
||||||
@@ -73,12 +77,15 @@ import eu.gaudian.translator.viewmodel.CorrectionViewModel
|
|||||||
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
// 1. STATEFUL COMPONENT (Connects to ViewModels)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CorrectionScreen(
|
fun CorrectionScreen(
|
||||||
correctionViewModel: CorrectionViewModel,
|
navController: NavController
|
||||||
languageViewModel: LanguageViewModel
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val correctionViewModel: CorrectionViewModel = hiltViewModel(activity)
|
||||||
|
val languageViewModel : LanguageViewModel = hiltViewModel(activity)
|
||||||
val textFieldValue by correctionViewModel.textFieldValue.collectAsState()
|
val textFieldValue by correctionViewModel.textFieldValue.collectAsState()
|
||||||
val explanation by correctionViewModel.explanation.collectAsState()
|
val explanation by correctionViewModel.explanation.collectAsState()
|
||||||
val isLoading by correctionViewModel.isLoading.collectAsState()
|
val isLoading by correctionViewModel.isLoading.collectAsState()
|
||||||
@@ -89,6 +96,15 @@ fun CorrectionScreen(
|
|||||||
|
|
||||||
val successColor = MaterialTheme.semanticColors.success
|
val successColor = MaterialTheme.semanticColors.success
|
||||||
|
|
||||||
|
Column(){
|
||||||
|
|
||||||
|
AppTopAppBar(
|
||||||
|
title = stringResource(R.string.label_correction),
|
||||||
|
onNavigateBack = {
|
||||||
|
navController.popBackStack()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
CorrectionScreenContent(
|
CorrectionScreenContent(
|
||||||
textFieldValue = textFieldValue,
|
textFieldValue = textFieldValue,
|
||||||
explanation = explanation,
|
explanation = explanation,
|
||||||
@@ -113,6 +129,7 @@ fun CorrectionScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. STATELESS COMPONENT (Handles UI Layout)
|
// 2. STATELESS COMPONENT (Handles UI Layout)
|
||||||
@@ -304,7 +321,6 @@ fun CorrectionScreenContent(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
package eu.gaudian.translator.view.dictionary
|
package eu.gaudian.translator.view.dictionary
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.utils.findActivity
|
import eu.gaudian.translator.utils.findActivity
|
||||||
|
import eu.gaudian.translator.view.composable.AppTopAppBar
|
||||||
import eu.gaudian.translator.viewmodel.DictionaryViewModel
|
import eu.gaudian.translator.viewmodel.DictionaryViewModel
|
||||||
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
||||||
|
|
||||||
@@ -20,6 +24,11 @@ fun DictionaryScreen(
|
|||||||
val dictionaryViewModel: DictionaryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val dictionaryViewModel: DictionaryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
|
Column {
|
||||||
|
AppTopAppBar(
|
||||||
|
title = stringResource(R.string.label_dictionary),
|
||||||
|
onNavigateBack = { navController.popBackStack() }
|
||||||
|
)
|
||||||
|
|
||||||
// Use the new refactored component
|
// Use the new refactored component
|
||||||
DictionaryScreenContent(
|
DictionaryScreenContent(
|
||||||
@@ -29,6 +38,7 @@ fun DictionaryScreen(
|
|||||||
languageViewModel = languageViewModel,
|
languageViewModel = languageViewModel,
|
||||||
onNavigateToOptions = onNavigateToOptions
|
onNavigateToOptions = onNavigateToOptions
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
@file:Suppress("HardCodedStringLiteral")
|
|
||||||
|
|
||||||
package eu.gaudian.translator.view.dictionary
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import androidx.navigation.compose.rememberNavController
|
|
||||||
import eu.gaudian.translator.R
|
|
||||||
import eu.gaudian.translator.ui.theme.ThemePreviews
|
|
||||||
import eu.gaudian.translator.utils.findActivity
|
|
||||||
import eu.gaudian.translator.view.composable.AppIcons
|
|
||||||
import eu.gaudian.translator.view.composable.AppTabLayout
|
|
||||||
import eu.gaudian.translator.view.composable.Screen
|
|
||||||
import eu.gaudian.translator.view.composable.TabItem
|
|
||||||
import eu.gaudian.translator.viewmodel.CorrectionViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.DictionaryViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun getDictionaryTabs(): List<TabItem> {
|
|
||||||
return listOf(
|
|
||||||
DictionaryTab(stringResource(R.string.label_dictionary), AppIcons.Dictionary),
|
|
||||||
DictionaryTab(stringResource(R.string.title_corrector), AppIcons.Check)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
private data class DictionaryTab(override val title: String, override val icon: ImageVector) :
|
|
||||||
TabItem
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun MainDictionaryScreen(
|
|
||||||
navController: NavController
|
|
||||||
) {
|
|
||||||
|
|
||||||
|
|
||||||
val activity = LocalContext.current.findActivity()
|
|
||||||
|
|
||||||
val dictionaryViewModel: DictionaryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
|
||||||
val correctionViewModel: CorrectionViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
|
||||||
val dictionaryTabs = getDictionaryTabs()
|
|
||||||
|
|
||||||
|
|
||||||
var selectedTab by remember { mutableStateOf(dictionaryTabs[0]) }
|
|
||||||
Column {
|
|
||||||
AppTabLayout(
|
|
||||||
tabs = dictionaryTabs,
|
|
||||||
selectedTab = selectedTab,
|
|
||||||
onTabSelected = { selectedTab = it },
|
|
||||||
onNavigateBack = {
|
|
||||||
if (!navController.popBackStack()) {
|
|
||||||
navController.navigate(Screen.Home.route) {
|
|
||||||
launchSingleTop = true
|
|
||||||
restoreState = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
when (selectedTab) {
|
|
||||||
dictionaryTabs[0] -> DictionaryScreen(
|
|
||||||
navController = navController,
|
|
||||||
onEntryClick = { entry ->
|
|
||||||
// Set flag indicating navigation is from external source (not DictionaryResultScreen)
|
|
||||||
dictionaryViewModel.setNavigatingFromDictionaryResult(false)
|
|
||||||
navController.navigate("dictionary_result/${entry.id}")
|
|
||||||
},
|
|
||||||
onNavigateToOptions = {
|
|
||||||
navController.navigate("dictionary_options")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
dictionaryTabs[1] -> CorrectionScreen(
|
|
||||||
correctionViewModel = correctionViewModel,
|
|
||||||
languageViewModel = languageViewModel
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ThemePreviews
|
|
||||||
@Composable
|
|
||||||
fun DictionaryHostScreenPreview() {
|
|
||||||
val navController = rememberNavController()
|
|
||||||
|
|
||||||
MainDictionaryScreen(
|
|
||||||
navController = navController,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1171,4 +1171,6 @@
|
|||||||
<!-- Explore Packs Hint -->
|
<!-- Explore Packs Hint -->
|
||||||
<string name="hint_explore_packs_title">About Vocabulary Packs</string>
|
<string name="hint_explore_packs_title">About Vocabulary Packs</string>
|
||||||
<string name="label_import_csv_or_lists">Import Lists or CSV</string>
|
<string name="label_import_csv_or_lists">Import Lists or CSV</string>
|
||||||
|
<string name="label_corrector">Corrector</string>
|
||||||
|
<string name="label_correction">Correction</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user