implement language direction and shuffling logic in StartExerciseScreen
This commit is contained in:
@@ -56,6 +56,8 @@ fun BaseLanguageDropDown(
|
||||
enableMultipleSelection: Boolean = false,
|
||||
onLanguagesSelected: (List<Language>) -> Unit = {},
|
||||
alternateLanguages: List<Language> = emptyList(),
|
||||
restrictToAlternateLanguages: Boolean = false,
|
||||
enabled: Boolean = true,
|
||||
iconEnabled: Boolean = true,
|
||||
noBorder: Boolean = false,
|
||||
) {
|
||||
@@ -68,9 +70,13 @@ fun BaseLanguageDropDown(
|
||||
var tempSelection by remember { mutableStateOf<List<Language>>(emptyList()) }
|
||||
var selectedLanguagesCount by remember { mutableIntStateOf(if (enableMultipleSelection) tempSelection.size else 0) }
|
||||
|
||||
val languages = remember(alternateLanguages, defaultLanguages) {
|
||||
val languages = remember(alternateLanguages, defaultLanguages, restrictToAlternateLanguages) {
|
||||
if (restrictToAlternateLanguages) {
|
||||
alternateLanguages
|
||||
} else {
|
||||
alternateLanguages.ifEmpty { defaultLanguages }
|
||||
}
|
||||
}
|
||||
|
||||
val buttonText = when {
|
||||
enableMultipleSelection && selectedLanguagesCount > 0 -> stringResource(
|
||||
@@ -90,6 +96,7 @@ fun BaseLanguageDropDown(
|
||||
AppOutlinedButton(
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
onClick = { expanded = true },
|
||||
enabled = enabled,
|
||||
contentPadding = if (!iconEnabled) PaddingValues(0.dp) else PaddingValues(horizontal = 16.dp, vertical = 8.dp),
|
||||
borderColor = if (noBorder) Color.Unspecified else null
|
||||
) {
|
||||
@@ -222,7 +229,12 @@ fun BaseLanguageDropDown(
|
||||
val isSearching = searchText.isNotBlank()
|
||||
|
||||
if (isSearching) {
|
||||
val searchResults = (favoriteLanguages + languageHistory + languages)
|
||||
val searchBase = if (restrictToAlternateLanguages) {
|
||||
alternateLanguages
|
||||
} else {
|
||||
favoriteLanguages + languageHistory + languages
|
||||
}
|
||||
val searchResults = searchBase
|
||||
.distinctBy { it.nameResId }
|
||||
.filter { language ->
|
||||
val matchesName = language.name.contains(searchText, ignoreCase = true)
|
||||
@@ -237,6 +249,16 @@ fun BaseLanguageDropDown(
|
||||
searchResults.forEach { language -> SingleSelectItem(language) }
|
||||
}
|
||||
|
||||
} else if (restrictToAlternateLanguages) {
|
||||
val sortedAlternate = alternateLanguages.sortedBy { it.name }
|
||||
if (enableMultipleSelection) {
|
||||
DropdownHeader(text = stringResource(R.string.text_all_languages))
|
||||
sortedAlternate.forEach { language -> MultiSelectItem(language) }
|
||||
} else {
|
||||
DropdownHeader(text = stringResource(R.string.text_all_languages))
|
||||
sortedAlternate.forEach { language -> SingleSelectItem(language) }
|
||||
}
|
||||
|
||||
} else if (alternateLanguages.isNotEmpty()) {
|
||||
val sortedAlternate = alternateLanguages.sortedBy { it.name }
|
||||
if (enableMultipleSelection) {
|
||||
@@ -458,7 +480,9 @@ fun SingleLanguageDropDown(
|
||||
onAutoSelected: () -> Unit = {},
|
||||
showNoneOption: Boolean = false,
|
||||
onNoneSelected: () -> Unit = {},
|
||||
alternateLanguages: List<Language> = emptyList()
|
||||
alternateLanguages: List<Language> = emptyList(),
|
||||
restrictToAlternateLanguages: Boolean = false,
|
||||
enabled: Boolean = true
|
||||
) {
|
||||
val languageHistory by languageViewModel.languageHistory.collectAsState()
|
||||
|
||||
@@ -477,6 +501,10 @@ fun SingleLanguageDropDown(
|
||||
showNoneOption = showNoneOption,
|
||||
onNoneSelected = onNoneSelected,
|
||||
enableMultipleSelection = false,
|
||||
alternateLanguages = alternateLanguages
|
||||
alternateLanguages = alternateLanguages,
|
||||
restrictToAlternateLanguages = restrictToAlternateLanguages,
|
||||
enabled = enabled,
|
||||
iconEnabled = enabled,
|
||||
noBorder = !enabled
|
||||
)
|
||||
}
|
||||
@@ -89,6 +89,7 @@ fun StartExerciseScreen(
|
||||
|
||||
var selectedOriginLanguage by remember { mutableStateOf<Language?>(null) }
|
||||
var selectedTargetLanguage by remember { mutableStateOf<Language?>(null) }
|
||||
val isDirectionPreferenceSet = selectedOriginLanguage != null || selectedTargetLanguage != null
|
||||
|
||||
val selectedPairsIds = remember(selectedLanguagePairs) {
|
||||
selectedLanguagePairs.map { it.first.nameResId to it.second.nameResId }
|
||||
@@ -115,6 +116,15 @@ fun StartExerciseScreen(
|
||||
val itemsToShow by filteredItemsFlow.collectAsState(initial = emptyList())
|
||||
val totalItemCount = itemsToShow.size
|
||||
|
||||
val availableLanguagesFromItems = remember(itemsToShow, selectedPairsIds) {
|
||||
val ids = if (selectedPairsIds.isNotEmpty()) {
|
||||
selectedPairsIds.flatMap { pair -> listOf(pair.first, pair.second) }.toSet()
|
||||
} else {
|
||||
itemsToShow.flatMap { listOfNotNull(it.languageFirstId, it.languageSecondId) }.toSet()
|
||||
}
|
||||
ids
|
||||
}
|
||||
|
||||
var amount by remember { mutableStateOf(0) }
|
||||
androidx.compose.runtime.LaunchedEffect(totalItemCount) {
|
||||
amount = totalItemCount
|
||||
@@ -140,6 +150,7 @@ fun StartExerciseScreen(
|
||||
onShuffleCardsChanged = { updateConfig(exerciseConfig.copy(shuffleCards = it)) },
|
||||
shuffleLanguages = exerciseConfig.shuffleLanguages,
|
||||
onShuffleLanguagesChanged = { updateConfig(exerciseConfig.copy(shuffleLanguages = it)) },
|
||||
shuffleLanguagesEnabled = !isDirectionPreferenceSet,
|
||||
trainingMode = exerciseConfig.trainingMode,
|
||||
onTrainingModeChanged = { updateConfig(exerciseConfig.copy(trainingMode = it)) },
|
||||
)
|
||||
@@ -154,21 +165,46 @@ fun StartExerciseScreen(
|
||||
item {
|
||||
LanguagePairSection(
|
||||
selectedPairs = selectedLanguagePairs,
|
||||
onPairsChanged = { selectedLanguagePairs = it },
|
||||
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))
|
||||
}
|
||||
},
|
||||
onOriginLanguageSelected = { language ->
|
||||
if (language?.nameResId == selectedOriginLanguage?.nameResId) {
|
||||
selectedOriginLanguage = null
|
||||
updateConfig(exerciseConfig.copy(originLanguageId = null))
|
||||
} else {
|
||||
selectedOriginLanguage = language
|
||||
if (selectedTargetLanguage?.nameResId == language?.nameResId) {
|
||||
selectedTargetLanguage = null
|
||||
updateConfig(exerciseConfig.copy(targetLanguageId = null))
|
||||
}
|
||||
updateConfig(exerciseConfig.copy(originLanguageId = language?.nameResId))
|
||||
}
|
||||
},
|
||||
onTargetLanguageSelected = { language ->
|
||||
if (language?.nameResId == selectedTargetLanguage?.nameResId) {
|
||||
selectedTargetLanguage = null
|
||||
updateConfig(exerciseConfig.copy(targetLanguageId = null))
|
||||
} else {
|
||||
selectedTargetLanguage = language
|
||||
if (selectedOriginLanguage?.nameResId == language?.nameResId) {
|
||||
selectedOriginLanguage = null
|
||||
updateConfig(exerciseConfig.copy(originLanguageId = null))
|
||||
}
|
||||
updateConfig(exerciseConfig.copy(targetLanguageId = language?.nameResId))
|
||||
}
|
||||
},
|
||||
languageSelectionEnabled = true,
|
||||
selectedPairsCount = selectedLanguagePairs.size,
|
||||
selectedOriginLanguage = selectedOriginLanguage,
|
||||
selectedTargetLanguage = selectedTargetLanguage
|
||||
)
|
||||
@@ -249,6 +285,7 @@ fun TopBarSection(
|
||||
onShuffleCardsChanged: (Boolean) -> Unit,
|
||||
shuffleLanguages: Boolean,
|
||||
onShuffleLanguagesChanged: (Boolean) -> Unit,
|
||||
shuffleLanguagesEnabled: Boolean,
|
||||
trainingMode: Boolean,
|
||||
onTrainingModeChanged: (Boolean) -> Unit
|
||||
) {
|
||||
@@ -306,6 +343,7 @@ fun TopBarSection(
|
||||
onShuffleCardsChanged = onShuffleCardsChanged,
|
||||
shuffleLanguages = shuffleLanguages,
|
||||
onShuffleLanguagesChanged = onShuffleLanguagesChanged,
|
||||
shuffleLanguagesEnabled = shuffleLanguagesEnabled,
|
||||
trainingMode = trainingMode,
|
||||
onTrainingModeChanged = onTrainingModeChanged,
|
||||
onDismiss = {
|
||||
@@ -348,9 +386,12 @@ fun SectionHeader(title: String, actionText: String? = null, onActionClick: () -
|
||||
@Composable
|
||||
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?
|
||||
) {
|
||||
@@ -360,9 +401,8 @@ fun LanguagePairSection(
|
||||
val languagesPresent by vocabularyViewModel.languagesPresent.collectAsState(initial = emptySet())
|
||||
val allLanguages by languageViewModel.allLanguages.collectAsState(initial = emptyList())
|
||||
|
||||
val availableLanguages = remember(languagesPresent, allLanguages) {
|
||||
val presentIds = languagesPresent.filterNotNull().toSet()
|
||||
allLanguages.filter { it.nameResId in presentIds }
|
||||
val availableLanguages = remember(availableLanguageIds, allLanguages) {
|
||||
allLanguages.filter { it.nameResId in availableLanguageIds }
|
||||
}
|
||||
|
||||
val allItems by vocabularyViewModel.vocabularyItems.collectAsState(initial = emptyList())
|
||||
@@ -433,6 +473,14 @@ fun LanguagePairSection(
|
||||
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),
|
||||
@@ -448,9 +496,15 @@ fun LanguagePairSection(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
languageViewModel = languageViewModel,
|
||||
selectedLanguage = selectedOriginLanguage,
|
||||
onLanguageSelected = onOriginLanguageSelected,
|
||||
onLanguageSelected = { language ->
|
||||
if (selectedTargetLanguage?.nameResId == language.nameResId) return@SingleLanguageDropDown
|
||||
onOriginLanguageSelected(language)
|
||||
},
|
||||
showNoneOption = true,
|
||||
alternateLanguages = availableLanguages
|
||||
onNoneSelected = { onOriginLanguageSelected(null) },
|
||||
alternateLanguages = availableLanguages,
|
||||
restrictToAlternateLanguages = true,
|
||||
enabled = languageSelectionEnabled
|
||||
)
|
||||
}
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
@@ -463,9 +517,15 @@ fun LanguagePairSection(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
languageViewModel = languageViewModel,
|
||||
selectedLanguage = selectedTargetLanguage,
|
||||
onLanguageSelected = onTargetLanguageSelected,
|
||||
onLanguageSelected = { language ->
|
||||
if (selectedOriginLanguage?.nameResId == language.nameResId) return@SingleLanguageDropDown
|
||||
onTargetLanguageSelected(language)
|
||||
},
|
||||
showNoneOption = true,
|
||||
alternateLanguages = availableLanguages
|
||||
onNoneSelected = { onTargetLanguageSelected(null) },
|
||||
alternateLanguages = availableLanguages,
|
||||
restrictToAlternateLanguages = true,
|
||||
enabled = languageSelectionEnabled
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -807,6 +867,7 @@ private fun StartExerciseSettingsBottomSheet(
|
||||
onShuffleCardsChanged: (Boolean) -> Unit,
|
||||
shuffleLanguages: Boolean,
|
||||
onShuffleLanguagesChanged: (Boolean) -> Unit,
|
||||
shuffleLanguagesEnabled: Boolean,
|
||||
trainingMode: Boolean,
|
||||
onTrainingModeChanged: (Boolean) -> Unit,
|
||||
onDismiss: () -> Unit
|
||||
@@ -835,9 +896,22 @@ private fun StartExerciseSettingsBottomSheet(
|
||||
OptionItemSwitch(
|
||||
title = stringResource(R.string.text_shuffle_languages),
|
||||
description = stringResource(R.string.text_shuffle_languages_description),
|
||||
checked = shuffleLanguages,
|
||||
onCheckedChange = onShuffleLanguagesChanged
|
||||
checked = shuffleLanguages && shuffleLanguagesEnabled,
|
||||
onCheckedChange = { enabled ->
|
||||
if (shuffleLanguagesEnabled) {
|
||||
onShuffleLanguagesChanged(enabled)
|
||||
} else {
|
||||
onShuffleLanguagesChanged(false)
|
||||
}
|
||||
}
|
||||
)
|
||||
if (!shuffleLanguagesEnabled) {
|
||||
Text(
|
||||
text = stringResource(R.string.text_shuffle_languages_disabled_by_direction),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
OptionItemSwitch(
|
||||
title = stringResource(R.string.label_training_mode),
|
||||
description = stringResource(R.string.text_training_mode_description),
|
||||
|
||||
@@ -680,6 +680,7 @@
|
||||
<string name="label_language_direction">Sprachenrichtung
|
||||
</string>
|
||||
<string name="text_language_direction_explanation">Du kannst eine optionale Einstellung vornehmen, welche Sprache zuerst oder zweit kommen soll.</string>
|
||||
<string name="text_language_direction_disabled_with_pairs">Entferne die Sprachpaar-Auswahl, um eine Richtung zu wählen.</string>
|
||||
<string name="label_guessing_exercise">Raten</string>
|
||||
<string name="label_spelling_exercise">Rechtschreibung</string>
|
||||
<string name="label_multiple_choice_exercise">Multiple Choice</string>
|
||||
@@ -687,6 +688,7 @@
|
||||
<string name="text_due_today_only_description">Nur Karten anzeigen, die heute fällig sind.</string>
|
||||
<string name="text_shuffle_card_order_description">Kartenmischung</string>
|
||||
<string name="text_shuffle_languages_description">Mische die Reihenfolge der Sprachen. Beeinflusst nicht deine Sprachrichtungs-Einstellungen.</string>
|
||||
<string name="text_shuffle_languages_disabled_by_direction">Deaktiviere die Sprachrichtungs-Einstellung, um das Mischen zu aktivieren.</string>
|
||||
<string name="label_conjugation">Konjugation: %1$s</string>
|
||||
<string name="label_collapse">Einklappen</string>
|
||||
<string name="label_expand">Ausklappen</string>
|
||||
|
||||
@@ -685,6 +685,7 @@
|
||||
<string name="text_due_today_only_description">Apenas perguntar cartas que estão a vencer hoje.</string>
|
||||
<string name="text_shuffle_card_order_description">Embaralhar Ordem das Cartas</string>
|
||||
<string name="text_shuffle_languages_description">Embaralhar qual idioma vem primeiro. Não afeta as preferências de direção do idioma.</string>
|
||||
<string name="text_shuffle_languages_disabled_by_direction">Desative a preferência de direção do idioma para habilitar o embaralhamento.</string>
|
||||
<string name="label_conjugation">Conjugação: %1$s</string>
|
||||
<string name="label_collapse">Recolher</string>
|
||||
<string name="label_expand">Expandir</string>
|
||||
@@ -844,6 +845,7 @@
|
||||
<string name="text_failed_to_fetch_manifest">Falha ao buscar informações de download sobre dicionários disponíveis: %1$s</string>
|
||||
<string name="text_translation_instructions">Defina o modelo para tradução e dê instruções opcionais sobre como traduzir.</string>
|
||||
<string name="text_language_direction_explanation">Você pode definir uma preferência opcional sobre qual idioma deve vir primeiro ou segundo.</string>
|
||||
<string name="text_language_direction_disabled_with_pairs">Limpe a seleção de pares de idiomas para escolher uma direção.</string>
|
||||
<string name="label_all_categories">Todas as Categorias</string>
|
||||
<string name="text_description_dictionary_prompt">Defina um modelo para gerar conteúdo do dicionário e dê instruções opcionais.</string>
|
||||
<string name="hint_vocabulary_progress_hint_title">Acompanhamento de Progresso de Vocabulário</string>
|
||||
|
||||
@@ -824,6 +824,7 @@
|
||||
<string name="text_label_word">Enter a word\n</string>
|
||||
<string name="text_language_code">Language Code</string>
|
||||
<string name="text_language_direction_explanation">You can set an optional preference which language should come first or second.</string>
|
||||
<string name="text_language_direction_disabled_with_pairs">Clear language pair selection to choose a direction.</string>
|
||||
<string name="text_language_options">Language Options</string>
|
||||
<string name="text_last_7_days">Last 7 Days</string>
|
||||
<string name="text_let_ai_find_vocabulary_for_you">Let AI find vocabulary for you!</string>
|
||||
@@ -900,6 +901,7 @@
|
||||
<string name="text_shuffle_card_order_description">Shuffle Card Order</string>
|
||||
<string name="text_shuffle_languages">Shuffle Languages</string>
|
||||
<string name="text_shuffle_languages_description">Shuffle what language comes first. Does not affect language direction preferences.</string>
|
||||
<string name="text_shuffle_languages_disabled_by_direction">Disable language direction preference to enable shuffling.</string>
|
||||
<string name="text_shuffle_questions">Shuffle questions</string>
|
||||
<string name="text_some_items_are_in_the_wrong_category">Some items are in the wrong category.</string>
|
||||
<string name="text_stage_2d">Stage %1$s</string>
|
||||
|
||||
Reference in New Issue
Block a user