mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-14 04:20:07 +02:00
add folder browsing support (#315)
This commit is contained in:
@@ -9,6 +9,7 @@ import {
|
||||
getAlbumSongsById,
|
||||
getGenreSongsById,
|
||||
getPlaylistSongsById,
|
||||
getSongsByFolder,
|
||||
} from '/@/renderer/features/player/utils';
|
||||
import { playlistsQueries } from '/@/renderer/features/playlists/api/playlists-api';
|
||||
import { useRecentPlaylists } from '/@/renderer/features/playlists/hooks/use-recent-playlists';
|
||||
@@ -97,49 +98,64 @@ export const AddToPlaylistAction = ({ items, itemType }: AddToPlaylistActionProp
|
||||
|
||||
const getSongsByAlbum = useCallback(
|
||||
async (albumId: string) => {
|
||||
if (!server) return null;
|
||||
return getAlbumSongsById({
|
||||
id: [albumId],
|
||||
queryClient,
|
||||
server,
|
||||
serverId,
|
||||
});
|
||||
},
|
||||
[queryClient, server],
|
||||
[queryClient, serverId],
|
||||
);
|
||||
|
||||
const getSongsByArtist = useCallback(
|
||||
async (artistId: string) => {
|
||||
if (!server) return null;
|
||||
return getAlbumArtistSongsById({
|
||||
id: [artistId],
|
||||
queryClient,
|
||||
server,
|
||||
serverId,
|
||||
});
|
||||
},
|
||||
[queryClient, server],
|
||||
[queryClient, serverId],
|
||||
);
|
||||
|
||||
const getSongsByGenre = useCallback(
|
||||
async (genreIds: string[]) => {
|
||||
if (!server) return null;
|
||||
return getGenreSongsById({
|
||||
id: genreIds,
|
||||
queryClient,
|
||||
server,
|
||||
serverId,
|
||||
});
|
||||
},
|
||||
[queryClient, server],
|
||||
[queryClient, serverId],
|
||||
);
|
||||
|
||||
const getSongsByPlaylist = useCallback(
|
||||
async (playlistId: string) => {
|
||||
if (!server) return null;
|
||||
return getPlaylistSongsById({
|
||||
id: playlistId,
|
||||
queryClient,
|
||||
server,
|
||||
serverId,
|
||||
});
|
||||
},
|
||||
[queryClient, serverId],
|
||||
);
|
||||
|
||||
const getSongsByFolderLocal = useCallback(
|
||||
async (folderId: string) => {
|
||||
if (!server) return null;
|
||||
|
||||
const songsResponse = await getSongsByFolder({
|
||||
id: [folderId],
|
||||
queryClient,
|
||||
serverId: server.id,
|
||||
});
|
||||
|
||||
return {
|
||||
items: songsResponse.items.map((song) => song.id),
|
||||
startIndex: 0,
|
||||
totalRecordCount: songsResponse.items.length,
|
||||
};
|
||||
},
|
||||
[queryClient, server],
|
||||
);
|
||||
|
||||
@@ -173,6 +189,11 @@ export const AddToPlaylistAction = ({ items, itemType }: AddToPlaylistActionProp
|
||||
const songs = await getSongsByPlaylist(id);
|
||||
allSongIds.push(...(songs?.items?.map((song) => song.id) || []));
|
||||
}
|
||||
} else if (itemType === LibraryItem.FOLDER) {
|
||||
for (const id of items) {
|
||||
const songs = await getSongsByFolderLocal(id);
|
||||
allSongIds.push(...(songs?.items || []));
|
||||
}
|
||||
}
|
||||
|
||||
if (allSongIds.length === 0) {
|
||||
@@ -213,6 +234,7 @@ export const AddToPlaylistAction = ({ items, itemType }: AddToPlaylistActionProp
|
||||
addToPlaylistMutation,
|
||||
getSongsByAlbum,
|
||||
getSongsByArtist,
|
||||
getSongsByFolderLocal,
|
||||
getSongsByGenre,
|
||||
getSongsByPlaylist,
|
||||
itemType,
|
||||
@@ -226,6 +248,7 @@ export const AddToPlaylistAction = ({ items, itemType }: AddToPlaylistActionProp
|
||||
const modalProps: {
|
||||
albumId?: string[];
|
||||
artistId?: string[];
|
||||
folderId?: string[];
|
||||
genreId?: string[];
|
||||
initialSelectedIds?: string[];
|
||||
playlistId?: string[];
|
||||
@@ -240,6 +263,9 @@ export const AddToPlaylistAction = ({ items, itemType }: AddToPlaylistActionProp
|
||||
case LibraryItem.ARTIST:
|
||||
modalProps.artistId = items;
|
||||
break;
|
||||
case LibraryItem.FOLDER:
|
||||
modalProps.folderId = items;
|
||||
break;
|
||||
case LibraryItem.GENRE:
|
||||
modalProps.genreId = items;
|
||||
break;
|
||||
|
||||
@@ -19,6 +19,8 @@ export const ShareAction = ({ ids, itemType }: ShareActionProps) => {
|
||||
return 'album';
|
||||
case LibraryItem.ALBUM_ARTIST:
|
||||
return 'albumArtist';
|
||||
case LibraryItem.FOLDER:
|
||||
return 'folder';
|
||||
case LibraryItem.PLAYLIST:
|
||||
return 'playlist';
|
||||
case LibraryItem.SONG:
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useParams } from 'react-router';
|
||||
import { AlbumArtistContextMenu } from '/@/renderer/features/context-menu/menus/album-artist-context-menu';
|
||||
import { AlbumContextMenu } from '/@/renderer/features/context-menu/menus/album-context-menu';
|
||||
import { ArtistContextMenu } from '/@/renderer/features/context-menu/menus/artist-context-menu';
|
||||
import { FolderContextMenu } from '/@/renderer/features/context-menu/menus/folder-context-menu';
|
||||
import { GenreContextMenu } from '/@/renderer/features/context-menu/menus/genre-context-menu';
|
||||
import { PlaylistContextMenu } from '/@/renderer/features/context-menu/menus/playlist-context-menu';
|
||||
import { PlaylistSongContextMenu } from '/@/renderer/features/context-menu/menus/playlist-song-context-menu';
|
||||
@@ -16,6 +17,7 @@ import {
|
||||
Album,
|
||||
AlbumArtist,
|
||||
Artist,
|
||||
Folder,
|
||||
Genre,
|
||||
LibraryItem,
|
||||
Playlist,
|
||||
@@ -82,6 +84,7 @@ export const ContextMenuController = createCallable<ContextMenuControllerProps,
|
||||
{cmd.type === LibraryItem.ALBUM && <AlbumContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.ALBUM_ARTIST && <AlbumArtistContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.ARTIST && <ArtistContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.FOLDER && <FolderContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.GENRE && <GenreContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.PLAYLIST && <PlaylistContextMenu {...cmd} />}
|
||||
{cmd.type === LibraryItem.PLAYLIST_SONG && <PlaylistSongContextMenu {...cmd} />}
|
||||
@@ -95,6 +98,7 @@ export type ContextMenuCommand =
|
||||
| AlbumArtistContextMenuProps
|
||||
| AlbumContextMenuProps
|
||||
| ArtistContextMenuProps
|
||||
| FolderContextMenuProps
|
||||
| GenreContextMenuProps
|
||||
| PlaylistContextMenuProps
|
||||
| PlaylistSongContextMenuProps
|
||||
@@ -116,6 +120,11 @@ type ArtistContextMenuProps = {
|
||||
type: LibraryItem.ARTIST;
|
||||
};
|
||||
|
||||
type FolderContextMenuProps = {
|
||||
items: Folder[];
|
||||
type: LibraryItem.FOLDER;
|
||||
};
|
||||
|
||||
type GenreContextMenuProps = {
|
||||
items: Genre[];
|
||||
type: LibraryItem.GENRE;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { AddToPlaylistAction } from '/@/renderer/features/context-menu/actions/add-to-playlist-action';
|
||||
import { DownloadAction } from '/@/renderer/features/context-menu/actions/download-action';
|
||||
import { PlayAction } from '/@/renderer/features/context-menu/actions/play-action';
|
||||
import { ShareAction } from '/@/renderer/features/context-menu/actions/share-action';
|
||||
import { ContextMenu } from '/@/shared/components/context-menu/context-menu';
|
||||
import { ContextMenuPreview } from '/@/shared/components/context-menu/context-menu-preview';
|
||||
import { Folder, LibraryItem } from '/@/shared/types/domain-types';
|
||||
|
||||
interface FolderContextMenuProps {
|
||||
items: Folder[];
|
||||
type: LibraryItem.FOLDER;
|
||||
}
|
||||
|
||||
export const FolderContextMenu = ({ items, type }: FolderContextMenuProps) => {
|
||||
const { ids } = useMemo(() => {
|
||||
const ids = items.map((item) => item.id);
|
||||
return { ids };
|
||||
}, [items]);
|
||||
|
||||
return (
|
||||
<ContextMenu.Content
|
||||
bottomStickyContent={<ContextMenuPreview items={items} itemType={type} />}
|
||||
>
|
||||
<PlayAction ids={ids} itemType={LibraryItem.FOLDER} />
|
||||
<ContextMenu.Divider />
|
||||
<AddToPlaylistAction items={ids} itemType={LibraryItem.FOLDER} />
|
||||
<ContextMenu.Divider />
|
||||
<DownloadAction ids={ids} />
|
||||
<ShareAction ids={ids} itemType={LibraryItem.FOLDER} />
|
||||
</ContextMenu.Content>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user