Compare commits

..

4 Commits

12 changed files with 250 additions and 40 deletions

View File

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

View File

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

View File

@@ -342,11 +342,13 @@ fun DictionarySimpleTopBar(
languageName: String?, languageName: String?,
onNavigateBack: () -> Unit onNavigateBack: () -> Unit
) { ) {
word?.let {
AppTopAppBar( AppTopAppBar(
title = "TODO", title = it,
onNavigateBack = onNavigateBack onNavigateBack = onNavigateBack
) )
} }
}
@Composable @Composable
fun DefinitionBody( fun DefinitionBody(

View File

@@ -93,7 +93,7 @@ fun EtymologyResultScreen(
AppScaffold( AppScaffold(
topBar = { topBar = {
AppTopAppBar( AppTopAppBar(
title = "TODO", title = "Result",
onNavigateBack = { navController.popBackStack() }, onNavigateBack = { navController.popBackStack() },
actions = { actions = {
etymologyData?.let { data -> etymologyData?.let { data ->

View File

@@ -77,6 +77,7 @@ import kotlinx.coroutines.launch
fun StartExerciseScreen( fun StartExerciseScreen(
navController: NavHostController, navController: NavHostController,
preselectedCategoryId: Int? = null, preselectedCategoryId: Int? = null,
dueTodayOnly: Boolean = false,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val activity = androidx.compose.ui.platform.LocalContext.current.findActivity() val activity = androidx.compose.ui.platform.LocalContext.current.findActivity()
@@ -84,6 +85,15 @@ fun StartExerciseScreen(
val categoryViewModel: CategoryViewModel = hiltViewModel(viewModelStoreOwner = activity) val categoryViewModel: CategoryViewModel = hiltViewModel(viewModelStoreOwner = activity)
val exerciseViewModel: VocabularyExerciseViewModel = hiltViewModel(viewModelStoreOwner = activity) val exerciseViewModel: VocabularyExerciseViewModel = hiltViewModel(viewModelStoreOwner = activity)
// Initialize exercise config with dueTodayOnly if specified
androidx.compose.runtime.LaunchedEffect(dueTodayOnly) {
if (dueTodayOnly) {
exerciseViewModel.updatePendingExerciseConfig(
exerciseViewModel.pendingExerciseConfig.value.copy(dueTodayOnly = true)
)
}
}
val exerciseConfig by exerciseViewModel.pendingExerciseConfig.collectAsState() val exerciseConfig by exerciseViewModel.pendingExerciseConfig.collectAsState()
val allCategories by categoryViewModel.categories.collectAsState(initial = emptyList()) val allCategories by categoryViewModel.categories.collectAsState(initial = emptyList())
var selectedLanguagePairs by remember { mutableStateOf<List<Pair<Language, Language>>>(emptyList()) } var selectedLanguagePairs by remember { mutableStateOf<List<Pair<Language, Language>>>(emptyList()) }

View File

@@ -0,0 +1,168 @@
package eu.gaudian.translator.view.home
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
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.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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.text.style.TextAlign
import androidx.compose.ui.unit.dp
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.utils.findActivity
import eu.gaudian.translator.view.NavigationRoutes
import eu.gaudian.translator.view.composable.AppButton
import eu.gaudian.translator.view.composable.AppScaffold
import eu.gaudian.translator.view.composable.AppTopAppBar
import eu.gaudian.translator.view.library.VocabularyCard
import eu.gaudian.translator.viewmodel.LanguageViewModel
import eu.gaudian.translator.viewmodel.VocabularyViewModel
@Composable
fun DailyReviewScreen(
navController: NavHostController,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
val activity = context.findActivity()
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
val dueTodayItems by vocabularyViewModel.dueTodayItems.collectAsStateWithLifecycle(initialValue = emptyList())
val allLanguages by languageViewModel.allLanguages.collectAsStateWithLifecycle(initialValue = emptyList())
val listState = rememberLazyListState()
AppScaffold(
topBar = {
AppTopAppBar(
title = stringResource(R.string.label_daily_review),
onNavigateBack = { navController.popBackStack() }
)
},
modifier = modifier.fillMaxSize()
) { paddingValues ->
if (dueTodayItems.isEmpty()) {
Box(
modifier = Modifier
.padding(paddingValues)
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(32.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
modifier = Modifier.size(200.dp),
painter = painterResource(id = R.drawable.ic_nothing_found),
contentDescription = null
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = stringResource(R.string.no_items_due_for_review),
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
textAlign = TextAlign.Center
)
}
}
} else {
Box(modifier = Modifier.fillMaxSize()) {
LazyColumn(
state = listState,
modifier = Modifier
.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(16.dp)
) {
items(
items = dueTodayItems,
key = { it.id }
) { item ->
VocabularyCard(
item = item,
allLanguages = allLanguages,
isSelected = false,
onItemClick = {
vocabularyViewModel.setNavigationContext(dueTodayItems, item.id)
navController.navigate("${NavigationRoutes.VOCABULARY_DETAIL}/${item.id}")
},
onItemLongClick = { },
onDeleteClick = { }
)
}
// Add spacing at the bottom for the button
item {
Spacer(modifier = Modifier.height(80.dp))
}
}
// Start Exercise Button (fixed at bottom)
Box(
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth()
.padding(paddingValues)
.padding(24.dp)
) {
AppButton(
onClick = {
navController.navigate(NavigationRoutes.START_EXERCISE_DAILY)
},
modifier = Modifier
.fillMaxWidth()
.height(56.dp),
enabled = dueTodayItems.isNotEmpty(),
shape = RoundedCornerShape(28.dp)
) {
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(R.string.label_start_exercise_2d, dueTodayItems.size),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.width(8.dp))
Icon(
imageVector = Icons.Default.PlayArrow,
contentDescription = stringResource(R.string.cd_play),
modifier = Modifier.size(20.dp)
)
}
}
}
}
}
}
}

View File

@@ -62,6 +62,7 @@ fun HomeScreen(
val streak by viewModel.streak.collectAsState() val streak by viewModel.streak.collectAsState()
val dailyGoal by viewModel.dailyGoal.collectAsState() val dailyGoal by viewModel.dailyGoal.collectAsState()
val todayCompletedCount by viewModel.todayCompletedCount.collectAsState() val todayCompletedCount by viewModel.todayCompletedCount.collectAsState()
val dueTodayCount by viewModel.dueTodayCount.collectAsState()
// Calculate daily goal progress // Calculate daily goal progress
val progress = if (dailyGoal > 0) { val progress = if (dailyGoal > 0) {
@@ -95,13 +96,12 @@ fun HomeScreen(
) )
} }
item { item {
//TODO replace with actual implementation
@Suppress("HardCodedStringLiteral")
ActionCard( ActionCard(
title = "Daily Review", title = stringResource(R.string.label_daily_review),
subtitle = "42 words need attention", subtitle = stringResource(R.string.desc_daily_review_due, dueTodayCount),
icon = Icons.Default.Psychology, icon = Icons.Default.Psychology,
contentColor = MaterialTheme.colorScheme.onPrimary contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
onClick = { navController.navigate(NavigationRoutes.DAILY_REVIEW) }
) )
} }
item { item {
@@ -146,6 +146,9 @@ fun TopProfileSection(navController: NavHostController, context: Context) {
) )
} }
Spacer(modifier = Modifier.width(8.dp))
IconButton( IconButton(
onClick = { navController.navigate(Screen.Settings.route) }, onClick = { navController.navigate(Screen.Settings.route) },
modifier = Modifier modifier = Modifier
@@ -158,6 +161,7 @@ fun TopProfileSection(navController: NavHostController, context: Context) {
tint = MaterialTheme.colorScheme.onSurfaceVariant tint = MaterialTheme.colorScheme.onSurfaceVariant
) )
} }
Spacer(modifier = Modifier.width(8.dp))
} }
} }

View File

@@ -3,7 +3,6 @@
package eu.gaudian.translator.view.vocabulary package eu.gaudian.translator.view.vocabulary
import android.annotation.SuppressLint import android.annotation.SuppressLint
import androidx.compose.foundation.BorderStroke
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.Column import androidx.compose.foundation.layout.Column
@@ -14,9 +13,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
@@ -50,6 +46,7 @@ import eu.gaudian.translator.model.TagCategory
import eu.gaudian.translator.model.VocabularyFilter import eu.gaudian.translator.model.VocabularyFilter
import eu.gaudian.translator.model.VocabularyItem import eu.gaudian.translator.model.VocabularyItem
import eu.gaudian.translator.utils.findActivity import eu.gaudian.translator.utils.findActivity
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.AppScaffold import eu.gaudian.translator.view.composable.AppScaffold
import eu.gaudian.translator.view.composable.AppTopAppBar import eu.gaudian.translator.view.composable.AppTopAppBar
@@ -58,6 +55,7 @@ import eu.gaudian.translator.view.dialogs.DeleteCategoryDialog
import eu.gaudian.translator.view.dialogs.DeleteItemsDialog import eu.gaudian.translator.view.dialogs.DeleteItemsDialog
import eu.gaudian.translator.view.dialogs.EditCategoryDialog import eu.gaudian.translator.view.dialogs.EditCategoryDialog
import eu.gaudian.translator.view.vocabulary.widgets.CategoryProgressCircle import eu.gaudian.translator.view.vocabulary.widgets.CategoryProgressCircle
import eu.gaudian.translator.view.vocabulary.widgets.ChartLegend
import eu.gaudian.translator.viewmodel.CategoryProgress import eu.gaudian.translator.viewmodel.CategoryProgress
import eu.gaudian.translator.viewmodel.CategoryViewModel import eu.gaudian.translator.viewmodel.CategoryViewModel
import eu.gaudian.translator.viewmodel.ExportImportViewModel import eu.gaudian.translator.viewmodel.ExportImportViewModel
@@ -313,16 +311,10 @@ fun CategoryHeaderCard(
onDeleteClick: () -> Unit, onDeleteClick: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
Card( AppCard(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 16.dp), .padding(horizontal = 16.dp, vertical = 16.dp),
shape = RoundedCornerShape(20.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.2f)
),
border = BorderStroke(1.dp, MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.4f)),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
@@ -343,6 +335,8 @@ fun CategoryHeaderCard(
// Progress Circle - smaller size // Progress Circle - smaller size
if (categoryProgress != null) { if (categoryProgress != null) {
CategoryProgressCircle( CategoryProgressCircle(
totalItems = categoryProgress.totalItems, totalItems = categoryProgress.totalItems,
itemsCompleted = categoryProgress.itemsCompleted, itemsCompleted = categoryProgress.itemsCompleted,
@@ -350,7 +344,9 @@ fun CategoryHeaderCard(
newItems = categoryProgress.newItems, newItems = categoryProgress.newItems,
circleSize = 100.dp, circleSize = 100.dp,
) )
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(4.dp))
ChartLegend()
Spacer(modifier = Modifier.height(16.dp))
} }
// Action Buttons // Action Buttons

View File

@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
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.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
@@ -85,7 +84,6 @@ fun VocabularyCardHost(
AppScaffold( AppScaffold(
topBar = { topBar = {
AppTopAppBar( AppTopAppBar(
modifier = Modifier.height(56.dp),
title = stringResource(R.string.item_details), title = stringResource(R.string.item_details),
onNavigateBack = { navController.popBackStack() }, onNavigateBack = { navController.popBackStack() },
actions = { actions = {

View File

@@ -290,7 +290,7 @@ fun CategoryProgressCircle(
} }
} }
@Composable @Composable
private fun ChartLegend() { fun ChartLegend() {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()

View File

@@ -79,7 +79,7 @@ fun WeeklyActivityChartWidget(
.fillMaxWidth() .fillMaxWidth()
.padding(8.dp) .padding(8.dp)
) { ) {
ChartLegend() WeeklyChartLegend()
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
Row( Row(
@@ -164,7 +164,7 @@ private fun RowScope.Bar(value: Int, maxValue: Int, color: Color) {
} }
@Composable @Composable
private fun ChartLegend() { private fun WeeklyChartLegend() {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()

View File

@@ -450,6 +450,7 @@
<string name="no_models_configured">No Models Configured</string> <string name="no_models_configured">No Models Configured</string>
<string name="no_models_found">No models found</string> <string name="no_models_found">No models found</string>
<string name="no_new_vocabulary_to_sort">No New Vocabulary to Sort</string> <string name="no_new_vocabulary_to_sort">No New Vocabulary to Sort</string>
<string name="no_items_due_for_review">No items due for review today. Great job!</string>
<string name="no_vocabulary_items_found_perhaps_try_changing_the_filters">No vocabulary items found. Perhaps try changing the filters?</string> <string name="no_vocabulary_items_found_perhaps_try_changing_the_filters">No vocabulary items found. Perhaps try changing the filters?</string>
<string name="not_available">Not available</string> <string name="not_available">Not available</string>
@@ -1088,6 +1089,9 @@
<string name="label_2d_days">%1$d Days</string> <string name="label_2d_days">%1$d Days</string>
<string name="label_current_streak">Current Streak</string> <string name="label_current_streak">Current Streak</string>
<string name="label_daily_goal">Daily Goal</string> <string name="label_daily_goal">Daily Goal</string>
<string name="label_daily_review">Daily Review</string>
<string name="desc_daily_review_due">%1$d words need attention</string>
<string name="text_daily_review_placeholder">Daily review screen - implementation pending</string>
<string name="text_desc_no_activity_data_available">No activity data available</string> <string name="text_desc_no_activity_data_available">No activity data available</string>
<string name="label_see_history">See History</string> <string name="label_see_history">See History</string>
<string name="label_weekly_progress">Weekly Progress</string> <string name="label_weekly_progress">Weekly Progress</string>