Toast action results and confirm destructive actions in one dialog

Inline banners and per-row status text reported action results all over
the app; they are replaced by a small toast stack (bottom-right, Level 3
floating well styling) that only speaks for actions: successes fade,
errors stay until dismissed, and form-field validation stays inline.

Destructive actions no longer use bespoke inline confirm steps (the
library detail's Confirm delete button) or fire immediately (duplicate
copies/items, delete page selections, temp cleanup, upload discard, job
cancellation): they all go through one promise-based confirm dialog
(confirmAction) with a danger variant, Escape/backdrop to cancel.
This commit is contained in:
2026-09-17 22:19:54 -05:00
parent 1053e3ee55
commit e97c3b9da0
21 changed files with 430 additions and 287 deletions
+3 -11
View File
@@ -31,6 +31,7 @@ import { formatBytes, formatDate } from "@/lib/format";
import type { DownloadTask, Paginated } from "@/lib/types";
import { useAuth } from "@/store/auth";
import { useE621 } from "@/store/e621";
import { toast } from "@/store/toasts";
const ratingText: Record<string, string> = {
s: "text-ctp-green",
@@ -115,6 +116,7 @@ export function OnlineDetail({ postId }: { postId: number }) {
queryKey: ["e621", "post", postId],
});
},
onError: (error) => toast.error(e621ErrorMessage(error)),
});
const [downloadTaskId, setDownloadTaskId] = useState<string | null>(null);
@@ -166,6 +168,7 @@ export function OnlineDetail({ postId }: { postId: number }) {
setDownloadTaskId(task.task_id);
void queryClient.invalidateQueries({ queryKey: ["status"] });
},
onError: (error) => toast.error(errorMessage(error)),
});
const taskQuery = useQuery({
@@ -400,17 +403,6 @@ export function OnlineDetail({ postId }: { postId: number }) {
) : null}
</p>
) : null}
{favoriteMutation.isError ? (
<p className="rounded-md border border-ctp-red/30 bg-ctp-red/10 px-3 py-2 text-sm text-ctp-red">
{e621ErrorMessage(favoriteMutation.error)}
</p>
) : null}
{downloadMutation.isError ? (
<p className="rounded-md border border-ctp-red/30 bg-ctp-red/10 px-3 py-2 text-sm text-ctp-red">
{errorMessage(downloadMutation.error)}
</p>
) : null}
</>
}
media={