Don't let user close browser window if they have unsaved changes (vibe-kanban) (#538)
* Perfect! The implementation is complete and passes all checks. ## Summary Added browser window close protection to prevent data loss when users have unsaved changes in the task dialog: - **Added `beforeunload` event listener** in [`TaskFormDialog.tsx`](file:///private/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban/worktrees/vk-f59b-don-t-let/frontend/src/components/tasks/TaskFormDialog.tsx#L95-L111) - **Integrates with existing `hasUnsavedChanges()` logic** - no duplication - **Only active when dialog is open AND has unsaved content** - **Automatic cleanup** when dialog closes or changes are saved - **Browser-standard confirmation dialog** appears when user tries to close tab/window The feature now protects against data loss from both dialog close (existing) and browser window close (new) scenarios. * Cleanup script changes for task attempt f59bea00-6158-4cdd-ad19-1f30e40c1324 * Perfect! Now the browser window close protection works for both scenarios: ## Updated Implementation **Create mode:** Warns when user has entered title or description content **Edit mode:** Warns when user has modified title, description, or status from original values The `beforeunload` event will now trigger when: - **Creating a task** and typing content, then trying to close browser - **Editing a task** and changing any field, then trying to close browser Both the existing dialog close warning and new browser window close warning now protect against data loss in all editing scenarios. * Cleanup script changes for task attempt f59bea00-6158-4cdd-ad19-1f30e40c1324
This commit is contained in:
committed by
GitHub
parent
e99f9807fb
commit
6fb7b939d9
@@ -229,7 +229,7 @@ impl ImageService {
|
|||||||
let rel = &caps[2];
|
let rel = &caps[2];
|
||||||
let abs = worktree_path.join(rel);
|
let abs = worktree_path.join(rel);
|
||||||
let abs = abs.to_string_lossy().replace('\\', "/");
|
let abs = abs.to_string_lossy().replace('\\', "/");
|
||||||
format!("", alt, abs)
|
format!("")
|
||||||
})
|
})
|
||||||
.into_owned()
|
.into_owned()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,13 +82,38 @@ export function TaskFormDialog({
|
|||||||
|
|
||||||
// Check if there's any content that would be lost
|
// Check if there's any content that would be lost
|
||||||
const hasUnsavedChanges = useCallback(() => {
|
const hasUnsavedChanges = useCallback(() => {
|
||||||
// Only warn in create mode when there's content
|
|
||||||
if (!isEditMode) {
|
if (!isEditMode) {
|
||||||
|
// Create mode - warn when there's content
|
||||||
return title.trim() !== '' || description.trim() !== '';
|
return title.trim() !== '' || description.trim() !== '';
|
||||||
|
} else if (task) {
|
||||||
|
// Edit mode - warn when current values differ from original task
|
||||||
|
const titleChanged = title.trim() !== task.title.trim();
|
||||||
|
const descriptionChanged =
|
||||||
|
(description || '').trim() !== (task.description || '').trim();
|
||||||
|
const statusChanged = status !== task.status;
|
||||||
|
return titleChanged || descriptionChanged || statusChanged;
|
||||||
}
|
}
|
||||||
// No warning for edit mode - users can always reopen the task
|
|
||||||
return false;
|
return false;
|
||||||
}, [title, description, isEditMode]);
|
}, [title, description, status, isEditMode, task]);
|
||||||
|
|
||||||
|
// Warn on browser/tab close if there are unsaved changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isOpen) return; // dialog closed → nothing to do
|
||||||
|
|
||||||
|
// always re-evaluate latest fields via hasUnsavedChanges()
|
||||||
|
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
|
||||||
|
if (hasUnsavedChanges()) {
|
||||||
|
e.preventDefault();
|
||||||
|
// Chrome / Edge still require returnValue to be set
|
||||||
|
e.returnValue = '';
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
// nothing returned → no prompt
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('beforeunload', handleBeforeUnload);
|
||||||
|
return () => window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||||
|
}, [isOpen, hasUnsavedChanges]); // hasUnsavedChanges is memoised with title/descr deps
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (task) {
|
if (task) {
|
||||||
|
|||||||
Reference in New Issue
Block a user