diff --git a/feature/browse/src/main/kotlin/dev/anmitali/nook/feature/browse/BrowseScreen.kt b/feature/browse/src/main/kotlin/dev/anmitali/nook/feature/browse/BrowseScreen.kt index e68dbe8..3058152 100644 --- a/feature/browse/src/main/kotlin/dev/anmitali/nook/feature/browse/BrowseScreen.kt +++ b/feature/browse/src/main/kotlin/dev/anmitali/nook/feature/browse/BrowseScreen.kt @@ -43,6 +43,7 @@ import androidx.compose.material.icons.automirrored.filled.Sort import androidx.compose.material.icons.filled.Storage import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.Button +import androidx.compose.material3.Checkbox import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.DrawerValue import androidx.compose.material3.DropdownMenu @@ -120,6 +121,7 @@ fun BrowseScreen( val volumes by viewModel.volumes.collectAsState() val sortOrder by viewModel.sortOrder.collectAsState() val groupBy by viewModel.groupBy.collectAsState() + val showHiddenFiles by viewModel.showHiddenFiles.collectAsState() val isSearchActive by viewModel.isSearchActive.collectAsState() val searchQuery by viewModel.searchQuery.collectAsState() val searchResults by viewModel.searchResults.collectAsState() @@ -266,9 +268,11 @@ fun BrowseScreen( onDismiss = { showSortMenu = false }, sortOrder = sortOrder, groupBy = groupBy, + showHiddenFiles = showHiddenFiles, onSortByChanged = viewModel::onSortByChanged, onDirectionToggle = viewModel::onSortDirectionToggled, onGroupByChanged = viewModel::onGroupByChanged, + onToggleShowHiddenFiles = viewModel::onToggleShowHiddenFiles, ) } }, @@ -500,9 +504,11 @@ private fun SortMenu( onDismiss: () -> Unit, sortOrder: SortOrder, groupBy: GroupBy, + showHiddenFiles: Boolean, onSortByChanged: (SortBy) -> Unit, onDirectionToggle: () -> Unit, onGroupByChanged: (GroupBy) -> Unit, + onToggleShowHiddenFiles: () -> Unit, ) { DropdownMenu(expanded = expanded, onDismissRequest = onDismiss) { listOf( @@ -534,6 +540,14 @@ private fun SortMenu( onClick = { onGroupByChanged(group) }, ) } + HorizontalDivider() + DropdownMenuItem( + text = { Text(stringResource(R.string.browse_show_hidden_files)) }, + leadingIcon = { + Checkbox(checked = showHiddenFiles, onCheckedChange = null) + }, + onClick = onToggleShowHiddenFiles, + ) } } diff --git a/feature/browse/src/main/kotlin/dev/anmitali/nook/feature/browse/BrowseViewModel.kt b/feature/browse/src/main/kotlin/dev/anmitali/nook/feature/browse/BrowseViewModel.kt index 082d7fe..98500f7 100644 --- a/feature/browse/src/main/kotlin/dev/anmitali/nook/feature/browse/BrowseViewModel.kt +++ b/feature/browse/src/main/kotlin/dev/anmitali/nook/feature/browse/BrowseViewModel.kt @@ -28,6 +28,7 @@ import dev.anmitali.nook.core.domain.WrongPasswordException import dev.anmitali.nook.core.model.Bookmark import dev.anmitali.nook.core.model.FileConflictPolicy import dev.anmitali.nook.core.model.FileDetails +import dev.anmitali.nook.core.model.FileGroup import dev.anmitali.nook.core.model.FileItem import dev.anmitali.nook.core.model.FileOperationProgress import dev.anmitali.nook.core.model.GroupBy @@ -113,6 +114,9 @@ class BrowseViewModel @Inject constructor( private val _isSearching = MutableStateFlow(false) val isSearching: StateFlow = _isSearching.asStateFlow() + private val _showHiddenFiles = MutableStateFlow(false) + val showHiddenFiles: StateFlow = _showHiddenFiles.asStateFlow() + private val _bookmarks = MutableStateFlow>(emptyList()) val bookmarks: StateFlow> = _bookmarks.asStateFlow() @@ -183,6 +187,11 @@ class BrowseViewModel @Inject constructor( applySortAndGroup() } + fun onToggleShowHiddenFiles() { + _showHiddenFiles.update { !it } + applySortAndGroup() + } + fun onSearchActivated() { _isSearchActive.value = true } @@ -207,7 +216,9 @@ class BrowseViewModel @Inject constructor( delay(SEARCH_DEBOUNCE_MILLIS) _isSearching.value = true searchFilesUseCase(currentPath, query) - .onEach { results -> _searchResults.value = results } + .onEach { results -> + _searchResults.value = if (_showHiddenFiles.value) results else results.filterNot { it.isHidden } + } .onCompletion { _isSearching.value = false } .catch { _isSearching.value = false } .launchIn(this) @@ -459,8 +470,13 @@ class BrowseViewModel @Inject constructor( private fun applySortAndGroup() { val state = _uiState.value as? BrowseUiState.Content ?: return - val sorted = sortFilesUseCase(rawItems, _sortOrder.value) - _uiState.value = state.copy(groups = groupFilesUseCase(sorted, _groupBy.value)) + _uiState.value = state.copy(groups = buildGroups(rawItems)) + } + + private fun buildGroups(items: List): List { + val visible = if (_showHiddenFiles.value) items else items.filterNot { it.isHidden } + val sorted = sortFilesUseCase(visible, _sortOrder.value) + return groupFilesUseCase(sorted, _groupBy.value) } private fun checkPermissionAndLoad(path: String) { @@ -474,11 +490,10 @@ class BrowseViewModel @Inject constructor( listFilesUseCase(path) .onEach { items -> rawItems = items - val sorted = sortFilesUseCase(items, _sortOrder.value) _uiState.value = BrowseUiState.Content( currentPath = path, breadcrumbs = path.split(File.separatorChar).filter { it.isNotBlank() }, - groups = groupFilesUseCase(sorted, _groupBy.value), + groups = buildGroups(items), ) } .catch { throwable -> diff --git a/feature/browse/src/main/res/values-ru/strings.xml b/feature/browse/src/main/res/values-ru/strings.xml index c1e14a9..6128448 100644 --- a/feature/browse/src/main/res/values-ru/strings.xml +++ b/feature/browse/src/main/res/values-ru/strings.xml @@ -61,6 +61,7 @@ Без группировки Группировать по типу Группировать по дате + Показывать скрытые файлы Папки Изображения diff --git a/feature/browse/src/main/res/values/strings.xml b/feature/browse/src/main/res/values/strings.xml index 0ceeca2..2119529 100644 --- a/feature/browse/src/main/res/values/strings.xml +++ b/feature/browse/src/main/res/values/strings.xml @@ -61,6 +61,7 @@ No grouping Group by type Group by date + Show hidden files Folders Images