From d48d97879bda590bbc88f4c083cc0fe02f85b82f Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Mon, 2 Feb 2026 20:41:24 +0100 Subject: [PATCH] Add show folders toggle to web app (#1598) --- apps/mobile-app/app/(tabs)/items/index.tsx | 30 +++++++++- .../Main/Pages/Items/Home.razor | 57 ++++++++++++++++--- .../Resources/Pages/Main/Items/Home.en.resx | 6 +- 3 files changed, 83 insertions(+), 10 deletions(-) diff --git a/apps/mobile-app/app/(tabs)/items/index.tsx b/apps/mobile-app/app/(tabs)/items/index.tsx index 740a3611c..2cafabdb5 100644 --- a/apps/mobile-app/app/(tabs)/items/index.tsx +++ b/apps/mobile-app/app/(tabs)/items/index.tsx @@ -1,4 +1,5 @@ import MaterialIcons from '@expo/vector-icons/MaterialIcons'; +import AsyncStorage from '@react-native-async-storage/async-storage'; import { useNavigation } from '@react-navigation/native'; import * as Haptics from 'expo-haptics'; import { useRouter, useLocalSearchParams } from 'expo-router'; @@ -36,6 +37,11 @@ import { SkeletonLoader } from '@/components/ui/SkeletonLoader'; import { useApp } from '@/context/AppContext'; import { useDb } from '@/context/DbContext'; +/** + * Storage key for the show folders preference. + */ +const SHOW_FOLDERS_STORAGE_KEY = 'items-show-folders'; + /** * Filter types for the items list. */ @@ -123,6 +129,24 @@ export default function ItemsScreen(): React.ReactNode { } }, [itemUrl]); + // Load saved showFolderItems preference from AsyncStorage + useEffect(() => { + /** + * Load the show folders preference from AsyncStorage. + */ + const loadShowFoldersPreference = async (): Promise => { + try { + const stored = await AsyncStorage.getItem(SHOW_FOLDERS_STORAGE_KEY); + if (stored !== null) { + setShowFolderItems(stored === 'true'); + } + } catch { + // Ignore storage errors, use default value + } + }; + loadShowFoldersPreference(); + }, []); + /** * Get folders with item counts for display. */ @@ -799,7 +823,11 @@ export default function ItemsScreen(): React.ReactNode { setShowFolderItems(!showFolderItems)} + onPress={() => { + const newValue = !showFolderItems; + setShowFolderItems(newValue); + AsyncStorage.setItem(SHOW_FOLDERS_STORAGE_KEY, String(newValue)); + }} hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} > diff --git a/apps/server/AliasVault.Client/Main/Pages/Items/Home.razor b/apps/server/AliasVault.Client/Main/Pages/Items/Home.razor index 0552bf901..cd63590f7 100644 --- a/apps/server/AliasVault.Client/Main/Pages/Items/Home.razor +++ b/apps/server/AliasVault.Client/Main/Pages/Items/Home.razor @@ -48,10 +48,25 @@
- @* All items filter *@ - + @* All items filter with show folders toggle (only show toggle on root view) *@ +
+ + @if (!IsInFolder) + { + + } +
@* Item type filters with icons *@ @@ -165,8 +180,8 @@ else
} - @* Folders section - only show at root level *@ - @if (!IsInFolder && !IsSearching) + @* Folders section - only show at root level when ShowFolders is enabled *@ + @if (!IsInFolder && !IsSearching && ShowFolders) {
@foreach (var folder in Folders) @@ -374,6 +389,16 @@ else /// private ItemFilterType FilterType { get; set; } = ItemFilterType.All; + /// + /// LocalStorage key for the show folders preference. + /// + private const string ShowFoldersStorageKey = "items-show-folders"; + + /// + /// Gets or sets whether to show folders (true) or show all items flat (false). + /// + private bool ShowFolders { get; set; } = true; + /// /// Gets or sets the number of visible items for infinite scroll. /// @@ -462,9 +487,10 @@ else // Show only items in this folder filtered = filtered.Where(x => x.FolderId == FolderId); } - else if (!IsSearching) + else if (!IsSearching && ShowFolders) { - // At root level, exclude items that are in folders + // When showing folders (checkbox ON): only show root items (exclude items in folders) + // When not showing folders (checkbox OFF): show all items flat filtered = filtered.Where(x => x.FolderId == null); } @@ -610,6 +636,17 @@ else StateHasChanged(); } + /// + /// Toggles the show folders setting. + /// + private async Task ToggleShowFoldersAsync() + { + ShowFolders = !ShowFolders; + VisibleItemCount = BatchSize; // Reset visible items when setting changes + await LocalStorage.SetItemAsync(ShowFoldersStorageKey, ShowFolders); + StateHasChanged(); + } + /// /// Handles the table column sort changed event. /// This applies sorting to ALL items before pagination. @@ -849,6 +886,10 @@ else // Initialize table sort state from saved sort order SyncTableSortWithSortOrder(); + // Load show folders preference from local storage + var storedShowFolders = await LocalStorage.GetItemAsync(ShowFoldersStorageKey); + ShowFolders = storedShowFolders ?? true; + // Load folder name if in folder view if (FolderId.HasValue) { diff --git a/apps/server/AliasVault.Client/Resources/Pages/Main/Items/Home.en.resx b/apps/server/AliasVault.Client/Resources/Pages/Main/Items/Home.en.resx index 7967f6f0c..7aaf9b345 100644 --- a/apps/server/AliasVault.Client/Resources/Pages/Main/Items/Home.en.resx +++ b/apps/server/AliasVault.Client/Resources/Pages/Main/Items/Home.en.resx @@ -145,9 +145,13 @@ Label for filter dropdown - (All) Items + Items Filter option to show all items + + show folders + Toggle option to show or hide folder structure + Passkeys Filter option to show only passkeys