mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-09 20:29:36 +02:00
add drag to add to playlist
This commit is contained in:
@@ -46,10 +46,12 @@ export const AddToPlaylistContextModal = ({
|
|||||||
albumId?: string[];
|
albumId?: string[];
|
||||||
artistId?: string[];
|
artistId?: string[];
|
||||||
genreId?: string[];
|
genreId?: string[];
|
||||||
|
initialSelectedIds?: string[];
|
||||||
|
playlistId?: string[];
|
||||||
songId?: string[];
|
songId?: string[];
|
||||||
}>) => {
|
}>) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { albumId, artistId, genreId, songId } = innerProps;
|
const { albumId, artistId, genreId, initialSelectedIds, playlistId, songId } = innerProps;
|
||||||
const server = useCurrentServer();
|
const server = useCurrentServer();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [search, setSearch] = useState<string>('');
|
const [search, setSearch] = useState<string>('');
|
||||||
@@ -60,7 +62,7 @@ export const AddToPlaylistContextModal = ({
|
|||||||
const form = useForm({
|
const form = useForm({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
newPlaylists: [] as string[],
|
newPlaylists: [] as string[],
|
||||||
selectedPlaylistIds: [] as string[],
|
selectedPlaylistIds: initialSelectedIds || [],
|
||||||
skipDuplicates: true,
|
skipDuplicates: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -159,6 +161,28 @@ export const AddToPlaylistContextModal = ({
|
|||||||
[server],
|
[server],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getSongsByPlaylist = useCallback(
|
||||||
|
async (playlistId: string) => {
|
||||||
|
const queryKey = queryKeys.playlists.songList(server?.id || '', playlistId);
|
||||||
|
|
||||||
|
const songsRes = await queryClient.fetchQuery({
|
||||||
|
queryFn: ({ signal }) => {
|
||||||
|
if (!server) throw new Error('No server');
|
||||||
|
return api.controller.getPlaylistSongList({
|
||||||
|
apiClientProps: { serverId: server?.id || '', signal },
|
||||||
|
query: {
|
||||||
|
id: playlistId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
queryKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
return songsRes;
|
||||||
|
},
|
||||||
|
[server],
|
||||||
|
);
|
||||||
|
|
||||||
const handleSubmit = form.onSubmit(async (values) => {
|
const handleSubmit = form.onSubmit(async (values) => {
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return;
|
return;
|
||||||
@@ -193,6 +217,13 @@ export const AddToPlaylistContextModal = ({
|
|||||||
allSongIds.push(...(songs?.items?.map((song) => song.id) || []));
|
allSongIds.push(...(songs?.items?.map((song) => song.id) || []));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (playlistId && playlistId.length > 0) {
|
||||||
|
for (const id of playlistId) {
|
||||||
|
const songs = await getSongsByPlaylist(id);
|
||||||
|
allSongIds.push(...(songs?.items?.map((song) => song.id) || []));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (songId && songId.length > 0) {
|
if (songId && songId.length > 0) {
|
||||||
allSongIds.push(...songId);
|
allSongIds.push(...songId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,3 +22,9 @@
|
|||||||
right: var(--theme-spacing-xs);
|
right: var(--theme-spacing-xs);
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.row-dragged-over {
|
||||||
|
border-radius: var(--mantine-radius-sm);
|
||||||
|
box-shadow: 0 0 0 2px var(--theme-colors-primary);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { closeAllModals, openModal } from '@mantine/modals';
|
import { closeAllModals, openContextModal, openModal } from '@mantine/modals';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { MouseEvent, useCallback, useMemo, useState } from 'react';
|
import { MouseEvent, useCallback, useMemo, useState } from 'react';
|
||||||
@@ -11,6 +11,7 @@ import { usePlayerContext } from '/@/renderer/features/player/context/player-con
|
|||||||
import { playlistsQueries } from '/@/renderer/features/playlists/api/playlists-api';
|
import { playlistsQueries } from '/@/renderer/features/playlists/api/playlists-api';
|
||||||
import { CreatePlaylistForm } from '/@/renderer/features/playlists/components/create-playlist-form';
|
import { CreatePlaylistForm } from '/@/renderer/features/playlists/components/create-playlist-form';
|
||||||
import { SidebarItem } from '/@/renderer/features/sidebar/components/sidebar-item';
|
import { SidebarItem } from '/@/renderer/features/sidebar/components/sidebar-item';
|
||||||
|
import { useDragDrop } from '/@/renderer/hooks/use-drag-drop';
|
||||||
import { AppRoute } from '/@/renderer/router/routes';
|
import { AppRoute } from '/@/renderer/router/routes';
|
||||||
import { useCurrentServer } from '/@/renderer/store';
|
import { useCurrentServer } from '/@/renderer/store';
|
||||||
import { Accordion } from '/@/shared/components/accordion/accordion';
|
import { Accordion } from '/@/shared/components/accordion/accordion';
|
||||||
@@ -23,8 +24,10 @@ import {
|
|||||||
Playlist,
|
Playlist,
|
||||||
PlaylistListSort,
|
PlaylistListSort,
|
||||||
ServerType,
|
ServerType,
|
||||||
|
Song,
|
||||||
SortOrder,
|
SortOrder,
|
||||||
} from '/@/shared/types/domain-types';
|
} from '/@/shared/types/domain-types';
|
||||||
|
import { DragOperation, DragTarget } from '/@/shared/types/drag-and-drop';
|
||||||
import { Play } from '/@/shared/types/types';
|
import { Play } from '/@/shared/types/types';
|
||||||
|
|
||||||
interface PlaylistRowButtonProps extends Omit<ButtonProps, 'onPlay'> {
|
interface PlaylistRowButtonProps extends Omit<ButtonProps, 'onPlay'> {
|
||||||
@@ -35,14 +38,94 @@ interface PlaylistRowButtonProps extends Omit<ButtonProps, 'onPlay'> {
|
|||||||
|
|
||||||
const PlaylistRowButton = ({ name, onPlay, to, ...props }: PlaylistRowButtonProps) => {
|
const PlaylistRowButton = ({ name, onPlay, to, ...props }: PlaylistRowButtonProps) => {
|
||||||
const url = generatePath(AppRoute.PLAYLISTS_DETAIL_SONGS, { playlistId: to });
|
const url = generatePath(AppRoute.PLAYLISTS_DETAIL_SONGS, { playlistId: to });
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
|
|
||||||
|
const { isDraggedOver, ref } = useDragDrop<HTMLDivElement>({
|
||||||
|
drop: {
|
||||||
|
canDrop: (args) => {
|
||||||
|
return (
|
||||||
|
args.source.itemType !== undefined &&
|
||||||
|
args.source.type !== DragTarget.PLAYLIST &&
|
||||||
|
(args.source.operation?.includes(DragOperation.ADD) ?? false)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
getData: () => {
|
||||||
|
return {
|
||||||
|
id: [to],
|
||||||
|
item: [],
|
||||||
|
itemType: LibraryItem.PLAYLIST,
|
||||||
|
type: DragTarget.PLAYLIST,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
onDrag: () => {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
onDragLeave: () => {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
onDrop: (args) => {
|
||||||
|
const sourceItemType = args.source.itemType as LibraryItem;
|
||||||
|
const sourceIds = args.source.id;
|
||||||
|
|
||||||
|
const modalProps: {
|
||||||
|
albumId?: string[];
|
||||||
|
artistId?: string[];
|
||||||
|
genreId?: string[];
|
||||||
|
initialSelectedIds?: string[];
|
||||||
|
playlistId?: string[];
|
||||||
|
songId?: string[];
|
||||||
|
} = {
|
||||||
|
initialSelectedIds: [to],
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (sourceItemType) {
|
||||||
|
case LibraryItem.ALBUM:
|
||||||
|
modalProps.albumId = sourceIds;
|
||||||
|
break;
|
||||||
|
case LibraryItem.ALBUM_ARTIST:
|
||||||
|
case LibraryItem.ARTIST:
|
||||||
|
modalProps.artistId = sourceIds;
|
||||||
|
break;
|
||||||
|
case LibraryItem.GENRE:
|
||||||
|
modalProps.genreId = sourceIds;
|
||||||
|
break;
|
||||||
|
case LibraryItem.PLAYLIST:
|
||||||
|
modalProps.playlistId = sourceIds;
|
||||||
|
break;
|
||||||
|
case LibraryItem.PLAYLIST_SONG:
|
||||||
|
case LibraryItem.QUEUE_SONG:
|
||||||
|
case LibraryItem.SONG:
|
||||||
|
if (args.source.item && Array.isArray(args.source.item)) {
|
||||||
|
const songs = args.source.item as Song[];
|
||||||
|
modalProps.songId = songs.map((song) => song.id);
|
||||||
|
} else {
|
||||||
|
modalProps.songId = sourceIds;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
openContextModal({
|
||||||
|
innerProps: modalProps,
|
||||||
|
modal: 'addToPlaylist',
|
||||||
|
title: t('form.addToPlaylist.title', { postProcess: 'titleCase' }),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isEnabled: true,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={styles.row}
|
className={clsx(styles.row, {
|
||||||
|
[styles.rowDraggedOver]: isDraggedOver,
|
||||||
|
})}
|
||||||
onMouseEnter={() => setIsHovered(true)}
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
onMouseLeave={() => setIsHovered(false)}
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
|
ref={ref}
|
||||||
>
|
>
|
||||||
<SidebarItem
|
<SidebarItem
|
||||||
className={clsx({
|
className={clsx({
|
||||||
|
|||||||
Reference in New Issue
Block a user