From 05bde683bdbaae8bd276821691bb108c6454ce8e Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Thu, 11 Dec 2025 21:30:51 +0100 Subject: [PATCH] Improve folder navigation (#1404) --- .../src/entrypoints/popup/App.tsx | 3 +- .../popup/pages/items/ItemsList.tsx | 109 ++++++++---------- 2 files changed, 48 insertions(+), 64 deletions(-) diff --git a/apps/browser-extension/src/entrypoints/popup/App.tsx b/apps/browser-extension/src/entrypoints/popup/App.tsx index c923cc907..efd53271a 100644 --- a/apps/browser-extension/src/entrypoints/popup/App.tsx +++ b/apps/browser-extension/src/entrypoints/popup/App.tsx @@ -187,11 +187,12 @@ const App: React.FC = () => { { path: '/unlock-success', element: , showBackButton: false }, { path: '/upgrade', element: , showBackButton: false }, { path: '/auth-settings', element: , showBackButton: true, title: t('settings.title') }, - { path: '/items', element: , showBackButton: false }, { path: '/credentials', element: , showBackButton: false }, { path: '/credentials/add', element: , showBackButton: true, title: t('credentials.addCredential') }, { path: '/credentials/:id', element: , showBackButton: true, title: t('credentials.credentialDetails') }, { path: '/credentials/:id/edit', element: , showBackButton: true, title: t('credentials.editCredential') }, + { path: '/items', element: , showBackButton: false }, + { path: '/items/folder/:folderId', element: , showBackButton: true, title: t('items.title') }, { path: '/items/select-type', element: , showBackButton: true, title: t('itemTypes.selectType') }, { path: '/items/add', element: , showBackButton: true, title: 'Add Item' }, { path: '/items/deleted', element: , showBackButton: true, title: t('recentlyDeleted.title') }, 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 3522c702d..0bae47296 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/items/ItemsList.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/items/ItemsList.tsx @@ -1,6 +1,6 @@ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useParams } from 'react-router-dom'; import FolderModal from '@/entrypoints/popup/components/Folders/FolderModal'; import HeaderButton from '@/entrypoints/popup/components/HeaderButton'; @@ -79,6 +79,7 @@ type FolderWithCount = { */ const ItemsList: React.FC = () => { const { t } = useTranslation(); + const { folderId: folderIdParam } = useParams<{ folderId?: string }>(); const dbContext = useDb(); const app = useApp(); const navigate = useNavigate(); @@ -89,12 +90,23 @@ const ItemsList: React.FC = () => { const [searchTerm, setSearchTerm] = useState(''); const [filterType, setFilterType] = useState(getStoredFilter()); const [showFilterMenu, setShowFilterMenu] = useState(false); - const [currentFolderId, setCurrentFolderId] = useState(null); - const [folderPath, setFolderPath] = useState(''); const [showFolderModal, setShowFolderModal] = useState(false); const [recentlyDeletedCount, setRecentlyDeletedCount] = useState(0); const { setIsInitialLoading } = useLoading(); + // Derive current folder from URL params + const currentFolderId = folderIdParam ?? null; + + // Get current folder name from database + const currentFolderName = useMemo(() => { + if (!currentFolderId || !dbContext?.sqliteClient) { + return null; + } + const folders = dbContext.sqliteClient.getAllFolders(); + const folder = folders.find(f => f.Id === currentFolderId); + return folder?.Name ?? null; + }, [currentFolderId, dbContext?.sqliteClient]); + /** * Loading state with minimum duration for more fluid UX. */ @@ -242,8 +254,8 @@ const ItemsList: React.FC = () => { * Get the title based on the active filter and current folder */ const getFilterTitle = () : string => { - if (currentFolderId && folderPath) { - return folderPath; + if (currentFolderId && currentFolderName) { + return currentFolderName; } switch (filterType) { @@ -257,21 +269,19 @@ const ItemsList: React.FC = () => { }; /** - * Navigate into a folder + * Navigate into a folder via URL */ - const handleFolderClick = useCallback((folderId: string, folderName: string) => { - setCurrentFolderId(folderId); - setFolderPath(folderName); + const handleFolderClick = useCallback((folderId: string, _folderName: string) => { setSearchTerm(''); // Clear search when entering folder - }, []); + navigate(`/items/folder/${folderId}`); + }, [navigate]); /** - * Navigate back to root or parent folder + * Navigate back to root (items list) */ const handleBackToRoot = useCallback(() => { - setCurrentFolderId(null); - setFolderPath(''); - }, []); + navigate('/items'); + }, [navigate]); /** * Get folders with item counts (only for root level when not searching) @@ -375,55 +385,28 @@ const ItemsList: React.FC = () => {
- {currentFolderId ? ( -
- -

- {getFilterTitle()} - ({filteredItems.length}) -

-
- ) : ( - - )} - + + + {showFilterMenu && ( <>