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 selectedScreen = Screen.fromDestination(currentDestination)
|
||||
|
||||
val isBottomBarHidden = currentDestination?.hierarchy?.any { destination ->
|
||||
@Suppress("HardCodedStringLiteral") val isBottomBarHidden = currentDestination?.hierarchy?.any { destination ->
|
||||
destination.route in setOf(
|
||||
Screen.Translation.route,
|
||||
Screen.Dictionary.route,
|
||||
@@ -310,6 +310,7 @@ fun TranslatorApp(
|
||||
}
|
||||
},
|
||||
onPlayClicked = {
|
||||
@Suppress("HardCodedStringLiteral")
|
||||
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.stats.StatsScreen
|
||||
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.CategoryListScreen
|
||||
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.VocabularyExerciseHostScreen
|
||||
import eu.gaudian.translator.view.vocabulary.VocabularyHeatmapScreen
|
||||
import eu.gaudian.translator.view.vocabulary.VocabularyListScreen
|
||||
import eu.gaudian.translator.view.vocabulary.VocabularySortingScreen
|
||||
|
||||
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
|
||||
fun AppNavHost(
|
||||
navController: NavHostController,
|
||||
@@ -130,15 +146,15 @@ fun AppNavHost(
|
||||
HomeScreen(navController = navController)
|
||||
}
|
||||
|
||||
composable("new_word") {
|
||||
composable(NavigationRoutes.NEW_WORD) {
|
||||
NewWordScreen(navController = navController)
|
||||
}
|
||||
|
||||
composable("new_word_review") {
|
||||
composable(NavigationRoutes.NEW_WORD_REVIEW) {
|
||||
NewWordReviewScreen(navController = navController)
|
||||
}
|
||||
|
||||
composable("start_exercise") {
|
||||
composable(NavigationRoutes.START_EXERCISE) {
|
||||
StartExerciseScreen(navController = navController)
|
||||
}
|
||||
|
||||
@@ -203,7 +219,7 @@ fun NavGraphBuilder.libraryGraph(navController: NavHostController) {
|
||||
composable("vocabulary_list/{showDueTodayOnly}/{categoryId}") { backStackEntry ->
|
||||
val showDueTodayOnly = backStackEntry.arguments?.getString("showDueTodayOnly")?.toBoolean() ?: false
|
||||
val categoryId = backStackEntry.arguments?.getString("categoryId")?.toIntOrNull()
|
||||
VocabularyListScreen(
|
||||
AllCardsListScreen(
|
||||
navController = navController,
|
||||
showDueTodayOnly = showDueTodayOnly,
|
||||
categoryId = categoryId,
|
||||
@@ -220,7 +236,7 @@ fun NavGraphBuilder.libraryGraph(navController: NavHostController) {
|
||||
)
|
||||
|
||||
}
|
||||
composable("vocabulary_heatmap") {
|
||||
composable(NavigationRoutes.STATS_VOCABULARY_HEATMAP) {
|
||||
VocabularyHeatmapScreen(
|
||||
navController = navController,
|
||||
)
|
||||
@@ -232,7 +248,7 @@ fun NavGraphBuilder.libraryGraph(navController: NavHostController) {
|
||||
if (it.equals("null", ignoreCase = true)) null else VocabularyStage.valueOf(it)
|
||||
}
|
||||
|
||||
VocabularyListScreen(
|
||||
AllCardsListScreen(
|
||||
navController = navController,
|
||||
showDueTodayOnly = showDueTodayOnly,
|
||||
stage = stage,
|
||||
@@ -373,7 +389,7 @@ fun NavGraphBuilder.statsGraph(
|
||||
composable("stats/vocabulary_list/{showDueTodayOnly}/{categoryId}") { backStackEntry ->
|
||||
val showDueTodayOnly = backStackEntry.arguments?.getString("showDueTodayOnly")?.toBoolean() ?: false
|
||||
val categoryId = backStackEntry.arguments?.getString("categoryId")?.toIntOrNull()
|
||||
VocabularyListScreen(
|
||||
AllCardsListScreen(
|
||||
navController = navController,
|
||||
showDueTodayOnly = showDueTodayOnly,
|
||||
categoryId = categoryId,
|
||||
@@ -384,12 +400,12 @@ fun NavGraphBuilder.statsGraph(
|
||||
enableNavigationButtons = true
|
||||
)
|
||||
}
|
||||
composable("stats/language_progress") {
|
||||
composable(NavigationRoutes.STATS_LANGUAGE_PROGRESS) {
|
||||
LanguageProgressScreen(
|
||||
navController = navController
|
||||
)
|
||||
}
|
||||
composable("stats/vocabulary_heatmap") {
|
||||
composable(NavigationRoutes.STATS_VOCABULARY_HEATMAP) {
|
||||
VocabularyHeatmapScreen(
|
||||
navController = navController,
|
||||
)
|
||||
@@ -401,7 +417,7 @@ fun NavGraphBuilder.statsGraph(
|
||||
if (it.equals("null", ignoreCase = true)) null else VocabularyStage.valueOf(it)
|
||||
}
|
||||
|
||||
VocabularyListScreen(
|
||||
AllCardsListScreen(
|
||||
navController = navController,
|
||||
showDueTodayOnly = showDueTodayOnly,
|
||||
stage = stage,
|
||||
|
||||
@@ -43,9 +43,8 @@ import eu.gaudian.translator.view.hints.LocalShowHints
|
||||
|
||||
@Composable
|
||||
fun AppTopAppBar(
|
||||
title: String,
|
||||
additionalContent: @Composable () -> Unit = {},
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
onNavigateBack: (() -> Unit)? = null,
|
||||
navigationIcon: @Composable (() -> Unit)? = null,
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
|
||||
@@ -15,7 +15,7 @@ import androidx.navigation.NavHostController
|
||||
import eu.gaudian.translator.R
|
||||
import eu.gaudian.translator.view.composable.AppButton
|
||||
import eu.gaudian.translator.view.composable.AppTopAppBar
|
||||
import eu.gaudian.translator.view.vocabulary.VocabularyListScreen
|
||||
import eu.gaudian.translator.view.vocabulary.AllCardsListScreen
|
||||
|
||||
@Composable
|
||||
fun ExerciseVocabularyScreen(
|
||||
@@ -41,7 +41,7 @@ fun ExerciseVocabularyScreen(
|
||||
) { paddingValues ->
|
||||
Box(modifier = Modifier.padding(paddingValues)) {
|
||||
|
||||
VocabularyListScreen(
|
||||
AllCardsListScreen(
|
||||
navController = navController as NavHostController?,
|
||||
onNavigateToItem = { item ->
|
||||
// Navigate to the detail screen for a specific vocabulary item
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package eu.gaudian.translator.view.home
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.navigation.NavHostController
|
||||
import eu.gaudian.translator.R
|
||||
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.Screen
|
||||
import eu.gaudian.translator.view.settings.SettingsRoutes
|
||||
@@ -78,33 +81,36 @@ fun HomeScreen(
|
||||
verticalArrangement = Arrangement.spacedBy(15.dp)
|
||||
) {
|
||||
item { Spacer(modifier = Modifier.height(16.dp)) }
|
||||
item { TopProfileSection(navController = navController) }
|
||||
item { TopProfileSection(
|
||||
navController = navController,
|
||||
context = LocalContext.current
|
||||
) }
|
||||
item {
|
||||
StreakAndGoalSection(
|
||||
streak = streak,
|
||||
progress = progress,
|
||||
progressTitle = "$todayCompletedCount / $dailyGoal",
|
||||
onGoalClick = { navController.navigate(SettingsRoutes.VOCABULARY_OPTIONS) },
|
||||
onStreakClick = { navController.navigate("stats/vocabulary_heatmap") }
|
||||
onStreakClick = { navController.navigate(NavigationRoutes.STATS_VOCABULARY_HEATMAP) }
|
||||
)
|
||||
}
|
||||
item {
|
||||
//TODO replace with actual implementation
|
||||
@Suppress("HardCodedStringLiteral")
|
||||
ActionCard(
|
||||
title = "Daily Review",
|
||||
subtitle = "42 words need attention",
|
||||
icon = Icons.Default.Psychology,
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
contentColor = MaterialTheme.colorScheme.onPrimary
|
||||
)
|
||||
}
|
||||
item {
|
||||
ActionCard(
|
||||
title = "New Words",
|
||||
subtitle = "Expand your vocabulary",
|
||||
title = stringResource(R.string.label_new_words),
|
||||
subtitle = stringResource(R.string.desc_expand_your_vocabulary),
|
||||
icon = Icons.Default.AddCircle,
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
onClick = { navController.navigate("new_word") }
|
||||
onClick = { navController.navigate(NavigationRoutes.NEW_WORD) }
|
||||
)
|
||||
}
|
||||
item { WeeklyProgressSection(navController = navController) }
|
||||
@@ -117,8 +123,8 @@ fun HomeScreen(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TopProfileSection(navController: NavHostController) {
|
||||
val context = LocalContext.current
|
||||
fun TopProfileSection(navController: NavHostController, context: Context) {
|
||||
|
||||
val motivationalPhrases = remember {
|
||||
context.resources.getStringArray(R.array.motivational_phrases)
|
||||
}
|
||||
@@ -126,7 +132,9 @@ fun TopProfileSection(navController: NavHostController) {
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth().padding(8.dp)
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
) {
|
||||
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
@@ -146,7 +154,7 @@ fun TopProfileSection(navController: NavHostController) {
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Settings,
|
||||
contentDescription = "Settings",
|
||||
contentDescription = stringResource(R.string.label_settings),
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
@@ -169,8 +177,8 @@ fun StreakAndGoalSection(
|
||||
StatCard(
|
||||
modifier = Modifier.weight(1f),
|
||||
icon = Icons.Default.LocalFireDepartment,
|
||||
title = "$streak Days",
|
||||
subtitle = "CURRENT STREAK",
|
||||
title = stringResource(R.string.label_2d_days, streak),
|
||||
subtitle = stringResource(R.string.label_current_streak).uppercase(),
|
||||
onClick = onStreakClick
|
||||
)
|
||||
// Goal Card
|
||||
@@ -178,7 +186,7 @@ fun StreakAndGoalSection(
|
||||
modifier = Modifier.weight(1f),
|
||||
progress = progress,
|
||||
title = progressTitle,
|
||||
subtitle = "DAILY GOAL",
|
||||
subtitle = stringResource(R.string.label_daily_goal).uppercase(),
|
||||
onClick = onGoalClick
|
||||
)
|
||||
}
|
||||
@@ -293,7 +301,6 @@ fun ActionCard(
|
||||
title: String,
|
||||
subtitle: String,
|
||||
icon: ImageVector,
|
||||
containerColor: Color,
|
||||
contentColor: Color,
|
||||
onClick: (() -> Unit)? = null
|
||||
) {
|
||||
@@ -314,7 +321,7 @@ fun ActionCard(
|
||||
}
|
||||
Icon(
|
||||
imageVector = Icons.Default.ChevronRight,
|
||||
contentDescription = "Go",
|
||||
contentDescription = stringResource(R.string.cd_go),
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
@@ -351,9 +358,9 @@ fun WeeklyProgressSection(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(text = "Weekly Progress", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
|
||||
TextButton(onClick = { navController.navigate("stats/vocabulary_heatmap") }) {
|
||||
Text("See History")
|
||||
Text(text = stringResource(R.string.label_weekly_progress), style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
|
||||
TextButton(onClick = { navController.navigate(NavigationRoutes.STATS_VOCABULARY_HEATMAP) }) {
|
||||
Text(stringResource(R.string.label_see_history))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,7 +368,7 @@ fun WeeklyProgressSection(
|
||||
|
||||
AppCard(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = { navController.navigate("stats/vocabulary_heatmap") }
|
||||
onClick = { navController.navigate(NavigationRoutes.STATS_VOCABULARY_HEATMAP) }
|
||||
) {
|
||||
if (weeklyActivityStats.isEmpty()) {
|
||||
Column(
|
||||
@@ -372,7 +379,7 @@ fun WeeklyProgressSection(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = "No activity data available",
|
||||
text = stringResource(R.string.text_desc_no_activity_data_available),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
@@ -403,7 +410,7 @@ fun BottomStatsSection(
|
||||
onClick = { navController.navigate(Screen.Library.route) }
|
||||
) {
|
||||
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))
|
||||
Text(text = totalWords.toString(), style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
@@ -413,7 +420,7 @@ fun BottomStatsSection(
|
||||
// Learned
|
||||
AppCard(
|
||||
modifier = Modifier.weight(1f),
|
||||
onClick = { navController.navigate("stats/language_progress") }
|
||||
onClick = { navController.navigate(NavigationRoutes.STATS_LANGUAGE_PROGRESS) }
|
||||
) {
|
||||
Column(modifier = Modifier.padding(20.dp)) {
|
||||
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
|
||||
|
||||
@@ -44,6 +44,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
@@ -61,8 +62,10 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavHostController
|
||||
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.utils.findActivity
|
||||
import eu.gaudian.translator.view.NavigationRoutes
|
||||
import eu.gaudian.translator.view.composable.AppIcons
|
||||
import eu.gaudian.translator.view.composable.AppSwitch
|
||||
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.CategorySelectionDialog
|
||||
import eu.gaudian.translator.view.dialogs.StageSelectionDialog
|
||||
import eu.gaudian.translator.viewmodel.CategoryViewModel
|
||||
import eu.gaudian.translator.viewmodel.LanguageConfigViewModel
|
||||
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
||||
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
||||
@@ -99,8 +101,6 @@ fun LibraryScreen(
|
||||
val lazyListState = rememberLazyListState()
|
||||
val vocabularyViewModel: VocabularyViewModel = 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 showFilterSheet by remember { mutableStateOf(false) }
|
||||
@@ -135,8 +135,8 @@ fun LibraryScreen(
|
||||
val vocabularyItems by vocabularyItemsFlow.collectAsStateWithLifecycle(initialValue = emptyList())
|
||||
|
||||
var isHeaderVisible by remember { mutableStateOf(true) }
|
||||
var previousIndex by remember { mutableStateOf(0) }
|
||||
var previousScrollOffset by remember { mutableStateOf(0) }
|
||||
var previousIndex by remember { mutableIntStateOf(0) }
|
||||
var previousScrollOffset by remember { mutableIntStateOf(0) }
|
||||
|
||||
// Set navigation context when vocabulary items are loaded
|
||||
LaunchedEffect(vocabularyItems) {
|
||||
@@ -229,10 +229,11 @@ fun LibraryScreen(
|
||||
CategoriesView(
|
||||
categories = categories,
|
||||
onCategoryClick = { category ->
|
||||
navController.navigate("category_detail/${category.id}")
|
||||
@Suppress("HardCodedStringLiteral")
|
||||
navController.navigate("${NavigationRoutes.CATEGORY_DETAIL}/${category.id}")
|
||||
},
|
||||
onExploreMoreClick = {
|
||||
navController.navigate("category_list_screen")
|
||||
navController.navigate(NavigationRoutes.CATEGORY_LIST)
|
||||
}
|
||||
)
|
||||
},
|
||||
@@ -251,7 +252,8 @@ fun LibraryScreen(
|
||||
}
|
||||
} else {
|
||||
vocabularyViewModel.setNavigationContext(vocabularyItems, item.id)
|
||||
navController.navigate("vocabulary_detail/${item.id}")
|
||||
@Suppress("HardCodedStringLiteral")
|
||||
navController.navigate("${NavigationRoutes.VOCABULARY_DETAIL}/${item.id}")
|
||||
}
|
||||
},
|
||||
onItemLongClick = { item ->
|
||||
@@ -287,7 +289,7 @@ fun LibraryScreen(
|
||||
modifier = Modifier.size(50.dp),
|
||||
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(
|
||||
icon = Icons.Rounded.Add, // Or any custom vector icon you prefer
|
||||
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 = {
|
||||
showAddMenu = false
|
||||
navController.navigate("new_word")
|
||||
navController.navigate(NavigationRoutes.NEW_WORD)
|
||||
}
|
||||
)
|
||||
|
||||
BottomSheetMenuItem(
|
||||
icon = Icons.Rounded.Folder,
|
||||
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 = {
|
||||
showAddMenu = false
|
||||
showAddCategoryDialog = true
|
||||
@@ -418,7 +420,7 @@ fun FilterBottomSheetContent(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = "Filter Cards",
|
||||
text = stringResource(R.string.label_filter_cards),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
@@ -430,7 +432,7 @@ fun FilterBottomSheetContent(
|
||||
sortOrder = SortOrder.NEWEST_FIRST
|
||||
onResetClick()
|
||||
}) {
|
||||
Text("Reset")
|
||||
Text(stringResource(R.string.label_reset))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,7 +448,7 @@ fun FilterBottomSheetContent(
|
||||
// Sort Order
|
||||
Column {
|
||||
Text(
|
||||
text = "SORT BY",
|
||||
text = stringResource(R.string.label_sort_by).uppercase(),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
letterSpacing = 1.sp,
|
||||
@@ -589,7 +591,7 @@ fun FilterBottomSheetContent(
|
||||
shape = RoundedCornerShape(28.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Apply Filters",
|
||||
text = stringResource(R.string.label_apply_filters),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@file:Suppress("HardCodedStringLiteral", "AssignedValueIsNeverRead", "UnusedMaterial3ScaffoldPaddingParameter")
|
||||
@file:Suppress("AssignedValueIsNeverRead")
|
||||
|
||||
package eu.gaudian.translator.view.stats
|
||||
|
||||
@@ -56,8 +56,8 @@ import androidx.navigation.compose.rememberNavController
|
||||
import eu.gaudian.translator.R
|
||||
import eu.gaudian.translator.model.VocabularyStage
|
||||
import eu.gaudian.translator.model.WidgetType
|
||||
import eu.gaudian.translator.utils.Log
|
||||
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.AppIcons
|
||||
import eu.gaudian.translator.view.composable.AppOutlinedCard
|
||||
@@ -82,14 +82,11 @@ import kotlinx.coroutines.launch
|
||||
@SuppressLint("FrequentlyChangingValue")
|
||||
@Composable
|
||||
fun StatsScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
navController: NavHostController,
|
||||
onShowCustomExerciseDialog: () -> Unit = {},
|
||||
startDailyExercise: (Boolean) -> Unit = {},
|
||||
onNavigateToCategoryDetail: ((Int) -> Unit)? = null,
|
||||
onNavigateToCategoryList: (() -> Unit)? = null,
|
||||
onShowWordPairExerciseDialog: () -> Unit = {},
|
||||
onScroll: (Boolean) -> Unit = {},
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val activity = LocalContext.current.findActivity()
|
||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||
@@ -128,10 +125,11 @@ fun StatsScreen(
|
||||
}
|
||||
|
||||
val handleNavigateToCategoryDetail = onNavigateToCategoryDetail ?: { categoryId ->
|
||||
navController.navigate("stats/category_detail/$categoryId")
|
||||
@Suppress("HardCodedStringLiteral")
|
||||
navController.navigate("${NavigationRoutes.STATS_CATEGORY_DETAIL}/$categoryId")
|
||||
}
|
||||
val handleNavigateToCategoryList = onNavigateToCategoryList ?: {
|
||||
navController.navigate("stats/category_list_screen")
|
||||
navController.navigate(NavigationRoutes.STATS_CATEGORY_LIST)
|
||||
}
|
||||
|
||||
AppOutlinedCard(modifier = modifier) {
|
||||
@@ -270,11 +268,8 @@ fun StatsScreen(
|
||||
navController = navController,
|
||||
vocabularyViewModel = vocabularyViewModel,
|
||||
progressViewModel = progressViewModel,
|
||||
onShowCustomExerciseDialog = onShowCustomExerciseDialog,
|
||||
startDailyExercise = startDailyExercise,
|
||||
onNavigateToCategoryDetail = handleNavigateToCategoryDetail,
|
||||
onNavigateToCategoryList = handleNavigateToCategoryList,
|
||||
onShowWordPairExerciseDialog = onShowWordPairExerciseDialog,
|
||||
onMissingLanguage = { missingId ->
|
||||
selectedMissingLanguageId = missingId
|
||||
showMissingLanguageDialog = true
|
||||
@@ -524,17 +519,15 @@ class DragDropState(
|
||||
// Remainder of your existing components
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
@Suppress("HardCodedStringLiteral")
|
||||
@Composable
|
||||
private fun LazyWidget(
|
||||
widgetType: WidgetType,
|
||||
navController: NavController,
|
||||
vocabularyViewModel: VocabularyViewModel,
|
||||
progressViewModel: ProgressViewModel,
|
||||
onShowCustomExerciseDialog: () -> Unit,
|
||||
startDailyExercise: (Boolean) -> Unit,
|
||||
onNavigateToCategoryDetail: (Int) -> Unit,
|
||||
onNavigateToCategoryList: () -> Unit,
|
||||
onShowWordPairExerciseDialog: () -> Unit,
|
||||
onMissingLanguage: (Int) -> Unit
|
||||
) {
|
||||
when (widgetType) {
|
||||
@@ -542,10 +535,10 @@ private fun LazyWidget(
|
||||
|
||||
WidgetType.Status -> LazyStatusWidget(
|
||||
vocabularyViewModel = vocabularyViewModel,
|
||||
onNavigateToNew = { navController.navigate("stats/vocabulary_sorting?mode=NEW") },
|
||||
onNavigateToDuplicates = { navController.navigate("stats/vocabulary_sorting?mode=DUPLICATES") },
|
||||
onNavigateToFaulty = { navController.navigate("stats/vocabulary_sorting?mode=FAULTY") },
|
||||
onNavigateToNoGrammar = { navController.navigate("stats/no_grammar_items") },
|
||||
onNavigateToNew = { navController.navigate("${NavigationRoutes.STATS_VOCABULARY_SORTING}?mode=NEW") },
|
||||
onNavigateToDuplicates = { navController.navigate("${NavigationRoutes.STATS_VOCABULARY_SORTING}?mode=DUPLICATES") },
|
||||
onNavigateToFaulty = { navController.navigate("${NavigationRoutes.STATS_VOCABULARY_SORTING}?mode=FAULTY") },
|
||||
onNavigateToNoGrammar = { navController.navigate(NavigationRoutes.STATS_NO_GRAMMAR_ITEMS) },
|
||||
onNavigateToMissingLanguage = onMissingLanguage
|
||||
)
|
||||
|
||||
@@ -557,7 +550,7 @@ private fun LazyWidget(
|
||||
lastSevenDays = progressViewModel.lastSevenDays.collectAsState().value,
|
||||
dueTodayCount = progressViewModel.dueTodayCount.collectAsState().value,
|
||||
wordsCompleted = progressViewModel.totalWordsCompleted.collectAsState().value,
|
||||
onStatisticsClicked = { navController.navigate("stats/vocabulary_heatmap") }
|
||||
onStatisticsClicked = { navController.navigate(NavigationRoutes.STATS_VOCABULARY_HEATMAP) }
|
||||
)
|
||||
|
||||
WidgetType.WeeklyActivityChart -> WeeklyActivityChartWidget(
|
||||
@@ -566,13 +559,19 @@ private fun LazyWidget(
|
||||
|
||||
WidgetType.AllVocabulary -> AllVocabularyWidget(
|
||||
vocabularyViewModel = vocabularyViewModel,
|
||||
onOpenAllVocabulary = { navController.navigate("stats/vocabulary_list/false/null") },
|
||||
onStageClicked = { stage -> navController.navigate("stats/vocabulary_list/false/$stage") }
|
||||
onOpenAllVocabulary = { navController.navigate("${NavigationRoutes.STATS_VOCABULARY_LIST}/false/null") },
|
||||
onStageClicked = { stage ->
|
||||
@Suppress("HardCodedStringLiteral")
|
||||
navController.navigate("${NavigationRoutes.STATS_VOCABULARY_LIST}/false/$stage")
|
||||
}
|
||||
)
|
||||
|
||||
WidgetType.DueToday -> DueTodayWidget(
|
||||
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(
|
||||
@@ -586,7 +585,7 @@ private fun LazyWidget(
|
||||
totalWords = vocabularyViewModel.vocabularyItems.collectAsState().value.size,
|
||||
learnedWords = vocabularyViewModel.stageStats.collectAsState().value
|
||||
.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()
|
||||
StatsScreen(
|
||||
navController = navController,
|
||||
onShowCustomExerciseDialog = {},
|
||||
onNavigateToCategoryDetail = {},
|
||||
startDailyExercise = {},
|
||||
onNavigateToCategoryList = {},
|
||||
onShowWordPairExerciseDialog = {},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -675,6 +671,7 @@ fun WidgetContainerPreview() {
|
||||
.padding(16.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
@Suppress("HardCodedStringLiteral")
|
||||
Text("Preview Content")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ fun CategoryDetailScreen(
|
||||
}
|
||||
) { paddingValues ->
|
||||
Column(modifier = Modifier.padding(paddingValues)) {
|
||||
VocabularyListScreen(
|
||||
AllCardsListScreen(
|
||||
categoryId = categoryId,
|
||||
showDueTodayOnly = false,
|
||||
onNavigateToItem = onNavigateToItem,
|
||||
@@ -324,8 +324,8 @@ fun CategoryHeaderCardWithProgressPreview() {
|
||||
MaterialTheme {
|
||||
CategoryHeaderCard(
|
||||
subtitle = "Travel Vocabulary",
|
||||
categoryProgress = eu.gaudian.translator.viewmodel.CategoryProgress(
|
||||
vocabularyCategory = eu.gaudian.translator.model.TagCategory(
|
||||
categoryProgress = CategoryProgress(
|
||||
vocabularyCategory = TagCategory(
|
||||
1,
|
||||
"Travel"
|
||||
),
|
||||
|
||||
@@ -80,8 +80,8 @@ fun NoGrammarItemsScreen(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Use the generic VocabularyListScreen to display the items
|
||||
VocabularyListScreen(
|
||||
// Use the generic AllCardsListScreen to display the items
|
||||
AllCardsListScreen(
|
||||
itemsToShow = itemsWithoutGrammar,
|
||||
onNavigateToItem = { item ->
|
||||
@Suppress("HardCodedStringLiteral")
|
||||
|
||||
@@ -49,7 +49,7 @@ fun StageDetailScreen(
|
||||
onStageTapped = {},
|
||||
)
|
||||
|
||||
VocabularyListScreen(
|
||||
AllCardsListScreen(
|
||||
categoryId = null,
|
||||
showDueTodayOnly = true,
|
||||
stage = stage,
|
||||
|
||||
@@ -5,19 +5,13 @@ package eu.gaudian.translator.view.vocabulary
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
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.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
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.size
|
||||
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.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.FabPosition
|
||||
@@ -43,7 +33,6 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.OutlinedCard
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
@@ -59,20 +48,15 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
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.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import eu.gaudian.translator.R
|
||||
import eu.gaudian.translator.model.Language
|
||||
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.AppTopAppBar
|
||||
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.CategorySelectionDialog
|
||||
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.LanguageConfigViewModel
|
||||
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
||||
@@ -110,7 +94,7 @@ private data class VocabularyFilterState(
|
||||
) : Parcelable
|
||||
|
||||
@Composable
|
||||
fun VocabularyListScreen(
|
||||
fun AllCardsListScreen(
|
||||
categoryId: Int? = null,
|
||||
showDueTodayOnly: Boolean? = null,
|
||||
stage: VocabularyStage? = null,
|
||||
@@ -245,11 +229,7 @@ fun VocabularyListScreen(
|
||||
)
|
||||
|
||||
"search" -> SearchTopAppBar(
|
||||
searchQuery = filterState.searchQuery,
|
||||
onQueryChanged = { newQuery ->
|
||||
filterState = filterState.copy(searchQuery = newQuery)
|
||||
},
|
||||
onCloseSearch = {
|
||||
onCloseSearch = {
|
||||
isSearchActive = false
|
||||
filterState = filterState.copy(searchQuery = "")
|
||||
}
|
||||
@@ -295,78 +275,40 @@ fun VocabularyListScreen(
|
||||
floatingActionButtonPosition = FabPosition.Center
|
||||
) { paddingValues ->
|
||||
Column(modifier = Modifier.padding(paddingValues).padding(bottom = 0.dp)) {
|
||||
if (vocabularyItems.isEmpty()) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
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
|
||||
AllCardsView(
|
||||
vocabularyItems = vocabularyItems,
|
||||
allLanguages = allLanguages,
|
||||
selection = selection,
|
||||
listState = lazyListState,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(8.dp), contentAlignment = Alignment.Center) {
|
||||
Text(
|
||||
text = stringResource(R.string.no_vocabulary_items_found_perhaps_try_changing_the_filters),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
textAlign = androidx.compose.ui.text.style.TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LazyColumn(
|
||||
state = lazyListState,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 8.dp),
|
||||
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()
|
||||
)
|
||||
.padding(horizontal = 8.dp),
|
||||
onItemClick = { item ->
|
||||
val isSelected = selection.contains(item.id.toLong())
|
||||
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 = { 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,
|
||||
languagesPresent = allLanguages.filter { it.nameResId in languagesPresent },
|
||||
hideCategory = categoryId != null && categoryId != 0,
|
||||
hideStage = stage != null,
|
||||
categoryViewModel = categoryViewModel
|
||||
hideStage = stage != null
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,21 +358,34 @@ fun VocabularyListScreen(
|
||||
}
|
||||
}
|
||||
|
||||
@ThemePreviews
|
||||
@Deprecated("Use AllCardsListScreen which renders AllCardsView")
|
||||
@Composable
|
||||
fun VocabularyListScreenPreview() {
|
||||
val navController = rememberNavController()
|
||||
VocabularyListScreen(
|
||||
categoryId = 1,
|
||||
showDueTodayOnly = false,
|
||||
stage = VocabularyStage.NEW,
|
||||
onNavigateToItem = {},
|
||||
onNavigateBack = {},
|
||||
navController = navController
|
||||
fun VocabularyListScreen(
|
||||
categoryId: Int? = null,
|
||||
showDueTodayOnly: Boolean? = null,
|
||||
stage: VocabularyStage? = null,
|
||||
onNavigateToItem: (VocabularyItem) -> Unit?,
|
||||
onNavigateBack: (() -> Unit)? = null,
|
||||
navController: NavHostController? = null,
|
||||
itemsToShow: List<VocabularyItem> = emptyList(),
|
||||
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
|
||||
private fun DefaultTopAppBar(
|
||||
title: String,
|
||||
@@ -505,8 +459,6 @@ private fun DefaultTopAppBar(
|
||||
|
||||
@Composable
|
||||
private fun SearchTopAppBar(
|
||||
searchQuery: String,
|
||||
onQueryChanged: (String) -> Unit,
|
||||
onCloseSearch: () -> Unit
|
||||
) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
@@ -518,37 +470,6 @@ private fun SearchTopAppBar(
|
||||
AppTopAppBar(
|
||||
modifier = Modifier.height(56.dp),
|
||||
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 = {
|
||||
IconButton(onClick = onCloseSearch) {
|
||||
Icon(
|
||||
@@ -566,8 +487,6 @@ private fun SearchTopAppBar(
|
||||
@Composable
|
||||
fun SearchTopAppBarPreview() {
|
||||
SearchTopAppBar(
|
||||
searchQuery = stringResource(R.string.search_query),
|
||||
onQueryChanged = {},
|
||||
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
|
||||
private fun FilterSortBottomSheet(
|
||||
@@ -785,8 +598,7 @@ private fun FilterSortBottomSheet(
|
||||
onDismiss: () -> Unit,
|
||||
onApplyFilters: (VocabularyFilterState) -> Unit,
|
||||
hideCategory: Boolean = false,
|
||||
hideStage: Boolean = false,
|
||||
categoryViewModel: CategoryViewModel
|
||||
hideStage: Boolean = false
|
||||
) {
|
||||
var selectedStage by rememberSaveable { mutableStateOf(currentFilterState.selectedStage) }
|
||||
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_library">Library</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>
|
||||
|
||||
Reference in New Issue
Block a user