feat: added playlist from queue creation (#2210)

* feat: added playlist from queue creation

- added functionality to create a playlist from queue
- prefilling is done as an extra function to be api agnostic since navidrome native api for example does not offer a parameter for filling a playlist with songs on creation

#2204

* fix: fixed wrong declaration
This commit is contained in:
Kevin
2026-07-17 07:54:42 +02:00
committed by GitHub
parent 49b8c9d08f
commit d99fe8b8c6
5 changed files with 101 additions and 12 deletions
@@ -6,6 +6,7 @@ import {
PlaylistQueryBuilder,
PlaylistQueryBuilderRef,
} from '/@/renderer/features/playlists/components/playlist-query-builder';
import { useAddToPlaylist } from '/@/renderer/features/playlists/mutations/add-to-playlist-mutation';
import { useCreatePlaylist } from '/@/renderer/features/playlists/mutations/create-playlist-mutation';
import { convertQueryGroupToNDQuery } from '/@/renderer/features/playlists/utils';
import { useCurrentServer } from '/@/renderer/store';
@@ -24,17 +25,22 @@ import {
CreatePlaylistBody,
ServerListItem,
ServerType,
Song,
SongListSort,
} from '/@/shared/types/domain-types';
import { ServerFeature } from '/@/shared/types/features-types';
interface CreatePlaylistFormProps {
onCancel: () => void;
songs?: Song[];
}
export const CreatePlaylistForm = ({ onCancel }: CreatePlaylistFormProps) => {
export const CreatePlaylistForm = ({ onCancel, songs }: CreatePlaylistFormProps) => {
const { t } = useTranslation();
const mutation = useCreatePlaylist({});
const createPlaylistMutation = useCreatePlaylist({});
const addToPlaylistMutation = useAddToPlaylist({});
const server = useCurrentServer();
const queryBuilderRef = useRef<PlaylistQueryBuilderRef>(null);
@@ -48,6 +54,8 @@ export const CreatePlaylistForm = ({ onCancel }: CreatePlaylistFormProps) => {
const [isSmartPlaylist, setIsSmartPlaylist] = useState(false);
const [step, setStep] = useState<1 | 2>(1);
const isPrefilledPlaylist = !!songs && songs.length > 0;
const handleSubmit = form.onSubmit((values) => {
if (!server) return;
@@ -78,7 +86,7 @@ export const CreatePlaylistForm = ({ onCancel }: CreatePlaylistFormProps) => {
}
: undefined;
mutation.mutate(
createPlaylistMutation.mutate(
{
apiClientProps: { serverId: server.id },
body: {
@@ -93,18 +101,45 @@ export const CreatePlaylistForm = ({ onCancel }: CreatePlaylistFormProps) => {
title: t('error.genericError'),
});
},
onSuccess: () => {
onSuccess: (data) => {
toast.success({
message: t('form.createPlaylist.success'),
});
handlePlaylistPrefilling(data?.id);
onCancel();
},
},
);
});
const handlePlaylistPrefilling = (playlistId?: string) => {
if (!songs || !playlistId) {
return;
}
const allSongIds = songs.map((song) => song.id);
addToPlaylistMutation.mutate(
{
apiClientProps: { serverId: server.id },
body: { songId: allSongIds },
query: { id: playlistId },
},
{
onError: (err) => {
toast.error({
message: `${err.message}`,
title: t('error.genericError'),
});
},
},
);
};
const isPublicDisplayed = hasFeature(server, ServerFeature.PUBLIC_PLAYLIST);
const isSubmitDisabled = !form.values.name || mutation.isPending;
const isSubmitDisabled = !form.values.name || createPlaylistMutation.isPending;
return (
<form onSubmit={handleSubmit}>
@@ -141,7 +176,8 @@ export const CreatePlaylistForm = ({ onCancel }: CreatePlaylistFormProps) => {
/>
)}
{server?.type === ServerType.NAVIDROME &&
hasFeature(server, ServerFeature.PLAYLISTS_SMART) && (
hasFeature(server, ServerFeature.PLAYLISTS_SMART) &&
!isPrefilledPlaylist && (
<Switch
checked={isSmartPlaylist}
label="Is smart playlist?"
@@ -182,7 +218,7 @@ export const CreatePlaylistForm = ({ onCancel }: CreatePlaylistFormProps) => {
</ModalButton>
<ModalButton
disabled={isSubmitDisabled}
loading={mutation.isPending}
loading={createPlaylistMutation.isPending}
type="submit"
variant="filled"
>
@@ -206,3 +242,17 @@ export const openCreatePlaylistModal = (
title: t('form.createPlaylist.title'),
});
};
export const openCreatePrefilledPlaylistModal = (
server?: ServerListItem,
songs?: Song[],
e?: MouseEvent<HTMLButtonElement>,
) => {
e?.stopPropagation();
openModal({
children: <CreatePlaylistForm onCancel={() => closeAllModals()} songs={songs} />,
size: server?.type === ServerType?.NAVIDROME ? 'xl' : 'sm',
title: t('form.createPrefilledPlaylist.title'),
});
};