From eed000e01e86c9dab889b0587547fa83a76decd4 Mon Sep 17 00:00:00 2001 From: malkoG Date: Wed, 30 Sep 2026 02:26:04 +0900 Subject: [PATCH] Hide language filter row by default, add settings toggle The "All languages" chip row above the Home and Explore feeds takes up vertical space for users who don't filter by language. Add a show_language_filter preference (default off) with a Settings switch. When the row is hidden, any previously selected language is reset so it cannot keep filtering invisibly. Co-Authored-By: Claude Sonnet 5.5 --- .../android/data/local/PreferencesManager.kt | 11 ++++++ .../pub/hackers/android/ui/HackersPubApp.kt | 3 ++ .../ui/screens/explore/ExploreScreen.kt | 20 +++++++---- .../ui/screens/settings/SettingsScreen.kt | 35 +++++++++++++++++++ .../ui/screens/settings/SettingsViewModel.kt | 10 +++++- .../ui/screens/timeline/TimelineScreen.kt | 20 +++++++---- app/src/main/res/values/strings.xml | 2 ++ 7 files changed, 88 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/pub/hackers/android/data/local/PreferencesManager.kt b/app/src/main/java/pub/hackers/android/data/local/PreferencesManager.kt index 14254caa..8804b5c5 100644 --- a/app/src/main/java/pub/hackers/android/data/local/PreferencesManager.kt +++ b/app/src/main/java/pub/hackers/android/data/local/PreferencesManager.kt @@ -29,6 +29,7 @@ class PreferencesManager @Inject constructor( private val FONT_SIZE_MULTIPLIER = intPreferencesKey("font_size_multiplier") // stored as percentage (100 = 1.0x) private val RECENT_SEARCHES = stringPreferencesKey("recent_searches") private val THEME_MODE = stringPreferencesKey("theme_mode") + private val SHOW_LANGUAGE_FILTER = booleanPreferencesKey("show_language_filter") private const val MAX_RECENT_SEARCHES = 10 } @@ -74,6 +75,16 @@ class PreferencesManager @Inject constructor( } } + val showLanguageFilter: Flow = context.preferencesDataStore.data.map { prefs -> + prefs[SHOW_LANGUAGE_FILTER] ?: false + } + + suspend fun setShowLanguageFilter(value: Boolean) { + context.preferencesDataStore.edit { prefs -> + prefs[SHOW_LANGUAGE_FILTER] = value + } + } + suspend fun setConfirmBeforeDelete(value: Boolean) { context.preferencesDataStore.edit { prefs -> prefs[CONFIRM_BEFORE_DELETE] = value diff --git a/app/src/main/java/pub/hackers/android/ui/HackersPubApp.kt b/app/src/main/java/pub/hackers/android/ui/HackersPubApp.kt index c46bb95a..8ba4cff6 100644 --- a/app/src/main/java/pub/hackers/android/ui/HackersPubApp.kt +++ b/app/src/main/java/pub/hackers/android/ui/HackersPubApp.kt @@ -182,6 +182,7 @@ fun HackersPubApp( val isLoggedInState by viewModel.isLoggedIn.collectAsState(initial = null as Boolean?) val fontSizePercent by viewModel.preferencesManager.fontSizePercent.collectAsState(initial = 100) + val showLanguageFilter by viewModel.preferencesManager.showLanguageFilter.collectAsState(initial = false) val hasUnread by viewModel.hasUnread.collectAsState() var selectedHomeFeed by remember { mutableStateOf(HomeFeed.TIMELINE) } @@ -364,6 +365,7 @@ fun HackersPubApp( .collectAsState() TimelineScreen( tabRetapped = tabRetapped, + showLanguageFilter = showLanguageFilter, selectedHomeFeed = selectedHomeFeed, onHomeFeedSelected = ::navigateHomeFeed, onPostClick = { postId -> @@ -419,6 +421,7 @@ fun HackersPubApp( composable(Screen.Explore.route) { ExploreScreen( + showLanguageFilter = showLanguageFilter, onPostClick = { postId -> navController.navigate(DetailScreen.PostDetail.createRoute(postId)) }, diff --git a/app/src/main/java/pub/hackers/android/ui/screens/explore/ExploreScreen.kt b/app/src/main/java/pub/hackers/android/ui/screens/explore/ExploreScreen.kt index 00f10692..bf9d5abc 100644 --- a/app/src/main/java/pub/hackers/android/ui/screens/explore/ExploreScreen.kt +++ b/app/src/main/java/pub/hackers/android/ui/screens/explore/ExploreScreen.kt @@ -59,6 +59,7 @@ fun ExploreScreen( onEditClick: (String) -> Unit = {}, onSignInClick: () -> Unit, isLoggedIn: Boolean, + showLanguageFilter: Boolean = false, viewModel: ExploreViewModel = hiltViewModel() ) { val items = viewModel.posts.collectAsLazyPagingItems() @@ -72,6 +73,11 @@ fun ExploreScreen( val colors = LocalAppColors.current val typography = LocalAppTypography.current + // A language selected before the filter row was hidden would otherwise keep filtering invisibly. + LaunchedEffect(showLanguageFilter) { + if (!showLanguageFilter) viewModel.selectLanguage(null) + } + // Scroll to top when tab changes. LaunchedEffect(selectedTab) { listState.scrollToItem(0) @@ -162,13 +168,15 @@ fun ExploreScreen( modifier = Modifier.padding(horizontal = 16.dp) ) - LanguageFilterRow( - languages = uiState.suggestedFilterLanguages, - selectedLanguage = uiState.selectedLanguage, - onLanguageSelected = viewModel::selectLanguage, - ) + if (showLanguageFilter) { + LanguageFilterRow( + languages = uiState.suggestedFilterLanguages, + selectedLanguage = uiState.selectedLanguage, + onLanguageSelected = viewModel::selectLanguage, + ) - HorizontalDivider(color = colors.divider) + HorizontalDivider(color = colors.divider) + } Box(modifier = Modifier.fillMaxSize()) { val refresh = items.loadState.refresh diff --git a/app/src/main/java/pub/hackers/android/ui/screens/settings/SettingsScreen.kt b/app/src/main/java/pub/hackers/android/ui/screens/settings/SettingsScreen.kt index ed1067d6..9861b5df 100644 --- a/app/src/main/java/pub/hackers/android/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/java/pub/hackers/android/ui/screens/settings/SettingsScreen.kt @@ -23,6 +23,7 @@ import androidx.compose.material.icons.filled.Bookmark import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Fingerprint import androidx.compose.material.icons.filled.Info +import androidx.compose.material.icons.filled.Language import androidx.compose.material.icons.filled.Login import androidx.compose.material.icons.filled.Palette import androidx.compose.material3.AlertDialog @@ -37,6 +38,7 @@ import androidx.compose.material3.RadioButtonDefaults import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -390,6 +392,39 @@ fun SettingsScreen( HorizontalDivider(color = colors.divider, thickness = 1.dp) + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { viewModel.setShowLanguageFilter(!uiState.showLanguageFilter) } + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Filled.Language, + contentDescription = null, + tint = colors.textSecondary + ) + Spacer(modifier = Modifier.width(16.dp)) + Column(modifier = Modifier.weight(1f)) { + Text( + text = stringResource(R.string.settings_show_language_filter), + style = typography.bodyLarge, + color = colors.textPrimary + ) + Text( + text = stringResource(R.string.settings_show_language_filter_description), + style = typography.bodyMedium, + color = colors.textSecondary + ) + } + Switch( + checked = uiState.showLanguageFilter, + onCheckedChange = viewModel::setShowLanguageFilter + ) + } + + HorizontalDivider(color = colors.divider, thickness = 1.dp) + Row( modifier = Modifier .fillMaxWidth() diff --git a/app/src/main/java/pub/hackers/android/ui/screens/settings/SettingsViewModel.kt b/app/src/main/java/pub/hackers/android/ui/screens/settings/SettingsViewModel.kt index d6b93aa7..93ac4725 100644 --- a/app/src/main/java/pub/hackers/android/ui/screens/settings/SettingsViewModel.kt +++ b/app/src/main/java/pub/hackers/android/ui/screens/settings/SettingsViewModel.kt @@ -45,6 +45,7 @@ data class SettingsUiState( val useInAppBrowser: Boolean = true, val fontSizePercent: Int = 100, val themeMode: ThemeMode = ThemeMode.SYSTEM, + val showLanguageFilter: Boolean = false, val passkeys: List = emptyList(), val accountId: String? = null, val isLoadingPasskeys: Boolean = false, @@ -112,6 +113,7 @@ class SettingsViewModel @Inject constructor( val inAppBrowser = preferencesManager.useInAppBrowser.first() val fontSize = preferencesManager.fontSizePercent.first() val theme = preferencesManager.themeMode.first() + val showLanguageFilter = preferencesManager.showLanguageFilter.first() _uiState.update { it.copy( @@ -120,7 +122,8 @@ class SettingsViewModel @Inject constructor( timelineMaxLength = maxLength, useInAppBrowser = inAppBrowser, fontSizePercent = fontSize, - themeMode = theme + themeMode = theme, + showLanguageFilter = showLanguageFilter ) } } @@ -156,6 +159,11 @@ class SettingsViewModel @Inject constructor( viewModelScope.launch { preferencesManager.setThemeMode(value) } } + fun setShowLanguageFilter(value: Boolean) { + _uiState.update { it.copy(showLanguageFilter = value) } + viewModelScope.launch { preferencesManager.setShowLanguageFilter(value) } + } + fun signOut() { viewModelScope.launch { fcmTokenManager.unregisterCurrentToken() diff --git a/app/src/main/java/pub/hackers/android/ui/screens/timeline/TimelineScreen.kt b/app/src/main/java/pub/hackers/android/ui/screens/timeline/TimelineScreen.kt index cafb6b89..1008013d 100644 --- a/app/src/main/java/pub/hackers/android/ui/screens/timeline/TimelineScreen.kt +++ b/app/src/main/java/pub/hackers/android/ui/screens/timeline/TimelineScreen.kt @@ -73,6 +73,7 @@ fun TimelineScreen( selectedHomeFeed: HomeFeed = HomeFeed.TIMELINE, onHomeFeedSelected: (HomeFeed) -> Unit = {}, tabRetapped: Long = 0L, + showLanguageFilter: Boolean = false, userAvatarUrl: String? = null, viewModel: TimelineViewModel = hiltViewModel() ) { @@ -85,6 +86,11 @@ fun TimelineScreen( val bookmarkRemovedMessage = stringResource(R.string.bookmark_removed) val colors = LocalAppColors.current + // A language selected before the filter row was hidden would otherwise keep filtering invisibly. + LaunchedEffect(showLanguageFilter) { + if (!showLanguageFilter) viewModel.selectLanguage(null) + } + // Refresh draft count when screen becomes visible (e.g., returning from Drafts) LaunchedEffect(Unit) { viewModel.loadDraftCount() @@ -233,12 +239,14 @@ fun TimelineScreen( .fillMaxSize() .padding(paddingValues) ) { - LanguageFilterRow( - languages = uiState.suggestedFilterLanguages, - selectedLanguage = uiState.selectedLanguage, - onLanguageSelected = viewModel::selectLanguage, - ) - HorizontalDivider(color = colors.divider) + if (showLanguageFilter) { + LanguageFilterRow( + languages = uiState.suggestedFilterLanguages, + selectedLanguage = uiState.selectedLanguage, + onLanguageSelected = viewModel::selectLanguage, + ) + HorizontalDivider(color = colors.divider) + } Box(modifier = Modifier.weight(1f)) { val refresh = items.loadState.refresh diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c978daec..1f9112f7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -208,6 +208,8 @@ Post preview length Unlimited Theme + Show language filter + Show language chips above the Home and Explore feeds Follow system Light Dark