Refactor VocabularyListScreen to AllCardsListScreen, introduce NavigationRoutes for centralized route management, and externalize hardcoded strings.
This commit is contained in:
@@ -253,7 +253,7 @@ fun TranslatorApp(
|
|||||||
val currentDestination = navBackStackEntry?.destination
|
val currentDestination = navBackStackEntry?.destination
|
||||||
val selectedScreen = Screen.fromDestination(currentDestination)
|
val selectedScreen = Screen.fromDestination(currentDestination)
|
||||||
|
|
||||||
val isBottomBarHidden = currentDestination?.hierarchy?.any { destination ->
|
@Suppress("HardCodedStringLiteral") val isBottomBarHidden = currentDestination?.hierarchy?.any { destination ->
|
||||||
destination.route in setOf(
|
destination.route in setOf(
|
||||||
Screen.Translation.route,
|
Screen.Translation.route,
|
||||||
Screen.Dictionary.route,
|
Screen.Dictionary.route,
|
||||||
@@ -310,6 +310,7 @@ fun TranslatorApp(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onPlayClicked = {
|
onPlayClicked = {
|
||||||
|
@Suppress("HardCodedStringLiteral")
|
||||||
navController.navigate("start_exercise")
|
navController.navigate("start_exercise")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ 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
|
||||||
import eu.gaudian.translator.view.translation.TranslationScreen
|
import eu.gaudian.translator.view.translation.TranslationScreen
|
||||||
|
import eu.gaudian.translator.view.vocabulary.AllCardsListScreen
|
||||||
import eu.gaudian.translator.view.vocabulary.CategoryDetailScreen
|
import eu.gaudian.translator.view.vocabulary.CategoryDetailScreen
|
||||||
import eu.gaudian.translator.view.vocabulary.CategoryListScreen
|
import eu.gaudian.translator.view.vocabulary.CategoryListScreen
|
||||||
import eu.gaudian.translator.view.vocabulary.LanguageProgressScreen
|
import eu.gaudian.translator.view.vocabulary.LanguageProgressScreen
|
||||||
@@ -47,11 +48,26 @@ import eu.gaudian.translator.view.vocabulary.StageDetailScreen
|
|||||||
import eu.gaudian.translator.view.vocabulary.VocabularyCardHost
|
import eu.gaudian.translator.view.vocabulary.VocabularyCardHost
|
||||||
import eu.gaudian.translator.view.vocabulary.VocabularyExerciseHostScreen
|
import eu.gaudian.translator.view.vocabulary.VocabularyExerciseHostScreen
|
||||||
import eu.gaudian.translator.view.vocabulary.VocabularyHeatmapScreen
|
import eu.gaudian.translator.view.vocabulary.VocabularyHeatmapScreen
|
||||||
import eu.gaudian.translator.view.vocabulary.VocabularyListScreen
|
|
||||||
import eu.gaudian.translator.view.vocabulary.VocabularySortingScreen
|
import eu.gaudian.translator.view.vocabulary.VocabularySortingScreen
|
||||||
|
|
||||||
private const val TRANSITION_DURATION = 300
|
private const val TRANSITION_DURATION = 300
|
||||||
|
|
||||||
|
object NavigationRoutes {
|
||||||
|
const val NEW_WORD = "new_word"
|
||||||
|
const val NEW_WORD_REVIEW = "new_word_review"
|
||||||
|
const val START_EXERCISE = "start_exercise"
|
||||||
|
const val CATEGORY_DETAIL = "category_detail"
|
||||||
|
const val CATEGORY_LIST = "category_list_screen"
|
||||||
|
const val VOCABULARY_DETAIL = "vocabulary_detail"
|
||||||
|
const val STATS_VOCABULARY_HEATMAP = "stats/vocabulary_heatmap"
|
||||||
|
const val STATS_LANGUAGE_PROGRESS = "stats/language_progress"
|
||||||
|
const val STATS_CATEGORY_DETAIL = "stats/category_detail"
|
||||||
|
const val STATS_CATEGORY_LIST = "stats/category_list_screen"
|
||||||
|
const val STATS_VOCABULARY_SORTING = "stats/vocabulary_sorting"
|
||||||
|
const val STATS_NO_GRAMMAR_ITEMS = "stats/no_grammar_items"
|
||||||
|
const val STATS_VOCABULARY_LIST = "stats/vocabulary_list"
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AppNavHost(
|
fun AppNavHost(
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
@@ -130,15 +146,15 @@ fun AppNavHost(
|
|||||||
HomeScreen(navController = navController)
|
HomeScreen(navController = navController)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable("new_word") {
|
composable(NavigationRoutes.NEW_WORD) {
|
||||||
NewWordScreen(navController = navController)
|
NewWordScreen(navController = navController)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable("new_word_review") {
|
composable(NavigationRoutes.NEW_WORD_REVIEW) {
|
||||||
NewWordReviewScreen(navController = navController)
|
NewWordReviewScreen(navController = navController)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable("start_exercise") {
|
composable(NavigationRoutes.START_EXERCISE) {
|
||||||
StartExerciseScreen(navController = navController)
|
StartExerciseScreen(navController = navController)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,7 +219,7 @@ fun NavGraphBuilder.libraryGraph(navController: NavHostController) {
|
|||||||
composable("vocabulary_list/{showDueTodayOnly}/{categoryId}") { backStackEntry ->
|
composable("vocabulary_list/{showDueTodayOnly}/{categoryId}") { backStackEntry ->
|
||||||
val showDueTodayOnly = backStackEntry.arguments?.getString("showDueTodayOnly")?.toBoolean() ?: false
|
val showDueTodayOnly = backStackEntry.arguments?.getString("showDueTodayOnly")?.toBoolean() ?: false
|
||||||
val categoryId = backStackEntry.arguments?.getString("categoryId")?.toIntOrNull()
|
val categoryId = backStackEntry.arguments?.getString("categoryId")?.toIntOrNull()
|
||||||
VocabularyListScreen(
|
AllCardsListScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
showDueTodayOnly = showDueTodayOnly,
|
showDueTodayOnly = showDueTodayOnly,
|
||||||
categoryId = categoryId,
|
categoryId = categoryId,
|
||||||
@@ -220,7 +236,7 @@ fun NavGraphBuilder.libraryGraph(navController: NavHostController) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
composable("vocabulary_heatmap") {
|
composable(NavigationRoutes.STATS_VOCABULARY_HEATMAP) {
|
||||||
VocabularyHeatmapScreen(
|
VocabularyHeatmapScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
)
|
)
|
||||||
@@ -232,7 +248,7 @@ fun NavGraphBuilder.libraryGraph(navController: NavHostController) {
|
|||||||
if (it.equals("null", ignoreCase = true)) null else VocabularyStage.valueOf(it)
|
if (it.equals("null", ignoreCase = true)) null else VocabularyStage.valueOf(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
VocabularyListScreen(
|
AllCardsListScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
showDueTodayOnly = showDueTodayOnly,
|
showDueTodayOnly = showDueTodayOnly,
|
||||||
stage = stage,
|
stage = stage,
|
||||||
@@ -373,7 +389,7 @@ fun NavGraphBuilder.statsGraph(
|
|||||||
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
|
||||||
val categoryId = backStackEntry.arguments?.getString("categoryId")?.toIntOrNull()
|
val categoryId = backStackEntry.arguments?.getString("categoryId")?.toIntOrNull()
|
||||||
VocabularyListScreen(
|
AllCardsListScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
showDueTodayOnly = showDueTodayOnly,
|
showDueTodayOnly = showDueTodayOnly,
|
||||||
categoryId = categoryId,
|
categoryId = categoryId,
|
||||||
@@ -384,12 +400,12 @@ fun NavGraphBuilder.statsGraph(
|
|||||||
enableNavigationButtons = true
|
enableNavigationButtons = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable("stats/language_progress") {
|
composable(NavigationRoutes.STATS_LANGUAGE_PROGRESS) {
|
||||||
LanguageProgressScreen(
|
LanguageProgressScreen(
|
||||||
navController = navController
|
navController = navController
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable("stats/vocabulary_heatmap") {
|
composable(NavigationRoutes.STATS_VOCABULARY_HEATMAP) {
|
||||||
VocabularyHeatmapScreen(
|
VocabularyHeatmapScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
)
|
)
|
||||||
@@ -401,7 +417,7 @@ fun NavGraphBuilder.statsGraph(
|
|||||||
if (it.equals("null", ignoreCase = true)) null else VocabularyStage.valueOf(it)
|
if (it.equals("null", ignoreCase = true)) null else VocabularyStage.valueOf(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
VocabularyListScreen(
|
AllCardsListScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
showDueTodayOnly = showDueTodayOnly,
|
showDueTodayOnly = showDueTodayOnly,
|
||||||
stage = stage,
|
stage = stage,
|
||||||
|
|||||||
@@ -43,9 +43,8 @@ import eu.gaudian.translator.view.hints.LocalShowHints
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AppTopAppBar(
|
fun AppTopAppBar(
|
||||||
title: String,
|
|
||||||
additionalContent: @Composable () -> Unit = {},
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
title: String,
|
||||||
onNavigateBack: (() -> Unit)? = null,
|
onNavigateBack: (() -> Unit)? = null,
|
||||||
navigationIcon: @Composable (() -> Unit)? = null,
|
navigationIcon: @Composable (() -> Unit)? = null,
|
||||||
actions: @Composable RowScope.() -> Unit = {},
|
actions: @Composable RowScope.() -> Unit = {},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import androidx.navigation.NavHostController
|
|||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.view.composable.AppButton
|
import eu.gaudian.translator.view.composable.AppButton
|
||||||
import eu.gaudian.translator.view.composable.AppTopAppBar
|
import eu.gaudian.translator.view.composable.AppTopAppBar
|
||||||
import eu.gaudian.translator.view.vocabulary.VocabularyListScreen
|
import eu.gaudian.translator.view.vocabulary.AllCardsListScreen
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ExerciseVocabularyScreen(
|
fun ExerciseVocabularyScreen(
|
||||||
@@ -41,7 +41,7 @@ fun ExerciseVocabularyScreen(
|
|||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
Box(modifier = Modifier.padding(paddingValues)) {
|
Box(modifier = Modifier.padding(paddingValues)) {
|
||||||
|
|
||||||
VocabularyListScreen(
|
AllCardsListScreen(
|
||||||
navController = navController as NavHostController?,
|
navController = navController as NavHostController?,
|
||||||
onNavigateToItem = { item ->
|
onNavigateToItem = { item ->
|
||||||
// Navigate to the detail screen for a specific vocabulary item
|
// Navigate to the detail screen for a specific vocabulary item
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package eu.gaudian.translator.view.home
|
package eu.gaudian.translator.view.home
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@@ -37,12 +38,14 @@ import androidx.compose.ui.draw.clip
|
|||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.utils.findActivity
|
import eu.gaudian.translator.utils.findActivity
|
||||||
|
import eu.gaudian.translator.view.NavigationRoutes
|
||||||
import eu.gaudian.translator.view.composable.AppCard
|
import eu.gaudian.translator.view.composable.AppCard
|
||||||
import eu.gaudian.translator.view.composable.Screen
|
import eu.gaudian.translator.view.composable.Screen
|
||||||
import eu.gaudian.translator.view.settings.SettingsRoutes
|
import eu.gaudian.translator.view.settings.SettingsRoutes
|
||||||
@@ -78,33 +81,36 @@ fun HomeScreen(
|
|||||||
verticalArrangement = Arrangement.spacedBy(15.dp)
|
verticalArrangement = Arrangement.spacedBy(15.dp)
|
||||||
) {
|
) {
|
||||||
item { Spacer(modifier = Modifier.height(16.dp)) }
|
item { Spacer(modifier = Modifier.height(16.dp)) }
|
||||||
item { TopProfileSection(navController = navController) }
|
item { TopProfileSection(
|
||||||
|
navController = navController,
|
||||||
|
context = LocalContext.current
|
||||||
|
) }
|
||||||
item {
|
item {
|
||||||
StreakAndGoalSection(
|
StreakAndGoalSection(
|
||||||
streak = streak,
|
streak = streak,
|
||||||
progress = progress,
|
progress = progress,
|
||||||
progressTitle = "$todayCompletedCount / $dailyGoal",
|
progressTitle = "$todayCompletedCount / $dailyGoal",
|
||||||
onGoalClick = { navController.navigate(SettingsRoutes.VOCABULARY_OPTIONS) },
|
onGoalClick = { navController.navigate(SettingsRoutes.VOCABULARY_OPTIONS) },
|
||||||
onStreakClick = { navController.navigate("stats/vocabulary_heatmap") }
|
onStreakClick = { navController.navigate(NavigationRoutes.STATS_VOCABULARY_HEATMAP) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
|
//TODO replace with actual implementation
|
||||||
|
@Suppress("HardCodedStringLiteral")
|
||||||
ActionCard(
|
ActionCard(
|
||||||
title = "Daily Review",
|
title = "Daily Review",
|
||||||
subtitle = "42 words need attention",
|
subtitle = "42 words need attention",
|
||||||
icon = Icons.Default.Psychology,
|
icon = Icons.Default.Psychology,
|
||||||
containerColor = MaterialTheme.colorScheme.primary,
|
|
||||||
contentColor = MaterialTheme.colorScheme.onPrimary
|
contentColor = MaterialTheme.colorScheme.onPrimary
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
ActionCard(
|
ActionCard(
|
||||||
title = "New Words",
|
title = stringResource(R.string.label_new_words),
|
||||||
subtitle = "Expand your vocabulary",
|
subtitle = stringResource(R.string.desc_expand_your_vocabulary),
|
||||||
icon = Icons.Default.AddCircle,
|
icon = Icons.Default.AddCircle,
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceVariant,
|
|
||||||
contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
onClick = { navController.navigate("new_word") }
|
onClick = { navController.navigate(NavigationRoutes.NEW_WORD) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
item { WeeklyProgressSection(navController = navController) }
|
item { WeeklyProgressSection(navController = navController) }
|
||||||
@@ -117,8 +123,8 @@ fun HomeScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TopProfileSection(navController: NavHostController) {
|
fun TopProfileSection(navController: NavHostController, context: Context) {
|
||||||
val context = LocalContext.current
|
|
||||||
val motivationalPhrases = remember {
|
val motivationalPhrases = remember {
|
||||||
context.resources.getStringArray(R.array.motivational_phrases)
|
context.resources.getStringArray(R.array.motivational_phrases)
|
||||||
}
|
}
|
||||||
@@ -126,7 +132,9 @@ fun TopProfileSection(navController: NavHostController) {
|
|||||||
|
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.fillMaxWidth().padding(8.dp)
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(8.dp)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
@@ -146,7 +154,7 @@ fun TopProfileSection(navController: NavHostController) {
|
|||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Settings,
|
imageVector = Icons.Default.Settings,
|
||||||
contentDescription = "Settings",
|
contentDescription = stringResource(R.string.label_settings),
|
||||||
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -169,8 +177,8 @@ fun StreakAndGoalSection(
|
|||||||
StatCard(
|
StatCard(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
icon = Icons.Default.LocalFireDepartment,
|
icon = Icons.Default.LocalFireDepartment,
|
||||||
title = "$streak Days",
|
title = stringResource(R.string.label_2d_days, streak),
|
||||||
subtitle = "CURRENT STREAK",
|
subtitle = stringResource(R.string.label_current_streak).uppercase(),
|
||||||
onClick = onStreakClick
|
onClick = onStreakClick
|
||||||
)
|
)
|
||||||
// Goal Card
|
// Goal Card
|
||||||
@@ -178,7 +186,7 @@ fun StreakAndGoalSection(
|
|||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
progress = progress,
|
progress = progress,
|
||||||
title = progressTitle,
|
title = progressTitle,
|
||||||
subtitle = "DAILY GOAL",
|
subtitle = stringResource(R.string.label_daily_goal).uppercase(),
|
||||||
onClick = onGoalClick
|
onClick = onGoalClick
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -293,7 +301,6 @@ fun ActionCard(
|
|||||||
title: String,
|
title: String,
|
||||||
subtitle: String,
|
subtitle: String,
|
||||||
icon: ImageVector,
|
icon: ImageVector,
|
||||||
containerColor: Color,
|
|
||||||
contentColor: Color,
|
contentColor: Color,
|
||||||
onClick: (() -> Unit)? = null
|
onClick: (() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
@@ -314,7 +321,7 @@ fun ActionCard(
|
|||||||
}
|
}
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.ChevronRight,
|
imageVector = Icons.Default.ChevronRight,
|
||||||
contentDescription = "Go",
|
contentDescription = stringResource(R.string.cd_go),
|
||||||
modifier = Modifier.size(24.dp)
|
modifier = Modifier.size(24.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -351,9 +358,9 @@ fun WeeklyProgressSection(
|
|||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Text(text = "Weekly Progress", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
|
Text(text = stringResource(R.string.label_weekly_progress), style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
|
||||||
TextButton(onClick = { navController.navigate("stats/vocabulary_heatmap") }) {
|
TextButton(onClick = { navController.navigate(NavigationRoutes.STATS_VOCABULARY_HEATMAP) }) {
|
||||||
Text("See History")
|
Text(stringResource(R.string.label_see_history))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,7 +368,7 @@ fun WeeklyProgressSection(
|
|||||||
|
|
||||||
AppCard(
|
AppCard(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
onClick = { navController.navigate("stats/vocabulary_heatmap") }
|
onClick = { navController.navigate(NavigationRoutes.STATS_VOCABULARY_HEATMAP) }
|
||||||
) {
|
) {
|
||||||
if (weeklyActivityStats.isEmpty()) {
|
if (weeklyActivityStats.isEmpty()) {
|
||||||
Column(
|
Column(
|
||||||
@@ -372,7 +379,7 @@ fun WeeklyProgressSection(
|
|||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "No activity data available",
|
text = stringResource(R.string.text_desc_no_activity_data_available),
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
@@ -403,7 +410,7 @@ fun BottomStatsSection(
|
|||||||
onClick = { navController.navigate(Screen.Library.route) }
|
onClick = { navController.navigate(Screen.Library.route) }
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.padding(20.dp)) {
|
Column(modifier = Modifier.padding(20.dp)) {
|
||||||
Text(text = "TOTAL WORDS", style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f))
|
Text(text = stringResource(R.string.label_total_words).uppercase(), style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f))
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
Text(text = totalWords.toString(), style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold)
|
Text(text = totalWords.toString(), style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold)
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
@@ -413,7 +420,7 @@ fun BottomStatsSection(
|
|||||||
// Learned
|
// Learned
|
||||||
AppCard(
|
AppCard(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
onClick = { navController.navigate("stats/language_progress") }
|
onClick = { navController.navigate(NavigationRoutes.STATS_LANGUAGE_PROGRESS) }
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.padding(20.dp)) {
|
Column(modifier = Modifier.padding(20.dp)) {
|
||||||
Text(text = "LEARNED", style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f))
|
Text(text = "LEARNED", style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@file:Suppress("HardCodedStringLiteral")
|
@file:Suppress("AssignedValueIsNeverRead")
|
||||||
|
|
||||||
package eu.gaudian.translator.view.library
|
package eu.gaudian.translator.view.library
|
||||||
|
|
||||||
@@ -44,6 +44,7 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
@@ -61,8 +62,10 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
|||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
|
import eu.gaudian.translator.R.string.text_add_new_word_to_list
|
||||||
import eu.gaudian.translator.model.VocabularyStage
|
import eu.gaudian.translator.model.VocabularyStage
|
||||||
import eu.gaudian.translator.utils.findActivity
|
import eu.gaudian.translator.utils.findActivity
|
||||||
|
import eu.gaudian.translator.view.NavigationRoutes
|
||||||
import eu.gaudian.translator.view.composable.AppIcons
|
import eu.gaudian.translator.view.composable.AppIcons
|
||||||
import eu.gaudian.translator.view.composable.AppSwitch
|
import eu.gaudian.translator.view.composable.AppSwitch
|
||||||
import eu.gaudian.translator.view.composable.BottomSheetMenuItem
|
import eu.gaudian.translator.view.composable.BottomSheetMenuItem
|
||||||
@@ -70,7 +73,6 @@ import eu.gaudian.translator.view.composable.MultipleLanguageDropdown
|
|||||||
import eu.gaudian.translator.view.dialogs.AddCategoryDialog
|
import eu.gaudian.translator.view.dialogs.AddCategoryDialog
|
||||||
import eu.gaudian.translator.view.dialogs.CategorySelectionDialog
|
import eu.gaudian.translator.view.dialogs.CategorySelectionDialog
|
||||||
import eu.gaudian.translator.view.dialogs.StageSelectionDialog
|
import eu.gaudian.translator.view.dialogs.StageSelectionDialog
|
||||||
import eu.gaudian.translator.viewmodel.CategoryViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.LanguageConfigViewModel
|
import eu.gaudian.translator.viewmodel.LanguageConfigViewModel
|
||||||
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
||||||
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
||||||
@@ -99,8 +101,6 @@ fun LibraryScreen(
|
|||||||
val lazyListState = rememberLazyListState()
|
val lazyListState = rememberLazyListState()
|
||||||
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val categoryViewModel: CategoryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
var filterState by rememberSaveable { mutableStateOf(LibraryFilterState()) }
|
var filterState by rememberSaveable { mutableStateOf(LibraryFilterState()) }
|
||||||
var showFilterSheet by remember { mutableStateOf(false) }
|
var showFilterSheet by remember { mutableStateOf(false) }
|
||||||
@@ -135,8 +135,8 @@ fun LibraryScreen(
|
|||||||
val vocabularyItems by vocabularyItemsFlow.collectAsStateWithLifecycle(initialValue = emptyList())
|
val vocabularyItems by vocabularyItemsFlow.collectAsStateWithLifecycle(initialValue = emptyList())
|
||||||
|
|
||||||
var isHeaderVisible by remember { mutableStateOf(true) }
|
var isHeaderVisible by remember { mutableStateOf(true) }
|
||||||
var previousIndex by remember { mutableStateOf(0) }
|
var previousIndex by remember { mutableIntStateOf(0) }
|
||||||
var previousScrollOffset by remember { mutableStateOf(0) }
|
var previousScrollOffset by remember { mutableIntStateOf(0) }
|
||||||
|
|
||||||
// Set navigation context when vocabulary items are loaded
|
// Set navigation context when vocabulary items are loaded
|
||||||
LaunchedEffect(vocabularyItems) {
|
LaunchedEffect(vocabularyItems) {
|
||||||
@@ -229,10 +229,11 @@ fun LibraryScreen(
|
|||||||
CategoriesView(
|
CategoriesView(
|
||||||
categories = categories,
|
categories = categories,
|
||||||
onCategoryClick = { category ->
|
onCategoryClick = { category ->
|
||||||
navController.navigate("category_detail/${category.id}")
|
@Suppress("HardCodedStringLiteral")
|
||||||
|
navController.navigate("${NavigationRoutes.CATEGORY_DETAIL}/${category.id}")
|
||||||
},
|
},
|
||||||
onExploreMoreClick = {
|
onExploreMoreClick = {
|
||||||
navController.navigate("category_list_screen")
|
navController.navigate(NavigationRoutes.CATEGORY_LIST)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -251,7 +252,8 @@ fun LibraryScreen(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
vocabularyViewModel.setNavigationContext(vocabularyItems, item.id)
|
vocabularyViewModel.setNavigationContext(vocabularyItems, item.id)
|
||||||
navController.navigate("vocabulary_detail/${item.id}")
|
@Suppress("HardCodedStringLiteral")
|
||||||
|
navController.navigate("${NavigationRoutes.VOCABULARY_DETAIL}/${item.id}")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onItemLongClick = { item ->
|
onItemLongClick = { item ->
|
||||||
@@ -287,7 +289,7 @@ fun LibraryScreen(
|
|||||||
modifier = Modifier.size(50.dp),
|
modifier = Modifier.size(50.dp),
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||||
) {
|
) {
|
||||||
Icon(AppIcons.ArrowCircleUp, contentDescription = "Scroll to top")
|
Icon(AppIcons.ArrowCircleUp, contentDescription = stringResource(R.string.cd_scroll_to_top))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -344,17 +346,17 @@ fun LibraryScreen(
|
|||||||
BottomSheetMenuItem(
|
BottomSheetMenuItem(
|
||||||
icon = Icons.Rounded.Add, // Or any custom vector icon you prefer
|
icon = Icons.Rounded.Add, // Or any custom vector icon you prefer
|
||||||
title = stringResource(R.string.label_add_vocabulary),
|
title = stringResource(R.string.label_add_vocabulary),
|
||||||
subtitle = "Füge ein neues Wort zu deiner Liste hinzu", // Suggest adding this to strings.xml
|
subtitle = stringResource(text_add_new_word_to_list), // Suggest adding this to strings.xml
|
||||||
onClick = {
|
onClick = {
|
||||||
showAddMenu = false
|
showAddMenu = false
|
||||||
navController.navigate("new_word")
|
navController.navigate(NavigationRoutes.NEW_WORD)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
BottomSheetMenuItem(
|
BottomSheetMenuItem(
|
||||||
icon = Icons.Rounded.Folder,
|
icon = Icons.Rounded.Folder,
|
||||||
title = stringResource(R.string.label_add_category),
|
title = stringResource(R.string.label_add_category),
|
||||||
subtitle = "Organisiere deine Vokabeln in Gruppen", // Suggest adding this to strings.xml
|
subtitle = stringResource(R.string.text_desc_organize_vocabulary_groups), // Suggest adding this to strings.xml
|
||||||
onClick = {
|
onClick = {
|
||||||
showAddMenu = false
|
showAddMenu = false
|
||||||
showAddCategoryDialog = true
|
showAddCategoryDialog = true
|
||||||
@@ -418,7 +420,7 @@ fun FilterBottomSheetContent(
|
|||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "Filter Cards",
|
text = stringResource(R.string.label_filter_cards),
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
@@ -430,7 +432,7 @@ fun FilterBottomSheetContent(
|
|||||||
sortOrder = SortOrder.NEWEST_FIRST
|
sortOrder = SortOrder.NEWEST_FIRST
|
||||||
onResetClick()
|
onResetClick()
|
||||||
}) {
|
}) {
|
||||||
Text("Reset")
|
Text(stringResource(R.string.label_reset))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,7 +448,7 @@ fun FilterBottomSheetContent(
|
|||||||
// Sort Order
|
// Sort Order
|
||||||
Column {
|
Column {
|
||||||
Text(
|
Text(
|
||||||
text = "SORT BY",
|
text = stringResource(R.string.label_sort_by).uppercase(),
|
||||||
style = MaterialTheme.typography.labelMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
letterSpacing = 1.sp,
|
letterSpacing = 1.sp,
|
||||||
@@ -589,7 +591,7 @@ fun FilterBottomSheetContent(
|
|||||||
shape = RoundedCornerShape(28.dp)
|
shape = RoundedCornerShape(28.dp)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "Apply Filters",
|
text = stringResource(R.string.label_apply_filters),
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@file:Suppress("HardCodedStringLiteral", "AssignedValueIsNeverRead", "UnusedMaterial3ScaffoldPaddingParameter")
|
@file:Suppress("AssignedValueIsNeverRead")
|
||||||
|
|
||||||
package eu.gaudian.translator.view.stats
|
package eu.gaudian.translator.view.stats
|
||||||
|
|
||||||
@@ -56,8 +56,8 @@ import androidx.navigation.compose.rememberNavController
|
|||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.VocabularyStage
|
import eu.gaudian.translator.model.VocabularyStage
|
||||||
import eu.gaudian.translator.model.WidgetType
|
import eu.gaudian.translator.model.WidgetType
|
||||||
import eu.gaudian.translator.utils.Log
|
|
||||||
import eu.gaudian.translator.utils.findActivity
|
import eu.gaudian.translator.utils.findActivity
|
||||||
|
import eu.gaudian.translator.view.NavigationRoutes
|
||||||
import eu.gaudian.translator.view.composable.AppCard
|
import eu.gaudian.translator.view.composable.AppCard
|
||||||
import eu.gaudian.translator.view.composable.AppIcons
|
import eu.gaudian.translator.view.composable.AppIcons
|
||||||
import eu.gaudian.translator.view.composable.AppOutlinedCard
|
import eu.gaudian.translator.view.composable.AppOutlinedCard
|
||||||
@@ -82,14 +82,11 @@ import kotlinx.coroutines.launch
|
|||||||
@SuppressLint("FrequentlyChangingValue")
|
@SuppressLint("FrequentlyChangingValue")
|
||||||
@Composable
|
@Composable
|
||||||
fun StatsScreen(
|
fun StatsScreen(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
onShowCustomExerciseDialog: () -> Unit = {},
|
|
||||||
startDailyExercise: (Boolean) -> Unit = {},
|
|
||||||
onNavigateToCategoryDetail: ((Int) -> Unit)? = null,
|
onNavigateToCategoryDetail: ((Int) -> Unit)? = null,
|
||||||
onNavigateToCategoryList: (() -> Unit)? = null,
|
onNavigateToCategoryList: (() -> Unit)? = null,
|
||||||
onShowWordPairExerciseDialog: () -> Unit = {},
|
|
||||||
onScroll: (Boolean) -> Unit = {},
|
onScroll: (Boolean) -> Unit = {},
|
||||||
modifier: Modifier = Modifier
|
|
||||||
) {
|
) {
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
@@ -128,10 +125,11 @@ fun StatsScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val handleNavigateToCategoryDetail = onNavigateToCategoryDetail ?: { categoryId ->
|
val handleNavigateToCategoryDetail = onNavigateToCategoryDetail ?: { categoryId ->
|
||||||
navController.navigate("stats/category_detail/$categoryId")
|
@Suppress("HardCodedStringLiteral")
|
||||||
|
navController.navigate("${NavigationRoutes.STATS_CATEGORY_DETAIL}/$categoryId")
|
||||||
}
|
}
|
||||||
val handleNavigateToCategoryList = onNavigateToCategoryList ?: {
|
val handleNavigateToCategoryList = onNavigateToCategoryList ?: {
|
||||||
navController.navigate("stats/category_list_screen")
|
navController.navigate(NavigationRoutes.STATS_CATEGORY_LIST)
|
||||||
}
|
}
|
||||||
|
|
||||||
AppOutlinedCard(modifier = modifier) {
|
AppOutlinedCard(modifier = modifier) {
|
||||||
@@ -270,11 +268,8 @@ fun StatsScreen(
|
|||||||
navController = navController,
|
navController = navController,
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
vocabularyViewModel = vocabularyViewModel,
|
||||||
progressViewModel = progressViewModel,
|
progressViewModel = progressViewModel,
|
||||||
onShowCustomExerciseDialog = onShowCustomExerciseDialog,
|
|
||||||
startDailyExercise = startDailyExercise,
|
|
||||||
onNavigateToCategoryDetail = handleNavigateToCategoryDetail,
|
onNavigateToCategoryDetail = handleNavigateToCategoryDetail,
|
||||||
onNavigateToCategoryList = handleNavigateToCategoryList,
|
onNavigateToCategoryList = handleNavigateToCategoryList,
|
||||||
onShowWordPairExerciseDialog = onShowWordPairExerciseDialog,
|
|
||||||
onMissingLanguage = { missingId ->
|
onMissingLanguage = { missingId ->
|
||||||
selectedMissingLanguageId = missingId
|
selectedMissingLanguageId = missingId
|
||||||
showMissingLanguageDialog = true
|
showMissingLanguageDialog = true
|
||||||
@@ -524,17 +519,15 @@ class DragDropState(
|
|||||||
// Remainder of your existing components
|
// Remainder of your existing components
|
||||||
// --------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Suppress("HardCodedStringLiteral")
|
||||||
@Composable
|
@Composable
|
||||||
private fun LazyWidget(
|
private fun LazyWidget(
|
||||||
widgetType: WidgetType,
|
widgetType: WidgetType,
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
vocabularyViewModel: VocabularyViewModel,
|
vocabularyViewModel: VocabularyViewModel,
|
||||||
progressViewModel: ProgressViewModel,
|
progressViewModel: ProgressViewModel,
|
||||||
onShowCustomExerciseDialog: () -> Unit,
|
|
||||||
startDailyExercise: (Boolean) -> Unit,
|
|
||||||
onNavigateToCategoryDetail: (Int) -> Unit,
|
onNavigateToCategoryDetail: (Int) -> Unit,
|
||||||
onNavigateToCategoryList: () -> Unit,
|
onNavigateToCategoryList: () -> Unit,
|
||||||
onShowWordPairExerciseDialog: () -> Unit,
|
|
||||||
onMissingLanguage: (Int) -> Unit
|
onMissingLanguage: (Int) -> Unit
|
||||||
) {
|
) {
|
||||||
when (widgetType) {
|
when (widgetType) {
|
||||||
@@ -542,10 +535,10 @@ private fun LazyWidget(
|
|||||||
|
|
||||||
WidgetType.Status -> LazyStatusWidget(
|
WidgetType.Status -> LazyStatusWidget(
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
vocabularyViewModel = vocabularyViewModel,
|
||||||
onNavigateToNew = { navController.navigate("stats/vocabulary_sorting?mode=NEW") },
|
onNavigateToNew = { navController.navigate("${NavigationRoutes.STATS_VOCABULARY_SORTING}?mode=NEW") },
|
||||||
onNavigateToDuplicates = { navController.navigate("stats/vocabulary_sorting?mode=DUPLICATES") },
|
onNavigateToDuplicates = { navController.navigate("${NavigationRoutes.STATS_VOCABULARY_SORTING}?mode=DUPLICATES") },
|
||||||
onNavigateToFaulty = { navController.navigate("stats/vocabulary_sorting?mode=FAULTY") },
|
onNavigateToFaulty = { navController.navigate("${NavigationRoutes.STATS_VOCABULARY_SORTING}?mode=FAULTY") },
|
||||||
onNavigateToNoGrammar = { navController.navigate("stats/no_grammar_items") },
|
onNavigateToNoGrammar = { navController.navigate(NavigationRoutes.STATS_NO_GRAMMAR_ITEMS) },
|
||||||
onNavigateToMissingLanguage = onMissingLanguage
|
onNavigateToMissingLanguage = onMissingLanguage
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -557,7 +550,7 @@ private fun LazyWidget(
|
|||||||
lastSevenDays = progressViewModel.lastSevenDays.collectAsState().value,
|
lastSevenDays = progressViewModel.lastSevenDays.collectAsState().value,
|
||||||
dueTodayCount = progressViewModel.dueTodayCount.collectAsState().value,
|
dueTodayCount = progressViewModel.dueTodayCount.collectAsState().value,
|
||||||
wordsCompleted = progressViewModel.totalWordsCompleted.collectAsState().value,
|
wordsCompleted = progressViewModel.totalWordsCompleted.collectAsState().value,
|
||||||
onStatisticsClicked = { navController.navigate("stats/vocabulary_heatmap") }
|
onStatisticsClicked = { navController.navigate(NavigationRoutes.STATS_VOCABULARY_HEATMAP) }
|
||||||
)
|
)
|
||||||
|
|
||||||
WidgetType.WeeklyActivityChart -> WeeklyActivityChartWidget(
|
WidgetType.WeeklyActivityChart -> WeeklyActivityChartWidget(
|
||||||
@@ -566,13 +559,19 @@ private fun LazyWidget(
|
|||||||
|
|
||||||
WidgetType.AllVocabulary -> AllVocabularyWidget(
|
WidgetType.AllVocabulary -> AllVocabularyWidget(
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
vocabularyViewModel = vocabularyViewModel,
|
||||||
onOpenAllVocabulary = { navController.navigate("stats/vocabulary_list/false/null") },
|
onOpenAllVocabulary = { navController.navigate("${NavigationRoutes.STATS_VOCABULARY_LIST}/false/null") },
|
||||||
onStageClicked = { stage -> navController.navigate("stats/vocabulary_list/false/$stage") }
|
onStageClicked = { stage ->
|
||||||
|
@Suppress("HardCodedStringLiteral")
|
||||||
|
navController.navigate("${NavigationRoutes.STATS_VOCABULARY_LIST}/false/$stage")
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
WidgetType.DueToday -> DueTodayWidget(
|
WidgetType.DueToday -> DueTodayWidget(
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
vocabularyViewModel = vocabularyViewModel,
|
||||||
onStageClicked = { stage -> navController.navigate("stats/vocabulary_list/false/$stage") }
|
onStageClicked = { stage ->
|
||||||
|
@Suppress("HardCodedStringLiteral")
|
||||||
|
navController.navigate("${NavigationRoutes.STATS_VOCABULARY_LIST}/false/$stage")
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
WidgetType.CategoryProgress -> CategoryProgressWidget(
|
WidgetType.CategoryProgress -> CategoryProgressWidget(
|
||||||
@@ -586,7 +585,7 @@ private fun LazyWidget(
|
|||||||
totalWords = vocabularyViewModel.vocabularyItems.collectAsState().value.size,
|
totalWords = vocabularyViewModel.vocabularyItems.collectAsState().value.size,
|
||||||
learnedWords = vocabularyViewModel.stageStats.collectAsState().value
|
learnedWords = vocabularyViewModel.stageStats.collectAsState().value
|
||||||
.firstOrNull { it.stage == VocabularyStage.LEARNED }?.itemCount ?: 0,
|
.firstOrNull { it.stage == VocabularyStage.LEARNED }?.itemCount ?: 0,
|
||||||
onNavigateToProgress = { navController.navigate("stats/language_progress") }
|
onNavigateToProgress = { navController.navigate(NavigationRoutes.STATS_LANGUAGE_PROGRESS) }
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -649,11 +648,8 @@ fun StatsScreenPreview() {
|
|||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
StatsScreen(
|
StatsScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
onShowCustomExerciseDialog = {},
|
|
||||||
onNavigateToCategoryDetail = {},
|
onNavigateToCategoryDetail = {},
|
||||||
startDailyExercise = {},
|
|
||||||
onNavigateToCategoryList = {},
|
onNavigateToCategoryList = {},
|
||||||
onShowWordPairExerciseDialog = {},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -675,6 +671,7 @@ fun WidgetContainerPreview() {
|
|||||||
.padding(16.dp),
|
.padding(16.dp),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
|
@Suppress("HardCodedStringLiteral")
|
||||||
Text("Preview Content")
|
Text("Preview Content")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ fun CategoryDetailScreen(
|
|||||||
}
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
Column(modifier = Modifier.padding(paddingValues)) {
|
Column(modifier = Modifier.padding(paddingValues)) {
|
||||||
VocabularyListScreen(
|
AllCardsListScreen(
|
||||||
categoryId = categoryId,
|
categoryId = categoryId,
|
||||||
showDueTodayOnly = false,
|
showDueTodayOnly = false,
|
||||||
onNavigateToItem = onNavigateToItem,
|
onNavigateToItem = onNavigateToItem,
|
||||||
@@ -324,8 +324,8 @@ fun CategoryHeaderCardWithProgressPreview() {
|
|||||||
MaterialTheme {
|
MaterialTheme {
|
||||||
CategoryHeaderCard(
|
CategoryHeaderCard(
|
||||||
subtitle = "Travel Vocabulary",
|
subtitle = "Travel Vocabulary",
|
||||||
categoryProgress = eu.gaudian.translator.viewmodel.CategoryProgress(
|
categoryProgress = CategoryProgress(
|
||||||
vocabularyCategory = eu.gaudian.translator.model.TagCategory(
|
vocabularyCategory = TagCategory(
|
||||||
1,
|
1,
|
||||||
"Travel"
|
"Travel"
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -80,8 +80,8 @@ fun NoGrammarItemsScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Use the generic VocabularyListScreen to display the items
|
// Use the generic AllCardsListScreen to display the items
|
||||||
VocabularyListScreen(
|
AllCardsListScreen(
|
||||||
itemsToShow = itemsWithoutGrammar,
|
itemsToShow = itemsWithoutGrammar,
|
||||||
onNavigateToItem = { item ->
|
onNavigateToItem = { item ->
|
||||||
@Suppress("HardCodedStringLiteral")
|
@Suppress("HardCodedStringLiteral")
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ fun StageDetailScreen(
|
|||||||
onStageTapped = {},
|
onStageTapped = {},
|
||||||
)
|
)
|
||||||
|
|
||||||
VocabularyListScreen(
|
AllCardsListScreen(
|
||||||
categoryId = null,
|
categoryId = null,
|
||||||
showDueTodayOnly = true,
|
showDueTodayOnly = true,
|
||||||
stage = stage,
|
stage = stage,
|
||||||
|
|||||||
@@ -5,19 +5,13 @@ package eu.gaudian.translator.view.vocabulary
|
|||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
import androidx.compose.animation.animateContentSize
|
|
||||||
import androidx.compose.foundation.BorderStroke
|
|
||||||
import androidx.compose.foundation.Image
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.combinedClickable
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.FlowRow
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
@@ -25,14 +19,10 @@ import androidx.compose.foundation.layout.navigationBarsPadding
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.items
|
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.text.BasicTextField
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.CardDefaults
|
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.FabPosition
|
import androidx.compose.material3.FabPosition
|
||||||
@@ -43,7 +33,6 @@ import androidx.compose.material3.Icon
|
|||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ModalBottomSheet
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.OutlinedCard
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
@@ -59,20 +48,15 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.SolidColor
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.rememberNavController
|
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.Language
|
import eu.gaudian.translator.model.Language
|
||||||
import eu.gaudian.translator.model.VocabularyItem
|
import eu.gaudian.translator.model.VocabularyItem
|
||||||
@@ -84,10 +68,10 @@ import eu.gaudian.translator.view.composable.AppScaffold
|
|||||||
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.AppTopAppBar
|
||||||
import eu.gaudian.translator.view.composable.MultipleLanguageDropdown
|
import eu.gaudian.translator.view.composable.MultipleLanguageDropdown
|
||||||
import eu.gaudian.translator.view.composable.insertBreakOpportunities
|
|
||||||
import eu.gaudian.translator.view.dialogs.CategoryDropdown
|
import eu.gaudian.translator.view.dialogs.CategoryDropdown
|
||||||
import eu.gaudian.translator.view.dialogs.CategorySelectionDialog
|
import eu.gaudian.translator.view.dialogs.CategorySelectionDialog
|
||||||
import eu.gaudian.translator.view.dialogs.StageSelectionDialog
|
import eu.gaudian.translator.view.dialogs.StageSelectionDialog
|
||||||
|
import eu.gaudian.translator.view.library.AllCardsView
|
||||||
import eu.gaudian.translator.viewmodel.CategoryViewModel
|
import eu.gaudian.translator.viewmodel.CategoryViewModel
|
||||||
import eu.gaudian.translator.viewmodel.LanguageConfigViewModel
|
import eu.gaudian.translator.viewmodel.LanguageConfigViewModel
|
||||||
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
||||||
@@ -110,7 +94,7 @@ private data class VocabularyFilterState(
|
|||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun VocabularyListScreen(
|
fun AllCardsListScreen(
|
||||||
categoryId: Int? = null,
|
categoryId: Int? = null,
|
||||||
showDueTodayOnly: Boolean? = null,
|
showDueTodayOnly: Boolean? = null,
|
||||||
stage: VocabularyStage? = null,
|
stage: VocabularyStage? = null,
|
||||||
@@ -245,10 +229,6 @@ fun VocabularyListScreen(
|
|||||||
)
|
)
|
||||||
|
|
||||||
"search" -> SearchTopAppBar(
|
"search" -> SearchTopAppBar(
|
||||||
searchQuery = filterState.searchQuery,
|
|
||||||
onQueryChanged = { newQuery ->
|
|
||||||
filterState = filterState.copy(searchQuery = newQuery)
|
|
||||||
},
|
|
||||||
onCloseSearch = {
|
onCloseSearch = {
|
||||||
isSearchActive = false
|
isSearchActive = false
|
||||||
filterState = filterState.copy(searchQuery = "")
|
filterState = filterState.copy(searchQuery = "")
|
||||||
@@ -295,78 +275,40 @@ fun VocabularyListScreen(
|
|||||||
floatingActionButtonPosition = FabPosition.Center
|
floatingActionButtonPosition = FabPosition.Center
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
Column(modifier = Modifier.padding(paddingValues).padding(bottom = 0.dp)) {
|
Column(modifier = Modifier.padding(paddingValues).padding(bottom = 0.dp)) {
|
||||||
if (vocabularyItems.isEmpty()) {
|
AllCardsView(
|
||||||
Column(
|
vocabularyItems = vocabularyItems,
|
||||||
modifier = Modifier
|
allLanguages = allLanguages,
|
||||||
.fillMaxSize()
|
selection = selection,
|
||||||
.padding(16.dp),
|
listState = lazyListState,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
modifier = Modifier
|
||||||
) {
|
|
||||||
Image(
|
|
||||||
modifier = Modifier.size(200.dp),
|
|
||||||
painter = painterResource(id = R.drawable.ic_nothing_found),
|
|
||||||
contentDescription = stringResource(id = R.string.no_vocabulary_items_found_perhaps_try_changing_the_filters)
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.size(16.dp))
|
|
||||||
|
|
||||||
Box(modifier = Modifier
|
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(8.dp), contentAlignment = Alignment.Center) {
|
.padding(horizontal = 8.dp),
|
||||||
Text(
|
onItemClick = { item ->
|
||||||
text = stringResource(R.string.no_vocabulary_items_found_perhaps_try_changing_the_filters),
|
val isSelected = selection.contains(item.id.toLong())
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
if (isInSelectionMode) {
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
selection = if (isSelected) {
|
||||||
textAlign = androidx.compose.ui.text.style.TextAlign.Center
|
selection - item.id.toLong()
|
||||||
)
|
} else {
|
||||||
}
|
selection + item.id.toLong()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LazyColumn(
|
if (navController != null && enableNavigationButtons) {
|
||||||
state = lazyListState,
|
vocabularyViewModel.setNavigationContext(vocabularyItems, item.id)
|
||||||
modifier = Modifier
|
navController.navigate("vocabulary_detail/${item.id}")
|
||||||
.fillMaxSize()
|
} else {
|
||||||
.padding(horizontal = 8.dp),
|
onNavigateToItem(item)
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
}
|
||||||
contentPadding = PaddingValues(vertical = 0.dp)
|
|
||||||
) {
|
|
||||||
items(
|
|
||||||
items = vocabularyItems,
|
|
||||||
key = { it.id }
|
|
||||||
) { item ->
|
|
||||||
val isSelected = selection.contains(item.id.toLong())
|
|
||||||
VocabularyListItem(
|
|
||||||
item = item,
|
|
||||||
allLanguages = allLanguages,
|
|
||||||
isSelected = isSelected,
|
|
||||||
onItemClick = {
|
|
||||||
if (isInSelectionMode) {
|
|
||||||
selection = if (isSelected) {
|
|
||||||
selection - item.id.toLong()
|
|
||||||
} else {
|
|
||||||
selection + item.id.toLong()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (navController != null && enableNavigationButtons) {
|
|
||||||
vocabularyViewModel.setNavigationContext(vocabularyItems, item.id)
|
|
||||||
navController.navigate("vocabulary_detail/${item.id}")
|
|
||||||
} else {
|
|
||||||
onNavigateToItem(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onItemLongClick = {
|
|
||||||
if (!isInSelectionMode) {
|
|
||||||
selection = setOf(item.id.toLong())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDeleteClick = {
|
|
||||||
vocabularyViewModel.deleteData(VocabularyViewModel.DeleteType.VOCABULARY_ITEM, item = item)
|
|
||||||
},
|
|
||||||
modifier = Modifier.animateItem()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
onItemLongClick = { item ->
|
||||||
|
if (!isInSelectionMode) {
|
||||||
|
selection = setOf(item.id.toLong())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDeleteClick = { item ->
|
||||||
|
vocabularyViewModel.deleteData(VocabularyViewModel.DeleteType.VOCABULARY_ITEM, item = item)
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -382,8 +324,7 @@ fun VocabularyListScreen(
|
|||||||
languageViewModel = languageViewModel,
|
languageViewModel = languageViewModel,
|
||||||
languagesPresent = allLanguages.filter { it.nameResId in languagesPresent },
|
languagesPresent = allLanguages.filter { it.nameResId in languagesPresent },
|
||||||
hideCategory = categoryId != null && categoryId != 0,
|
hideCategory = categoryId != null && categoryId != 0,
|
||||||
hideStage = stage != null,
|
hideStage = stage != null
|
||||||
categoryViewModel = categoryViewModel
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,21 +358,34 @@ fun VocabularyListScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ThemePreviews
|
@Deprecated("Use AllCardsListScreen which renders AllCardsView")
|
||||||
@Composable
|
@Composable
|
||||||
fun VocabularyListScreenPreview() {
|
fun VocabularyListScreen(
|
||||||
val navController = rememberNavController()
|
categoryId: Int? = null,
|
||||||
VocabularyListScreen(
|
showDueTodayOnly: Boolean? = null,
|
||||||
categoryId = 1,
|
stage: VocabularyStage? = null,
|
||||||
showDueTodayOnly = false,
|
onNavigateToItem: (VocabularyItem) -> Unit?,
|
||||||
stage = VocabularyStage.NEW,
|
onNavigateBack: (() -> Unit)? = null,
|
||||||
onNavigateToItem = {},
|
navController: NavHostController? = null,
|
||||||
onNavigateBack = {},
|
itemsToShow: List<VocabularyItem> = emptyList(),
|
||||||
navController = navController
|
isRemoveFromCategoryEnabled: Boolean = false,
|
||||||
|
showTopBar: Boolean = true,
|
||||||
|
enableNavigationButtons: Boolean = false
|
||||||
|
) {
|
||||||
|
AllCardsListScreen(
|
||||||
|
categoryId = categoryId,
|
||||||
|
showDueTodayOnly = showDueTodayOnly,
|
||||||
|
stage = stage,
|
||||||
|
onNavigateToItem = onNavigateToItem,
|
||||||
|
onNavigateBack = onNavigateBack,
|
||||||
|
navController = navController,
|
||||||
|
itemsToShow = itemsToShow,
|
||||||
|
isRemoveFromCategoryEnabled = isRemoveFromCategoryEnabled,
|
||||||
|
showTopBar = showTopBar,
|
||||||
|
enableNavigationButtons = enableNavigationButtons
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DefaultTopAppBar(
|
private fun DefaultTopAppBar(
|
||||||
title: String,
|
title: String,
|
||||||
@@ -505,8 +459,6 @@ private fun DefaultTopAppBar(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SearchTopAppBar(
|
private fun SearchTopAppBar(
|
||||||
searchQuery: String,
|
|
||||||
onQueryChanged: (String) -> Unit,
|
|
||||||
onCloseSearch: () -> Unit
|
onCloseSearch: () -> Unit
|
||||||
) {
|
) {
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
@@ -518,37 +470,6 @@ private fun SearchTopAppBar(
|
|||||||
AppTopAppBar(
|
AppTopAppBar(
|
||||||
modifier = Modifier.height(56.dp),
|
modifier = Modifier.height(56.dp),
|
||||||
title = "TODO",
|
title = "TODO",
|
||||||
additionalContent = {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.fillMaxHeight(),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
BasicTextField(
|
|
||||||
value = searchQuery,
|
|
||||||
onValueChange = onQueryChanged,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.focusRequester(focusRequester),
|
|
||||||
textStyle = MaterialTheme.typography.bodyLarge.copy(
|
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
|
||||||
),
|
|
||||||
singleLine = true,
|
|
||||||
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
|
|
||||||
decorationBox = { innerTextField ->
|
|
||||||
Box(contentAlignment = Alignment.CenterStart) {
|
|
||||||
if (searchQuery.isEmpty()) {
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.search_vocabulary),
|
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
|
||||||
)
|
|
||||||
}
|
|
||||||
innerTextField()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(onClick = onCloseSearch) {
|
IconButton(onClick = onCloseSearch) {
|
||||||
Icon(
|
Icon(
|
||||||
@@ -566,8 +487,6 @@ private fun SearchTopAppBar(
|
|||||||
@Composable
|
@Composable
|
||||||
fun SearchTopAppBarPreview() {
|
fun SearchTopAppBarPreview() {
|
||||||
SearchTopAppBar(
|
SearchTopAppBar(
|
||||||
searchQuery = stringResource(R.string.search_query),
|
|
||||||
onQueryChanged = {},
|
|
||||||
onCloseSearch = {}
|
onCloseSearch = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -670,112 +589,6 @@ fun ContextualTopAppBarPreview() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun VocabularyListItem(
|
|
||||||
item: VocabularyItem,
|
|
||||||
allLanguages: List<Language>,
|
|
||||||
isSelected: Boolean,
|
|
||||||
onItemClick: () -> Unit,
|
|
||||||
onItemLongClick: () -> Unit,
|
|
||||||
onDeleteClick: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier
|
|
||||||
) {
|
|
||||||
val languageMap = remember(allLanguages) { allLanguages.associateBy { it.nameResId } }
|
|
||||||
val langFirst = item.languageFirstId?.let { languageMap[it]?.name } ?: ""
|
|
||||||
val langSecond = item.languageSecondId?.let { languageMap[it]?.name } ?: ""
|
|
||||||
|
|
||||||
OutlinedCard(
|
|
||||||
modifier = modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.clip(CardDefaults.shape)
|
|
||||||
.combinedClickable(
|
|
||||||
onClick = onItemClick,
|
|
||||||
onLongClick = onItemLongClick
|
|
||||||
)
|
|
||||||
.animateContentSize(),
|
|
||||||
elevation = CardDefaults.cardElevation(defaultElevation = if (isSelected) 4.dp else 0.dp),
|
|
||||||
colors = CardDefaults.cardColors(
|
|
||||||
containerColor = if (isSelected) MaterialTheme.colorScheme.primaryContainer
|
|
||||||
else MaterialTheme.colorScheme.surface
|
|
||||||
),
|
|
||||||
border = BorderStroke(
|
|
||||||
width = if (isSelected) 1.5.dp else 1.dp,
|
|
||||||
color = if (isSelected) MaterialTheme.colorScheme.primary
|
|
||||||
else MaterialTheme.colorScheme.outline.copy(alpha = 0.5f)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.padding(horizontal = 4.dp, vertical = 6.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(2.dp)
|
|
||||||
) {
|
|
||||||
LanguageRow(word = item.wordFirst, language = langFirst)
|
|
||||||
HorizontalDivider(color = MaterialTheme.colorScheme.outline.copy(alpha = 0.3f))
|
|
||||||
LanguageRow(word = item.wordSecond, language = langSecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.padding(4.dp),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
Crossfade(targetState = isSelected, label = "action-icon-fade") { selected ->
|
|
||||||
if (selected) {
|
|
||||||
Icon(
|
|
||||||
imageVector = AppIcons.Check,
|
|
||||||
contentDescription = "Selected",
|
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
|
||||||
modifier = Modifier.size(24.dp)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
IconButton(onClick = onDeleteClick) {
|
|
||||||
Icon(
|
|
||||||
imageVector = AppIcons.Delete,
|
|
||||||
contentDescription = stringResource(id = R.string.label_delete),
|
|
||||||
tint = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun LanguageRow(word: String, language: String) {
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(vertical = 2.dp)) {
|
|
||||||
Text(
|
|
||||||
text = insertBreakOpportunities(word),
|
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
|
||||||
fontWeight = FontWeight.Medium,
|
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
|
||||||
modifier = Modifier.weight(0.7f)
|
|
||||||
)
|
|
||||||
Spacer(Modifier.width(8.dp))
|
|
||||||
Text(
|
|
||||||
text = language,
|
|
||||||
style = MaterialTheme.typography.labelMedium,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
maxLines = 1,
|
|
||||||
modifier = Modifier.weight(0.3f)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ThemePreviews
|
|
||||||
@Composable
|
|
||||||
fun LanguageRowPreview() {
|
|
||||||
LanguageRow(
|
|
||||||
word = "Hello",
|
|
||||||
language = "English"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun FilterSortBottomSheet(
|
private fun FilterSortBottomSheet(
|
||||||
@@ -785,8 +598,7 @@ private fun FilterSortBottomSheet(
|
|||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
onApplyFilters: (VocabularyFilterState) -> Unit,
|
onApplyFilters: (VocabularyFilterState) -> Unit,
|
||||||
hideCategory: Boolean = false,
|
hideCategory: Boolean = false,
|
||||||
hideStage: Boolean = false,
|
hideStage: Boolean = false
|
||||||
categoryViewModel: CategoryViewModel
|
|
||||||
) {
|
) {
|
||||||
var selectedStage by rememberSaveable { mutableStateOf(currentFilterState.selectedStage) }
|
var selectedStage by rememberSaveable { mutableStateOf(currentFilterState.selectedStage) }
|
||||||
var dueTodayOnly by rememberSaveable { mutableStateOf(currentFilterState.dueTodayOnly) }
|
var dueTodayOnly by rememberSaveable { mutableStateOf(currentFilterState.dueTodayOnly) }
|
||||||
@@ -931,20 +743,3 @@ private fun FilterSortBottomSheet(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ThemePreviews
|
|
||||||
@Composable
|
|
||||||
fun FilterSortBottomSheetPreview() {
|
|
||||||
val activity = LocalContext.current.findActivity()
|
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
|
||||||
val categoryViewModel: CategoryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
|
||||||
FilterSortBottomSheet(
|
|
||||||
currentFilterState = VocabularyFilterState(),
|
|
||||||
languageViewModel = languageViewModel,
|
|
||||||
languagesPresent = emptyList(),
|
|
||||||
onDismiss = {},
|
|
||||||
onApplyFilters = {},
|
|
||||||
hideCategory = false,
|
|
||||||
hideStage = false,
|
|
||||||
categoryViewModel = categoryViewModel
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1120,4 +1120,22 @@
|
|||||||
<string name="label_stats">Stats</string>
|
<string name="label_stats">Stats</string>
|
||||||
<string name="label_library">Library</string>
|
<string name="label_library">Library</string>
|
||||||
<string name="label_edit">Edit</string>
|
<string name="label_edit">Edit</string>
|
||||||
|
<string name="label_total_wordss">Total Words</string>
|
||||||
|
<string name="label_new_words">New Words</string>
|
||||||
|
<string name="desc_expand_your_vocabulary">Expand your vocabulary</string>
|
||||||
|
<string name="label_settings">Settings</string>
|
||||||
|
<string name="label_2d_days">%1$d Days</string>
|
||||||
|
<string name="label_current_streak">Current Streak</string>
|
||||||
|
<string name="label_daily_goal">Daily Goal</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_weekly_progress">Weekly Progress</string>
|
||||||
|
<string name="cd_go">Go</string>
|
||||||
|
<string name="label_aapply_filters">Apply Filters</string>
|
||||||
|
<string name="label_sort_by">Sort By</string>
|
||||||
|
<string name="label_reset">Reset</string>
|
||||||
|
<string name="label_filter_cards">Filter Cards</string>
|
||||||
|
<string name="text_desc_organize_vocabulary_groups">Organize Your Vocabulary in Groups</string>
|
||||||
|
<string name="text_add_new_word_to_list">Extract a New Word to Your List</string>
|
||||||
|
<string name="cd_scroll_to_top">Scroll to top</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user