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 49cf2246b8
commit 8a82d56950
7 changed files with 103 additions and 9 deletions

View file

@ -18,6 +18,7 @@ import { formatEta } from "../lib/format";
import { notify } from "../lib/notifications";
import Tooltip from "./Tooltip";
import Avatar from "./Avatar";
import { useConfirm } from "./ConfirmProvider";
export type ChannelStatusFilter = "all" | "needs_full" | "fully_synced" | "hidden";
@ -43,6 +44,7 @@ export default function Channels({
}) {
const { t } = useTranslation();
const qc = useQueryClient();
const confirm = useConfirm();
// A YouTube-gated action (sync, backfill, unsubscribe) that 403s means the user hasn't
// granted the needed scope — surface that with a "Connect" action instead of a vague fail.
@ -309,13 +311,14 @@ export default function Channels({
c={c}
userTags={userTags}
canWrite={canWrite}
onUnsubscribe={() => {
if (
window.confirm(
t("channels.confirmUnsubscribe", { name: c.title ?? c.id })
)
)
unsubscribe.mutate(c.id);
onUnsubscribe={async () => {
const ok = await confirm({
title: t("channels.row.unsubscribeOnYoutube"),
message: t("channels.confirmUnsubscribe", { name: c.title ?? c.id }),
confirmLabel: t("channels.row.unsubscribeOnYoutube"),
danger: true,
});
if (ok) unsubscribe.mutate(c.id);
}}
onView={() => onViewChannel(c.id, c.title ?? t("channels.row.thisChannel"))}
onPriority={(d) => bumpPriority(c.id, c.priority, d)}