fix playlist song list context menu showing remove item

This commit is contained in:
jeffvli
2025-12-03 22:25:41 -08:00
parent ccdd16292a
commit 20830cb979
3 changed files with 51 additions and 27 deletions
@@ -295,10 +295,15 @@ export const useDefaultItemListControls = (args?: UseDefaultItemListControlsArgs
return; return;
} }
// Use the item's _itemType if available, otherwise fall back to the prop itemType console.log(item, itemType);
// This allows mixed lists (e.g., folders + songs) to show the correct context menu
// For context menus, prioritize the itemType prop when it's PLAYLIST_SONG or QUEUE_SONG
// This is because playlist/queue songs are Song objects (_itemType: SONG) but need special context menus
// Otherwise, use the item's _itemType if available, or fall back to the mapped itemType
const actualItemType = const actualItemType =
(item as any)?._itemType || itemTypeMapping[itemType] || itemType; itemType === LibraryItem.PLAYLIST_SONG || itemType === LibraryItem.QUEUE_SONG
? itemType
: (item as any)?._itemType || itemTypeMapping[itemType] || itemType;
// If no internalState, call ContextMenuController directly // If no internalState, call ContextMenuController directly
if (!internalState) { if (!internalState) {
@@ -331,11 +336,14 @@ export const useDefaultItemListControls = (args?: UseDefaultItemListControlsArgs
const selectedItems = internalState.getSelected(); const selectedItems = internalState.getSelected();
// For multiple selected items, use the itemType prop (assumes all selected items are of the same type) // For multiple selected items, prioritize the itemType prop for PLAYLIST_SONG/QUEUE_SONG
// Otherwise use the first item's _itemType or the mapped type
const selectedItemType = const selectedItemType =
selectedItems.length > 0 && (selectedItems[0] as any)?._itemType itemType === LibraryItem.PLAYLIST_SONG || itemType === LibraryItem.QUEUE_SONG
? (selectedItems[0] as any)._itemType ? itemType
: actualItemType; : selectedItems.length > 0 && (selectedItems[0] as any)?._itemType
? (selectedItems[0] as any)._itemType
: itemTypeMapping[itemType] || itemType;
return ContextMenuController.call({ return ContextMenuController.call({
cmd: { items: selectedItems as any[], type: selectedItemType as any }, cmd: { items: selectedItems as any[], type: selectedItemType as any },
@@ -1,3 +1,4 @@
import { closeAllModals, openModal } from '@mantine/modals';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router'; import { useParams } from 'react-router';
@@ -5,6 +6,8 @@ import { useParams } from 'react-router';
import { useRemoveFromPlaylist } from '/@/renderer/features/playlists/mutations/remove-from-playlist-mutation'; import { useRemoveFromPlaylist } from '/@/renderer/features/playlists/mutations/remove-from-playlist-mutation';
import { useCurrentServerId } from '/@/renderer/store'; import { useCurrentServerId } from '/@/renderer/store';
import { ContextMenu } from '/@/shared/components/context-menu/context-menu'; import { ContextMenu } from '/@/shared/components/context-menu/context-menu';
import { ConfirmModal } from '/@/shared/components/modal/modal';
import { Text } from '/@/shared/components/text/text';
import { toast } from '/@/shared/components/toast/toast'; import { toast } from '/@/shared/components/toast/toast';
import { Song } from '/@/shared/types/domain-types'; import { Song } from '/@/shared/types/domain-types';
@@ -19,41 +22,52 @@ export const RemoveFromPlaylistAction = ({ items }: RemoveFromPlaylistActionProp
const removeFromPlaylistMutation = useRemoveFromPlaylist(); const removeFromPlaylistMutation = useRemoveFromPlaylist();
const { ids } = useMemo(() => { const { ids } = useMemo(() => {
const ids = items.map((item) => item.id); const ids = items.map((item) => item.playlistItemId).filter((id) => id !== undefined);
return { ids }; return { ids };
}, [items]); }, [items]);
const handleRemoveFromPlaylist = useCallback(() => { const handleRemoveFromPlaylist = useCallback(async () => {
if (ids.length === 0 || !serverId || !playlistId) return; if (ids.length === 0 || !serverId || !playlistId) return;
removeFromPlaylistMutation.mutate( try {
{ await removeFromPlaylistMutation.mutateAsync({
apiClientProps: { serverId }, apiClientProps: { serverId },
query: { query: {
id: playlistId, id: playlistId,
songId: ids, songId: ids,
}, },
}, });
{
onError: (err) => { toast.success({
toast.error({ message: t('action.removeFromPlaylist', { postProcess: 'sentenceCase' }),
message: err.message, });
title: t('error.genericError', { postProcess: 'sentenceCase' }), } catch (err: any) {
}); toast.error({
}, message: err.message,
onSuccess: () => { title: t('error.genericError', { postProcess: 'sentenceCase' }),
toast.success({ });
message: t('action.removeFromPlaylist', { postProcess: 'sentenceCase' }), }
});
}, closeAllModals();
},
);
}, [ids, playlistId, removeFromPlaylistMutation, serverId, t]); }, [ids, playlistId, removeFromPlaylistMutation, serverId, t]);
const openRemoveFromPlaylistModal = useCallback(() => {
if (ids.length === 0 || !playlistId) return;
openModal({
children: (
<ConfirmModal onConfirm={handleRemoveFromPlaylist}>
<Text>{t('common.areYouSure', { postProcess: 'sentenceCase' })}</Text>
</ConfirmModal>
),
title: t('action.removeFromPlaylist', { postProcess: 'sentenceCase' }),
});
}, [handleRemoveFromPlaylist, ids, playlistId, t]);
if (ids.length === 0 || !playlistId) return null; if (ids.length === 0 || !playlistId) return null;
return ( return (
<ContextMenu.Item leftIcon="remove" onSelect={handleRemoveFromPlaylist}> <ContextMenu.Item leftIcon="remove" onSelect={openRemoveFromPlaylistModal}>
{t('action.removeFromPlaylist', { postProcess: 'sentenceCase' })} {t('action.removeFromPlaylist', { postProcess: 'sentenceCase' })}
</ContextMenu.Item> </ContextMenu.Item>
); );
@@ -24,6 +24,8 @@ export const PlaylistSongContextMenu = ({ items, type }: PlaylistSongContextMenu
return { ids }; return { ids };
}, [items]); }, [items]);
console.log('items', items, ids);
return ( return (
<ContextMenu.Content <ContextMenu.Content
bottomStickyContent={<ContextMenuPreview items={items} itemType={type} />} bottomStickyContent={<ContextMenuPreview items={items} itemType={type} />}