mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-21 22:15:40 +00:00
chore(orbit): refine song-row click semantics
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { suggestOrbitTrack, hostEnqueueToOrbit } from '../utils/orbit';
|
||||
import { showToast } from '../utils/toast';
|
||||
|
||||
/**
|
||||
* Shared behaviour for song rows that in "normal mode" swallow a full list
|
||||
* into the queue on single-click (AlbumDetail, PlaylistDetail, Favorites,
|
||||
* ArtistDetail top-songs, SearchResults, RandomMix, AdvancedSearch).
|
||||
*
|
||||
* In an active Orbit session this is too destructive — the list would
|
||||
* propagate to every guest's player. Instead:
|
||||
*
|
||||
* - `queueHint()` — show a toast telling the user to double-click.
|
||||
* Safe to call on every single-click; 220 ms debounce
|
||||
* suppresses the pileup that browsers emit before a
|
||||
* dblclick fires.
|
||||
* - `addTrackToOrbit(songId)` — cancel any pending hint and add just that
|
||||
* one track: suggestOrbitTrack for guests,
|
||||
* hostEnqueueToOrbit for the host.
|
||||
*
|
||||
* `orbitActive` is the gate — when false, callers should skip the hint and
|
||||
* run their original bulk-play path unchanged.
|
||||
*/
|
||||
export function useOrbitSongRowBehavior() {
|
||||
const { t } = useTranslation();
|
||||
const orbitRole = useOrbitStore(s => s.role);
|
||||
const orbitActive = orbitRole === 'host' || orbitRole === 'guest';
|
||||
const clickTimerRef = useRef<number | null>(null);
|
||||
|
||||
const queueHint = useCallback(() => {
|
||||
if (clickTimerRef.current !== null) return;
|
||||
clickTimerRef.current = window.setTimeout(() => {
|
||||
clickTimerRef.current = null;
|
||||
showToast(t('albumDetail.orbitDoubleClickHint'), 2400, 'info');
|
||||
}, 220);
|
||||
}, [t]);
|
||||
|
||||
const addTrackToOrbit = useCallback((songId: string) => {
|
||||
if (clickTimerRef.current !== null) {
|
||||
clearTimeout(clickTimerRef.current);
|
||||
clickTimerRef.current = null;
|
||||
}
|
||||
if (orbitRole === 'guest') {
|
||||
suggestOrbitTrack(songId)
|
||||
.then(() => showToast(t('orbit.ctxSuggestedToast'), 2200, 'info'))
|
||||
.catch(() => showToast(t('orbit.ctxSuggestFailed'), 3000, 'error'));
|
||||
} else if (orbitRole === 'host') {
|
||||
hostEnqueueToOrbit(songId)
|
||||
.then(() => showToast(t('orbit.ctxAddedHostToast'), 2200, 'info'))
|
||||
.catch(() => showToast(t('orbit.ctxAddHostFailed'), 3000, 'error'));
|
||||
}
|
||||
}, [orbitRole, t]);
|
||||
|
||||
return { orbitActive, queueHint, addTrackToOrbit };
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import AlbumRow from '../components/AlbumRow';
|
||||
import ArtistRow from '../components/ArtistRow';
|
||||
import CustomSelect from '../components/CustomSelect';
|
||||
import { useDragDrop } from '../contexts/DragDropContext';
|
||||
import { useOrbitSongRowBehavior } from '../hooks/useOrbitSongRowBehavior';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
|
||||
@@ -44,6 +45,8 @@ export default function AdvancedSearch() {
|
||||
}))
|
||||
);
|
||||
|
||||
const { orbitActive, addTrackToOrbit } = useOrbitSongRowBehavior();
|
||||
|
||||
const [contextMenuSongId, setContextMenuSongId] = useState<string | null>(null);
|
||||
const contextMenuOpen = usePlayerStore(s => s.contextMenu.isOpen);
|
||||
|
||||
@@ -302,7 +305,7 @@ export default function AdvancedSearch() {
|
||||
key={song.id}
|
||||
className={`track-row${contextMenuSongId === song.id ? ' context-active' : ''}`}
|
||||
style={{ gridTemplateColumns: '60px minmax(150px, 1fr) minmax(80px, 1fr) minmax(80px, 1fr) 90px 65px' }}
|
||||
onDoubleClick={() => playTrack(track, results.songs.map(songToTrack))}
|
||||
onDoubleClick={() => orbitActive ? addTrackToOrbit(song.id) : playTrack(track, results.songs.map(songToTrack))}
|
||||
role="row"
|
||||
onContextMenu={e => {
|
||||
e.preventDefault();
|
||||
@@ -328,7 +331,7 @@ export default function AdvancedSearch() {
|
||||
<button
|
||||
className="btn btn-ghost"
|
||||
style={{ padding: 4 }}
|
||||
onClick={e => { e.stopPropagation(); playTrack(track, results.songs.map(songToTrack)); }}
|
||||
onClick={e => { e.stopPropagation(); if (orbitActive) { addTrackToOrbit(song.id); return; } playTrack(track, results.songs.map(songToTrack)); }}
|
||||
>
|
||||
<Play size={13} fill="currentColor" />
|
||||
</button>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import React, { useEffect, useRef, useState, useCallback, useMemo } from 'react';
|
||||
import React, { useEffect, useState, useCallback, useMemo } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { Search, X } from 'lucide-react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { getAlbum, getArtist, getArtistInfo, setRating, buildCoverArtUrl, coverArtCacheKey, buildDownloadUrl, star, unstar, SubsonicSong, SubsonicAlbum } from '../api/subsonic';
|
||||
import { usePlayerStore, songToTrack } from '../store/playerStore';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { suggestOrbitTrack, hostEnqueueToOrbit } from '../utils/orbit';
|
||||
import { useOrbitSongRowBehavior } from '../hooks/useOrbitSongRowBehavior';
|
||||
import { useDownloadModalStore } from '../store/downloadModalStore';
|
||||
import { useOfflineStore } from '../store/offlineStore';
|
||||
import { useOfflineJobStore } from '../store/offlineJobStore';
|
||||
@@ -155,24 +154,10 @@ const handleShuffleAll = () => {
|
||||
if (shuffled[0]) playTrack(shuffled[0], shuffled);
|
||||
};
|
||||
|
||||
const orbitRole = useOrbitStore(s => s.role);
|
||||
const orbitActive = orbitRole === 'host' || orbitRole === 'guest';
|
||||
const orbitClickTimerRef = useRef<number | null>(null);
|
||||
const { orbitActive, queueHint, addTrackToOrbit } = useOrbitSongRowBehavior();
|
||||
|
||||
const handlePlaySong = (song: SubsonicSong) => {
|
||||
// Orbit-Modus: ein Single-Click würde das ganze Album in die geteilte
|
||||
// Queue schaufeln — für eine kollaborative Session zu destruktiv.
|
||||
// Wir schlucken den Click und blenden einen Hint ein; ein folgender
|
||||
// Doppelklick cancelt den Timer und fügt stattdessen nur diesen Song
|
||||
// via handleDoubleClickSong hinzu.
|
||||
if (orbitActive) {
|
||||
if (orbitClickTimerRef.current !== null) return;
|
||||
orbitClickTimerRef.current = window.setTimeout(() => {
|
||||
orbitClickTimerRef.current = null;
|
||||
showToast(t('albumDetail.orbitDoubleClickHint'), 2400, 'info');
|
||||
}, 220);
|
||||
return;
|
||||
}
|
||||
if (orbitActive) { queueHint(); return; }
|
||||
if (!album) return;
|
||||
const albumGenre = album.album.genre;
|
||||
const tracks = album.songs.map(s => {
|
||||
@@ -184,22 +169,7 @@ const handleShuffleAll = () => {
|
||||
playTrack(track, tracks);
|
||||
};
|
||||
|
||||
const handleDoubleClickSong = (song: SubsonicSong) => {
|
||||
if (!orbitActive) return;
|
||||
if (orbitClickTimerRef.current !== null) {
|
||||
clearTimeout(orbitClickTimerRef.current);
|
||||
orbitClickTimerRef.current = null;
|
||||
}
|
||||
if (orbitRole === 'guest') {
|
||||
suggestOrbitTrack(song.id)
|
||||
.then(() => showToast(t('orbit.ctxSuggestedToast'), 2200, 'info'))
|
||||
.catch(() => showToast(t('orbit.ctxSuggestFailed'), 3000, 'error'));
|
||||
} else {
|
||||
hostEnqueueToOrbit(song.id)
|
||||
.then(() => showToast(t('orbit.ctxAddedHostToast'), 2200, 'info'))
|
||||
.catch(() => showToast(t('orbit.ctxAddHostFailed'), 3000, 'error'));
|
||||
}
|
||||
};
|
||||
const handleDoubleClickSong = (song: SubsonicSong) => addTrackToOrbit(song.id);
|
||||
|
||||
const handleRate = async (songId: string, rating: number) => {
|
||||
setRatings(r => ({ ...r, [songId]: rating }));
|
||||
|
||||
@@ -6,6 +6,7 @@ import CachedImage from '../components/CachedImage';
|
||||
import CoverLightbox from '../components/CoverLightbox';
|
||||
import { ArrowLeft, Users, ExternalLink, Heart, Play, Shuffle, Radio, HardDriveDownload, Check, Camera, Loader2, ChevronDown, ChevronUp, Share2 } from 'lucide-react';
|
||||
import { useIsMobile } from '../hooks/useIsMobile';
|
||||
import { useOrbitSongRowBehavior } from '../hooks/useOrbitSongRowBehavior';
|
||||
import { open } from '@tauri-apps/plugin-shell';
|
||||
import { usePlayerStore, songToTrack } from '../store/playerStore';
|
||||
import { useOfflineStore } from '../store/offlineStore';
|
||||
@@ -74,6 +75,7 @@ export default function ArtistDetail() {
|
||||
|
||||
const playTrack = usePlayerStore(state => state.playTrack);
|
||||
const enqueue = usePlayerStore(state => state.enqueue);
|
||||
const { orbitActive, queueHint, addTrackToOrbit } = useOrbitSongRowBehavior();
|
||||
const clearQueue = usePlayerStore(state => state.clearQueue);
|
||||
const openContextMenu = usePlayerStore(state => state.openContextMenu);
|
||||
const currentTrack = usePlayerStore(state => state.currentTrack);
|
||||
@@ -702,14 +704,19 @@ export default function ArtistDetail() {
|
||||
style={{ gridTemplateColumns: '60px minmax(150px, 1fr) minmax(100px, 1fr) 65px' }}
|
||||
onClick={e => {
|
||||
if ((e.target as HTMLElement).closest('button, a, input')) return;
|
||||
if (orbitActive) { queueHint(); return; }
|
||||
playTopSongWithContinuation(idx);
|
||||
}}
|
||||
onDoubleClick={orbitActive ? e => {
|
||||
if ((e.target as HTMLElement).closest('button, a, input')) return;
|
||||
addTrackToOrbit(song.id);
|
||||
} : undefined}
|
||||
onContextMenu={(e) => {
|
||||
e.preventDefault();
|
||||
openContextMenu(e.clientX, e.clientY, track, 'song');
|
||||
}}
|
||||
>
|
||||
<div className={`track-num${currentTrack?.id === song.id ? ' track-num-active' : ''}`} style={{ cursor: 'pointer' }} onClick={e => { e.stopPropagation(); playTopSongWithContinuation(idx); }}>
|
||||
<div className={`track-num${currentTrack?.id === song.id ? ' track-num-active' : ''}`} style={{ cursor: 'pointer' }} onClick={e => { e.stopPropagation(); if (orbitActive) { queueHint(); return; } playTopSongWithContinuation(idx); }}>
|
||||
{currentTrack?.id === song.id && isPlaying && <span className="track-num-eq"><div className="eq-bars"><span className="eq-bar" /><span className="eq-bar" /><span className="eq-bar" /></div></span>}
|
||||
<span className="track-num-play"><Play size={13} fill="currentColor" /></span>
|
||||
<span className="track-num-number">{idx + 1}</span>
|
||||
|
||||
+10
-1
@@ -17,6 +17,7 @@ import { unstar } from '../api/subsonic';
|
||||
import { useDragDrop } from '../contexts/DragDropContext';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useSelectionStore } from '../store/selectionStore';
|
||||
import { useOrbitSongRowBehavior } from '../hooks/useOrbitSongRowBehavior';
|
||||
import { AddToPlaylistSubmenu } from '../components/ContextMenu';
|
||||
import GenreFilterBar from '../components/GenreFilterBar';
|
||||
|
||||
@@ -78,6 +79,7 @@ export default function Favorites() {
|
||||
|
||||
const playTrack = usePlayerStore(s => s.playTrack);
|
||||
const enqueue = usePlayerStore(s => s.enqueue);
|
||||
const { orbitActive, queueHint, addTrackToOrbit } = useOrbitSongRowBehavior();
|
||||
const playRadio = usePlayerStore(s => s.playRadio);
|
||||
const stop = usePlayerStore(s => s.stop);
|
||||
const currentTrack = usePlayerStore(s => s.currentTrack);
|
||||
@@ -629,10 +631,17 @@ export default function Favorites() {
|
||||
toggleSelect(song.id, i, false);
|
||||
} else if (inSelectMode) {
|
||||
toggleSelect(song.id, i, e.shiftKey);
|
||||
} else if (orbitActive) {
|
||||
queueHint();
|
||||
} else {
|
||||
playTrack(track, visibleSongs.map(songToTrack));
|
||||
}
|
||||
}}
|
||||
onDoubleClick={orbitActive ? e => {
|
||||
if ((e.target as HTMLElement).closest('button, a, input')) return;
|
||||
if (e.ctrlKey || e.metaKey || inSelectMode) return;
|
||||
addTrackToOrbit(song.id);
|
||||
} : undefined}
|
||||
onContextMenu={e => { e.preventDefault(); openContextMenu(e.clientX, e.clientY, track, 'favorite-song'); }}
|
||||
role="row"
|
||||
onMouseDown={e => {
|
||||
@@ -660,7 +669,7 @@ export default function Favorites() {
|
||||
{visibleCols.map(colDef => {
|
||||
switch (colDef.key) {
|
||||
case 'num': return (
|
||||
<div key="num" className={`track-num${currentTrack?.id === song.id ? ' track-num-active' : ''}${currentTrack?.id === song.id && !isPlaying ? ' track-num-paused' : ''}`} style={{ cursor: 'pointer' }} onClick={e => { e.stopPropagation(); playTrack(track, visibleSongs.map(songToTrack)); }}>
|
||||
<div key="num" className={`track-num${currentTrack?.id === song.id ? ' track-num-active' : ''}${currentTrack?.id === song.id && !isPlaying ? ' track-num-paused' : ''}`} style={{ cursor: 'pointer' }} onClick={e => { e.stopPropagation(); if (orbitActive) { queueHint(); return; } playTrack(track, visibleSongs.map(songToTrack)); }}>
|
||||
<span className={`bulk-check${isSelected ? ' checked' : ''}${inSelectMode ? ' bulk-check-visible' : ''}`} onClick={e => { e.stopPropagation(); toggleSelect(song.id, i, e.shiftKey); }} />
|
||||
{currentTrack?.id === song.id && isPlaying && <span className="track-num-eq"><div className="eq-bars"><span className="eq-bar" /><span className="eq-bar" /><span className="eq-bar" /></div></span>}
|
||||
<span className="track-num-play"><Play size={13} fill="currentColor" /></span>
|
||||
|
||||
@@ -17,6 +17,7 @@ import { useOfflineJobStore } from '../store/offlineJobStore';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useThemeStore } from '../store/themeStore';
|
||||
import { useDownloadModalStore } from '../store/downloadModalStore';
|
||||
import { useOrbitSongRowBehavior } from '../hooks/useOrbitSongRowBehavior';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { join } from '@tauri-apps/api/path';
|
||||
import { open as openDialog } from '@tauri-apps/plugin-dialog';
|
||||
@@ -237,6 +238,7 @@ export default function PlaylistDetail() {
|
||||
userRatingOverrides: s.userRatingOverrides,
|
||||
}))
|
||||
);
|
||||
const { orbitActive, queueHint, addTrackToOrbit } = useOrbitSongRowBehavior();
|
||||
const touchPlaylist = usePlaylistStore((s) => s.touchPlaylist);
|
||||
const { startDrag, isDragging } = useDragDrop();
|
||||
const downloadPlaylist = useOfflineStore(s => s.downloadPlaylist);
|
||||
@@ -1611,10 +1613,17 @@ export default function PlaylistDetail() {
|
||||
toggleSelect(song.id, i, false);
|
||||
} else if (selectedIds.size > 0) {
|
||||
toggleSelect(song.id, i, e.shiftKey);
|
||||
} else if (orbitActive) {
|
||||
queueHint();
|
||||
} else {
|
||||
playTrack(displayedTracks[i], displayedTracks);
|
||||
}
|
||||
}}
|
||||
onDoubleClick={orbitActive ? e => {
|
||||
if ((e.target as HTMLElement).closest('button, a, input')) return;
|
||||
if (e.ctrlKey || e.metaKey || selectedIds.size > 0) return;
|
||||
addTrackToOrbit(song.id);
|
||||
} : undefined}
|
||||
onContextMenu={e => {
|
||||
e.preventDefault();
|
||||
setContextMenuSongId(song.id);
|
||||
@@ -1625,7 +1634,7 @@ export default function PlaylistDetail() {
|
||||
const inSelectMode = selectedIds.size > 0;
|
||||
switch (colDef.key) {
|
||||
case 'num': return (
|
||||
<div key="num" className={`track-num${currentTrack?.id === song.id ? ' track-num-active' : ''}${currentTrack?.id === song.id && !isPlaying ? ' track-num-paused' : ''}`} style={{ cursor: 'pointer' }} onClick={e => { e.stopPropagation(); playTrack(displayedTracks[i], displayedTracks); }}>
|
||||
<div key="num" className={`track-num${currentTrack?.id === song.id ? ' track-num-active' : ''}${currentTrack?.id === song.id && !isPlaying ? ' track-num-paused' : ''}`} style={{ cursor: 'pointer' }} onClick={e => { e.stopPropagation(); if (orbitActive) { queueHint(); return; } playTrack(displayedTracks[i], displayedTracks); }}>
|
||||
<span className={`bulk-check${selectedIds.has(song.id) ? ' checked' : ''}${inSelectMode ? ' bulk-check-visible' : ''}`} onClick={e => { e.stopPropagation(); toggleSelect(song.id, i, e.shiftKey); }} />
|
||||
{currentTrack?.id === song.id && isPlaying && <span className="track-num-eq"><div className="eq-bars"><span className="eq-bar" /><span className="eq-bar" /><span className="eq-bar" /></div></span>}
|
||||
<span className="track-num-play"><Play size={13} fill="currentColor" /></span>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Play, RefreshCw, ChevronDown, ChevronUp, Heart } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDragDrop } from '../contexts/DragDropContext';
|
||||
import { useIsMobile } from '../hooks/useIsMobile';
|
||||
import { useOrbitSongRowBehavior } from '../hooks/useOrbitSongRowBehavior';
|
||||
import {
|
||||
fetchRandomMixSongsUntilFull,
|
||||
getMixMinRatingsConfigFromAuth,
|
||||
@@ -32,6 +33,7 @@ export default function RandomMix() {
|
||||
const [songs, setSongs] = useState<SubsonicSong[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const playTrack = usePlayerStore(s => s.playTrack);
|
||||
const { orbitActive, queueHint, addTrackToOrbit } = useOrbitSongRowBehavior();
|
||||
const openContextMenu = usePlayerStore(s => s.openContextMenu);
|
||||
const contextMenuOpen = usePlayerStore(s => s.contextMenu.isOpen);
|
||||
const currentTrack = usePlayerStore(s => s.currentTrack);
|
||||
@@ -427,7 +429,8 @@ export default function RandomMix() {
|
||||
key={song.id}
|
||||
className={`track-row${isCurrentTrack ? ' active' : ''}${contextMenuSongId === song.id ? ' context-active' : ''}`}
|
||||
style={{ gridTemplateColumns: '60px minmax(150px, 1fr) minmax(80px, 1fr) minmax(80px, 1fr) 70px 65px' }}
|
||||
onClick={e => { if ((e.target as HTMLElement).closest('button, a, input')) return; playTrack(track, queueSongs); }}
|
||||
onClick={e => { if ((e.target as HTMLElement).closest('button, a, input')) return; if (orbitActive) { queueHint(); return; } playTrack(track, queueSongs); }}
|
||||
onDoubleClick={orbitActive ? e => { if ((e.target as HTMLElement).closest('button, a, input')) return; addTrackToOrbit(song.id); } : undefined}
|
||||
role="row"
|
||||
onContextMenu={e => { e.preventDefault(); setContextMenuSongId(song.id); openContextMenu(e.clientX, e.clientY, track, 'song'); }}
|
||||
onMouseDown={e => {
|
||||
@@ -446,7 +449,7 @@ export default function RandomMix() {
|
||||
document.addEventListener('mouseup', onUp);
|
||||
}}
|
||||
>
|
||||
<div className={`track-num${isCurrentTrack ? ' track-num-active' : ''}`} style={{ cursor: 'pointer' }} onClick={e => { e.stopPropagation(); playTrack(track, queueSongs); }}>
|
||||
<div className={`track-num${isCurrentTrack ? ' track-num-active' : ''}`} style={{ cursor: 'pointer' }} onClick={e => { e.stopPropagation(); if (orbitActive) { queueHint(); return; } playTrack(track, queueSongs); }}>
|
||||
{isCurrentTrack && isPlaying && <span className="track-num-eq"><div className="eq-bars"><span className="eq-bar" /><span className="eq-bar" /><span className="eq-bar" /></div></span>}
|
||||
<span className="track-num-play"><Play size={13} fill="currentColor" /></span>
|
||||
<span className="track-num-number">{idx + 1}</span>
|
||||
@@ -526,7 +529,8 @@ export default function RandomMix() {
|
||||
key={song.id}
|
||||
className={`track-row${isCurrentTrack ? ' active' : ''}${contextMenuSongId === song.id ? ' context-active' : ''}`}
|
||||
style={{ gridTemplateColumns: '60px minmax(150px, 1fr) minmax(80px, 1fr) minmax(80px, 1fr) 120px 70px 65px' }}
|
||||
onClick={e => { if ((e.target as HTMLElement).closest('button, a, input')) return; playTrack(track, queueSongs); }}
|
||||
onClick={e => { if ((e.target as HTMLElement).closest('button, a, input')) return; if (orbitActive) { queueHint(); return; } playTrack(track, queueSongs); }}
|
||||
onDoubleClick={orbitActive ? e => { if ((e.target as HTMLElement).closest('button, a, input')) return; addTrackToOrbit(song.id); } : undefined}
|
||||
role="row"
|
||||
onContextMenu={e => {
|
||||
e.preventDefault();
|
||||
@@ -549,7 +553,7 @@ export default function RandomMix() {
|
||||
document.addEventListener('mouseup', onUp);
|
||||
}}
|
||||
>
|
||||
<div className={`track-num${isCurrentTrack ? ' track-num-active' : ''}`} style={{ cursor: 'pointer' }} onClick={e => { e.stopPropagation(); playTrack(track, queueSongs); }}>
|
||||
<div className={`track-num${isCurrentTrack ? ' track-num-active' : ''}`} style={{ cursor: 'pointer' }} onClick={e => { e.stopPropagation(); if (orbitActive) { queueHint(); return; } playTrack(track, queueSongs); }}>
|
||||
{isCurrentTrack && isPlaying && <span className="track-num-eq"><div className="eq-bars"><span className="eq-bar" /><span className="eq-bar" /><span className="eq-bar" /></div></span>}
|
||||
<span className="track-num-play"><Play size={13} fill="currentColor" /></span>
|
||||
<span className="track-num-number">{idx + 1}</span>
|
||||
|
||||
@@ -8,6 +8,7 @@ import ArtistRow from '../components/ArtistRow';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDragDrop } from '../contexts/DragDropContext';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useOrbitSongRowBehavior } from '../hooks/useOrbitSongRowBehavior';
|
||||
import { useThemeStore } from '../store/themeStore';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
|
||||
@@ -51,7 +52,10 @@ export default function SearchResults() {
|
||||
|
||||
const hasResults = results && (results.artists.length || results.albums.length || results.songs.length);
|
||||
|
||||
const { orbitActive, addTrackToOrbit } = useOrbitSongRowBehavior();
|
||||
|
||||
const playSong = (song: SubsonicSong, list: SubsonicSong[]) => {
|
||||
if (orbitActive) { addTrackToOrbit(song.id); return; }
|
||||
playTrack(songToTrack(song), list.map(songToTrack));
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user