mirror of
https://github.com/jeffvli/feishin.git
synced 2026-06-10 06:12:43 +02:00
prevent item drop on smart playlists
This commit is contained in:
@@ -7,8 +7,10 @@ import { ActionBar } from '/@/renderer/features/sidebar/components/action-bar';
|
||||
import { SidebarIcon } from '/@/renderer/features/sidebar/components/sidebar-icon';
|
||||
import { SidebarItem } from '/@/renderer/features/sidebar/components/sidebar-item';
|
||||
import {
|
||||
SidebarPlaylistAddDragContext,
|
||||
SidebarPlaylistList,
|
||||
SidebarSharedPlaylistList,
|
||||
useSidebarPlaylistAddDragMonitor,
|
||||
} from '/@/renderer/features/sidebar/components/sidebar-playlist-list';
|
||||
import {
|
||||
SidebarItemType,
|
||||
@@ -20,6 +22,17 @@ import { Group } from '/@/shared/components/group/group';
|
||||
import { ScrollArea } from '/@/shared/components/scroll-area/scroll-area';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
|
||||
const MobileSidebarPlaylistSection = () => {
|
||||
const isAddDragActive = useSidebarPlaylistAddDragMonitor();
|
||||
|
||||
return (
|
||||
<SidebarPlaylistAddDragContext.Provider value={isAddDragActive}>
|
||||
<SidebarPlaylistList />
|
||||
<SidebarSharedPlaylistList />
|
||||
</SidebarPlaylistAddDragContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const MobileSidebar = () => {
|
||||
const { t } = useTranslation();
|
||||
const sidebarPlaylistList = useSidebarPlaylistList();
|
||||
@@ -93,12 +106,7 @@ export const MobileSidebar = () => {
|
||||
})}
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
{sidebarPlaylistList && (
|
||||
<>
|
||||
<SidebarPlaylistList />
|
||||
<SidebarSharedPlaylistList />
|
||||
</>
|
||||
)}
|
||||
{sidebarPlaylistList && <MobileSidebarPlaylistSection />}
|
||||
</Accordion>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { openContextModal } from '@mantine/modals';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import clsx from 'clsx';
|
||||
import { memo, MouseEvent, useCallback, useMemo, useState } from 'react';
|
||||
import { motion } from 'motion/react';
|
||||
import { createContext, memo, MouseEvent, useCallback, useContext, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { generatePath, Link } from 'react-router';
|
||||
|
||||
@@ -27,6 +28,7 @@ import {
|
||||
usePlaylistNavigationState,
|
||||
} from '/@/renderer/features/sidebar/components/playlist-folder-tree';
|
||||
import { useDragDrop } from '/@/renderer/hooks/use-drag-drop';
|
||||
import { useDragMonitor } from '/@/renderer/hooks/use-drag-monitor';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import {
|
||||
useCurrentServer,
|
||||
@@ -39,6 +41,8 @@ import {
|
||||
import { formatDurationString } from '/@/renderer/utils';
|
||||
import { Accordion } from '/@/shared/components/accordion/accordion';
|
||||
import { ActionIcon, ActionIconGroup } from '/@/shared/components/action-icon/action-icon';
|
||||
import { animationProps } from '/@/shared/components/animations/animation-props';
|
||||
import { animationVariants } from '/@/shared/components/animations/animation-variants';
|
||||
import { ButtonProps } from '/@/shared/components/button/button';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Icon } from '/@/shared/components/icon/icon';
|
||||
@@ -52,14 +56,50 @@ import {
|
||||
Song,
|
||||
SortOrder,
|
||||
} from '/@/shared/types/domain-types';
|
||||
import { DragOperation, DragTarget } from '/@/shared/types/drag-and-drop';
|
||||
import { DragData, DragOperation, DragTarget } from '/@/shared/types/drag-and-drop';
|
||||
import { Play } from '/@/shared/types/types';
|
||||
|
||||
const MotionLink = motion.create(Link);
|
||||
|
||||
const playlistRowDimVariants = animationVariants.combine(animationVariants.fadeIn, {
|
||||
hidden: { opacity: 0.5 },
|
||||
});
|
||||
|
||||
const getPlaylistOrderKey = (serverId: string | undefined, scope: 'owned' | 'shared') => {
|
||||
const sid = serverId || 'local';
|
||||
return `playlist_order:${sid}:${scope}`;
|
||||
};
|
||||
|
||||
export const SidebarPlaylistAddDragContext = createContext(false);
|
||||
|
||||
const isAddToPlaylistDragSource = (source: DragData) => {
|
||||
return (
|
||||
source.itemType !== undefined &&
|
||||
source.type !== DragTarget.PLAYLIST &&
|
||||
(source.operation?.includes(DragOperation.ADD) ?? false)
|
||||
);
|
||||
};
|
||||
|
||||
export const useSidebarPlaylistAddDragMonitor = () => {
|
||||
const [isAddDragActive, setIsAddDragActive] = useState(false);
|
||||
|
||||
const handleAddDragStart = useCallback(() => {
|
||||
setIsAddDragActive(true);
|
||||
}, []);
|
||||
|
||||
const handleAddDragDrop = useCallback(() => {
|
||||
setIsAddDragActive(false);
|
||||
}, []);
|
||||
|
||||
useDragMonitor({
|
||||
canMonitor: isAddToPlaylistDragSource,
|
||||
onDragStart: handleAddDragStart,
|
||||
onDrop: handleAddDragDrop,
|
||||
});
|
||||
|
||||
return isAddDragActive;
|
||||
};
|
||||
|
||||
export interface PlaylistRowButtonProps extends Omit<ButtonProps, 'onContextMenu' | 'onPlay'> {
|
||||
item: Playlist;
|
||||
name: string;
|
||||
@@ -80,6 +120,8 @@ export const PlaylistRowButton = memo(
|
||||
const isCompact = sidebarPlaylistMode === 'compact';
|
||||
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const isSmartPlaylist = Boolean(item.rules);
|
||||
const isAddDragActive = useContext(SidebarPlaylistAddDragContext);
|
||||
|
||||
const { isDraggedOver, isDragging, ref } = useDragDrop<HTMLAnchorElement>({
|
||||
drag: {
|
||||
@@ -97,6 +139,7 @@ export const PlaylistRowButton = memo(
|
||||
canDrop: (args) => {
|
||||
// Allow dropping items into a playlist (ADD)
|
||||
const canAdd =
|
||||
!isSmartPlaylist &&
|
||||
args.source.itemType !== undefined &&
|
||||
args.source.type !== DragTarget.PLAYLIST &&
|
||||
(args.source.operation?.includes(DragOperation.ADD) ?? false);
|
||||
@@ -118,6 +161,7 @@ export const PlaylistRowButton = memo(
|
||||
};
|
||||
},
|
||||
onDrag: () => {
|
||||
console.log('started drag');
|
||||
return;
|
||||
},
|
||||
onDragLeave: () => {
|
||||
@@ -150,6 +194,10 @@ export const PlaylistRowButton = memo(
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSmartPlaylist) {
|
||||
return;
|
||||
}
|
||||
|
||||
const modalProps: {
|
||||
albumId?: string[];
|
||||
artistId?: string[];
|
||||
@@ -222,13 +270,18 @@ export const PlaylistRowButton = memo(
|
||||
type: 'table',
|
||||
});
|
||||
|
||||
const isDimmed = isDragging || (isSmartPlaylist && isAddDragActive);
|
||||
|
||||
return (
|
||||
<Link
|
||||
<MotionLink
|
||||
{...animationProps.fadeIn}
|
||||
animate={isDimmed ? 'hidden' : 'show'}
|
||||
className={clsx(styles.row, {
|
||||
[styles.rowCompact]: isCompact,
|
||||
[styles.rowDraggedOver]: isDraggedOver,
|
||||
[styles.rowDraggedOver]: isDraggedOver && !isSmartPlaylist,
|
||||
[styles.rowHover]: isHovered,
|
||||
})}
|
||||
initial={false}
|
||||
onContextMenu={(e: MouseEvent<HTMLAnchorElement>) => {
|
||||
e.preventDefault();
|
||||
onContextMenu(e, item);
|
||||
@@ -236,10 +289,8 @@ export const PlaylistRowButton = memo(
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
ref={ref}
|
||||
style={{
|
||||
opacity: isDragging ? 0.5 : 1,
|
||||
}}
|
||||
to={url}
|
||||
variants={playlistRowDimVariants}
|
||||
>
|
||||
{isCompact ? (
|
||||
<>
|
||||
@@ -297,7 +348,7 @@ export const PlaylistRowButton = memo(
|
||||
{isHovered && <RowControls id={to} onPlay={handlePlay} />}
|
||||
</>
|
||||
)}
|
||||
</Link>
|
||||
</MotionLink>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -16,8 +16,10 @@ import { SidebarCollectionList } from '/@/renderer/features/sidebar/components/s
|
||||
import { SidebarIcon } from '/@/renderer/features/sidebar/components/sidebar-icon';
|
||||
import { SidebarItem } from '/@/renderer/features/sidebar/components/sidebar-item';
|
||||
import {
|
||||
SidebarPlaylistAddDragContext,
|
||||
SidebarPlaylistList,
|
||||
SidebarSharedPlaylistList,
|
||||
useSidebarPlaylistAddDragMonitor,
|
||||
} from '/@/renderer/features/sidebar/components/sidebar-playlist-list';
|
||||
import {
|
||||
useAppStore,
|
||||
@@ -45,6 +47,17 @@ import { Tooltip } from '/@/shared/components/tooltip/tooltip';
|
||||
import { ExplicitStatus, LibraryItem } from '/@/shared/types/domain-types';
|
||||
import { Platform } from '/@/shared/types/types';
|
||||
|
||||
const SidebarPlaylistSection = () => {
|
||||
const isAddDragActive = useSidebarPlaylistAddDragMonitor();
|
||||
|
||||
return (
|
||||
<SidebarPlaylistAddDragContext.Provider value={isAddDragActive}>
|
||||
<SidebarPlaylistList />
|
||||
<SidebarSharedPlaylistList />
|
||||
</SidebarPlaylistAddDragContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const Sidebar = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -140,12 +153,7 @@ export const Sidebar = () => {
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
<SidebarCollectionList />
|
||||
{sidebarPlaylistList && (
|
||||
<>
|
||||
<SidebarPlaylistList />
|
||||
<SidebarSharedPlaylistList />
|
||||
</>
|
||||
)}
|
||||
{sidebarPlaylistList && <SidebarPlaylistSection />}
|
||||
</Accordion>
|
||||
</ScrollArea>
|
||||
<AnimatePresence initial={false} mode="popLayout">
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { DragData } from '/@/shared/types/drag-and-drop';
|
||||
|
||||
interface UseDragMonitorProps {
|
||||
canMonitor?: (source: DragData) => boolean;
|
||||
isEnabled?: boolean;
|
||||
onDragStart?: (source: DragData) => void;
|
||||
onDrop?: () => void;
|
||||
}
|
||||
|
||||
export const useDragMonitor = ({
|
||||
canMonitor,
|
||||
isEnabled = true,
|
||||
onDragStart,
|
||||
onDrop,
|
||||
}: UseDragMonitorProps) => {
|
||||
useEffect(() => {
|
||||
if (!isEnabled) return;
|
||||
|
||||
return monitorForElements({
|
||||
onDragStart: ({ source }) => {
|
||||
const data = source.data as unknown as DragData;
|
||||
if (canMonitor && !canMonitor(data)) return;
|
||||
onDragStart?.(data);
|
||||
},
|
||||
onDrop: () => {
|
||||
onDrop?.();
|
||||
},
|
||||
});
|
||||
}, [canMonitor, isEnabled, onDragStart, onDrop]);
|
||||
};
|
||||
Reference in New Issue
Block a user