refactor CategoryDropdown and improve vocabulary filtering with multi-category support
This commit is contained in:
@@ -15,29 +15,34 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.TagCategory
|
import eu.gaudian.translator.model.TagCategory
|
||||||
import eu.gaudian.translator.model.VocabularyCategory
|
import eu.gaudian.translator.model.VocabularyCategory
|
||||||
import eu.gaudian.translator.model.VocabularyFilter
|
import eu.gaudian.translator.model.VocabularyFilter
|
||||||
import eu.gaudian.translator.ui.theme.ThemePreviews
|
import eu.gaudian.translator.ui.theme.ThemePreviews
|
||||||
|
import eu.gaudian.translator.utils.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppButton
|
import eu.gaudian.translator.view.composable.AppButton
|
||||||
import eu.gaudian.translator.view.composable.AppCheckbox
|
import eu.gaudian.translator.view.composable.AppCheckbox
|
||||||
import eu.gaudian.translator.view.composable.AppDropdownMenuItem
|
import eu.gaudian.translator.view.composable.AppDropdownMenuItem
|
||||||
import eu.gaudian.translator.view.composable.AppIcons
|
import eu.gaudian.translator.view.composable.AppIcons
|
||||||
import eu.gaudian.translator.view.composable.AppOutlinedButton
|
import eu.gaudian.translator.view.composable.AppOutlinedButton
|
||||||
import eu.gaudian.translator.view.composable.AppOutlinedTextField
|
import eu.gaudian.translator.view.composable.AppOutlinedTextField
|
||||||
|
import eu.gaudian.translator.viewmodel.CategoryViewModel
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,7 +100,7 @@ fun CategoryDropdownContent(
|
|||||||
text = when {
|
text = when {
|
||||||
state.selectedCategories.isEmpty() -> stringResource(R.string.text_select_category)
|
state.selectedCategories.isEmpty() -> stringResource(R.string.text_select_category)
|
||||||
state.selectedCategories.size == 1 -> state.selectedCategories.first()?.name
|
state.selectedCategories.size == 1 -> state.selectedCategories.first()?.name
|
||||||
?: stringResource(R.string.text_none)
|
?: stringResource(R.string.label_no_category)
|
||||||
else -> stringResource(R.string.text_2d_categories_selected, state.selectedCategories.size)
|
else -> stringResource(R.string.text_2d_categories_selected, state.selectedCategories.size)
|
||||||
},
|
},
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
@@ -139,7 +144,7 @@ fun CategoryDropdownContent(
|
|||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
}
|
}
|
||||||
Text(stringResource(R.string.text_none))
|
Text(stringResource(R.string.label_no_category))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
@@ -270,13 +275,13 @@ fun CategoryDropdownContent(
|
|||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun CategoryDropdown(
|
fun CategoryDropdown(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
initialCategoryId: Int? = null,
|
initialCategoryId: Int? = null,
|
||||||
onCategorySelected: (List<VocabularyCategory?>) -> Unit,
|
onCategorySelected: (List<VocabularyCategory?>) -> Unit,
|
||||||
noneSelectable: Boolean? = true,
|
noneSelectable: Boolean? = true,
|
||||||
multipleSelectable: Boolean = false,
|
multipleSelectable: Boolean = false,
|
||||||
onlyLists: Boolean = false,
|
onlyLists: Boolean = false,
|
||||||
addCategory: Boolean = false,
|
addCategory: Boolean = false,
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
) {
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
var selectedCategories by remember {
|
var selectedCategories by remember {
|
||||||
@@ -284,9 +289,12 @@ fun CategoryDropdown(
|
|||||||
}
|
}
|
||||||
var newCategoryName by remember { mutableStateOf("") }
|
var newCategoryName by remember { mutableStateOf("") }
|
||||||
|
|
||||||
// For production use, this would come from ViewModel
|
val activity = LocalContext.current.findActivity()
|
||||||
// For preview, we'll use empty list or pass via state
|
val categoryViewModel: CategoryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val categories by remember { mutableStateOf(emptyList<VocabularyCategory>()) }
|
val categories by categoryViewModel.categories.collectAsState(initial = emptyList())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Find initial category
|
// Find initial category
|
||||||
val initialCategory = remember(categories, initialCategoryId) {
|
val initialCategory = remember(categories, initialCategoryId) {
|
||||||
|
|||||||
@@ -8,9 +8,6 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
@@ -18,7 +15,6 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.TagCategory
|
|
||||||
import eu.gaudian.translator.model.VocabularyCategory
|
import eu.gaudian.translator.model.VocabularyCategory
|
||||||
import eu.gaudian.translator.utils.findActivity
|
import eu.gaudian.translator.utils.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppDialog
|
import eu.gaudian.translator.view.composable.AppDialog
|
||||||
@@ -34,9 +30,6 @@ fun CategorySelectionDialog(
|
|||||||
val categoryViewModel: CategoryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val categoryViewModel: CategoryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
val categories by categoryViewModel.categories.collectAsState(initial = emptyList())
|
val categories by categoryViewModel.categories.collectAsState(initial = emptyList())
|
||||||
var selectedCategories by remember { mutableStateOf<List<VocabularyCategory?>>(emptyList()) }
|
|
||||||
var newCategoryName by remember { mutableStateOf("") }
|
|
||||||
var expanded by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
AppDialog(
|
AppDialog(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
@@ -45,21 +38,8 @@ fun CategorySelectionDialog(
|
|||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
// Dropdown button and menu
|
// Dropdown button and menu
|
||||||
CategoryDropdownContent(
|
CategoryDropdown(
|
||||||
state = CategoryDropdownState(
|
onCategorySelected = onCategorySelected,
|
||||||
expanded = expanded,
|
|
||||||
selectedCategories = selectedCategories,
|
|
||||||
newCategoryName = newCategoryName,
|
|
||||||
categories = categories,
|
|
||||||
),
|
|
||||||
onExpand = { isExpanded -> expanded = isExpanded },
|
|
||||||
onCategorySelected = { selectedCategories = it },
|
|
||||||
onNewCategoryNameChange = { newCategoryName = it },
|
|
||||||
onAddCategory = { name ->
|
|
||||||
val newCategory = TagCategory(id = 0, name = name.trim())
|
|
||||||
categoryViewModel.createCategory(newCategory)
|
|
||||||
newCategoryName = ""
|
|
||||||
},
|
|
||||||
noneSelectable = false,
|
noneSelectable = false,
|
||||||
multipleSelectable = true,
|
multipleSelectable = true,
|
||||||
onlyLists = true,
|
onlyLists = true,
|
||||||
@@ -79,10 +59,11 @@ fun CategorySelectionDialog(
|
|||||||
|
|
||||||
DialogButton(
|
DialogButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
onCategorySelected(selectedCategories)
|
// The selected categories are handled by CategoryDropdown's internal state
|
||||||
|
// and passed to onCategorySelected callback
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
},
|
},
|
||||||
enabled = selectedCategories.isNotEmpty()
|
enabled = true // Always enabled since CategoryDropdown handles validation
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.label_confirm))
|
Text(stringResource(R.string.label_confirm))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,9 +55,8 @@ fun StartExerciseDialog(
|
|||||||
// Map displayed Language to its DB id (lid) using position mapping from load
|
// Map displayed Language to its DB id (lid) using position mapping from load
|
||||||
var languageIdMap by remember { mutableStateOf<Map<Language, Int>>(emptyMap()) }
|
var languageIdMap by remember { mutableStateOf<Map<Language, Int>>(emptyMap()) }
|
||||||
var selectedLanguages by remember { mutableStateOf<List<Language>>(emptyList()) }
|
var selectedLanguages by remember { mutableStateOf<List<Language>>(emptyList()) }
|
||||||
var selectedCategories by remember { mutableStateOf<List<VocabularyCategory>>(emptyList()) }
|
|
||||||
var selectedStages by remember { mutableStateOf<List<VocabularyStage>>(emptyList()) }
|
var selectedStages by remember { mutableStateOf<List<VocabularyStage>>(emptyList()) }
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var selectedCategories by remember { mutableStateOf<List<VocabularyCategory>>(emptyList()) }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
@@ -87,19 +86,10 @@ fun StartExerciseDialog(
|
|||||||
},
|
},
|
||||||
languages
|
languages
|
||||||
)
|
)
|
||||||
CategoryDropdownContent(
|
CategoryDropdown(
|
||||||
state = CategoryDropdownState(
|
|
||||||
expanded = expanded,
|
|
||||||
selectedCategories = selectedCategories,
|
|
||||||
newCategoryName = "",
|
|
||||||
categories = categories,
|
|
||||||
),
|
|
||||||
onExpand = { isExpanded -> expanded = isExpanded },
|
|
||||||
onCategorySelected = { cats ->
|
onCategorySelected = { cats ->
|
||||||
selectedCategories = cats.filterIsInstance<VocabularyCategory>()
|
selectedCategories = cats.filterIsInstance<VocabularyCategory>()
|
||||||
},
|
},
|
||||||
onNewCategoryNameChange = {},
|
|
||||||
onAddCategory = {},
|
|
||||||
multipleSelectable = true,
|
multipleSelectable = true,
|
||||||
onlyLists = false, // Show both filters and lists
|
onlyLists = false, // Show both filters and lists
|
||||||
addCategory = false,
|
addCategory = false,
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.TagCategory
|
|
||||||
import eu.gaudian.translator.model.VocabularyCategory
|
import eu.gaudian.translator.model.VocabularyCategory
|
||||||
import eu.gaudian.translator.model.VocabularyItem
|
import eu.gaudian.translator.model.VocabularyItem
|
||||||
import eu.gaudian.translator.utils.findActivity
|
import eu.gaudian.translator.utils.findActivity
|
||||||
@@ -54,8 +53,6 @@ fun VocabularyReviewScreen(
|
|||||||
val selectedItems = remember { mutableStateListOf<VocabularyItem>() }
|
val selectedItems = remember { mutableStateListOf<VocabularyItem>() }
|
||||||
val duplicates = remember { mutableStateListOf<Boolean>() }
|
val duplicates = remember { mutableStateListOf<Boolean>() }
|
||||||
var selectedCategories by remember { mutableStateOf<List<VocabularyCategory?>>(emptyList()) }
|
var selectedCategories by remember { mutableStateOf<List<VocabularyCategory?>>(emptyList()) }
|
||||||
var newCategoryName by remember { mutableStateOf("") }
|
|
||||||
var expanded by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
LaunchedEffect(generatedItems) {
|
LaunchedEffect(generatedItems) {
|
||||||
val duplicateResults = vocabularyViewModel.findDuplicates(generatedItems)
|
val duplicateResults = vocabularyViewModel.findDuplicates(generatedItems)
|
||||||
@@ -134,21 +131,8 @@ fun VocabularyReviewScreen(
|
|||||||
style = MaterialTheme.typography.labelLarge,
|
style = MaterialTheme.typography.labelLarge,
|
||||||
modifier = Modifier.padding(8.dp)
|
modifier = Modifier.padding(8.dp)
|
||||||
)
|
)
|
||||||
CategoryDropdownContent(
|
CategoryDropdown(
|
||||||
state = CategoryDropdownState(
|
|
||||||
expanded = expanded,
|
|
||||||
selectedCategories = selectedCategories,
|
|
||||||
newCategoryName = newCategoryName,
|
|
||||||
categories = categories,
|
|
||||||
),
|
|
||||||
onExpand = { isExpanded -> expanded = isExpanded },
|
|
||||||
onCategorySelected = { selectedCategories = it },
|
onCategorySelected = { selectedCategories = it },
|
||||||
onNewCategoryNameChange = { newCategoryName = it },
|
|
||||||
onAddCategory = { name ->
|
|
||||||
val newCategory = TagCategory(id = 0, name = name.trim())
|
|
||||||
categoryViewModel.createCategory(newCategory)
|
|
||||||
newCategoryName = ""
|
|
||||||
},
|
|
||||||
noneSelectable = false,
|
noneSelectable = false,
|
||||||
multipleSelectable = true,
|
multipleSelectable = true,
|
||||||
onlyLists = true,
|
onlyLists = true,
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ fun VocabularyCardHost(
|
|||||||
listOf(currentVocabularyItem),
|
listOf(currentVocabularyItem),
|
||||||
it.mapNotNull { category -> category?.id }
|
it.mapNotNull { category -> category?.id }
|
||||||
)
|
)
|
||||||
showCategoryDialog = false
|
//showCategoryDialog = false
|
||||||
},
|
},
|
||||||
onDismissRequest = { showCategoryDialog = false }
|
onDismissRequest = { showCategoryDialog = false }
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ private data class VocabularyFilterState(
|
|||||||
val searchQuery: String = "",
|
val searchQuery: String = "",
|
||||||
val selectedStage: VocabularyStage? = null,
|
val selectedStage: VocabularyStage? = null,
|
||||||
val sortOrder: SortOrder = SortOrder.NEWEST_FIRST,
|
val sortOrder: SortOrder = SortOrder.NEWEST_FIRST,
|
||||||
val categoryId: Int? = null,
|
val categoryIds: List<Int> = emptyList(),
|
||||||
val dueTodayOnly: Boolean = false,
|
val dueTodayOnly: Boolean = false,
|
||||||
val selectedLanguageIds: List<Int> = emptyList(),
|
val selectedLanguageIds: List<Int> = emptyList(),
|
||||||
val selectedWordClass: String? = null
|
val selectedWordClass: String? = null
|
||||||
@@ -133,7 +133,7 @@ fun VocabularyListScreen(
|
|||||||
var filterState by rememberSaveable {
|
var filterState by rememberSaveable {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
VocabularyFilterState(
|
VocabularyFilterState(
|
||||||
categoryId = categoryId,
|
categoryIds = categoryId?.let { listOf(it) } ?: emptyList(),
|
||||||
dueTodayOnly = showDueTodayOnly == true,
|
dueTodayOnly = showDueTodayOnly == true,
|
||||||
selectedStage = stage
|
selectedStage = stage
|
||||||
)
|
)
|
||||||
@@ -142,7 +142,7 @@ fun VocabularyListScreen(
|
|||||||
val isFilterActive by remember(filterState) {
|
val isFilterActive by remember(filterState) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
filterState.selectedStage != null ||
|
filterState.selectedStage != null ||
|
||||||
(filterState.categoryId != null && filterState.categoryId != 0) ||
|
filterState.categoryIds.isNotEmpty() ||
|
||||||
filterState.dueTodayOnly ||
|
filterState.dueTodayOnly ||
|
||||||
filterState.selectedLanguageIds.isNotEmpty() ||
|
filterState.selectedLanguageIds.isNotEmpty() ||
|
||||||
!filterState.selectedWordClass.isNullOrBlank()
|
!filterState.selectedWordClass.isNullOrBlank()
|
||||||
@@ -165,7 +165,7 @@ fun VocabularyListScreen(
|
|||||||
vocabularyViewModel.filterVocabularyItems(
|
vocabularyViewModel.filterVocabularyItems(
|
||||||
languages = filterState.selectedLanguageIds,
|
languages = filterState.selectedLanguageIds,
|
||||||
query = filterState.searchQuery.takeIf { it.isNotBlank() },
|
query = filterState.searchQuery.takeIf { it.isNotBlank() },
|
||||||
categoryId = filterState.categoryId,
|
categoryIds = filterState.categoryIds,
|
||||||
stage = filterState.selectedStage,
|
stage = filterState.selectedStage,
|
||||||
wordClass = filterState.selectedWordClass,
|
wordClass = filterState.selectedWordClass,
|
||||||
dueTodayOnly = filterState.dueTodayOnly,
|
dueTodayOnly = filterState.dueTodayOnly,
|
||||||
@@ -179,7 +179,7 @@ fun VocabularyListScreen(
|
|||||||
|
|
||||||
LaunchedEffect(categoryId, showDueTodayOnly, stage) {
|
LaunchedEffect(categoryId, showDueTodayOnly, stage) {
|
||||||
filterState = filterState.copy(
|
filterState = filterState.copy(
|
||||||
categoryId = categoryId,
|
categoryIds = categoryId?.let { listOf(it) } ?: emptyList(),
|
||||||
dueTodayOnly = showDueTodayOnly == true,
|
dueTodayOnly = showDueTodayOnly == true,
|
||||||
selectedStage = stage
|
selectedStage = stage
|
||||||
)
|
)
|
||||||
@@ -374,7 +374,7 @@ fun VocabularyListScreen(
|
|||||||
FilterSortBottomSheet(
|
FilterSortBottomSheet(
|
||||||
currentFilterState = filterState,
|
currentFilterState = filterState,
|
||||||
onDismiss = { showFilterSheet = false },
|
onDismiss = { showFilterSheet = false },
|
||||||
onApplyFilters = { newState ->
|
onApplyFilters = { newState ->
|
||||||
filterState = newState
|
filterState = newState
|
||||||
showFilterSheet = false
|
showFilterSheet = false
|
||||||
scope.launch { lazyListState.scrollToItem(0) }
|
scope.launch { lazyListState.scrollToItem(0) }
|
||||||
@@ -382,7 +382,8 @@ fun VocabularyListScreen(
|
|||||||
languageViewModel = languageViewModel,
|
languageViewModel = languageViewModel,
|
||||||
languagesPresent = allLanguages.filter { it.nameResId in languagesPresent },
|
languagesPresent = allLanguages.filter { it.nameResId in languagesPresent },
|
||||||
hideCategory = categoryId != null && categoryId != 0,
|
hideCategory = categoryId != null && categoryId != 0,
|
||||||
hideStage = stage != null
|
hideStage = stage != null,
|
||||||
|
categoryViewModel = categoryViewModel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,7 +395,7 @@ fun VocabularyListScreen(
|
|||||||
selectedItems,
|
selectedItems,
|
||||||
it.mapNotNull { category -> category?.id }
|
it.mapNotNull { category -> category?.id }
|
||||||
)
|
)
|
||||||
showCategoryDialog = false
|
//showCategoryDialog = false
|
||||||
},
|
},
|
||||||
onDismissRequest = { showCategoryDialog = false }
|
onDismissRequest = { showCategoryDialog = false }
|
||||||
)
|
)
|
||||||
@@ -807,11 +808,12 @@ private fun FilterSortBottomSheet(
|
|||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
onApplyFilters: (VocabularyFilterState) -> Unit,
|
onApplyFilters: (VocabularyFilterState) -> Unit,
|
||||||
hideCategory: Boolean = false,
|
hideCategory: Boolean = false,
|
||||||
hideStage: Boolean = false
|
hideStage: Boolean = false,
|
||||||
|
categoryViewModel: CategoryViewModel
|
||||||
) {
|
) {
|
||||||
var selectedStage by rememberSaveable { mutableStateOf(currentFilterState.selectedStage) }
|
var selectedStage by rememberSaveable { mutableStateOf(currentFilterState.selectedStage) }
|
||||||
var dueTodayOnly by rememberSaveable { mutableStateOf(currentFilterState.dueTodayOnly) }
|
var dueTodayOnly by rememberSaveable { mutableStateOf(currentFilterState.dueTodayOnly) }
|
||||||
var selectedCategoryId by rememberSaveable { mutableStateOf(currentFilterState.categoryId) }
|
var selectedCategoryIds by rememberSaveable { mutableStateOf(currentFilterState.categoryIds) }
|
||||||
var selectedLanguageIds by rememberSaveable { mutableStateOf(currentFilterState.selectedLanguageIds) }
|
var selectedLanguageIds by rememberSaveable { mutableStateOf(currentFilterState.selectedLanguageIds) }
|
||||||
var selectedWordClass by rememberSaveable { mutableStateOf(currentFilterState.selectedWordClass) }
|
var selectedWordClass by rememberSaveable { mutableStateOf(currentFilterState.selectedWordClass) }
|
||||||
|
|
||||||
@@ -840,7 +842,7 @@ private fun FilterSortBottomSheet(
|
|||||||
TextButton(onClick = {
|
TextButton(onClick = {
|
||||||
if (!hideStage) selectedStage = null
|
if (!hideStage) selectedStage = null
|
||||||
dueTodayOnly = false
|
dueTodayOnly = false
|
||||||
if (!hideCategory) selectedCategoryId = null
|
if (!hideCategory) selectedCategoryIds = emptyList()
|
||||||
selectedLanguageIds = emptyList()
|
selectedLanguageIds = emptyList()
|
||||||
selectedWordClass = null
|
selectedWordClass = null
|
||||||
}) {
|
}) {
|
||||||
@@ -853,7 +855,7 @@ private fun FilterSortBottomSheet(
|
|||||||
currentFilterState.copy(
|
currentFilterState.copy(
|
||||||
selectedStage = selectedStage,
|
selectedStage = selectedStage,
|
||||||
dueTodayOnly = dueTodayOnly,
|
dueTodayOnly = dueTodayOnly,
|
||||||
categoryId = selectedCategoryId,
|
categoryIds = selectedCategoryIds,
|
||||||
selectedLanguageIds = selectedLanguageIds,
|
selectedLanguageIds = selectedLanguageIds,
|
||||||
selectedWordClass = selectedWordClass
|
selectedWordClass = selectedWordClass
|
||||||
)
|
)
|
||||||
@@ -893,16 +895,18 @@ private fun FilterSortBottomSheet(
|
|||||||
Text(stringResource(R.string.label_category), style = MaterialTheme.typography.titleMedium)
|
Text(stringResource(R.string.label_category), style = MaterialTheme.typography.titleMedium)
|
||||||
Spacer(Modifier.height(8.dp))
|
Spacer(Modifier.height(8.dp))
|
||||||
CategoryDropdown(
|
CategoryDropdown(
|
||||||
initialCategoryId = selectedCategoryId,
|
initialCategoryId = selectedCategoryIds.firstOrNull(),
|
||||||
onCategorySelected = { categories ->
|
onCategorySelected = { categories ->
|
||||||
selectedCategoryId = categories.firstOrNull()?.id
|
selectedCategoryIds = categories.mapNotNull { it?.id }
|
||||||
}
|
},
|
||||||
|
multipleSelectable = true,
|
||||||
|
noneSelectable = false
|
||||||
)
|
)
|
||||||
Spacer(Modifier.height(16.dp))
|
Spacer(Modifier.height(16.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hideStage) {
|
if (!hideStage) {
|
||||||
Text(stringResource(R.string.filter_by_stage), style = MaterialTheme.typography.titleMedium)
|
Text(stringResource(R.string.label_filter_by_stage), style = MaterialTheme.typography.titleMedium)
|
||||||
FlowRow(
|
FlowRow(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
@@ -955,6 +959,7 @@ private fun FilterSortBottomSheet(
|
|||||||
fun FilterSortBottomSheetPreview() {
|
fun FilterSortBottomSheetPreview() {
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
val categoryViewModel: CategoryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
FilterSortBottomSheet(
|
FilterSortBottomSheet(
|
||||||
currentFilterState = VocabularyFilterState(),
|
currentFilterState = VocabularyFilterState(),
|
||||||
languageViewModel = languageViewModel,
|
languageViewModel = languageViewModel,
|
||||||
@@ -962,6 +967,7 @@ fun FilterSortBottomSheetPreview() {
|
|||||||
onDismiss = {},
|
onDismiss = {},
|
||||||
onApplyFilters = {},
|
onApplyFilters = {},
|
||||||
hideCategory = false,
|
hideCategory = false,
|
||||||
hideStage = false
|
hideStage = false,
|
||||||
|
categoryViewModel = categoryViewModel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -299,6 +299,7 @@ fun VocabularySortingItem(
|
|||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
val languageConfigViewModel: LanguageConfigViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageConfigViewModel: LanguageConfigViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
val categoryViewModel: CategoryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
var wordFirst by remember { mutableStateOf(item.wordFirst) }
|
var wordFirst by remember { mutableStateOf(item.wordFirst) }
|
||||||
var wordSecond by remember { mutableStateOf(item.wordSecond) }
|
var wordSecond by remember { mutableStateOf(item.wordSecond) }
|
||||||
var selectedCategories by remember { mutableStateOf<List<Int>>(emptyList()) }
|
var selectedCategories by remember { mutableStateOf<List<Int>>(emptyList()) }
|
||||||
@@ -313,6 +314,7 @@ fun VocabularySortingItem(
|
|||||||
var articlesLangSecond by remember { mutableStateOf(emptySet<String>()) }
|
var articlesLangSecond by remember { mutableStateOf(emptySet<String>()) }
|
||||||
|
|
||||||
var showDuplicateDialog by remember { mutableStateOf(false) }
|
var showDuplicateDialog by remember { mutableStateOf(false) }
|
||||||
|
val categories by categoryViewModel.categories.collectAsState(initial = emptyList())
|
||||||
|
|
||||||
// NEW: Calculate if the item is valid for the "Done" button in faulty mode
|
// NEW: Calculate if the item is valid for the "Done" button in faulty mode
|
||||||
val isItemNowValid by remember(wordFirst, wordSecond, langFirst, langSecond) {
|
val isItemNowValid by remember(wordFirst, wordSecond, langFirst, langSecond) {
|
||||||
|
|||||||
@@ -360,7 +360,7 @@
|
|||||||
<string name="days_2d">%1$d Tage</string>
|
<string name="days_2d">%1$d Tage</string>
|
||||||
<string name="progress_by_category">Fortschritt nach Kategorie</string>
|
<string name="progress_by_category">Fortschritt nach Kategorie</string>
|
||||||
<string name="label_apply_filters">Filter anwenden</string>
|
<string name="label_apply_filters">Filter anwenden</string>
|
||||||
<string name="filter_by_stage">Nach Stufe filtern</string>
|
<string name="label_filter_by_stage">Nach Stufe filtern</string>
|
||||||
<string name="label_category">Kategorie</string>
|
<string name="label_category">Kategorie</string>
|
||||||
<string name="language">Sprache</string>
|
<string name="language">Sprache</string>
|
||||||
<string name="label_clear_all">Alle löschen</string>
|
<string name="label_clear_all">Alle löschen</string>
|
||||||
|
|||||||
@@ -358,7 +358,7 @@
|
|||||||
<string name="days_2d">%1$d dias</string>
|
<string name="days_2d">%1$d dias</string>
|
||||||
<string name="progress_by_category">Progresso por Categoria</string>
|
<string name="progress_by_category">Progresso por Categoria</string>
|
||||||
<string name="label_apply_filters">Aplicar Filtros</string>
|
<string name="label_apply_filters">Aplicar Filtros</string>
|
||||||
<string name="filter_by_stage">Filtrar por Estágio</string>
|
<string name="label_filter_by_stage">Filtrar por Estágio</string>
|
||||||
<string name="label_category">Categoria</string>
|
<string name="label_category">Categoria</string>
|
||||||
<string name="language">Idioma</string>
|
<string name="language">Idioma</string>
|
||||||
<string name="label_clear_all">Limpar Tudo</string>
|
<string name="label_clear_all">Limpar Tudo</string>
|
||||||
|
|||||||
@@ -140,7 +140,7 @@
|
|||||||
<string name="fetching_grammar_details">Fetching Grammar Details</string>
|
<string name="fetching_grammar_details">Fetching Grammar Details</string>
|
||||||
|
|
||||||
<string name="filter_and_sort">Filter and Sort</string>
|
<string name="filter_and_sort">Filter and Sort</string>
|
||||||
<string name="filter_by_stage">Filter by Stage</string>
|
<string name="label_filter_by_stage">Filter by Stage</string>
|
||||||
<string name="filter_by_word_type">Filter by Word Type</string>
|
<string name="filter_by_word_type">Filter by Word Type</string>
|
||||||
|
|
||||||
<string name="find_translations">Find Translations</string>
|
<string name="find_translations">Find Translations</string>
|
||||||
@@ -1039,4 +1039,5 @@
|
|||||||
<string name="duplicate">Duplicate</string>
|
<string name="duplicate">Duplicate</string>
|
||||||
<string name="hint_scan_hint_title">Finding the right AI model</string>
|
<string name="hint_scan_hint_title">Finding the right AI model</string>
|
||||||
<string name="hint_translate_how_it_works">How translation works</string>
|
<string name="hint_translate_how_it_works">How translation works</string>
|
||||||
|
<string name="label_no_category">None</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user