Files
Psychotoxical-psysonic/src/features/playlist/utils/startPlaylistRowDrag.ts
T
Psychotoxical 7e9cb60763 refactor(api): co-locate server-protocol clients into lib/api
Move the Subsonic + Navidrome protocol clients and the library IPC facade
(subsonic*, navidrome*, library.ts + tests) from src/api/ into src/lib/api/,
the feature-free infra layer (plan M4, decision §10.3: shared client core → lib/).
Pure move: deep-path import specifiers @/api/* -> @/lib/api/* across ~150
consumers; no behaviour change. Domain REST (lyrics/events) and cover/analysis
infra stay in src/api/ pending their own M4 placement.

tsc 0, lint 0/0, full suite 319 files / 2353 tests green.
2026-06-30 07:53:25 +02:00

48 lines
1.9 KiB
TypeScript

import type React from 'react';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { songToTrack } from '@/utils/playback/songToTrack';
export interface StartPlaylistRowDragDeps {
e: React.MouseEvent;
idx: number;
songs: SubsonicSong[];
selectedIds: Set<string>;
isFiltered: boolean;
startDrag: (payload: { data: string; label: string }, x: number, y: number) => void;
}
export function startPlaylistRowDrag(deps: StartPlaylistRowDragDeps): void {
const { e, idx, songs, selectedIds, isFiltered, startDrag } = deps;
if (e.button !== 0) return;
if ((e.target as HTMLElement).closest('button, input')) return;
e.preventDefault();
const sx = e.clientX, sy = e.clientY;
const onMove = (me: MouseEvent) => {
if (Math.abs(me.clientX - sx) > 5 || Math.abs(me.clientY - sy) > 5) {
document.removeEventListener('mousemove', onMove);
document.removeEventListener('mouseup', onUp);
if (!isFiltered && selectedIds.has(songs[idx]?.id) && selectedIds.size > 1) {
const bulkTracks = songs.filter(s => selectedIds.has(s.id)).map(songToTrack);
startDrag({ data: JSON.stringify({ type: 'songs', tracks: bulkTracks }), label: `${bulkTracks.length} Songs` }, me.clientX, me.clientY);
} else if (!isFiltered) {
startDrag(
{ data: JSON.stringify({ type: 'playlist_reorder', index: idx }), label: songs[idx]?.title ?? '' },
me.clientX, me.clientY
);
} else {
// filtered view: single-song drag to queue
startDrag(
{ data: JSON.stringify({ type: 'song', track: songToTrack(songs[idx]) }), label: songs[idx]?.title ?? '' },
me.clientX, me.clientY
);
}
}
};
const onUp = () => {
document.removeEventListener('mousemove', onMove);
document.removeEventListener('mouseup', onUp);
};
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
}