mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-02-07 04:43:00 -05:00
Add show folders toggle to web app (#1598)
This commit is contained in:
committed by
Leendert de Borst
parent
32421ef286
commit
d48d97879b
@@ -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}>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user