mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-24 02:28:12 +00:00
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.
This commit is contained in:
parent
3011946c56
commit
3749953c2c
9 changed files with 303 additions and 50 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 = ({
|
|||
>
|
||||
<Box mb={20}>{message}</Box>
|
||||
|
||||
{actionKey && (
|
||||
<Checkbox
|
||||
label="Don't ask me again"
|
||||
checked={suppressChecked}
|
||||
onChange={handleToggleSuppress}
|
||||
mb={20}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showDeleteFileOption && (
|
||||
<Checkbox
|
||||
checked={deleteFiles}
|
||||
|
|
@ -94,6 +119,24 @@ const ConfirmationDialog = ({
|
|||
/>
|
||||
)}
|
||||
|
||||
{showStopStreamOption && (
|
||||
<Checkbox
|
||||
checked={stopStream}
|
||||
onChange={(event) => setStopStream(event.currentTarget.checked)}
|
||||
label={stopStreamLabel}
|
||||
mb="md"
|
||||
/>
|
||||
)}
|
||||
|
||||
{actionKey && (
|
||||
<Checkbox
|
||||
label="Don't ask me again"
|
||||
checked={suppressChecked}
|
||||
onChange={handleToggleSuppress}
|
||||
mb={20}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Group justify="flex-end">
|
||||
<Button variant="outline" onClick={handleClose} disabled={loading}>
|
||||
{cancelLabel}
|
||||
|
|
|
|||
|
|
@ -38,13 +38,18 @@ describe('ConfirmationDialog', () => {
|
|||
const mockOnSuppressChange = vi.fn();
|
||||
const mockSuppressWarning = vi.fn();
|
||||
const mockIsWarningSuppressed = vi.fn();
|
||||
const mockSetActionPreference = vi.fn();
|
||||
const mockGetActionPreference = vi.fn(() => false);
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockGetActionPreference.mockReturnValue(false);
|
||||
useWarningsStore.mockImplementation((selector) => {
|
||||
const state = {
|
||||
suppressWarning: mockSuppressWarning,
|
||||
isWarningSuppressed: mockIsWarningSuppressed,
|
||||
setActionPreference: mockSetActionPreference,
|
||||
getActionPreference: mockGetActionPreference,
|
||||
};
|
||||
return selector ? selector(state) : state;
|
||||
});
|
||||
|
|
@ -211,6 +216,93 @@ describe('ConfirmationDialog', () => {
|
|||
expect(mockOnConfirm).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it('should show stop stream option when enabled', () => {
|
||||
render(
|
||||
<ConfirmationDialog
|
||||
opened={true}
|
||||
onClose={mockOnClose}
|
||||
onConfirm={mockOnConfirm}
|
||||
showStopStreamOption={true}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByLabelText('Also stop active stream if playing')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should pass stopStream false by default when stop option enabled', () => {
|
||||
render(
|
||||
<ConfirmationDialog
|
||||
opened={true}
|
||||
onClose={mockOnClose}
|
||||
onConfirm={mockOnConfirm}
|
||||
showStopStreamOption={true}
|
||||
/>
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByText('Confirm'));
|
||||
|
||||
expect(mockOnConfirm).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('should pass stopStream true when stop option is checked', () => {
|
||||
render(
|
||||
<ConfirmationDialog
|
||||
opened={true}
|
||||
onClose={mockOnClose}
|
||||
onConfirm={mockOnConfirm}
|
||||
showStopStreamOption={true}
|
||||
actionKey="delete-channel"
|
||||
/>
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByLabelText('Also stop active stream if playing'));
|
||||
fireEvent.click(screen.getByText('Confirm'));
|
||||
|
||||
expect(mockOnConfirm).toHaveBeenCalledWith(true);
|
||||
expect(mockSetActionPreference).toHaveBeenCalledWith('delete-channel', {
|
||||
stopStream: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should render stop stream checkbox before do not ask again', () => {
|
||||
render(
|
||||
<ConfirmationDialog
|
||||
opened={true}
|
||||
onClose={mockOnClose}
|
||||
onConfirm={mockOnConfirm}
|
||||
showStopStreamOption={true}
|
||||
actionKey="delete-channel"
|
||||
/>
|
||||
);
|
||||
|
||||
const checkboxes = screen.getAllByRole('checkbox');
|
||||
expect(checkboxes).toHaveLength(2);
|
||||
expect(checkboxes[0]).toHaveAccessibleName(
|
||||
'Also stop active stream if playing'
|
||||
);
|
||||
expect(checkboxes[1]).toHaveAccessibleName("Don't ask me again");
|
||||
});
|
||||
|
||||
it('should restore saved stopStream preference when dialog opens', () => {
|
||||
mockGetActionPreference.mockReturnValue(true);
|
||||
|
||||
render(
|
||||
<ConfirmationDialog
|
||||
opened={true}
|
||||
onClose={mockOnClose}
|
||||
onConfirm={mockOnConfirm}
|
||||
showStopStreamOption={true}
|
||||
actionKey="delete-channel"
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByLabelText('Also stop active stream if playing')
|
||||
).toBeChecked();
|
||||
});
|
||||
|
||||
it('should reset deleteFiles state after confirmation', () => {
|
||||
const { rerender } = render(
|
||||
<ConfirmationDialog
|
||||
|
|
|
|||
|
|
@ -332,6 +332,7 @@ const ChannelsTable = ({ onReady }) => {
|
|||
// store/warnings
|
||||
const isWarningSuppressed = useWarningsStore((s) => s.isWarningSuppressed);
|
||||
const suppressWarning = useWarningsStore((s) => s.suppressWarning);
|
||||
const getActionPreference = useWarningsStore((s) => s.getActionPreference);
|
||||
|
||||
/**
|
||||
* useState
|
||||
|
|
@ -575,8 +576,9 @@ const ChannelsTable = ({ onReady }) => {
|
|||
setChannelToDelete(null);
|
||||
|
||||
if (isWarningSuppressed('delete-channels')) {
|
||||
// Skip warning if suppressed
|
||||
return executeDeleteChannels();
|
||||
return executeDeleteChannels(
|
||||
getActionPreference('delete-channels', 'stopStream', false)
|
||||
);
|
||||
}
|
||||
|
||||
setConfirmDeleteOpen(true);
|
||||
|
|
@ -589,17 +591,19 @@ const ChannelsTable = ({ onReady }) => {
|
|||
setChannelToDelete(knownChannel); // Store the channel object for displaying details
|
||||
|
||||
if (isWarningSuppressed('delete-channel')) {
|
||||
// Skip warning if suppressed
|
||||
return executeDeleteChannel(id);
|
||||
return executeDeleteChannel(
|
||||
id,
|
||||
getActionPreference('delete-channel', 'stopStream', false)
|
||||
);
|
||||
}
|
||||
|
||||
setConfirmDeleteOpen(true);
|
||||
};
|
||||
|
||||
const executeDeleteChannel = async (id) => {
|
||||
const executeDeleteChannel = async (id, stopStream = false) => {
|
||||
setDeleting(true);
|
||||
try {
|
||||
await deleteChannel(id);
|
||||
await deleteChannel(id, { stopStream });
|
||||
requeryChannels();
|
||||
} finally {
|
||||
setDeleting(false);
|
||||
|
|
@ -609,19 +613,20 @@ const ChannelsTable = ({ onReady }) => {
|
|||
|
||||
const handleDeleteChannels = async () => {
|
||||
if (isWarningSuppressed('delete-channels')) {
|
||||
// Skip warning if suppressed
|
||||
return executeDeleteChannels();
|
||||
return executeDeleteChannels(
|
||||
getActionPreference('delete-channels', 'stopStream', false)
|
||||
);
|
||||
}
|
||||
|
||||
setIsBulkDelete(true);
|
||||
setConfirmDeleteOpen(true);
|
||||
};
|
||||
|
||||
const executeDeleteChannels = async () => {
|
||||
const executeDeleteChannels = async (stopStream = false) => {
|
||||
setIsLoading(true);
|
||||
setDeleting(true);
|
||||
try {
|
||||
await deleteChannels(table.selectedTableIds);
|
||||
await deleteChannels(table.selectedTableIds, { stopStream });
|
||||
await requeryChannels();
|
||||
setSelectedChannelIds([]);
|
||||
table.setSelectedTableIds([]);
|
||||
|
|
@ -1668,11 +1673,12 @@ const ChannelsTable = ({ onReady }) => {
|
|||
<ConfirmationDialog
|
||||
opened={confirmDeleteOpen}
|
||||
onClose={() => setConfirmDeleteOpen(false)}
|
||||
onConfirm={() =>
|
||||
isBulkDelete
|
||||
? executeDeleteChannels()
|
||||
: executeDeleteChannel(deleteTarget)
|
||||
}
|
||||
onConfirm={(stopStream) => {
|
||||
const shouldStop = stopStream === true;
|
||||
return isBulkDelete
|
||||
? executeDeleteChannels(shouldStop)
|
||||
: executeDeleteChannel(deleteTarget, shouldStop);
|
||||
}}
|
||||
loading={deleting}
|
||||
title={`Confirm ${isBulkDelete ? 'Bulk ' : ''}Channel Deletion`}
|
||||
message={
|
||||
|
|
@ -1695,6 +1701,8 @@ This action cannot be undone.`}
|
|||
cancelLabel="Cancel"
|
||||
actionKey={isBulkDelete ? 'delete-channels' : 'delete-channel'}
|
||||
onSuppressChange={suppressWarning}
|
||||
showStopStreamOption
|
||||
stopStreamLabel="Also stop active stream if playing"
|
||||
size="md"
|
||||
/>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -200,7 +200,11 @@ vi.mock('../../ConfirmationDialog', () => ({
|
|||
opened ? (
|
||||
<div data-testid="confirm-dialog">
|
||||
<span data-testid="confirm-title">{title}</span>
|
||||
<button data-testid="confirm-ok" onClick={onConfirm} disabled={loading}>
|
||||
<button
|
||||
data-testid="confirm-ok"
|
||||
onClick={() => onConfirm()}
|
||||
disabled={loading}
|
||||
>
|
||||
{confirmLabel}
|
||||
</button>
|
||||
<button data-testid="confirm-cancel" onClick={onClose}>
|
||||
|
|
@ -474,6 +478,7 @@ const setupMocks = ({
|
|||
authUser = makeAdminUser(),
|
||||
isWarningSuppressed = vi.fn(() => false),
|
||||
suppressWarning = vi.fn(),
|
||||
getActionPreference = vi.fn(() => false),
|
||||
selectedProfileId = '0',
|
||||
profiles = { 0: { name: 'Default', channels: new Set() } },
|
||||
pageCount = 1,
|
||||
|
|
@ -532,7 +537,7 @@ const setupMocks = ({
|
|||
);
|
||||
|
||||
vi.mocked(useWarningsStore).mockImplementation((sel) =>
|
||||
sel({ isWarningSuppressed, suppressWarning })
|
||||
sel({ isWarningSuppressed, suppressWarning, getActionPreference })
|
||||
);
|
||||
|
||||
vi.mocked(useLocalStorage).mockReturnValue([{}, vi.fn()]);
|
||||
|
|
@ -820,10 +825,26 @@ describe('ChannelsTable', () => {
|
|||
render(<ChannelsTable />);
|
||||
const { getByTestId } = renderActionCell(channel, tableInstance);
|
||||
fireEvent.click(getByTestId('icon-square-minus').closest('button'));
|
||||
await waitFor(() => expect(deleteChannel).toHaveBeenCalledWith(7));
|
||||
await waitFor(() =>
|
||||
expect(deleteChannel).toHaveBeenCalledWith(7, { stopStream: false })
|
||||
);
|
||||
expect(screen.queryByTestId('confirm-dialog')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('uses saved stopStream preference when warning is suppressed', async () => {
|
||||
const channel = makeChannel({ id: 8 });
|
||||
const { tableInstance } = setupMocks({
|
||||
isWarningSuppressed: vi.fn(() => true),
|
||||
getActionPreference: vi.fn(() => true),
|
||||
});
|
||||
render(<ChannelsTable />);
|
||||
const { getByTestId } = renderActionCell(channel, tableInstance);
|
||||
fireEvent.click(getByTestId('icon-square-minus').closest('button'));
|
||||
await waitFor(() =>
|
||||
expect(deleteChannel).toHaveBeenCalledWith(8, { stopStream: true })
|
||||
);
|
||||
});
|
||||
|
||||
it('calls deleteChannel when confirm dialog is confirmed', async () => {
|
||||
const channel = makeChannel({ id: 5 });
|
||||
const { tableInstance } = setupMocks({
|
||||
|
|
@ -833,7 +854,9 @@ describe('ChannelsTable', () => {
|
|||
const { getByTestId } = renderActionCell(channel, tableInstance);
|
||||
fireEvent.click(getByTestId('icon-square-minus').closest('button'));
|
||||
fireEvent.click(screen.getByTestId('confirm-ok'));
|
||||
await waitFor(() => expect(deleteChannel).toHaveBeenCalledWith(5));
|
||||
await waitFor(() =>
|
||||
expect(deleteChannel).toHaveBeenCalledWith(5, { stopStream: false })
|
||||
);
|
||||
});
|
||||
|
||||
it('calls requeryChannels after deleteChannel succeeds', async () => {
|
||||
|
|
@ -913,7 +936,9 @@ describe('ChannelsTable', () => {
|
|||
render(<ChannelsTable />);
|
||||
fireEvent.click(screen.getByTestId('header-delete-channels'));
|
||||
fireEvent.click(screen.getByTestId('confirm-ok'));
|
||||
await waitFor(() => expect(deleteChannels).toHaveBeenCalledWith([1, 2]));
|
||||
await waitFor(() =>
|
||||
expect(deleteChannels).toHaveBeenCalledWith([1, 2], { stopStream: false })
|
||||
);
|
||||
});
|
||||
|
||||
it('awaits requeryChannels before clearing bulk selection', async () => {
|
||||
|
|
@ -939,7 +964,9 @@ describe('ChannelsTable', () => {
|
|||
fireEvent.click(screen.getByTestId('header-delete-channels'));
|
||||
fireEvent.click(screen.getByTestId('confirm-ok'));
|
||||
|
||||
await waitFor(() => expect(deleteChannels).toHaveBeenCalledWith([1, 2]));
|
||||
await waitFor(() =>
|
||||
expect(deleteChannels).toHaveBeenCalledWith([1, 2], { stopStream: false })
|
||||
);
|
||||
expect(requeryChannels).toHaveBeenCalled();
|
||||
expect(setSelectedTableIds).not.toHaveBeenCalled();
|
||||
|
||||
|
|
@ -961,7 +988,9 @@ describe('ChannelsTable', () => {
|
|||
});
|
||||
render(<ChannelsTable />);
|
||||
fireEvent.click(screen.getByTestId('header-delete-channels'));
|
||||
await waitFor(() => expect(deleteChannels).toHaveBeenCalledWith([1, 2]));
|
||||
await waitFor(() =>
|
||||
expect(deleteChannels).toHaveBeenCalledWith([1, 2], { stopStream: false })
|
||||
);
|
||||
expect(screen.queryByTestId('confirm-dialog')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ describe('useWarningsStore', () => {
|
|||
beforeEach(() => {
|
||||
useWarningsStore.setState({
|
||||
suppressedWarnings: {},
|
||||
actionPreferences: {},
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -13,6 +14,7 @@ describe('useWarningsStore', () => {
|
|||
const { result } = renderHook(() => useWarningsStore());
|
||||
|
||||
expect(result.current.suppressedWarnings).toEqual({});
|
||||
expect(result.current.actionPreferences).toEqual({});
|
||||
});
|
||||
|
||||
it('should suppress a warning', () => {
|
||||
|
|
@ -192,4 +194,36 @@ describe('useWarningsStore', () => {
|
|||
expect(result.current.isWarningSuppressed('action2')).toBe(false);
|
||||
expect(result.current.isWarningSuppressed('action3')).toBe(false);
|
||||
});
|
||||
|
||||
it('should store and read action preferences', () => {
|
||||
const { result } = renderHook(() => useWarningsStore());
|
||||
|
||||
expect(result.current.getActionPreference('delete-channel', 'stopStream')).toBe(
|
||||
false
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.setActionPreference('delete-channel', { stopStream: true });
|
||||
});
|
||||
|
||||
expect(
|
||||
result.current.getActionPreference('delete-channel', 'stopStream')
|
||||
).toBe(true);
|
||||
expect(
|
||||
result.current.getActionPreference('delete-channels', 'stopStream', false)
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('should clear action preferences when resetSuppressions is called', () => {
|
||||
const { result } = renderHook(() => useWarningsStore());
|
||||
|
||||
act(() => {
|
||||
result.current.suppressWarning('delete-channel');
|
||||
result.current.setActionPreference('delete-channel', { stopStream: true });
|
||||
result.current.resetSuppressions();
|
||||
});
|
||||
|
||||
expect(result.current.suppressedWarnings).toEqual({});
|
||||
expect(result.current.actionPreferences).toEqual({});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
import { create } from 'zustand';
|
||||
|
||||
const useWarningsStore = create((set) => ({
|
||||
const useWarningsStore = create((set, get) => ({
|
||||
// Map of action keys to whether they're suppressed
|
||||
suppressedWarnings: {},
|
||||
|
||||
// Optional per-action preferences remembered with confirmations
|
||||
// e.g. { 'delete-channel': { stopStream: true } }
|
||||
actionPreferences: {},
|
||||
|
||||
// Function to check if a warning is suppressed
|
||||
isWarningSuppressed: (actionKey) => {
|
||||
const state = useWarningsStore.getState();
|
||||
const state = get();
|
||||
return state.suppressedWarnings[actionKey] === true;
|
||||
},
|
||||
|
||||
|
|
@ -20,9 +24,32 @@ const useWarningsStore = create((set) => ({
|
|||
}));
|
||||
},
|
||||
|
||||
setActionPreference: (actionKey, preference) => {
|
||||
if (!actionKey || !preference || typeof preference !== 'object') {
|
||||
return;
|
||||
}
|
||||
set((state) => ({
|
||||
actionPreferences: {
|
||||
...state.actionPreferences,
|
||||
[actionKey]: {
|
||||
...(state.actionPreferences[actionKey] || {}),
|
||||
...preference,
|
||||
},
|
||||
},
|
||||
}));
|
||||
},
|
||||
|
||||
getActionPreference: (actionKey, key, defaultValue = false) => {
|
||||
const prefs = get().actionPreferences[actionKey];
|
||||
if (!prefs || !(key in prefs)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return prefs[key];
|
||||
},
|
||||
|
||||
// Function to reset all suppressions
|
||||
resetSuppressions: () => {
|
||||
set({ suppressedWarnings: {} });
|
||||
set({ suppressedWarnings: {}, actionPreferences: {} });
|
||||
},
|
||||
}));
|
||||
|
||||
|
|
|
|||
|
|
@ -107,11 +107,11 @@ export const hdhrUrlBase = `${window.location.protocol}//${window.location.host}
|
|||
export const reorderChannel = (channelId, insertAfterId) => {
|
||||
return API.reorderChannel(channelId, insertAfterId);
|
||||
};
|
||||
export const deleteChannel = (id) => {
|
||||
return API.deleteChannel(id);
|
||||
export const deleteChannel = (id, options = {}) => {
|
||||
return API.deleteChannel(id, options);
|
||||
};
|
||||
export const deleteChannels = (channelIds) => {
|
||||
return API.deleteChannels(channelIds);
|
||||
export const deleteChannels = (channelIds, options = {}) => {
|
||||
return API.deleteChannels(channelIds, options);
|
||||
};
|
||||
export const queryChannels = (params) => {
|
||||
return API.queryChannels(params);
|
||||
|
|
|
|||
|
|
@ -576,12 +576,24 @@ describe('ChannelsTableUtils', () => {
|
|||
|
||||
it('deleteChannel calls API.deleteChannel', () => {
|
||||
ChannelsTableUtils.deleteChannel(5);
|
||||
expect(API.deleteChannel).toHaveBeenCalledWith(5);
|
||||
expect(API.deleteChannel).toHaveBeenCalledWith(5, {});
|
||||
});
|
||||
|
||||
it('deleteChannel forwards stopStream option', () => {
|
||||
ChannelsTableUtils.deleteChannel(5, { stopStream: true });
|
||||
expect(API.deleteChannel).toHaveBeenCalledWith(5, { stopStream: true });
|
||||
});
|
||||
|
||||
it('deleteChannels calls API.deleteChannels', () => {
|
||||
ChannelsTableUtils.deleteChannels([1, 2, 3]);
|
||||
expect(API.deleteChannels).toHaveBeenCalledWith([1, 2, 3]);
|
||||
expect(API.deleteChannels).toHaveBeenCalledWith([1, 2, 3], {});
|
||||
});
|
||||
|
||||
it('deleteChannels forwards stopStream option', () => {
|
||||
ChannelsTableUtils.deleteChannels([1, 2], { stopStream: true });
|
||||
expect(API.deleteChannels).toHaveBeenCalledWith([1, 2], {
|
||||
stopStream: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('queryChannels calls API.queryChannels', () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue