From 4ef0814ccdab96a87121d6be146294f1ebe369d5 Mon Sep 17 00:00:00 2001 From: drelich Date: Sat, 21 Mar 2026 22:34:05 +0100 Subject: [PATCH] feat: add category renaming functionality (v0.1.5) - Add double-click to rename categories (deprecated in favor of pencil icon) - Add pencil icon on hover for intuitive category renaming - Click pencil icon to enter inline rename mode - Show helpful hint (Enter to save, Esc to cancel) - Update all notes with old category name to new name - Sync category changes to server - Update selected category if currently viewing renamed category - Bump version to 0.1.5 --- package.json | 2 +- src-tauri/tauri.conf.json | 2 +- src/App.tsx | 26 +++++++ src/components/CategoriesSidebar.tsx | 100 +++++++++++++++++++++++---- 4 files changed, 114 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 9a8272c..5a75efa 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "nextcloud-notes-tauri", "private": true, - "version": "0.1.4", + "version": "0.1.5", "type": "module", "scripts": { "dev": "vite", diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index c24d800..28a6337 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Nextcloud Notes", - "version": "0.1.4", + "version": "0.1.5", "identifier": "com.davidrelich.nextcloud-notes", "build": { "beforeDevCommand": "npm run dev", diff --git a/src/App.tsx b/src/App.tsx index 3926272..f6430a2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -220,6 +220,31 @@ function App() { } }; + const handleRenameCategory = async (oldName: string, newName: string) => { + // Update all notes with the old category to the new category + const notesToUpdate = notes.filter(note => note.category === oldName); + + for (const note of notesToUpdate) { + try { + const updatedNote = { ...note, category: newName }; + await syncManager.updateNote(updatedNote); + setNotes(prevNotes => prevNotes.map(n => n.id === note.id ? updatedNote : n)); + } catch (error) { + console.error(`Failed to update note ${note.id}:`, error); + } + } + + // Update manual categories list + setManualCategories(prev => + prev.map(cat => cat === oldName ? newName : cat) + ); + + // Update selected category if it was the renamed one + if (selectedCategory === oldName) { + setSelectedCategory(newName); + } + }; + const handleUpdateNote = async (updatedNote: Note) => { try { await syncManager.updateNote(updatedNote); @@ -271,6 +296,7 @@ function App() { selectedCategory={selectedCategory} onSelectCategory={setSelectedCategory} onCreateCategory={handleCreateCategory} + onRenameCategory={handleRenameCategory} isCollapsed={isCategoriesCollapsed} onToggleCollapse={() => setIsCategoriesCollapsed(!isCategoriesCollapsed)} username={username} diff --git a/src/components/CategoriesSidebar.tsx b/src/components/CategoriesSidebar.tsx index 2b6ec4c..8623f70 100644 --- a/src/components/CategoriesSidebar.tsx +++ b/src/components/CategoriesSidebar.tsx @@ -20,6 +20,7 @@ interface CategoriesSidebarProps { selectedCategory: string; onSelectCategory: (category: string) => void; onCreateCategory: (name: string) => void; + onRenameCategory: (oldName: string, newName: string) => void; isCollapsed: boolean; onToggleCollapse: () => void; username: string; @@ -41,6 +42,7 @@ export function CategoriesSidebar({ selectedCategory, onSelectCategory, onCreateCategory, + onRenameCategory, isCollapsed, onToggleCollapse, username, @@ -58,8 +60,11 @@ export function CategoriesSidebar({ }: CategoriesSidebarProps) { const [isCreating, setIsCreating] = useState(false); const [newCategoryName, setNewCategoryName] = useState(''); + const [renamingCategory, setRenamingCategory] = useState(null); + const [renameCategoryValue, setRenameCategoryValue] = useState(''); const [isSettingsCollapsed, setIsSettingsCollapsed] = useState(true); const inputRef = useRef(null); + const renameInputRef = useRef(null); useEffect(() => { if (isCreating && inputRef.current) { @@ -67,6 +72,13 @@ export function CategoriesSidebar({ } }, [isCreating]); + useEffect(() => { + if (renamingCategory && renameInputRef.current) { + renameInputRef.current.focus(); + renameInputRef.current.select(); + } + }, [renamingCategory]); + const handleCreateCategory = () => { if (newCategoryName.trim()) { onCreateCategory(newCategoryName.trim()); @@ -75,6 +87,19 @@ export function CategoriesSidebar({ } }; + const handleRenameCategory = () => { + if (renameCategoryValue.trim() && renamingCategory && renameCategoryValue.trim() !== renamingCategory) { + onRenameCategory(renamingCategory, renameCategoryValue.trim()); + } + setRenamingCategory(null); + setRenameCategoryValue(''); + }; + + const startRenaming = (category: string) => { + setRenamingCategory(category); + setRenameCategoryValue(category); + }; + const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { handleCreateCategory(); @@ -84,6 +109,15 @@ export function CategoriesSidebar({ } }; + const handleRenameKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + handleRenameCategory(); + } else if (e.key === 'Escape') { + setRenamingCategory(null); + setRenameCategoryValue(''); + } + }; + if (isCollapsed) { return ( + renamingCategory === category ? ( +
+
+ + + + setRenameCategoryValue(e.target.value)} + onKeyDown={handleRenameKeyDown} + onBlur={handleRenameCategory} + className="flex-1 text-sm px-2 py-1 border-none bg-transparent text-gray-900 dark:text-gray-100 focus:outline-none" + /> +
+
+ Press Enter to save, Esc to cancel +
+
+ ) : ( +
+ + +
+ ) ))} {isCreating && (