Fix all playlist actions
This commit is contained in:
parent
07d4dc37b5
commit
3c62de8347
7 changed files with 154 additions and 14 deletions
|
@ -195,7 +195,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
|
|||
const handleDeletePlaylist = useCallback(() => {
|
||||
for (const item of ctx.data) {
|
||||
deletePlaylistMutation?.mutate(
|
||||
{ query: { id: item.id } },
|
||||
{ query: { id: item.id }, serverId: item.serverId },
|
||||
{
|
||||
onError: (err) => {
|
||||
toast.error({
|
||||
|
@ -432,6 +432,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
|
|||
id: ctx.context.playlistId,
|
||||
songId,
|
||||
},
|
||||
serverId: ctx.data?.[0]?.serverId,
|
||||
},
|
||||
{
|
||||
onError: (err) => {
|
||||
|
@ -465,6 +466,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
|
|||
}, [
|
||||
ctx.context?.playlistId,
|
||||
ctx.context?.tableRef,
|
||||
ctx.data,
|
||||
ctx.dataNodes,
|
||||
removeFromPlaylistMutation,
|
||||
serverType,
|
||||
|
|
|
@ -213,7 +213,11 @@ export const PlaylistDetailSongListContent = ({ tableRef }: PlaylistDetailConten
|
|||
const handleRowDoubleClick = (e: RowDoubleClickedEvent<QueueSong>) => {
|
||||
if (!e.data) return;
|
||||
handlePlayQueueAdd?.({
|
||||
byData: [e.data],
|
||||
byItemType: {
|
||||
id: [playlistId],
|
||||
type: LibraryItem.PLAYLIST,
|
||||
},
|
||||
initialSongId: e.data.id,
|
||||
playType: playButtonBehavior,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@ import { useCallback, ChangeEvent, MutableRefObject, MouseEvent } from 'react';
|
|||
import { IDatasource } from '@ag-grid-community/core';
|
||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
import { Flex, Group, Stack } from '@mantine/core';
|
||||
import { closeAllModals, openModal } from '@mantine/modals';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import {
|
||||
RiSortAsc,
|
||||
|
@ -18,7 +19,16 @@ import {
|
|||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { LibraryItem, PlaylistSongListQuery, SongListSort, SortOrder } from '/@/renderer/api/types';
|
||||
import { DropdownMenu, Button, Slider, MultiSelect, Switch, Text } from '/@/renderer/components';
|
||||
import {
|
||||
DropdownMenu,
|
||||
Button,
|
||||
Slider,
|
||||
MultiSelect,
|
||||
Switch,
|
||||
Text,
|
||||
ConfirmModal,
|
||||
toast,
|
||||
} from '/@/renderer/components';
|
||||
import { usePlayQueueAdd } from '/@/renderer/features/player';
|
||||
import { useContainerQuery } from '/@/renderer/hooks';
|
||||
import {
|
||||
|
@ -34,6 +44,8 @@ import { ListDisplayType, ServerType, Play, TableColumn } from '/@/renderer/type
|
|||
import { usePlaylistDetail } from '/@/renderer/features/playlists/queries/playlist-detail-query';
|
||||
import { useParams } from 'react-router';
|
||||
import { SONG_TABLE_COLUMNS } from '/@/renderer/components/virtual-table';
|
||||
import { openUpdatePlaylistModal } from '/@/renderer/features/playlists/components/update-playlist-form';
|
||||
import { useDeletePlaylist } from '/@/renderer/features/playlists/mutations/delete-playlist-mutation';
|
||||
|
||||
const FILTERS = {
|
||||
jellyfin: [
|
||||
|
@ -238,6 +250,40 @@ export const PlaylistDetailSongListHeaderFilters = ({
|
|||
});
|
||||
};
|
||||
|
||||
const deletePlaylistMutation = useDeletePlaylist({});
|
||||
|
||||
const handleDeletePlaylist = useCallback(() => {
|
||||
if (!detailQuery.data) return;
|
||||
deletePlaylistMutation?.mutate(
|
||||
{ query: { id: detailQuery.data.id }, serverId: detailQuery.data.id },
|
||||
{
|
||||
onError: (err) => {
|
||||
toast.error({
|
||||
message: err.message,
|
||||
title: 'Error deleting playlist',
|
||||
});
|
||||
},
|
||||
onSuccess: () => {
|
||||
toast.success({
|
||||
message: `Playlist has been deleted`,
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
closeAllModals();
|
||||
}, [deletePlaylistMutation, detailQuery.data]);
|
||||
|
||||
const openDeletePlaylistModal = () => {
|
||||
openModal({
|
||||
children: (
|
||||
<ConfirmModal onConfirm={handleDeletePlaylist}>
|
||||
<Text>Are you sure you want to delete this playlist?</Text>
|
||||
</ConfirmModal>
|
||||
),
|
||||
title: 'Delete playlist(s)',
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex justify="space-between">
|
||||
<Group
|
||||
|
@ -320,16 +366,20 @@ export const PlaylistDetailSongListHeaderFilters = ({
|
|||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Divider />
|
||||
<DropdownMenu.Item
|
||||
disabled
|
||||
icon={<RiEditFill />}
|
||||
onClick={() => handlePlay(Play.LAST)}
|
||||
onClick={() =>
|
||||
openUpdatePlaylistModal({
|
||||
playlist: detailQuery.data!,
|
||||
server: server!,
|
||||
})
|
||||
}
|
||||
>
|
||||
Edit playlist
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
disabled
|
||||
icon={<RiDeleteBinFill />}
|
||||
onClick={() => handlePlay(Play.LAST)}
|
||||
onClick={openDeletePlaylistModal}
|
||||
>
|
||||
Delete playlist
|
||||
</DropdownMenu.Item>
|
||||
|
|
|
@ -184,6 +184,10 @@ export const PlaylistListHeaderFilters = ({ tableRef }: PlaylistListHeaderFilter
|
|||
setTable({ rowHeight: e });
|
||||
};
|
||||
|
||||
const handleRefresh = () => {
|
||||
tableRef?.current?.api?.purgeInfiniteCache();
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex justify="space-between">
|
||||
<Group
|
||||
|
@ -246,7 +250,12 @@ export const PlaylistListHeaderFilters = ({ tableRef }: PlaylistListHeaderFilter
|
|||
</Button>
|
||||
</DropdownMenu.Target>
|
||||
<DropdownMenu.Dropdown>
|
||||
<DropdownMenu.Item icon={<RiRefreshLine />}>Refresh</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
icon={<RiRefreshLine />}
|
||||
onClick={handleRefresh}
|
||||
>
|
||||
Refresh
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
</Group>
|
||||
|
|
|
@ -9,9 +9,15 @@ interface SaveAsPlaylistFormProps {
|
|||
body: Partial<CreatePlaylistBody>;
|
||||
onCancel: () => void;
|
||||
onSuccess: (data: CreatePlaylistResponse) => void;
|
||||
serverId: string | undefined;
|
||||
}
|
||||
|
||||
export const SaveAsPlaylistForm = ({ body, onSuccess, onCancel }: SaveAsPlaylistFormProps) => {
|
||||
export const SaveAsPlaylistForm = ({
|
||||
body,
|
||||
serverId,
|
||||
onSuccess,
|
||||
onCancel,
|
||||
}: SaveAsPlaylistFormProps) => {
|
||||
const mutation = useCreatePlaylist({});
|
||||
const server = useCurrentServer();
|
||||
|
||||
|
@ -31,7 +37,7 @@ export const SaveAsPlaylistForm = ({ body, onSuccess, onCancel }: SaveAsPlaylist
|
|||
|
||||
const handleSubmit = form.onSubmit((values) => {
|
||||
mutation.mutate(
|
||||
{ body: values },
|
||||
{ body: values, serverId },
|
||||
{
|
||||
onError: (err) => {
|
||||
toast.error({ message: err.message, title: 'Error creating playlist' });
|
||||
|
|
|
@ -1,8 +1,22 @@
|
|||
import { Group, Stack } from '@mantine/core';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { ServerType, UpdatePlaylistBody, UpdatePlaylistQuery, User } from '/@/renderer/api/types';
|
||||
import { openModal, closeAllModals } from '@mantine/modals';
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import {
|
||||
PlaylistDetailResponse,
|
||||
ServerListItem,
|
||||
ServerType,
|
||||
SortOrder,
|
||||
UpdatePlaylistBody,
|
||||
UpdatePlaylistQuery,
|
||||
User,
|
||||
UserListQuery,
|
||||
UserListSort,
|
||||
} from '/@/renderer/api/types';
|
||||
import { Button, Select, Switch, TextInput, toast } from '/@/renderer/components';
|
||||
import { useUpdatePlaylist } from '/@/renderer/features/playlists/mutations/update-playlist-mutation';
|
||||
import { queryClient } from '/@/renderer/lib/react-query';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
|
||||
interface UpdatePlaylistFormProps {
|
||||
|
@ -103,3 +117,49 @@ export const UpdatePlaylistForm = ({ users, query, body, onCancel }: UpdatePlayl
|
|||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export const openUpdatePlaylistModal = async (args: {
|
||||
playlist: PlaylistDetailResponse;
|
||||
server: ServerListItem;
|
||||
}) => {
|
||||
const { playlist, server } = args;
|
||||
|
||||
const query: UserListQuery = {
|
||||
sortBy: UserListSort.NAME,
|
||||
sortOrder: SortOrder.ASC,
|
||||
startIndex: 0,
|
||||
};
|
||||
|
||||
if (!server) return;
|
||||
|
||||
const users = await queryClient.fetchQuery({
|
||||
queryFn: ({ signal }) =>
|
||||
api.controller.getUserList({ apiClientProps: { server, signal }, query }),
|
||||
queryKey: queryKeys.users.list(server?.id || '', query),
|
||||
});
|
||||
|
||||
openModal({
|
||||
children: (
|
||||
<UpdatePlaylistForm
|
||||
body={{
|
||||
_custom: {
|
||||
navidrome: {
|
||||
owner: playlist?.owner || undefined,
|
||||
ownerId: playlist?.ownerId || undefined,
|
||||
public: playlist?.public || false,
|
||||
rules: playlist?.rules || undefined,
|
||||
sync: playlist?.sync || undefined,
|
||||
},
|
||||
},
|
||||
comment: playlist?.description || undefined,
|
||||
genres: playlist?.genres,
|
||||
name: playlist?.name,
|
||||
}}
|
||||
query={{ id: playlist?.id }}
|
||||
users={users?.items}
|
||||
onCancel={closeAllModals}
|
||||
/>
|
||||
),
|
||||
title: 'Edit playlist',
|
||||
});
|
||||
};
|
||||
|
|
|
@ -57,6 +57,7 @@ const PlaylistDetailSongListRoute = () => {
|
|||
comment: detailQuery?.data?.description || '',
|
||||
name: detailQuery?.data?.name,
|
||||
},
|
||||
serverId: detailQuery?.data?.serverId,
|
||||
},
|
||||
{
|
||||
onSuccess: (data) => {
|
||||
|
@ -64,13 +65,19 @@ const PlaylistDetailSongListRoute = () => {
|
|||
navigate(generatePath(AppRoute.PLAYLISTS_DETAIL_SONGS, { playlistId: data?.id || '' }), {
|
||||
replace: true,
|
||||
});
|
||||
deletePlaylistMutation.mutate({ query: { id: playlistId } });
|
||||
deletePlaylistMutation.mutate({
|
||||
query: { id: playlistId },
|
||||
serverId: detailQuery?.data?.serverId,
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const handleSaveAs = (filter: Record<string, any>) => {
|
||||
const handleSaveAs = (
|
||||
filter: Record<string, any>,
|
||||
extraFilters: { limit?: number; sortBy?: string; sortOrder?: string },
|
||||
) => {
|
||||
openModal({
|
||||
children: (
|
||||
<SaveAsPlaylistForm
|
||||
|
@ -82,8 +89,9 @@ const PlaylistDetailSongListRoute = () => {
|
|||
public: detailQuery?.data?.public || false,
|
||||
rules: {
|
||||
...filter,
|
||||
order: 'desc',
|
||||
sort: 'year',
|
||||
limit: extraFilters.limit || undefined,
|
||||
order: extraFilters.sortOrder || 'desc',
|
||||
sort: extraFilters.sortBy || 'dateAdded',
|
||||
},
|
||||
sync: detailQuery?.data?.sync || false,
|
||||
},
|
||||
|
@ -91,6 +99,7 @@ const PlaylistDetailSongListRoute = () => {
|
|||
comment: detailQuery?.data?.description || '',
|
||||
name: detailQuery?.data?.name,
|
||||
}}
|
||||
serverId={detailQuery?.data?.serverId}
|
||||
onCancel={closeAllModals}
|
||||
onSuccess={(data) =>
|
||||
navigate(generatePath(AppRoute.PLAYLISTS_DETAIL_SONGS, { playlistId: data?.id || '' }))
|
||||
|
|
Reference in a new issue