|
|
@@ -1,9 +1,10 @@
|
|
|
import { useDebounce } from '@react-hook/debounce';
|
|
|
-import React, { useCallback, useContext, useEffect, useState } from 'react';
|
|
|
+import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
|
|
|
|
|
import { useArtistsAlbumsAndSongs } from '../../../../hooks/fetch/artists';
|
|
|
-import { Keys, useKeyBinding, useVerticalScrollBindings } from '../../../../hooks/vim';
|
|
|
+import { BoundAction, Key, useVerticalScrollBindings } from '../../../../hooks/vim';
|
|
|
import { Song } from '../../../../types/songs';
|
|
|
+import { scrollThroughItems } from '../../../../utils/delta';
|
|
|
import { LibraryDispatchContext, LibraryStateContext } from '../context';
|
|
|
|
|
|
import * as Styled from './styles';
|
|
|
@@ -17,27 +18,17 @@ type PropsArtist = {
|
|
|
albums?: string[];
|
|
|
songs?: Song[];
|
|
|
active: boolean;
|
|
|
+ parentActive: boolean;
|
|
|
expanded: boolean;
|
|
|
activeAlbum: string | null;
|
|
|
};
|
|
|
|
|
|
-type ArtistKeyBindingsProps = {
|
|
|
- artist: string;
|
|
|
- toggleExpanded: (artist: string) => void;
|
|
|
-};
|
|
|
-
|
|
|
-const ArtistKeyBindings: React.FC<ArtistKeyBindingsProps> = ({ artist, toggleExpanded }) => {
|
|
|
- const toggleMeExpanded = useCallback(() => toggleExpanded(artist), [artist, toggleExpanded]);
|
|
|
- useKeyBinding(Keys.Space, toggleMeExpanded);
|
|
|
-
|
|
|
- return null;
|
|
|
-};
|
|
|
-
|
|
|
const Artist: React.FC<PropsArtist> = ({
|
|
|
artist,
|
|
|
albums,
|
|
|
songs,
|
|
|
active,
|
|
|
+ parentActive,
|
|
|
expanded,
|
|
|
activeAlbum,
|
|
|
}) => {
|
|
|
@@ -45,13 +36,19 @@ const Artist: React.FC<PropsArtist> = ({
|
|
|
|
|
|
useEffect(() => {
|
|
|
if (active) {
|
|
|
- dispatch((last) => ({ ...last, visibleSongs: songs ?? [] }));
|
|
|
+ dispatch((last) => ({
|
|
|
+ ...last,
|
|
|
+ visibleSongs: songs ?? [],
|
|
|
+ activeSong: songs?.[0]?.id ?? null,
|
|
|
+ }));
|
|
|
}
|
|
|
}, [dispatch, songs, active]);
|
|
|
|
|
|
return (
|
|
|
<Styled.ArtistRow key={artist}>
|
|
|
- <Styled.ArtistTitle active={active}>{artist || 'Unknown Artist'}</Styled.ArtistTitle>
|
|
|
+ <Styled.ArtistTitle active={active} parentActive={parentActive}>
|
|
|
+ {artist || 'Unknown Artist'}
|
|
|
+ </Styled.ArtistTitle>
|
|
|
{expanded && (
|
|
|
<Styled.ArtistAlbums>
|
|
|
{albums?.map((album) => (
|
|
|
@@ -65,15 +62,18 @@ const Artist: React.FC<PropsArtist> = ({
|
|
|
);
|
|
|
};
|
|
|
|
|
|
-export const Artists: React.FC<Props> = () => {
|
|
|
+export const Artists: React.FC<Props> = ({ active }) => {
|
|
|
const dispatch = useContext(LibraryDispatchContext);
|
|
|
const { artists, activeArtist, activeAlbum } = useContext(LibraryStateContext);
|
|
|
|
|
|
const [expandedArtists, setExpandedArtists] = useState<Record<string, boolean>>({});
|
|
|
|
|
|
- const toggleArtistExpanded = useCallback(
|
|
|
- (artist: string) => setExpandedArtists((last) => ({ ...last, [artist]: !last[artist] })),
|
|
|
- [],
|
|
|
+ const toggleActiveArtistExpanded = useCallback(
|
|
|
+ () =>
|
|
|
+ setExpandedArtists((last) =>
|
|
|
+ activeArtist ? { ...last, [activeArtist]: !last[activeArtist] } : last,
|
|
|
+ ),
|
|
|
+ [activeArtist],
|
|
|
);
|
|
|
|
|
|
const [debouncedActiveArtist, setDebouncedActiveArtist] = useDebounce(activeArtist, 100);
|
|
|
@@ -102,34 +102,25 @@ export const Artists: React.FC<Props> = () => {
|
|
|
(delta: number) =>
|
|
|
dispatch((last) => ({
|
|
|
...last,
|
|
|
- activeArtist:
|
|
|
- last.artists[
|
|
|
- Math.max(
|
|
|
- 0,
|
|
|
- Math.min(
|
|
|
- last.artists.length - 1,
|
|
|
- last.artists.findIndex((compare) => compare === last.activeArtist) + delta,
|
|
|
- ),
|
|
|
- )
|
|
|
- ],
|
|
|
+ activeSong: null,
|
|
|
+ activeArtist: scrollThroughItems(
|
|
|
+ last.artists,
|
|
|
+ (compare) => compare === last.activeArtist,
|
|
|
+ delta,
|
|
|
+ ),
|
|
|
})),
|
|
|
[dispatch],
|
|
|
);
|
|
|
|
|
|
- const scrollDown = useCallback(() => scrollDelta(1), [scrollDelta]);
|
|
|
- const scrollUp = useCallback(() => scrollDelta(-1), [scrollDelta]);
|
|
|
+ const boundActions = useMemo<BoundAction[]>(
|
|
|
+ () => [{ binding: Key.Toggle, action: toggleActiveArtistExpanded }],
|
|
|
+ [toggleActiveArtistExpanded],
|
|
|
+ );
|
|
|
|
|
|
- useVerticalScrollBindings(scrollDown, scrollUp);
|
|
|
+ useVerticalScrollBindings(active, scrollDelta, boundActions);
|
|
|
|
|
|
return (
|
|
|
<Styled.Container>
|
|
|
- {activeArtist && (
|
|
|
- <ArtistKeyBindings
|
|
|
- key={activeArtist}
|
|
|
- artist={activeArtist}
|
|
|
- toggleExpanded={toggleArtistExpanded}
|
|
|
- />
|
|
|
- )}
|
|
|
{artists.map((artist) => (
|
|
|
<Artist
|
|
|
key={artist}
|
|
|
@@ -137,6 +128,7 @@ export const Artists: React.FC<Props> = () => {
|
|
|
albums={albumsCache[artist]}
|
|
|
songs={songsCache[artist]}
|
|
|
active={artist === activeArtist}
|
|
|
+ parentActive={active}
|
|
|
expanded={!!expandedArtists[artist]}
|
|
|
activeAlbum={activeAlbum}
|
|
|
/>
|