Prevent note switching when there are unsaved changes

- Added hasUnsavedChanges state tracking in App.tsx
- NoteEditor now notifies parent via onUnsavedChanges callback
- NotesList receives hasUnsavedChanges prop
- Clicking on other notes is prevented when current note has unsaved changes
- Visual feedback: other notes become semi-transparent with cursor-not-allowed
- Tooltip shows 'Save current note before switching' on disabled notes
- Much simpler and more reliable than trying to auto-save on switch
This commit is contained in:
drelich
2026-03-17 09:24:30 +01:00
parent e6ecab13fa
commit 9a229dcc00
3 changed files with 26 additions and 4 deletions

View File

@@ -11,6 +11,7 @@ interface NoteEditorProps {
note: Note | null;
onUpdateNote: (note: Note) => void;
fontSize: number;
onUnsavedChanges?: (hasChanges: boolean) => void;
}
const turndownService = new TurndownService({
@@ -18,7 +19,7 @@ const turndownService = new TurndownService({
codeBlockStyle: 'fenced',
});
export function NoteEditor({ note, onUpdateNote, fontSize }: NoteEditorProps) {
export function NoteEditor({ note, onUpdateNote, fontSize, onUnsavedChanges }: NoteEditorProps) {
const [localTitle, setLocalTitle] = useState('');
const [localFavorite, setLocalFavorite] = useState(false);
const [isSaving, setIsSaving] = useState(false);
@@ -26,6 +27,11 @@ export function NoteEditor({ note, onUpdateNote, fontSize }: NoteEditorProps) {
const [titleManuallyEdited, setTitleManuallyEdited] = useState(false);
const previousNoteIdRef = useRef<number | null>(null);
// Notify parent component when unsaved changes state changes
useEffect(() => {
onUnsavedChanges?.(hasUnsavedChanges);
}, [hasUnsavedChanges, onUnsavedChanges]);
const editor = useEditor({
extensions: [
StarterKit,

View File

@@ -16,6 +16,7 @@ interface NotesListProps {
onSearchChange: (text: string) => void;
showFavoritesOnly: boolean;
onToggleFavorites: () => void;
hasUnsavedChanges: boolean;
}
export function NotesList({
@@ -33,6 +34,7 @@ export function NotesList({
onSearchChange,
showFavoritesOnly,
onToggleFavorites,
hasUnsavedChanges,
}: NotesListProps) {
const [isSyncing, setIsSyncing] = React.useState(false);
const [deleteClickedId, setDeleteClickedId] = React.useState<number | null>(null);
@@ -144,10 +146,21 @@ export function NotesList({
notes.map((note) => (
<div
key={note.id}
onClick={() => onSelectNote(note.id)}
className={`p-3 border-b border-gray-200 dark:border-gray-700 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors group ${
note.id === selectedNoteId ? 'bg-blue-50 dark:bg-gray-800 border-l-4 border-l-blue-500' : ''
onClick={() => {
// Prevent switching if current note has unsaved changes
if (hasUnsavedChanges && note.id !== selectedNoteId) {
return;
}
onSelectNote(note.id);
}}
className={`p-3 border-b border-gray-200 dark:border-gray-700 transition-colors group ${
note.id === selectedNoteId
? 'bg-blue-50 dark:bg-gray-800 border-l-4 border-l-blue-500'
: hasUnsavedChanges
? 'cursor-not-allowed opacity-50'
: 'cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800'
}`}
title={hasUnsavedChanges && note.id !== selectedNoteId ? 'Save current note before switching' : ''}
>
<div className="flex items-start justify-between mb-1">
<div className="flex items-center flex-1 min-w-0">