Fix all playlist actions

This commit is contained in:
jeffvli 2023-05-21 18:20:46 -07:00
parent 07d4dc37b5
commit 3c62de8347
7 changed files with 154 additions and 14 deletions

View file

@ -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,

View file

@ -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,
});
};

View file

@ -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>

View file

@ -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>

View file

@ -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' });

View file

@ -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',
});
};

View file

@ -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 || '' }))