From f9abb34e5a1789532ae9a85bcdda103600e27dda Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Wed, 21 Jan 2026 14:34:05 +0100 Subject: [PATCH] Add better active filter state with clear filter action (#1473) --- .../popup/pages/items/ItemsList.tsx | 95 +++++++++-- .../src/i18n/locales/en.json | 4 + apps/mobile-app/app/(tabs)/items/index.tsx | 150 ++++++++++++++++-- apps/mobile-app/i18n/locales/en.json | 4 + .../Main/Pages/Items/Home.razor | 39 +++++ .../Resources/Pages/Main/Items/Home.en.resx | 10 ++ 6 files changed, 277 insertions(+), 25 deletions(-) diff --git a/apps/browser-extension/src/entrypoints/popup/pages/items/ItemsList.tsx b/apps/browser-extension/src/entrypoints/popup/pages/items/ItemsList.tsx index 90f29e463..89f51ec04 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/items/ItemsList.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/items/ItemsList.tsx @@ -685,19 +685,59 @@ const ItemsList: React.FC = () => { )} ) : filteredItems.length === 0 && folders.length === 0 ? ( -
+
{/* Show filter/search-specific messages only when actively filtering or searching */} {(filterType !== 'all' || searchTerm) && ( -

- {filterType === 'passkeys' - ? t('items.noPasskeysFound') - : filterType === 'attachments' - ? t('items.noAttachmentsFound') - : isItemTypeFilter(filterType) - ? t('items.noItemsOfTypeFound', { type: getFilterTitle() }) - : t('items.noMatchingItems') - } -

+ <> +

+ {/* Different messages based on what's causing no results */} + {searchTerm && filterType !== 'all' + // Both search and filter active + ? t('items.noMatchingItemsWithFilter', { filter: getFilterTitle(), search: searchTerm }) + : searchTerm + // Only search active + ? t('items.noMatchingItemsSearch', { search: searchTerm }) + // Only filter active (no search) + : filterType === 'passkeys' + ? t('items.noPasskeysFound') + : filterType === 'attachments' + ? t('items.noAttachmentsFound') + : isItemTypeFilter(filterType) + ? t('items.noItemsOfTypeFound', { type: getFilterTitle() }) + : t('items.noMatchingItems') + } +

+ {/* Clear filter/search buttons */} +
+ {searchTerm && ( + + )} + {filterType !== 'all' && ( + + )} +
+ )} {/* Show help text when inside an empty folder */} {currentFolderId && ( @@ -757,6 +797,39 @@ const ItemsList: React.FC = () => { )} + {/* Clear filter/search pills at bottom of list when filtering or searching */} + {(filterType !== 'all' || searchTerm) && ( +
+ {searchTerm && ( + + )} + {filterType !== 'all' && ( + + )} +
+ )} + {/* Recently Deleted link (only show at root level when not searching and not filtering) */} {!currentFolderId && !searchTerm && filterType === 'all' && ( +
+ } @* Infinite scroll sentinel for table view *@ @if (HasMoreItems) @@ -174,6 +188,21 @@ else else {
+ @* Active filter indicator - show persistently when a filter is active *@ + @if (FilterType != ItemFilterType.All) + { +
+ @Localizer["FilteringBy"] + +
+ } + @* Folders section - only show at root level *@ @if (!IsInFolder && !IsSearching) { @@ -544,6 +573,16 @@ else StateHasChanged(); } + /// + /// Clears the active filter and resets to showing all items. + /// + private void ClearFilter() + { + FilterType = ItemFilterType.All; + VisibleItemCount = BatchSize; // Reset visible items when filter changes + StateHasChanged(); + } + /// /// Loads more items for infinite scroll. Called from JavaScript via IntersectionObserver. /// 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 cb9c02a7e..d863765cb 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 @@ -236,4 +236,14 @@ Loading more... Text shown when loading more items during infinite scroll + + + + Clear filter + Button text for clearing the active filter + + + Filtering by: + Label shown when a filter is active + \ No newline at end of file