From 3749953c2cc2f1af54a88e2db8537d26f8339253 Mon Sep 17 00:00:00 2001 From: SergeantPanda Date: Sun, 19 Jul 2026 18:52:34 +0000 Subject: [PATCH] feat(confirmation-dialog): add stop stream option to confirmation dialog This update enhances the ConfirmationDialog component by introducing an option to stop the active stream when confirming actions. The new checkbox allows users to choose whether to stop the stream if it is playing, with the preference being remembered for future confirmations. The implementation includes updates to the state management and effects to handle the new option, as well as corresponding tests to ensure functionality. Additionally, the ChannelsTable component has been updated to utilize this new feature, improving user experience during channel deletions. --- frontend/src/api.js | 20 ++-- .../src/components/ConfirmationDialog.jsx | 69 +++++++++++--- .../__tests__/ConfirmationDialog.test.jsx | 92 +++++++++++++++++++ .../src/components/tables/ChannelsTable.jsx | 38 +++++--- .../tables/__tests__/ChannelsTable.test.jsx | 43 +++++++-- .../src/store/__tests__/warnings.test.jsx | 34 +++++++ frontend/src/store/warnings.jsx | 33 ++++++- .../src/utils/tables/ChannelsTableUtils.js | 8 +- .../__tests__/ChannelsTableUtils.test.js | 16 +++- 9 files changed, 303 insertions(+), 50 deletions(-) diff --git a/frontend/src/api.js b/frontend/src/api.js index 3a745896..31963452 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -610,11 +610,19 @@ export default class API { } } - static async deleteChannel(id) { + static async deleteChannel(id, { stopStream = false } = {}) { try { - await request(`${host}/api/channels/channels/${id}/`, { - method: 'DELETE', - }); + const params = new URLSearchParams(); + if (stopStream) { + params.set('stop_stream', 'true'); + } + const query = params.toString(); + await request( + `${host}/api/channels/channels/${id}/${query ? `?${query}` : ''}`, + { + method: 'DELETE', + } + ); useChannelsStore.getState().removeChannels([id]); await API.requeryStreams(); @@ -624,11 +632,11 @@ export default class API { } // @TODO: the bulk delete endpoint is currently broken - static async deleteChannels(channel_ids) { + static async deleteChannels(channel_ids, { stopStream = false } = {}) { try { await request(`${host}/api/channels/channels/bulk-delete/`, { method: 'DELETE', - body: { channel_ids }, + body: { channel_ids, stop_stream: Boolean(stopStream) }, }); useChannelsStore.getState().removeChannels(channel_ids); diff --git a/frontend/src/components/ConfirmationDialog.jsx b/frontend/src/components/ConfirmationDialog.jsx index 86e7d7af..e9a6f433 100644 --- a/frontend/src/components/ConfirmationDialog.jsx +++ b/frontend/src/components/ConfirmationDialog.jsx @@ -1,5 +1,5 @@ import { Modal, Group, Button, Checkbox, Box } from '@mantine/core'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import useWarningsStore from '../store/warnings'; /** @@ -17,6 +17,10 @@ import useWarningsStore from '../store/warnings'; * @param {Function} props.onSuppressChange - Called when "don't show again" option changes * @param {string} [props.size='md'] - Size of the modal * @param {boolean} [props.loading=false] - Whether the confirm button should show loading state + * @param {boolean} [props.showDeleteFileOption=false] - Show "also delete files" checkbox + * @param {string} [props.deleteFileLabel] - Label for delete-files checkbox + * @param {boolean} [props.showStopStreamOption=false] - Show "also stop stream" checkbox + * @param {string} [props.stopStreamLabel] - Label for stop-stream checkbox */ const ConfirmationDialog = ({ opened, @@ -32,14 +36,38 @@ const ConfirmationDialog = ({ zIndex = 1000, showDeleteFileOption = false, deleteFileLabel = 'Also delete files from disk', + showStopStreamOption = false, + stopStreamLabel = 'Also stop active stream if playing', loading = false, }) => { const suppressWarning = useWarningsStore((s) => s.suppressWarning); const isWarningSuppressed = useWarningsStore((s) => s.isWarningSuppressed); + const setActionPreference = useWarningsStore((s) => s.setActionPreference); + const getActionPreference = useWarningsStore((s) => s.getActionPreference); const [suppressChecked, setSuppressChecked] = useState( isWarningSuppressed(actionKey) ); const [deleteFiles, setDeleteFiles] = useState(false); + const [stopStream, setStopStream] = useState(false); + + useEffect(() => { + if (!opened) { + return; + } + setSuppressChecked(isWarningSuppressed(actionKey)); + setDeleteFiles(false); + if (showStopStreamOption && actionKey) { + setStopStream(getActionPreference(actionKey, 'stopStream', false)); + } else { + setStopStream(false); + } + }, [ + opened, + actionKey, + showStopStreamOption, + isWarningSuppressed, + getActionPreference, + ]); const handleToggleSuppress = (e) => { setSuppressChecked(e.currentTarget.checked); @@ -49,19 +77,25 @@ const ConfirmationDialog = ({ }; const handleConfirm = () => { + if (showStopStreamOption && actionKey) { + setActionPreference(actionKey, { stopStream }); + } if (suppressChecked) { suppressWarning(actionKey); } - if (showDeleteFileOption) { + if (showStopStreamOption) { + onConfirm(stopStream); + } else if (showDeleteFileOption) { onConfirm(deleteFiles); } else { onConfirm(); } - setDeleteFiles(false); // Reset for next time + setDeleteFiles(false); }; const handleClose = () => { - setDeleteFiles(false); // Reset for next time + setDeleteFiles(false); + setStopStream(false); onClose(); }; @@ -76,15 +110,6 @@ const ConfirmationDialog = ({ > {message} - {actionKey && ( - - )} - {showDeleteFileOption && ( )} + {showStopStreamOption && ( + setStopStream(event.currentTarget.checked)} + label={stopStreamLabel} + mb="md" + /> + )} + + {actionKey && ( + + )} +