Add show folders toggle to web app (#1598)

This commit is contained in:
Leendert de Borst
2026-02-02 20:41:24 +01:00
committed by Leendert de Borst
parent 32421ef286
commit d48d97879b
3 changed files with 83 additions and 10 deletions

View File

@@ -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<void> => {
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 {
</TouchableOpacity>
<TouchableOpacity
style={styles.filterMenuItemToggle}
onPress={() => 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 }}
>
<ThemedText style={styles.filterMenuToggleHint}>

View File

@@ -48,10 +48,25 @@
<ClickOutsideHandler OnClose="CloseFilterDropdown" ContentId="filterDropdown,filterButton">
<div id="filterDropdown" class="absolute left-0 top-full z-10 mt-2 w-56 origin-top-left rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:bg-gray-700">
<div class="py-1">
@* All items filter *@
<button @onclick="() => SetFilter(ItemFilterType.All)" class="w-full text-left px-4 py-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-600 @(FilterType == ItemFilterType.All ? "bg-orange-50 dark:bg-orange-900/20 text-orange-600 dark:text-orange-400" : "text-gray-700 dark:text-gray-300")">
@Localizer["FilterAllOption"]
</button>
@* All items filter with show folders toggle (only show toggle on root view) *@
<div class="flex items-center justify-between px-4 py-2 @(FilterType == ItemFilterType.All ? "bg-orange-50 dark:bg-orange-900/20" : "")">
<button @onclick="() => SetFilter(ItemFilterType.All)" class="text-left text-sm hover:text-gray-900 dark:hover:text-white @(FilterType == ItemFilterType.All ? "text-orange-600 dark:text-orange-400" : "text-gray-700 dark:text-gray-300")">
@Localizer["FilterAllOption"]
</button>
@if (!IsInFolder)
{
<button @onclick="ToggleShowFoldersAsync" @onclick:stopPropagation="true" class="flex items-center gap-1.5 text-xs text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300">
<span>@Localizer["ShowFoldersOption"]</span>
<svg class="w-5 h-5 @(ShowFolders ? "text-orange-500 dark:text-orange-400" : "text-gray-400 dark:text-gray-500")" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="18" height="18" rx="2" />
@if (ShowFolders)
{
<polyline points="7 12 10 15 17 8" />
}
</svg>
</button>
}
</div>
<div class="border-t border-gray-200 dark:border-gray-600 my-1"></div>
@* Item type filters with icons *@
@@ -165,8 +180,8 @@ else
</div>
}
@* 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)
{
<div class="flex flex-wrap items-center gap-2 mb-4">
@foreach (var folder in Folders)
@@ -374,6 +389,16 @@ else
/// </summary>
private ItemFilterType FilterType { get; set; } = ItemFilterType.All;
/// <summary>
/// LocalStorage key for the show folders preference.
/// </summary>
private const string ShowFoldersStorageKey = "items-show-folders";
/// <summary>
/// Gets or sets whether to show folders (true) or show all items flat (false).
/// </summary>
private bool ShowFolders { get; set; } = true;
/// <summary>
/// Gets or sets the number of visible items for infinite scroll.
/// </summary>
@@ -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();
}
/// <summary>
/// Toggles the show folders setting.
/// </summary>
private async Task ToggleShowFoldersAsync()
{
ShowFolders = !ShowFolders;
VisibleItemCount = BatchSize; // Reset visible items when setting changes
await LocalStorage.SetItemAsync(ShowFoldersStorageKey, ShowFolders);
StateHasChanged();
}
/// <summary>
/// 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<bool?>(ShowFoldersStorageKey);
ShowFolders = storedShowFolders ?? true;
// Load folder name if in folder view
if (FolderId.HasValue)
{

View File

@@ -145,9 +145,13 @@
<comment>Label for filter dropdown</comment>
</data>
<data name="FilterAllOption" xml:space="preserve">
<value>(All) Items</value>
<value>Items</value>
<comment>Filter option to show all items</comment>
</data>
<data name="ShowFoldersOption" xml:space="preserve">
<value>show folders</value>
<comment>Toggle option to show or hide folder structure</comment>
</data>
<data name="FilterPasskeysOption" xml:space="preserve">
<value>Passkeys</value>
<comment>Filter option to show only passkeys</comment>