feat(ui): reusable app-styled confirm dialog, replacing window.confirm

Add a promise-based ConfirmProvider + useConfirm() hook (built on the Modal shell,
liquid-glass styling, danger variant, Esc/backdrop = cancel, Enter = confirm) so
confirmations match the app instead of the native browser dialog. Wire it in at the
app root and replace both window.confirm call sites (playlist delete, channel
unsubscribe). Trilingual common.confirm/confirmTitle strings.
This commit is contained in:
npeter83 2026-06-15 15:06:47 +02:00
parent 50bc35d78f
commit 48cb11434d
7 changed files with 103 additions and 9 deletions

View file

@ -29,6 +29,7 @@ import {
import { api, type Playlist, type Video } from "../lib/api";
import { formatDuration } from "../lib/format";
import PlayerModal from "./PlayerModal";
import { useConfirm } from "./ConfirmProvider";
function Row({
video,
@ -98,6 +99,7 @@ function Row({
export default function Playlists() {
const { t } = useTranslation();
const qc = useQueryClient();
const confirm = useConfirm();
const [selectedId, setSelectedId] = useState<number | null>(null);
const [newName, setNewName] = useState("");
const [renaming, setRenaming] = useState(false);
@ -173,7 +175,13 @@ export default function Playlists() {
async function deletePlaylist() {
if (selectedId == null || !detail) return;
if (!window.confirm(t("playlists.confirmDelete", { name: detail.name }))) return;
const ok = await confirm({
title: t("playlists.delete"),
message: t("playlists.confirmDelete", { name: detail.name }),
confirmLabel: t("playlists.delete"),
danger: true,
});
if (!ok) return;
await api.deletePlaylist(selectedId);
setSelectedId(null);
qc.invalidateQueries({ queryKey: ["playlists"] });