diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json
index 9374d3680..82cfcbb35 100644
--- a/src/i18n/locales/en.json
+++ b/src/i18n/locales/en.json
@@ -364,6 +364,10 @@
"title": "Add items to the queue",
"description": "This action will add all items in the current filtered view"
},
+ "queueChangeConfirmation": {
+ "title": "Discard the current queue?",
+ "description": "This will remove all items from the current queue."
+ },
"addToPlaylist": {
"create": "Create $t(entity.playlist, {\"count\": 1}) {{playlist}}",
"input_playlists": "$t(entity.playlist, {\"count\": 2})",
@@ -1075,6 +1079,8 @@
"passwordStore": "Passwords/secret store",
"playerFilters": "Filter songs from the queue",
"playerFilters_description": "Omit songs from being added to the queue based on the following criteria",
+ "confirmQueueChanges": "Confirm queue changes",
+ "confirmQueueChanges_description": "Ask for confirmation before discarding the current queue",
"playbackStyle_description": "Select the playback style to use for the audio player",
"playbackStyle_optionCrossFade": "Crossfade",
"playbackStyle_optionNormal": "Normal",
diff --git a/src/renderer/features/now-playing/components/play-queue-list-controls.tsx b/src/renderer/features/now-playing/components/play-queue-list-controls.tsx
index 719b2ce31..1a274614e 100644
--- a/src/renderer/features/now-playing/components/play-queue-list-controls.tsx
+++ b/src/renderer/features/now-playing/components/play-queue-list-controls.tsx
@@ -138,7 +138,7 @@ const QueuePlaybackIcons = ({ tableRef }: { tableRef: RefObject
{
const { t } = useTranslation();
const queryClient = useQueryClient();
const storeActions = usePlayerActions();
+ const settingsActions = useSettingsStoreActions();
const timeoutIds = useRef>>({});
const [doNotShowAgain, setDoNotShowAgain] = useLocalStorage({
@@ -171,6 +177,48 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
key: 'large_fetch_confirmation',
});
+ const confirmQueueChange = useCallback(
+ (onConfirm: () => void) => {
+ const shouldConfirm = useSettingsStore.getState().general.confirmQueueChanges;
+
+ if (!shouldConfirm || storeActions.getQueue().items.length === 0) {
+ onConfirm();
+ return;
+ }
+
+ openModal({
+ children: (
+ {
+ closeAllModals();
+ onConfirm();
+ }}
+ >
+
+ {t('form.queueChangeConfirmation.description')}
+ {
+ settingsActions.setSettings({
+ general: {
+ confirmQueueChanges: !event.currentTarget.checked,
+ },
+ });
+ }}
+ />
+
+
+ ),
+ title: t('form.queueChangeConfirmation.title'),
+ });
+ },
+ [settingsActions, storeActions, t],
+ );
+
const confirmLargeFetch = useCallback((): Promise => {
if (doNotShowAgain) {
return Promise.resolve(true);
@@ -225,29 +273,42 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
filteredData = tagPlaylistContext(filteredData, resolvedContextId);
}
- if (typeof type === 'object' && 'edge' in type && type.edge !== null) {
- const edge = type.edge === 'top' ? 'top' : 'bottom';
+ const addToQueue = () => {
+ if (typeof type === 'object' && 'edge' in type && type.edge !== null) {
+ const edge = type.edge === 'top' ? 'top' : 'bottom';
- logger.debug('Added to queue by data', {
- data: data.length,
- edge,
- filtered: filteredData.length,
- type,
- uniqueId: type.uniqueId,
- });
+ logger.debug('Added to queue by data', {
+ data: data.length,
+ edge,
+ filtered: filteredData.length,
+ type,
+ uniqueId: type.uniqueId,
+ });
- storeActions.addToQueueByUniqueId(filteredData, type.uniqueId, edge, playSongId);
+ storeActions.addToQueueByUniqueId(
+ filteredData,
+ type.uniqueId,
+ edge,
+ playSongId,
+ );
+ } else {
+ logger.debug('Added to queue by type', {
+ data: data.length,
+ filtered: filteredData.length,
+ type,
+ });
+
+ storeActions.addToQueueByType(filteredData, type as Play, playSongId);
+ }
+ };
+
+ if (isReplaceQueueType(type)) {
+ confirmQueueChange(addToQueue);
} else {
- logger.debug('Added to queue by type', {
- data: data.length,
- filtered: filteredData.length,
- type,
- });
-
- storeActions.addToQueueByType(filteredData, type as Play, playSongId);
+ addToQueue();
}
},
- [storeActions],
+ [confirmQueueChange, storeActions],
);
const addToQueueByFetch = useCallback(
@@ -324,11 +385,19 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
filteredSongs = tagPlaylistContext(filteredSongs, resolvedContextId);
}
- if (typeof type === 'object' && 'edge' in type && type.edge !== null) {
- const edge = type.edge === 'top' ? 'top' : 'bottom';
- storeActions.addToQueueByUniqueId(filteredSongs, type.uniqueId, edge);
+ const addToQueue = () => {
+ if (typeof type === 'object' && 'edge' in type && type.edge !== null) {
+ const edge = type.edge === 'top' ? 'top' : 'bottom';
+ storeActions.addToQueueByUniqueId(filteredSongs, type.uniqueId, edge);
+ } else {
+ storeActions.addToQueueByType(filteredSongs, type as Play);
+ }
+ };
+
+ if (isReplaceQueueType(type)) {
+ confirmQueueChange(addToQueue);
} else {
- storeActions.addToQueueByType(filteredSongs, type as Play);
+ addToQueue();
}
} catch (err: any) {
if (instanceOfCancellationError(err)) {
@@ -347,7 +416,7 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
});
}
},
- [queryClient, storeActions, t],
+ [confirmQueueChange, queryClient, storeActions, t],
);
const addToQueueByListQuery = useCallback(
@@ -526,10 +595,12 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
);
const clearQueue = useCallback(() => {
- logger.debug('Cleared queue');
+ confirmQueueChange(() => {
+ logger.debug('Cleared queue');
- storeActions.clearQueue();
- }, [storeActions]);
+ storeActions.clearQueue();
+ });
+ }, [confirmQueueChange, storeActions]);
const clearSelected = useCallback(
(items: QueueSong[]) => {
@@ -637,15 +708,17 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
const setQueue = useCallback(
(data: Song[], index?: number, position?: number) => {
- logger.debug('Set queue', {
- data: data.length,
- index,
- position,
- });
+ confirmQueueChange(() => {
+ logger.debug('Set queue', {
+ data: data.length,
+ index,
+ position,
+ });
- storeActions.setQueue(data, index, position);
+ storeActions.setQueue(data, index, position);
+ });
},
- [storeActions],
+ [confirmQueueChange, storeActions],
);
const setSpeed = useCallback(
diff --git a/src/renderer/features/settings/components/general/application-settings.tsx b/src/renderer/features/settings/components/general/application-settings.tsx
index 2404aa915..e5038e038 100644
--- a/src/renderer/features/settings/components/general/application-settings.tsx
+++ b/src/renderer/features/settings/components/general/application-settings.tsx
@@ -363,6 +363,24 @@ export const ApplicationSettings = memo(() => {
isHidden: !isElectron(),
title: t('setting.savePlayQueue'),
},
+ {
+ control: (
+ {
+ setSettings({
+ general: {
+ ...settings,
+ confirmQueueChanges: event.currentTarget.checked,
+ },
+ });
+ }}
+ />
+ ),
+ description: t('setting.confirmQueueChanges', { context: 'description' }),
+ title: t('setting.confirmQueueChanges'),
+ },
{
control: (