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:
@@ -16,6 +16,7 @@ function App() {
|
|||||||
const [username, setUsername] = useState('');
|
const [username, setUsername] = useState('');
|
||||||
const [theme, setTheme] = useState<'light' | 'dark' | 'system'>('system');
|
const [theme, setTheme] = useState<'light' | 'dark' | 'system'>('system');
|
||||||
const [effectiveTheme, setEffectiveTheme] = useState<'light' | 'dark'>('light');
|
const [effectiveTheme, setEffectiveTheme] = useState<'light' | 'dark'>('light');
|
||||||
|
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const savedServer = localStorage.getItem('serverURL');
|
const savedServer = localStorage.getItem('serverURL');
|
||||||
@@ -193,11 +194,13 @@ function App() {
|
|||||||
onSearchChange={setSearchText}
|
onSearchChange={setSearchText}
|
||||||
showFavoritesOnly={showFavoritesOnly}
|
showFavoritesOnly={showFavoritesOnly}
|
||||||
onToggleFavorites={() => setShowFavoritesOnly(!showFavoritesOnly)}
|
onToggleFavorites={() => setShowFavoritesOnly(!showFavoritesOnly)}
|
||||||
|
hasUnsavedChanges={hasUnsavedChanges}
|
||||||
/>
|
/>
|
||||||
<NoteEditor
|
<NoteEditor
|
||||||
note={selectedNote}
|
note={selectedNote}
|
||||||
onUpdateNote={handleUpdateNote}
|
onUpdateNote={handleUpdateNote}
|
||||||
fontSize={fontSize}
|
fontSize={fontSize}
|
||||||
|
onUnsavedChanges={setHasUnsavedChanges}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ interface NoteEditorProps {
|
|||||||
note: Note | null;
|
note: Note | null;
|
||||||
onUpdateNote: (note: Note) => void;
|
onUpdateNote: (note: Note) => void;
|
||||||
fontSize: number;
|
fontSize: number;
|
||||||
|
onUnsavedChanges?: (hasChanges: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const turndownService = new TurndownService({
|
const turndownService = new TurndownService({
|
||||||
@@ -18,7 +19,7 @@ const turndownService = new TurndownService({
|
|||||||
codeBlockStyle: 'fenced',
|
codeBlockStyle: 'fenced',
|
||||||
});
|
});
|
||||||
|
|
||||||
export function NoteEditor({ note, onUpdateNote, fontSize }: NoteEditorProps) {
|
export function NoteEditor({ note, onUpdateNote, fontSize, onUnsavedChanges }: NoteEditorProps) {
|
||||||
const [localTitle, setLocalTitle] = useState('');
|
const [localTitle, setLocalTitle] = useState('');
|
||||||
const [localFavorite, setLocalFavorite] = useState(false);
|
const [localFavorite, setLocalFavorite] = useState(false);
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
@@ -26,6 +27,11 @@ export function NoteEditor({ note, onUpdateNote, fontSize }: NoteEditorProps) {
|
|||||||
const [titleManuallyEdited, setTitleManuallyEdited] = useState(false);
|
const [titleManuallyEdited, setTitleManuallyEdited] = useState(false);
|
||||||
const previousNoteIdRef = useRef<number | null>(null);
|
const previousNoteIdRef = useRef<number | null>(null);
|
||||||
|
|
||||||
|
// Notify parent component when unsaved changes state changes
|
||||||
|
useEffect(() => {
|
||||||
|
onUnsavedChanges?.(hasUnsavedChanges);
|
||||||
|
}, [hasUnsavedChanges, onUnsavedChanges]);
|
||||||
|
|
||||||
const editor = useEditor({
|
const editor = useEditor({
|
||||||
extensions: [
|
extensions: [
|
||||||
StarterKit,
|
StarterKit,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ interface NotesListProps {
|
|||||||
onSearchChange: (text: string) => void;
|
onSearchChange: (text: string) => void;
|
||||||
showFavoritesOnly: boolean;
|
showFavoritesOnly: boolean;
|
||||||
onToggleFavorites: () => void;
|
onToggleFavorites: () => void;
|
||||||
|
hasUnsavedChanges: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NotesList({
|
export function NotesList({
|
||||||
@@ -33,6 +34,7 @@ export function NotesList({
|
|||||||
onSearchChange,
|
onSearchChange,
|
||||||
showFavoritesOnly,
|
showFavoritesOnly,
|
||||||
onToggleFavorites,
|
onToggleFavorites,
|
||||||
|
hasUnsavedChanges,
|
||||||
}: NotesListProps) {
|
}: NotesListProps) {
|
||||||
const [isSyncing, setIsSyncing] = React.useState(false);
|
const [isSyncing, setIsSyncing] = React.useState(false);
|
||||||
const [deleteClickedId, setDeleteClickedId] = React.useState<number | null>(null);
|
const [deleteClickedId, setDeleteClickedId] = React.useState<number | null>(null);
|
||||||
@@ -144,10 +146,21 @@ export function NotesList({
|
|||||||
notes.map((note) => (
|
notes.map((note) => (
|
||||||
<div
|
<div
|
||||||
key={note.id}
|
key={note.id}
|
||||||
onClick={() => onSelectNote(note.id)}
|
onClick={() => {
|
||||||
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 ${
|
// Prevent switching if current note has unsaved changes
|
||||||
note.id === selectedNoteId ? 'bg-blue-50 dark:bg-gray-800 border-l-4 border-l-blue-500' : ''
|
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-start justify-between mb-1">
|
||||||
<div className="flex items-center flex-1 min-w-0">
|
<div className="flex items-center flex-1 min-w-0">
|
||||||
|
|||||||
Reference in New Issue
Block a user