|
|
|
|
@@ -57,7 +57,6 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
|
|
|
|
import androidx.navigation.NavHostController
|
|
|
|
|
import eu.gaudian.translator.R
|
|
|
|
|
import eu.gaudian.translator.model.Language
|
|
|
|
|
import eu.gaudian.translator.model.TagCategory
|
|
|
|
|
import eu.gaudian.translator.model.VocabularyCategory
|
|
|
|
|
import eu.gaudian.translator.model.VocabularyStage
|
|
|
|
|
import eu.gaudian.translator.utils.findActivity
|
|
|
|
|
@@ -157,6 +156,13 @@ fun StartExerciseScreen(
|
|
|
|
|
.widthIn(max = 700.dp) // Keeps it from over-stretching on tablets
|
|
|
|
|
.fillMaxSize()
|
|
|
|
|
) {
|
|
|
|
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
|
|
|
|
val allLanguages by languageViewModel.allLanguages.collectAsState(initial = emptyList())
|
|
|
|
|
|
|
|
|
|
val availableLanguages = remember(availableLanguagesFromItems, allLanguages) {
|
|
|
|
|
allLanguages.filter { it.nameResId in availableLanguagesFromItems }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TopBarSection(
|
|
|
|
|
onBackClick = { navController.popBackStack() },
|
|
|
|
|
shuffleCards = exerciseConfig.shuffleCards,
|
|
|
|
|
@@ -166,30 +172,10 @@ fun StartExerciseScreen(
|
|
|
|
|
shuffleLanguagesEnabled = !isDirectionPreferenceSet,
|
|
|
|
|
trainingMode = exerciseConfig.trainingMode,
|
|
|
|
|
onTrainingModeChanged = { updateConfig(exerciseConfig.copy(trainingMode = it)) },
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
LazyColumn(
|
|
|
|
|
modifier = Modifier
|
|
|
|
|
.weight(1f)
|
|
|
|
|
.padding(horizontal = 24.dp),
|
|
|
|
|
verticalArrangement = Arrangement.spacedBy(32.dp)
|
|
|
|
|
) {
|
|
|
|
|
item { Spacer(modifier = Modifier.height(8.dp)) }
|
|
|
|
|
item {
|
|
|
|
|
LanguagePairSection(
|
|
|
|
|
selectedPairs = selectedLanguagePairs,
|
|
|
|
|
availableLanguageIds = availableLanguagesFromItems,
|
|
|
|
|
onPairsChanged = { updatedPairs ->
|
|
|
|
|
val hadPairs = selectedLanguagePairs.isNotEmpty()
|
|
|
|
|
selectedLanguagePairs = updatedPairs
|
|
|
|
|
if (updatedPairs.isNotEmpty()) {
|
|
|
|
|
selectedOriginLanguage = null
|
|
|
|
|
selectedTargetLanguage = null
|
|
|
|
|
updateConfig(exerciseConfig.copy(originLanguageId = null, targetLanguageId = null))
|
|
|
|
|
} else if (hadPairs) {
|
|
|
|
|
updateConfig(exerciseConfig.copy(originLanguageId = null, targetLanguageId = null))
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
selectedOriginLanguage = selectedOriginLanguage,
|
|
|
|
|
selectedTargetLanguage = selectedTargetLanguage,
|
|
|
|
|
languageSelectionEnabled = true,
|
|
|
|
|
availableLanguages = availableLanguages,
|
|
|
|
|
onOriginLanguageSelected = { language ->
|
|
|
|
|
if (language?.nameResId == selectedOriginLanguage?.nameResId) {
|
|
|
|
|
selectedOriginLanguage = null
|
|
|
|
|
@@ -215,11 +201,32 @@ fun StartExerciseScreen(
|
|
|
|
|
}
|
|
|
|
|
updateConfig(exerciseConfig.copy(targetLanguageId = language?.nameResId))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
LazyColumn(
|
|
|
|
|
modifier = Modifier
|
|
|
|
|
.weight(1f)
|
|
|
|
|
.padding(horizontal = 24.dp),
|
|
|
|
|
verticalArrangement = Arrangement.spacedBy(32.dp)
|
|
|
|
|
) {
|
|
|
|
|
item { Spacer(modifier = Modifier.height(8.dp)) }
|
|
|
|
|
item {
|
|
|
|
|
LanguagePairSection(
|
|
|
|
|
selectedPairs = selectedLanguagePairs,
|
|
|
|
|
availableLanguageIds = availableLanguagesFromItems,
|
|
|
|
|
onPairsChanged = { updatedPairs ->
|
|
|
|
|
val hadPairs = selectedLanguagePairs.isNotEmpty()
|
|
|
|
|
selectedLanguagePairs = updatedPairs
|
|
|
|
|
if (updatedPairs.isNotEmpty()) {
|
|
|
|
|
selectedOriginLanguage = null
|
|
|
|
|
selectedTargetLanguage = null
|
|
|
|
|
updateConfig(exerciseConfig.copy(originLanguageId = null, targetLanguageId = null))
|
|
|
|
|
} else if (hadPairs) {
|
|
|
|
|
updateConfig(exerciseConfig.copy(originLanguageId = null, targetLanguageId = null))
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
languageSelectionEnabled = true,
|
|
|
|
|
selectedPairsCount = selectedLanguagePairs.size,
|
|
|
|
|
selectedOriginLanguage = selectedOriginLanguage,
|
|
|
|
|
selectedTargetLanguage = selectedTargetLanguage
|
|
|
|
|
selectedPairsCount = selectedLanguagePairs.size
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
item {
|
|
|
|
|
@@ -298,7 +305,13 @@ fun TopBarSection(
|
|
|
|
|
onShuffleLanguagesChanged: (Boolean) -> Unit,
|
|
|
|
|
shuffleLanguagesEnabled: Boolean,
|
|
|
|
|
trainingMode: Boolean,
|
|
|
|
|
onTrainingModeChanged: (Boolean) -> Unit
|
|
|
|
|
onTrainingModeChanged: (Boolean) -> Unit,
|
|
|
|
|
selectedOriginLanguage: Language?,
|
|
|
|
|
selectedTargetLanguage: Language?,
|
|
|
|
|
languageSelectionEnabled: Boolean,
|
|
|
|
|
availableLanguages: List<Language>,
|
|
|
|
|
onOriginLanguageSelected: (Language?) -> Unit,
|
|
|
|
|
onTargetLanguageSelected: (Language?) -> Unit
|
|
|
|
|
) {
|
|
|
|
|
var showSettings by remember { mutableStateOf(false) }
|
|
|
|
|
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
|
|
|
|
@@ -357,6 +370,12 @@ fun TopBarSection(
|
|
|
|
|
shuffleLanguagesEnabled = shuffleLanguagesEnabled,
|
|
|
|
|
trainingMode = trainingMode,
|
|
|
|
|
onTrainingModeChanged = onTrainingModeChanged,
|
|
|
|
|
selectedOriginLanguage = selectedOriginLanguage,
|
|
|
|
|
selectedTargetLanguage = selectedTargetLanguage,
|
|
|
|
|
languageSelectionEnabled = languageSelectionEnabled,
|
|
|
|
|
availableLanguages = availableLanguages,
|
|
|
|
|
onOriginLanguageSelected = onOriginLanguageSelected,
|
|
|
|
|
onTargetLanguageSelected = onTargetLanguageSelected,
|
|
|
|
|
onDismiss = {
|
|
|
|
|
scope.launch { sheetState.hide() }.invokeOnCompletion {
|
|
|
|
|
if (!sheetState.isVisible) {
|
|
|
|
|
@@ -402,12 +421,7 @@ fun LanguagePairSection(
|
|
|
|
|
selectedPairs: List<Pair<Language, Language>>,
|
|
|
|
|
availableLanguageIds: Set<Int>,
|
|
|
|
|
onPairsChanged: (List<Pair<Language, Language>>) -> Unit,
|
|
|
|
|
onOriginLanguageSelected: (Language?) -> Unit,
|
|
|
|
|
onTargetLanguageSelected: (Language?) -> Unit,
|
|
|
|
|
languageSelectionEnabled: Boolean,
|
|
|
|
|
selectedPairsCount: Int,
|
|
|
|
|
selectedOriginLanguage: Language?,
|
|
|
|
|
selectedTargetLanguage: Language?
|
|
|
|
|
selectedPairsCount: Int
|
|
|
|
|
) {
|
|
|
|
|
val activity = androidx.compose.ui.platform.LocalContext.current.findActivity()
|
|
|
|
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
|
|
|
|
@@ -442,8 +456,17 @@ fun LanguagePairSection(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var isExpanded by remember { mutableStateOf(false) }
|
|
|
|
|
val displayedPairs = if (isExpanded) availablePairs else availablePairs.take(3)
|
|
|
|
|
|
|
|
|
|
Column {
|
|
|
|
|
SectionHeader(title = stringResource(R.string.language_pair))
|
|
|
|
|
SectionHeader(
|
|
|
|
|
title = stringResource(R.string.language_pair),
|
|
|
|
|
actionText = if (availablePairs.size > 3) {
|
|
|
|
|
if (isExpanded) stringResource(R.string.label_show_less) else stringResource(R.string.label_show_more)
|
|
|
|
|
} else null,
|
|
|
|
|
onActionClick = { isExpanded = !isExpanded }
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if (availablePairs.isEmpty()) {
|
|
|
|
|
Text(
|
|
|
|
|
@@ -456,7 +479,7 @@ fun LanguagePairSection(
|
|
|
|
|
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
|
|
|
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
|
|
|
|
) {
|
|
|
|
|
availablePairs.forEach { pair ->
|
|
|
|
|
displayedPairs.forEach { pair ->
|
|
|
|
|
val isSelected = selectedPairs.contains(pair)
|
|
|
|
|
LanguageChip(
|
|
|
|
|
text = "${pair.first.name} ⇄ ${pair.second.name}",
|
|
|
|
|
@@ -474,74 +497,6 @@ fun LanguagePairSection(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Spacer(modifier = Modifier.height(16.dp))
|
|
|
|
|
Text(
|
|
|
|
|
text = stringResource(R.string.label_language_direction),
|
|
|
|
|
style = MaterialTheme.typography.titleMedium,
|
|
|
|
|
fontWeight = FontWeight.Bold
|
|
|
|
|
)
|
|
|
|
|
Text(
|
|
|
|
|
text = stringResource(R.string.text_language_direction_explanation),
|
|
|
|
|
style = MaterialTheme.typography.bodyMedium,
|
|
|
|
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
|
|
|
|
)
|
|
|
|
|
if (!languageSelectionEnabled && selectedPairsCount > 0) {
|
|
|
|
|
Spacer(modifier = Modifier.height(8.dp))
|
|
|
|
|
Text(
|
|
|
|
|
text = stringResource(R.string.text_language_direction_disabled_with_pairs),
|
|
|
|
|
style = MaterialTheme.typography.bodyMedium,
|
|
|
|
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
Spacer(modifier = Modifier.height(12.dp))
|
|
|
|
|
Row(
|
|
|
|
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
|
|
|
|
modifier = Modifier.fillMaxWidth()
|
|
|
|
|
) {
|
|
|
|
|
Column(modifier = Modifier.weight(1f)) {
|
|
|
|
|
Text(
|
|
|
|
|
text = stringResource(R.string.label_origin_language),
|
|
|
|
|
style = MaterialTheme.typography.bodyMedium,
|
|
|
|
|
modifier = Modifier.padding(bottom = 8.dp)
|
|
|
|
|
)
|
|
|
|
|
eu.gaudian.translator.view.composable.SingleLanguageDropDown(
|
|
|
|
|
modifier = Modifier.fillMaxWidth(),
|
|
|
|
|
languageViewModel = languageViewModel,
|
|
|
|
|
selectedLanguage = selectedOriginLanguage,
|
|
|
|
|
onLanguageSelected = { language ->
|
|
|
|
|
if (selectedTargetLanguage?.nameResId == language.nameResId) return@SingleLanguageDropDown
|
|
|
|
|
onOriginLanguageSelected(language)
|
|
|
|
|
},
|
|
|
|
|
showNoneOption = true,
|
|
|
|
|
onNoneSelected = { onOriginLanguageSelected(null) },
|
|
|
|
|
alternateLanguages = availableLanguages,
|
|
|
|
|
restrictToAlternateLanguages = true,
|
|
|
|
|
enabled = languageSelectionEnabled
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
Column(modifier = Modifier.weight(1f)) {
|
|
|
|
|
Text(
|
|
|
|
|
text = stringResource(R.string.label_target_language),
|
|
|
|
|
style = MaterialTheme.typography.bodyMedium,
|
|
|
|
|
modifier = Modifier.padding(bottom = 8.dp)
|
|
|
|
|
)
|
|
|
|
|
eu.gaudian.translator.view.composable.SingleLanguageDropDown(
|
|
|
|
|
modifier = Modifier.fillMaxWidth(),
|
|
|
|
|
languageViewModel = languageViewModel,
|
|
|
|
|
selectedLanguage = selectedTargetLanguage,
|
|
|
|
|
onLanguageSelected = { language ->
|
|
|
|
|
if (selectedOriginLanguage?.nameResId == language.nameResId) return@SingleLanguageDropDown
|
|
|
|
|
onTargetLanguageSelected(language)
|
|
|
|
|
},
|
|
|
|
|
showNoneOption = true,
|
|
|
|
|
onNoneSelected = { onTargetLanguageSelected(null) },
|
|
|
|
|
alternateLanguages = availableLanguages,
|
|
|
|
|
restrictToAlternateLanguages = true,
|
|
|
|
|
enabled = languageSelectionEnabled
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -590,10 +545,25 @@ fun CategoriesSection(
|
|
|
|
|
val categories by categoryViewModel.categories.collectAsState(initial = emptyList())
|
|
|
|
|
|
|
|
|
|
Column {
|
|
|
|
|
SectionHeader(title = stringResource(R.string.label_categories))
|
|
|
|
|
val tagCategories = categories
|
|
|
|
|
var isExpanded by remember { mutableStateOf(false) }
|
|
|
|
|
val displayedCategories = if (isExpanded) tagCategories else tagCategories.take(3)
|
|
|
|
|
|
|
|
|
|
val tagCategories = categories.filterIsInstance<TagCategory>()
|
|
|
|
|
if (tagCategories.size > 15) {
|
|
|
|
|
SectionHeader(
|
|
|
|
|
title = stringResource(R.string.label_categories),
|
|
|
|
|
actionText = if (tagCategories.size > 3) {
|
|
|
|
|
if (isExpanded) stringResource(R.string.label_show_less) else stringResource(R.string.label_show_more)
|
|
|
|
|
} else null,
|
|
|
|
|
onActionClick = { isExpanded = !isExpanded }
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if (tagCategories.isEmpty()) {
|
|
|
|
|
Text(
|
|
|
|
|
text = stringResource(R.string.no_vocabulary_items_found_perhaps_try_changing_the_filters),
|
|
|
|
|
style = MaterialTheme.typography.bodyMedium,
|
|
|
|
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
|
|
|
|
)
|
|
|
|
|
} else if (tagCategories.size > 15) {
|
|
|
|
|
CategoryDropdown(
|
|
|
|
|
onCategorySelected = { selections ->
|
|
|
|
|
onCategoriesChanged(selections.filterNotNull())
|
|
|
|
|
@@ -610,7 +580,7 @@ fun CategoriesSection(
|
|
|
|
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
|
|
|
|
modifier = Modifier.fillMaxWidth()
|
|
|
|
|
) {
|
|
|
|
|
tagCategories.forEach { category ->
|
|
|
|
|
displayedCategories.forEach { category ->
|
|
|
|
|
val isSelected = selectedCategories.contains(category)
|
|
|
|
|
Surface(
|
|
|
|
|
shape = RoundedCornerShape(20.dp),
|
|
|
|
|
@@ -893,8 +863,17 @@ private fun StartExerciseSettingsBottomSheet(
|
|
|
|
|
shuffleLanguagesEnabled: Boolean,
|
|
|
|
|
trainingMode: Boolean,
|
|
|
|
|
onTrainingModeChanged: (Boolean) -> Unit,
|
|
|
|
|
selectedOriginLanguage: Language?,
|
|
|
|
|
selectedTargetLanguage: Language?,
|
|
|
|
|
languageSelectionEnabled: Boolean,
|
|
|
|
|
availableLanguages: List<Language>,
|
|
|
|
|
onOriginLanguageSelected: (Language?) -> Unit,
|
|
|
|
|
onTargetLanguageSelected: (Language?) -> Unit,
|
|
|
|
|
onDismiss: () -> Unit
|
|
|
|
|
) {
|
|
|
|
|
val activity = androidx.compose.ui.platform.LocalContext.current.findActivity()
|
|
|
|
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
|
|
|
|
|
|
|
|
|
ModalBottomSheet(
|
|
|
|
|
onDismissRequest = onDismiss,
|
|
|
|
|
sheetState = sheetState
|
|
|
|
|
@@ -903,7 +882,7 @@ private fun StartExerciseSettingsBottomSheet(
|
|
|
|
|
modifier = Modifier
|
|
|
|
|
.fillMaxWidth()
|
|
|
|
|
.padding(horizontal = 20.dp, vertical = 16.dp),
|
|
|
|
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
|
|
|
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
|
|
|
|
) {
|
|
|
|
|
Text(
|
|
|
|
|
text = stringResource(R.string.options),
|
|
|
|
|
@@ -941,6 +920,73 @@ private fun StartExerciseSettingsBottomSheet(
|
|
|
|
|
checked = trainingMode,
|
|
|
|
|
onCheckedChange = onTrainingModeChanged
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Language Direction Section
|
|
|
|
|
Text(
|
|
|
|
|
text = stringResource(R.string.label_language_direction),
|
|
|
|
|
style = MaterialTheme.typography.titleMedium,
|
|
|
|
|
fontWeight = FontWeight.Bold
|
|
|
|
|
)
|
|
|
|
|
Text(
|
|
|
|
|
text = stringResource(R.string.text_language_direction_explanation),
|
|
|
|
|
style = MaterialTheme.typography.bodyMedium,
|
|
|
|
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
|
|
|
|
)
|
|
|
|
|
if (!languageSelectionEnabled) {
|
|
|
|
|
Text(
|
|
|
|
|
text = stringResource(R.string.text_language_direction_disabled_with_pairs),
|
|
|
|
|
style = MaterialTheme.typography.bodyMedium,
|
|
|
|
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
Spacer(modifier = Modifier.height(12.dp))
|
|
|
|
|
Row(
|
|
|
|
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
|
|
|
|
modifier = Modifier.fillMaxWidth()
|
|
|
|
|
) {
|
|
|
|
|
Column(modifier = Modifier.weight(1f)) {
|
|
|
|
|
Text(
|
|
|
|
|
text = stringResource(R.string.label_origin_language),
|
|
|
|
|
style = MaterialTheme.typography.bodyMedium,
|
|
|
|
|
modifier = Modifier.padding(bottom = 8.dp)
|
|
|
|
|
)
|
|
|
|
|
eu.gaudian.translator.view.composable.SingleLanguageDropDown(
|
|
|
|
|
modifier = Modifier.fillMaxWidth(),
|
|
|
|
|
languageViewModel = languageViewModel,
|
|
|
|
|
selectedLanguage = selectedOriginLanguage,
|
|
|
|
|
onLanguageSelected = { language ->
|
|
|
|
|
if (selectedTargetLanguage?.nameResId == language.nameResId) return@SingleLanguageDropDown
|
|
|
|
|
onOriginLanguageSelected(language)
|
|
|
|
|
},
|
|
|
|
|
showNoneOption = true,
|
|
|
|
|
onNoneSelected = { onOriginLanguageSelected(null) },
|
|
|
|
|
alternateLanguages = availableLanguages,
|
|
|
|
|
restrictToAlternateLanguages = true,
|
|
|
|
|
enabled = languageSelectionEnabled
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
Column(modifier = Modifier.weight(1f)) {
|
|
|
|
|
Text(
|
|
|
|
|
text = stringResource(R.string.label_target_language),
|
|
|
|
|
style = MaterialTheme.typography.bodyMedium,
|
|
|
|
|
modifier = Modifier.padding(bottom = 8.dp)
|
|
|
|
|
)
|
|
|
|
|
eu.gaudian.translator.view.composable.SingleLanguageDropDown(
|
|
|
|
|
modifier = Modifier.fillMaxWidth(),
|
|
|
|
|
languageViewModel = languageViewModel,
|
|
|
|
|
selectedLanguage = selectedTargetLanguage,
|
|
|
|
|
onLanguageSelected = { language ->
|
|
|
|
|
if (selectedOriginLanguage?.nameResId == language.nameResId) return@SingleLanguageDropDown
|
|
|
|
|
onTargetLanguageSelected(language)
|
|
|
|
|
},
|
|
|
|
|
showNoneOption = true,
|
|
|
|
|
onNoneSelected = { onTargetLanguageSelected(null) },
|
|
|
|
|
alternateLanguages = availableLanguages,
|
|
|
|
|
restrictToAlternateLanguages = true,
|
|
|
|
|
enabled = languageSelectionEnabled
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|