Set verification modal on escape (#470) (#473)

* fixup! Fix priority auto (#470)

* Add translations
This commit is contained in:
Chris 2025-11-03 16:27:13 +02:00 committed by GitHub
parent 1e0d35a8c9
commit b8c46e1d0d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 1060 additions and 160 deletions

View file

@ -15,6 +15,7 @@ import { useStore } from '../../store/useStore';
import { useTranslation } from 'react-i18next';
import ProjectDropdown from '../Shared/ProjectDropdown';
import ConfirmDialog from '../Shared/ConfirmDialog';
import DiscardChangesDialog from '../Shared/DiscardChangesDialog';
import {
EyeIcon,
PencilIcon,
@ -63,6 +64,7 @@ const NoteModal: React.FC<NoteModalProps> = ({
const [isClosing, setIsClosing] = useState(false);
const [activeTab, setActiveTab] = useState<'edit' | 'preview'>('edit');
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
const [showDiscardDialog, setShowDiscardDialog] = useState(false);
// Project-related state
const [filteredProjects, setFilteredProjects] = useState<Project[]>([]);
@ -149,11 +151,40 @@ const NoteModal: React.FC<NoteModalProps> = ({
// Tags are now loaded automatically via getTags() when needed
// Check if there are unsaved changes
const hasUnsavedChanges = () => {
if (!note) {
// New note - check if any field has been filled
return (
formData.title.trim() !== '' ||
formData.content?.trim() !== '' ||
tags.length > 0 ||
formData.project_id !== null
);
}
// Existing note - compare with original
const formChanged =
formData.title !== note.title ||
formData.content !== note.content ||
formData.project_id !== note.project_id;
// Compare tags
const originalTags =
(note.tags || note.Tags)?.map((tag) => tag.name) || [];
const tagsChanged =
tags.length !== originalTags.length ||
tags.some((tag, index) => tag !== originalTags[index]);
return formChanged || tagsChanged;
};
const handleClose = useCallback(() => {
setIsClosing(true);
setTimeout(() => {
onClose();
setIsClosing(false);
setShowDiscardDialog(false);
// Reset expanded sections when closing
setExpandedSections({
tags: false,
@ -162,6 +193,15 @@ const NoteModal: React.FC<NoteModalProps> = ({
}, 300);
}, [onClose]);
const handleDiscardChanges = () => {
setShowDiscardDialog(false);
handleClose();
};
const handleCancelDiscard = () => {
setShowDiscardDialog(false);
};
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
@ -180,10 +220,30 @@ const NoteModal: React.FC<NoteModalProps> = ({
};
}, [isOpen, handleClose]);
// Use ref to store hasUnsavedChanges so it's always current in the event handler
const hasUnsavedChangesRef = useRef(hasUnsavedChanges);
useEffect(() => {
hasUnsavedChangesRef.current = hasUnsavedChanges;
});
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
handleClose();
// Don't show discard dialog if already showing a dialog
if (showConfirmDialog || showDiscardDialog) {
// Let the dialog handle its own Escape
return;
}
event.preventDefault();
event.stopPropagation();
// Check for unsaved changes using ref to get current value
if (hasUnsavedChangesRef.current()) {
setShowDiscardDialog(true);
} else {
handleClose();
}
}
};
@ -193,7 +253,7 @@ const NoteModal: React.FC<NoteModalProps> = ({
return () => {
document.removeEventListener('keydown', handleKeyDown);
};
}, [isOpen, handleClose]);
}, [isOpen, handleClose, showConfirmDialog, showDiscardDialog]);
// Handle body scroll when modal opens/closes
useEffect(() => {
@ -735,6 +795,12 @@ const NoteModal: React.FC<NoteModalProps> = ({
onCancel={() => setShowConfirmDialog(false)}
/>
)}
{showDiscardDialog && (
<DiscardChangesDialog
onDiscard={handleDiscardChanges}
onCancel={handleCancelDiscard}
/>
)}
</>
);
};