import React, { CSSProperties, useContext, useMemo, useRef } from 'react'; import AutoSizer from 'react-virtualized-auto-sizer'; import { FixedSizeList as List } from 'react-window'; import { namedMemo } from '../../../../utils/component'; import { CmusUIStateContext } from '../reducer'; import { NoWrapFill } from '../styled/layout'; import { SpinnerOrEmpty } from '../styled/spinner'; import { getArtistAlbumScrollIndex, lineHeight, useAutoJumpyScroll } from '../utils/scroll'; import * as Styled from './artists.styles'; export type Props = { active: boolean; currentArtist: string | null; }; type ArtistData = { id: string; artist: string; loading: boolean; active: boolean; parentActive: boolean; highlight: boolean; }; type AlbumData = { id: string; album: string; active: boolean; parentActive: boolean; }; type RowData = ArtistData | AlbumData; const isArtist = (data: RowData): data is ArtistData => Reflect.has(data, 'artist'); const itemKey = (index: number, data: RowData[]): string => data[index].id; const Artist = namedMemo<{ row: ArtistData; style: CSSProperties }>( 'Artist', ({ row: { artist, loading, active, parentActive, highlight }, style }) => ( {artist || 'Unknown Artist'} ), ); const Album = namedMemo<{ row: AlbumData; style: CSSProperties }>( 'Album', ({ row: { album, active, parentActive }, style }) => ( {album || 'Unknown Album'} ), ); const Row = namedMemo<{ index: number; data: RowData[]; style: CSSProperties }>( 'ArtistListRow', ({ index, data, style }) => { const row = data[index]; if (isArtist(row)) { return ; } return ; }, ); export const Artists: React.FC = ({ active: parentActive, currentArtist }) => { const { artists, artistAlbums, library: { activeArtist, activeAlbum, expandedArtists }, } = useContext(CmusUIStateContext); const itemData = useMemo( () => artists.reduce((last, artist) => { const expanded = expandedArtists.includes(artist); const artistRow: ArtistData = { id: artist, artist, loading: !(artist in artistAlbums) && expandedArtists.includes(artist), active: activeArtist === artist && activeAlbum === null, parentActive, highlight: currentArtist === artist, }; if (!expanded) { return [...last, artistRow]; } return [ ...last, artistRow, ...(artistAlbums[artist] ?? []).map((album) => ({ id: `${artist}-${album}`, album, active: activeArtist === artist && activeAlbum === album, parentActive: parentActive && activeArtist === artist && activeAlbum === album, })), ]; }, []), [ parentActive, artists, artistAlbums, activeArtist, activeAlbum, expandedArtists, currentArtist, ], ); const windowRef = useRef(null); const scrollIndex = getArtistAlbumScrollIndex( artists, artistAlbums, activeArtist, activeAlbum, expandedArtists, ); useAutoJumpyScroll(windowRef, scrollIndex); return ( {({ height, width }): React.ReactElement => ( {Row} )} ); };