diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/ringtones/components/RingtonesSettings/RingtonesSettings.tsx b/apps/desktop/src/renderer/routes/_authenticated/settings/ringtones/components/RingtonesSettings/RingtonesSettings.tsx index fc64e429b03..a8bb6831933 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/settings/ringtones/components/RingtonesSettings/RingtonesSettings.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/ringtones/components/RingtonesSettings/RingtonesSettings.tsx @@ -33,6 +33,7 @@ import { SETTING_ITEM_ID, type SettingItemId, } from "../../../utils/settings-search"; +import { DeleteRingtoneDialog } from "./components/DeleteRingtoneDialog"; import { RenameRingtoneDialog } from "./components/RenameRingtoneDialog"; import { VolumeDropdown } from "./components/VolumeDropdown"; import { YouTubeImportDialog } from "./components/YouTubeImportDialog"; @@ -273,15 +274,19 @@ export function RingtonesSettings({ visibleItems }: RingtonesSettingsProps) { }, }); + const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); + const [deleteError, setDeleteError] = useState(null); const deleteCustomRingtone = electronTrpc.ringtone.deleteCustom.useMutation({ onSuccess: async () => { if (selectedRingtoneId === CUSTOM_RINGTONE_ID) { setRingtone(AVAILABLE_RINGTONES[0]?.id ?? ""); } + setDeleteError(null); + setDeleteDialogOpen(false); await utils.ringtone.getCustom.invalidate(); }, onError: (error) => { - console.error("Failed to delete custom ringtone:", error); + setDeleteError(error.message); }, }); @@ -299,11 +304,14 @@ export function RingtonesSettings({ visibleItems }: RingtonesSettingsProps) { }, []); const handleDeleteCustom = useCallback(() => { - const confirmed = window.confirm( - "Delete the custom notification sound? This cannot be undone.", - ); - if (!confirmed) return; - deleteCustomRingtone.mutate(); + setDeleteError(null); + setDeleteDialogOpen(true); + }, []); + + const handleConfirmDelete = useCallback(async () => { + await deleteCustomRingtone.mutateAsync().catch(() => { + // Error surfaced via deleteError state. + }); }, [deleteCustomRingtone]); // Clean up timer and stop any playing sound on unmount @@ -489,6 +497,18 @@ export function RingtonesSettings({ visibleItems }: RingtonesSettingsProps) { errorMessage={youtubeError} /> + { + setDeleteDialogOpen(open); + if (!open) setDeleteError(null); + }} + ringtoneName={customRingtone?.name ?? ""} + onConfirm={handleConfirmDelete} + isSubmitting={deleteCustomRingtone.isPending} + errorMessage={deleteError} + /> + { diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/ringtones/components/RingtonesSettings/components/DeleteRingtoneDialog/DeleteRingtoneDialog.tsx b/apps/desktop/src/renderer/routes/_authenticated/settings/ringtones/components/RingtonesSettings/components/DeleteRingtoneDialog/DeleteRingtoneDialog.tsx new file mode 100644 index 00000000000..8ecd2c9dd4b --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/ringtones/components/RingtonesSettings/components/DeleteRingtoneDialog/DeleteRingtoneDialog.tsx @@ -0,0 +1,77 @@ +import { Button } from "@superset/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@superset/ui/dialog"; +import { LuLoaderCircle } from "react-icons/lu"; + +interface DeleteRingtoneDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + ringtoneName: string; + onConfirm: () => Promise | void; + isSubmitting: boolean; + errorMessage?: string | null; +} + +export function DeleteRingtoneDialog({ + open, + onOpenChange, + ringtoneName, + onConfirm, + isSubmitting, + errorMessage, +}: DeleteRingtoneDialogProps) { + return ( + { + if (!next && isSubmitting) return; + onOpenChange(next); + }} + > + + + Delete custom audio + + {ringtoneName + ? `Delete “${ringtoneName}”? This cannot be undone.` + : "Delete the custom notification sound? This cannot be undone."} + + + + {errorMessage && ( +

{errorMessage}

+ )} + + + + + +
+
+ ); +} diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/ringtones/components/RingtonesSettings/components/DeleteRingtoneDialog/index.ts b/apps/desktop/src/renderer/routes/_authenticated/settings/ringtones/components/RingtonesSettings/components/DeleteRingtoneDialog/index.ts new file mode 100644 index 00000000000..e669f0f4238 --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/ringtones/components/RingtonesSettings/components/DeleteRingtoneDialog/index.ts @@ -0,0 +1 @@ +export { DeleteRingtoneDialog } from "./DeleteRingtoneDialog";