Browse Source
- add artist view/route - add album view/route - add links from other components to artist/album routes - separate viewtype settings by type - add list types for albumsmaster
jeffvli
3 years ago
20 changed files with 930 additions and 160 deletions
@ -0,0 +1,161 @@ |
|||
import React, { useState } from 'react'; |
|||
import settings from 'electron-settings'; |
|||
import { ButtonToolbar, Tag } from 'rsuite'; |
|||
import { useQuery } from 'react-query'; |
|||
import { useParams, useHistory } from 'react-router-dom'; |
|||
import { |
|||
DeleteButton, |
|||
EditButton, |
|||
PlayAppendButton, |
|||
PlayButton, |
|||
PlayShuffleAppendButton, |
|||
PlayShuffleButton, |
|||
} from '../shared/ToolbarButtons'; |
|||
import { getAlbum } from '../../api/api'; |
|||
import { useAppDispatch } from '../../redux/hooks'; |
|||
import { fixPlayer2Index, setPlayQueue } from '../../redux/playQueueSlice'; |
|||
import { |
|||
toggleSelected, |
|||
setRangeSelected, |
|||
toggleRangeSelected, |
|||
setSelected, |
|||
clearSelected, |
|||
} from '../../redux/multiSelectSlice'; |
|||
import useSearchQuery from '../../hooks/useSearchQuery'; |
|||
import GenericPage from '../layout/GenericPage'; |
|||
import ListViewType from '../viewtypes/ListViewType'; |
|||
import Loader from '../loader/Loader'; |
|||
import GenericPageHeader from '../layout/GenericPageHeader'; |
|||
import { TagLink } from './styled'; |
|||
|
|||
interface AlbumParams { |
|||
id: string; |
|||
} |
|||
|
|||
const AlbumView = () => { |
|||
const dispatch = useAppDispatch(); |
|||
const history = useHistory(); |
|||
const { id } = useParams<AlbumParams>(); |
|||
const { isLoading, isError, data, error }: any = useQuery(['album', id], () => |
|||
getAlbum(id) |
|||
); |
|||
const [searchQuery, setSearchQuery] = useState(''); |
|||
const filteredData = useSearchQuery(searchQuery, data?.song, [ |
|||
'title', |
|||
'artist', |
|||
'album', |
|||
'genre', |
|||
]); |
|||
|
|||
let timeout: any = null; |
|||
const handleRowClick = (e: any, rowData: any) => { |
|||
if (timeout === null) { |
|||
timeout = window.setTimeout(() => { |
|||
timeout = null; |
|||
|
|||
if (e.ctrlKey) { |
|||
dispatch(toggleSelected(rowData)); |
|||
} else if (e.shiftKey) { |
|||
dispatch(setRangeSelected(rowData)); |
|||
|
|||
dispatch(toggleRangeSelected(data.song)); |
|||
} else { |
|||
dispatch(setSelected(rowData)); |
|||
} |
|||
}, 300); |
|||
} |
|||
}; |
|||
|
|||
const handleRowDoubleClick = (e: any) => { |
|||
window.clearTimeout(timeout); |
|||
timeout = null; |
|||
const newPlayQueue = data.song.slice([e.index], data.song.length); |
|||
|
|||
dispatch(clearSelected()); |
|||
dispatch(setPlayQueue(newPlayQueue)); |
|||
dispatch(fixPlayer2Index()); |
|||
}; |
|||
|
|||
if (isLoading) { |
|||
return <Loader />; |
|||
} |
|||
|
|||
if (isError) { |
|||
return <span>Error: {error.message}</span>; |
|||
} |
|||
|
|||
return ( |
|||
<GenericPage |
|||
header={ |
|||
<GenericPageHeader |
|||
image={data.image} |
|||
title={data.name} |
|||
subtitle={ |
|||
<div> |
|||
<div |
|||
style={{ |
|||
overflow: 'hidden', |
|||
textOverflow: 'ellipsis', |
|||
whiteSpace: 'nowrap', |
|||
}} |
|||
> |
|||
{data.artist && ( |
|||
<Tag> |
|||
<TagLink |
|||
onClick={() => |
|||
history.push(`/library/artist/${data.artistId}`) |
|||
} |
|||
> |
|||
Artist: {data.artist} |
|||
</TagLink> |
|||
</Tag> |
|||
)} |
|||
{data.year && ( |
|||
<Tag> |
|||
<TagLink>Year: {data.year}</TagLink> |
|||
</Tag> |
|||
)} |
|||
{data.genre && ( |
|||
<Tag> |
|||
<TagLink>Genre: {data.genre}</TagLink> |
|||
</Tag> |
|||
)} |
|||
</div> |
|||
<div style={{ marginTop: '10px' }}> |
|||
<ButtonToolbar> |
|||
<PlayButton appearance="primary" size="lg" circle /> |
|||
<PlayShuffleButton /> |
|||
<PlayAppendButton /> |
|||
<PlayShuffleAppendButton /> |
|||
<EditButton /> |
|||
<DeleteButton /> |
|||
</ButtonToolbar> |
|||
</div> |
|||
</div> |
|||
} |
|||
searchQuery={searchQuery} |
|||
handleSearch={(e: any) => setSearchQuery(e)} |
|||
clearSearchQuery={() => setSearchQuery('')} |
|||
showSearchBar |
|||
/> |
|||
} |
|||
> |
|||
<ListViewType |
|||
data={searchQuery !== '' ? filteredData : data.song} |
|||
tableColumns={settings.getSync('songListColumns')} |
|||
handleRowClick={handleRowClick} |
|||
handleRowDoubleClick={handleRowDoubleClick} |
|||
tableHeight={700} |
|||
virtualized |
|||
rowHeight={Number(settings.getSync('songListRowHeight'))} |
|||
fontSize={Number(settings.getSync('songListFontSize'))} |
|||
cacheImages={{ |
|||
enabled: settings.getSync('cacheImages'), |
|||
cacheType: 'album', |
|||
}} |
|||
/> |
|||
</GenericPage> |
|||
); |
|||
}; |
|||
|
|||
export default AlbumView; |
@ -0,0 +1,212 @@ |
|||
import React, { useState } from 'react'; |
|||
import settings from 'electron-settings'; |
|||
import { ButtonToolbar, Tag, Whisper, Button, Popover, TagGroup } from 'rsuite'; |
|||
import { useQuery } from 'react-query'; |
|||
import { useParams, useHistory } from 'react-router-dom'; |
|||
import { |
|||
PlayAppendButton, |
|||
PlayButton, |
|||
PlayShuffleAppendButton, |
|||
PlayShuffleButton, |
|||
EditButton, |
|||
} from '../shared/ToolbarButtons'; |
|||
import { getArtist, getArtistInfo } from '../../api/api'; |
|||
import { useAppDispatch } from '../../redux/hooks'; |
|||
import { fixPlayer2Index, setPlayQueue } from '../../redux/playQueueSlice'; |
|||
import { |
|||
toggleSelected, |
|||
setRangeSelected, |
|||
toggleRangeSelected, |
|||
setSelected, |
|||
clearSelected, |
|||
} from '../../redux/multiSelectSlice'; |
|||
import useSearchQuery from '../../hooks/useSearchQuery'; |
|||
import GenericPage from '../layout/GenericPage'; |
|||
import ListViewType from '../viewtypes/ListViewType'; |
|||
import GridViewType from '../viewtypes/GridViewType'; |
|||
import Loader from '../loader/Loader'; |
|||
import GenericPageHeader from '../layout/GenericPageHeader'; |
|||
import CustomTooltip from '../shared/CustomTooltip'; |
|||
import { TagLink } from './styled'; |
|||
|
|||
interface ArtistParams { |
|||
id: string; |
|||
} |
|||
|
|||
const ArtistView = () => { |
|||
const dispatch = useAppDispatch(); |
|||
const history = useHistory(); |
|||
const [viewType, setViewType] = useState( |
|||
settings.getSync('albumViewType') || 'list' |
|||
); |
|||
const { id } = useParams<ArtistParams>(); |
|||
const { isLoading, isError, data, error }: any = useQuery( |
|||
['artist', id], |
|||
() => getArtist(id) |
|||
); |
|||
const { |
|||
isLoading: isLoadingAI, |
|||
isError: isErrorAI, |
|||
data: artistInfo, |
|||
error: errorAI, |
|||
}: any = useQuery(['artistInfo', id], () => getArtistInfo(id, 8)); |
|||
|
|||
const [searchQuery, setSearchQuery] = useState(''); |
|||
const filteredData = useSearchQuery(searchQuery, data?.song, [ |
|||
'title', |
|||
'artist', |
|||
'album', |
|||
'genre', |
|||
]); |
|||
|
|||
let timeout: any = null; |
|||
const handleRowClick = (e: any, rowData: any) => { |
|||
if (timeout === null) { |
|||
timeout = window.setTimeout(() => { |
|||
timeout = null; |
|||
|
|||
if (e.ctrlKey) { |
|||
dispatch(toggleSelected(rowData)); |
|||
} else if (e.shiftKey) { |
|||
dispatch(setRangeSelected(rowData)); |
|||
|
|||
dispatch(toggleRangeSelected(data.album)); |
|||
} else { |
|||
dispatch(setSelected(rowData)); |
|||
} |
|||
}, 300); |
|||
} |
|||
}; |
|||
|
|||
const handleRowDoubleClick = (e: any) => { |
|||
window.clearTimeout(timeout); |
|||
timeout = null; |
|||
const newPlayQueue = data.album.slice([e.index], data.album.length); |
|||
|
|||
dispatch(clearSelected()); |
|||
dispatch(setPlayQueue(newPlayQueue)); |
|||
dispatch(fixPlayer2Index()); |
|||
}; |
|||
|
|||
if (isLoading || isLoadingAI) { |
|||
return <Loader />; |
|||
} |
|||
|
|||
if (isError || isErrorAI) { |
|||
return ( |
|||
<span> |
|||
Error: {error.message} {errorAI.message} |
|||
</span> |
|||
); |
|||
} |
|||
|
|||
return ( |
|||
<GenericPage |
|||
header={ |
|||
<GenericPageHeader |
|||
image={data.image} |
|||
imageHeight={145} |
|||
title={data.name} |
|||
subtitle={ |
|||
<> |
|||
<CustomTooltip |
|||
text={artistInfo.biography |
|||
?.replace(/<[^>]*>/, '') |
|||
.replace('Read more on Last.fm</a>', '')} |
|||
placement="bottomStart" |
|||
> |
|||
<span> |
|||
{artistInfo.biography |
|||
?.replace(/<[^>]*>/, '') |
|||
.replace('Read more on Last.fm</a>', '') !== '' |
|||
? `${artistInfo.biography |
|||
?.replace(/<[^>]*>/, '') |
|||
.replace('Read more on Last.fm</a>', '')}` |
|||
: 'No artist biography found'} |
|||
</span> |
|||
</CustomTooltip> |
|||
<div style={{ marginTop: '10px' }}> |
|||
<ButtonToolbar> |
|||
<PlayButton appearance="primary" size="lg" circle /> |
|||
<PlayShuffleButton /> |
|||
<PlayAppendButton /> |
|||
<PlayShuffleAppendButton /> |
|||
<EditButton style={{ marginRight: '10px' }} /> |
|||
<Whisper |
|||
placement="bottomStart" |
|||
trigger="click" |
|||
speaker={ |
|||
<Popover style={{ width: '400px' }}> |
|||
<TagGroup> |
|||
{artistInfo.similarArtist?.map((artist: any) => ( |
|||
<Tag key={artist.id}> |
|||
<TagLink |
|||
onClick={() => |
|||
history.push(`/library/artist/${artist.id}`) |
|||
} |
|||
> |
|||
{artist.name} |
|||
</TagLink> |
|||
</Tag> |
|||
))} |
|||
</TagGroup> |
|||
</Popover> |
|||
} |
|||
> |
|||
<Button>Related Artists</Button> |
|||
</Whisper> |
|||
</ButtonToolbar> |
|||
</div> |
|||
</> |
|||
} |
|||
searchQuery={searchQuery} |
|||
handleSearch={(e: any) => setSearchQuery(e)} |
|||
clearSearchQuery={() => setSearchQuery('')} |
|||
showSearchBar |
|||
showViewTypeButtons |
|||
viewTypeSetting="album" |
|||
handleListClick={() => setViewType('list')} |
|||
handleGridClick={() => setViewType('grid')} |
|||
/> |
|||
} |
|||
> |
|||
<> |
|||
{viewType === 'list' && ( |
|||
<ListViewType |
|||
data={searchQuery !== '' ? filteredData : data.album} |
|||
tableColumns={settings.getSync('albumListColumns')} |
|||
handleRowClick={handleRowClick} |
|||
handleRowDoubleClick={handleRowDoubleClick} |
|||
virtualized |
|||
rowHeight={Number(settings.getSync('albumListRowHeight'))} |
|||
fontSize={Number(settings.getSync('albumListFontSize'))} |
|||
cacheImages={{ |
|||
enabled: settings.getSync('cacheImages'), |
|||
cacheType: 'album', |
|||
}} |
|||
/> |
|||
)} |
|||
|
|||
{viewType === 'grid' && ( |
|||
<GridViewType |
|||
data={searchQuery === '' ? data.album : filteredData} |
|||
cardTitle={{ |
|||
prefix: '/library/album', |
|||
property: 'name', |
|||
urlProperty: 'albumId', |
|||
}} |
|||
cardSubtitle={{ |
|||
property: 'songCount', |
|||
unit: ' tracks', |
|||
}} |
|||
playClick={{ type: 'album', idProperty: 'id' }} |
|||
size="150px" |
|||
cacheType="album" |
|||
/> |
|||
)} |
|||
</> |
|||
</GenericPage> |
|||
); |
|||
}; |
|||
|
|||
export default ArtistView; |
@ -0,0 +1,11 @@ |
|||
import styled from 'styled-components'; |
|||
|
|||
export const TagLink = styled.a` |
|||
color: #e9ebf0; |
|||
cursor: pointer; |
|||
|
|||
&:hover { |
|||
color: #e9ebf0; |
|||
text-decoration: none; |
|||
} |
|||
`;
|
Loading…
Reference in new issue