feat: warn before leaving a form with unsaved changes
Drag-dropping page images then clicking a nav link silently discarded the whole form. Add a reusable guardUnsavedChanges helper (SvelteKit beforeNavigate confirm + native beforeunload) and wire it into the manga upload, manga edit, and chapter upload forms. The dirty check returns false while submitting so the post-save redirect isn't prompted. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { ApiError, fileUrl } from '$lib/api/client';
|
||||
import { guardUnsavedChanges } from '$lib/unsaved-guard';
|
||||
import {
|
||||
deleteMangaCover,
|
||||
updateManga,
|
||||
@@ -49,6 +50,25 @@
|
||||
mangaTitle.trim().length > 0 && !coverError && !submitting
|
||||
);
|
||||
|
||||
// Warn before leaving with unsaved edits. `submitting` stays true through
|
||||
// the post-save redirect, so that navigation isn't prompted.
|
||||
guardUnsavedChanges(
|
||||
() =>
|
||||
!submitting &&
|
||||
(mangaTitle !== data.manga.title ||
|
||||
mangaStatus !== data.manga.status ||
|
||||
mangaDescription !== (data.manga.description ?? '') ||
|
||||
JSON.stringify(mangaAuthors) !==
|
||||
JSON.stringify(data.manga.authors.map((a) => a.name)) ||
|
||||
JSON.stringify(mangaAltTitles) !== JSON.stringify([...data.manga.alt_titles]) ||
|
||||
JSON.stringify(mangaGenreIds) !==
|
||||
JSON.stringify(data.manga.genres.map((g) => g.id)) ||
|
||||
coverFile != null ||
|
||||
pendingCoverRemoval ||
|
||||
authorDraft.trim() !== '' ||
|
||||
altTitleDraft.trim() !== '')
|
||||
);
|
||||
|
||||
function addAuthor() {
|
||||
const name = authorDraft.trim();
|
||||
if (!name) return;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { ApiError, fileUrl } from '$lib/api/client';
|
||||
import { guardUnsavedChanges } from '$lib/unsaved-guard';
|
||||
import { createChapter } from '$lib/api/chapters';
|
||||
import { session } from '$lib/session.svelte';
|
||||
import ChapterPagesEditor, {
|
||||
@@ -19,6 +20,16 @@
|
||||
let submitting = $state(false);
|
||||
let error: string | null = $state(null);
|
||||
|
||||
// Warn before leaving with staged pages / edits. `submitting` stays true
|
||||
// through the post-save redirect, so that navigation isn't prompted.
|
||||
guardUnsavedChanges(
|
||||
() =>
|
||||
!submitting &&
|
||||
(pages.length > 0 ||
|
||||
title.trim() !== '' ||
|
||||
(number != null && number !== data.defaultNumber))
|
||||
);
|
||||
|
||||
const allPagesValid = $derived(pages.every((p) => !p.error));
|
||||
const canSubmit = $derived(
|
||||
Boolean(session.user) &&
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { ApiError } from '$lib/api/client';
|
||||
import { guardUnsavedChanges } from '$lib/unsaved-guard';
|
||||
import { createManga, type MangaStatus } from '$lib/api/mangas';
|
||||
import { createChapter } from '$lib/api/chapters';
|
||||
import { session } from '$lib/session.svelte';
|
||||
@@ -43,6 +44,23 @@
|
||||
let mangaError = $state<string | null>(null);
|
||||
let success = $state<string | null>(null);
|
||||
|
||||
// Warn before navigating away with an in-progress upload. Suppressed while
|
||||
// submitting and after a successful save (which redirects).
|
||||
guardUnsavedChanges(
|
||||
() =>
|
||||
!success &&
|
||||
!submitting &&
|
||||
(mangaTitle.trim() !== '' ||
|
||||
mangaDescription.trim() !== '' ||
|
||||
mangaAuthors.length > 0 ||
|
||||
mangaAltTitles.length > 0 ||
|
||||
mangaGenreIds.length > 0 ||
|
||||
coverFile != null ||
|
||||
stagedChapters.length > 0 ||
|
||||
authorDraft.trim() !== '' ||
|
||||
altTitleDraft.trim() !== '')
|
||||
);
|
||||
|
||||
const allChapterPagesValid = $derived(
|
||||
stagedChapters.every((c) => c.pages.every((p) => !p.error))
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user