diff --git a/src/renderer/components/item-list/item-detail-list/columns/row-index-column.module.css b/src/renderer/components/item-list/item-detail-list/columns/row-index-column.module.css
new file mode 100644
index 000000000..3b0048ae7
--- /dev/null
+++ b/src/renderer/components/item-list/item-detail-list/columns/row-index-column.module.css
@@ -0,0 +1,5 @@
+.icon-wrapper {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
diff --git a/src/renderer/components/item-list/item-detail-list/columns/row-index-column.tsx b/src/renderer/components/item-list/item-detail-list/columns/row-index-column.tsx
index b96e40116..817f0acec 100644
--- a/src/renderer/components/item-list/item-detail-list/columns/row-index-column.tsx
+++ b/src/renderer/components/item-list/item-detail-list/columns/row-index-column.tsx
@@ -1,4 +1,23 @@
+import styles from './row-index-column.module.css';
import { ItemDetailListCellProps } from './types';
-export const RowIndexColumn = ({ rowIndex }: ItemDetailListCellProps) =>
- String((rowIndex ?? 0) + 1);
+import { useIsCurrentSong } from '/@/renderer/features/player/hooks/use-is-current-song';
+import { usePlayerStatus } from '/@/renderer/store';
+import { Icon } from '/@/shared/components/icon/icon';
+import { PlayerStatus } from '/@/shared/types/types';
+
+export const RowIndexColumn = ({ rowIndex, song }: ItemDetailListCellProps) => {
+ const status = usePlayerStatus();
+ const { isActive } = useIsCurrentSong(song);
+ const isPlaying = isActive && status === PlayerStatus.PLAYING;
+
+ if (isActive) {
+ return (
+
+
+
+ );
+ }
+
+ return <>{String((rowIndex ?? 0) + 1)}>;
+};
diff --git a/src/renderer/components/item-list/item-detail-list/columns/title-artist-column.tsx b/src/renderer/components/item-list/item-detail-list/columns/title-artist-column.tsx
index 0c16a5791..bfd60def1 100644
--- a/src/renderer/components/item-list/item-detail-list/columns/title-artist-column.tsx
+++ b/src/renderer/components/item-list/item-detail-list/columns/title-artist-column.tsx
@@ -1,9 +1,18 @@
+import clsx from 'clsx';
+
+import styles from './title-column.module.css';
+
import { ItemDetailListCellProps } from '/@/renderer/components/item-list/item-detail-list/columns/types';
+import { useIsCurrentSong } from '/@/renderer/features/player/hooks/use-is-current-song';
import { ExplicitIndicator } from '/@/shared/components/explicit-indicator/explicit-indicator';
-export const TitleArtistColumn = ({ song }: ItemDetailListCellProps) => (
- <>
-
- {[song.name, song.artistName].filter(Boolean).join(' — ') ?? <> >}
- >
-);
+export const TitleArtistColumn = ({ song }: ItemDetailListCellProps) => {
+ const { isActive } = useIsCurrentSong(song);
+
+ return (
+
+
+ {[song.name, song.artistName].filter(Boolean).join(' — ') ?? <> >}
+
+ );
+};
diff --git a/src/renderer/components/item-list/item-detail-list/columns/title-column.module.css b/src/renderer/components/item-list/item-detail-list/columns/title-column.module.css
new file mode 100644
index 000000000..9b1d26b7a
--- /dev/null
+++ b/src/renderer/components/item-list/item-detail-list/columns/title-column.module.css
@@ -0,0 +1,3 @@
+.active {
+ color: var(--theme-colors-primary);
+}
diff --git a/src/renderer/components/item-list/item-detail-list/columns/title-column.tsx b/src/renderer/components/item-list/item-detail-list/columns/title-column.tsx
index e4d588488..c46697fc8 100644
--- a/src/renderer/components/item-list/item-detail-list/columns/title-column.tsx
+++ b/src/renderer/components/item-list/item-detail-list/columns/title-column.tsx
@@ -1,9 +1,18 @@
+import clsx from 'clsx';
+
+import styles from './title-column.module.css';
+
import { ItemDetailListCellProps } from '/@/renderer/components/item-list/item-detail-list/columns/types';
+import { useIsCurrentSong } from '/@/renderer/features/player/hooks/use-is-current-song';
import { ExplicitIndicator } from '/@/shared/components/explicit-indicator/explicit-indicator';
-export const TitleColumn = ({ song }: ItemDetailListCellProps) => (
- <>
-
- {song.name ?? <> >}
- >
-);
+export const TitleColumn = ({ song }: ItemDetailListCellProps) => {
+ const { isActive } = useIsCurrentSong(song);
+
+ return (
+
+
+ {song.name ?? <> >}
+
+ );
+};
diff --git a/src/renderer/components/item-list/item-detail-list/columns/title-combined-column.tsx b/src/renderer/components/item-list/item-detail-list/columns/title-combined-column.tsx
index 4d2701947..17e6de49c 100644
--- a/src/renderer/components/item-list/item-detail-list/columns/title-combined-column.tsx
+++ b/src/renderer/components/item-list/item-detail-list/columns/title-combined-column.tsx
@@ -1,9 +1,18 @@
+import clsx from 'clsx';
+
+import styles from './title-column.module.css';
+
import { ItemDetailListCellProps } from '/@/renderer/components/item-list/item-detail-list/columns/types';
+import { useIsCurrentSong } from '/@/renderer/features/player/hooks/use-is-current-song';
import { ExplicitIndicator } from '/@/shared/components/explicit-indicator/explicit-indicator';
-export const TitleCombinedColumn = ({ song }: ItemDetailListCellProps) => (
- <>
-
- {[song.name, song.artistName].filter(Boolean).join(' — ') ?? <> >}
- >
-);
+export const TitleCombinedColumn = ({ song }: ItemDetailListCellProps) => {
+ const { isActive } = useIsCurrentSong(song);
+
+ return (
+
+
+ {[song.name, song.artistName].filter(Boolean).join(' — ') ?? <> >}
+
+ );
+};
diff --git a/src/renderer/components/item-list/item-detail-list/item-detail.tsx b/src/renderer/components/item-list/item-detail-list/item-detail.tsx
index dcf86a9b7..3f2f34bae 100644
--- a/src/renderer/components/item-list/item-detail-list/item-detail.tsx
+++ b/src/renderer/components/item-list/item-detail-list/item-detail.tsx
@@ -56,8 +56,9 @@ import { formatDateAbsoluteUTC, formatDurationString } from '/@/renderer/utils';
import { ExplicitIndicator } from '/@/shared/components/explicit-indicator/explicit-indicator';
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
import { Text } from '/@/shared/components/text/text';
+import { useDoubleClick } from '/@/shared/hooks/use-double-click';
import { Album, LibraryItem, Song, SongListSort, SortOrder } from '/@/shared/types/domain-types';
-import { ItemListKey, TableColumn } from '/@/shared/types/types';
+import { ItemListKey, Play, TableColumn } from '/@/shared/types/types';
const DEFAULT_ROW_HEIGHT = 300;
@@ -93,6 +94,7 @@ interface RowData {
}
interface TrackRowProps {
+ albumSongs: Song[];
columns: ItemTableListColumnConfig[];
columnWidthPercents: number[];
controls?: ItemControls;
@@ -113,6 +115,7 @@ const textAlignFromAlign = (align: ItemTableListColumnConfig['align']) =>
const TrackRow = memo(
({
+ albumSongs,
columns,
columnWidthPercents,
controls,
@@ -139,6 +142,17 @@ const TrackRow = memo(
const [isRowHovered, setIsRowHovered] = useState(false);
const isSelected = useItemSelectionState(internalState, song.id);
+ const handleDoubleClick = useCallback(
+ (e: React.MouseEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ if (isSongsLoading || albumSongs.length === 0) return;
+ internalState.setSelected([song]);
+ playerContext.addToQueueByData(albumSongs, Play.NOW, song.id);
+ },
+ [albumSongs, internalState, isSongsLoading, playerContext, song],
+ );
+
const handleRowClick = useCallback(
(e: React.MouseEvent) => {
e.preventDefault();
@@ -223,6 +237,11 @@ const TrackRow = memo(
[internalState, song],
);
+ const handleClick = useDoubleClick({
+ onDoubleClick: handleDoubleClick,
+ onSingleClick: handleRowClick,
+ });
+
const handleContextMenu = useCallback(
(event: React.MouseEvent) => {
if (isSongsLoading || !controls?.onMore) return;
@@ -254,7 +273,7 @@ const TrackRow = memo(
[styles.trackRowSizeLarge]: size === 'large',
[styles.trackRowWithHorizontalBorder]: rowIndex > 0,
})}
- onClick={handleRowClick}
+ onClick={handleClick}
onContextMenu={handleContextMenu}
onMouseEnter={() => setIsRowHovered(true)}
onMouseLeave={() => setIsRowHovered(false)}
@@ -653,6 +672,7 @@ const RowContent = memo(
{songs.map((song, rowIndex) => (
{
+export const useIsCurrentSong = (song: QueueSong | Song) => {
const currentSong = usePlayerSong();
const isActive = useMemo(() => {
- return song._uniqueId === currentSong?._uniqueId;
- }, [song._uniqueId, currentSong?._uniqueId]);
+ const queueSong = song as QueueSong;
+
+ if (queueSong._uniqueId != null && queueSong._uniqueId !== '') {
+ return queueSong._uniqueId === currentSong?._uniqueId;
+ }
+
+ return song.id === currentSong?.id;
+ }, [song, currentSong?.id, currentSong?._uniqueId]);
return { isActive };
};