Browse Source

Support types for multiple artists, genres

master
jeffvli 3 years ago
committed by Jeff
parent
commit
e52ca9bc1e
  1. 16
      src/api/api.ts
  2. 16
      src/components/dashboard/Dashboard.tsx
  3. 2
      src/components/layout/Titlebar.tsx
  4. 57
      src/components/library/AlbumView.tsx
  5. 11
      src/components/player/PlayerBar.tsx
  6. 4
      src/components/search/SearchView.tsx
  7. 4
      src/components/starred/StarredView.tsx
  8. 114
      src/components/viewtypes/ListViewTable.tsx
  9. 10
      src/hooks/useSearchQuery.ts
  10. 2
      src/redux/miscSlice.ts
  11. 30
      src/types.ts

16
src/api/api.ts

@ -174,11 +174,13 @@ const normalizeSong = (item: any) => {
title: item.title, title: item.title,
album: item.album, album: item.album,
albumId: item.albumId, albumId: item.albumId,
artist: item.artist, albumArtist: item.artist,
artistId: item.artistId, albumArtistId: item.artistId,
artist: [{ id: item.artistId, title: item.artist }],
track: item.track, track: item.track,
year: item.year, year: item.year,
genre: item.genre, genre: [{ id: item.genre, title: item.genre }],
albumGenre: item.genre,
size: item.size, size: item.size,
contentType: item.contentType, contentType: item.contentType,
suffix: item.suffix, suffix: item.suffix,
@ -201,13 +203,15 @@ const normalizeAlbum = (item: any) => {
id: item.id, id: item.id,
title: item.name, title: item.name,
albumId: item.id, albumId: item.id,
artist: item.artist, albumArtist: item.artist,
artistId: item.artistId, albumArtistId: item.artistId,
artist: [{ id: item.artistId, title: item.artist }],
songCount: item.songCount, songCount: item.songCount,
duration: item.duration, duration: item.duration,
created: item.created, created: item.created,
year: item.year, year: item.year,
genre: item.genre, genre: [{ id: item.genre, title: item.genre }],
albumGenre: item.genre,
image: getCoverArtUrl(item, legacyAuth, 350), image: getCoverArtUrl(item, legacyAuth, 350),
isDir: false, isDir: false,
starred: item.starred, starred: item.starred,

16
src/components/dashboard/Dashboard.tsx

@ -183,8 +183,8 @@ const Dashboard = () => {
}} }}
cardSubtitle={{ cardSubtitle={{
prefix: '/library/artist', prefix: '/library/artist',
property: 'artist', property: 'albumArtist',
urlProperty: 'artistId', urlProperty: 'albumArtistId',
}} }}
cardSize={config.lookAndFeel.gridView.cardSize} cardSize={config.lookAndFeel.gridView.cardSize}
onClickTitle={() => { onClickTitle={() => {
@ -207,8 +207,8 @@ const Dashboard = () => {
}} }}
cardSubtitle={{ cardSubtitle={{
prefix: '/library/artist', prefix: '/library/artist',
property: 'artist', property: 'albumArtist',
urlProperty: 'artistId', urlProperty: 'albumArtistId',
}} }}
cardSize={config.lookAndFeel.gridView.cardSize} cardSize={config.lookAndFeel.gridView.cardSize}
onClickTitle={() => { onClickTitle={() => {
@ -231,8 +231,8 @@ const Dashboard = () => {
}} }}
cardSubtitle={{ cardSubtitle={{
prefix: '/library/artist', prefix: '/library/artist',
property: 'artist', property: 'albumArtist',
urlProperty: 'artistId', urlProperty: 'albumArtistId',
}} }}
cardSize={config.lookAndFeel.gridView.cardSize} cardSize={config.lookAndFeel.gridView.cardSize}
onClickTitle={() => { onClickTitle={() => {
@ -255,8 +255,8 @@ const Dashboard = () => {
}} }}
cardSubtitle={{ cardSubtitle={{
prefix: '/library/artist', prefix: '/library/artist',
property: 'artist', property: 'albumArtist',
urlProperty: 'artistId', urlProperty: 'albumArtistId',
}} }}
cardSize={config.lookAndFeel.gridView.cardSize} cardSize={config.lookAndFeel.gridView.cardSize}
onClickTitle={() => { onClickTitle={() => {

2
src/components/layout/Titlebar.tsx

@ -29,7 +29,7 @@ const Titlebar = ({ font }: any) => {
const songTitle = playQueue[currentEntryList][playQueue.currentIndex]?.title const songTitle = playQueue[currentEntryList][playQueue.currentIndex]?.title
? `(${playQueue.currentIndex + 1} / ${playQueue[currentEntryList].length}) ~ ${ ? `(${playQueue.currentIndex + 1} / ${playQueue[currentEntryList].length}) ~ ${
playQueue[currentEntryList][playQueue.currentIndex]?.title playQueue[currentEntryList][playQueue.currentIndex]?.title
} ~ ${playQueue[currentEntryList][playQueue.currentIndex]?.artist} ` } ~ ${playQueue[currentEntryList][playQueue.currentIndex]?.artist[0]?.title} `
: 'Sonixd'; : 'Sonixd';
setTitle(`${playStatus} ${songTitle}`); setTitle(`${playStatus} ${songTitle}`);

57
src/components/library/AlbumView.tsx

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import _ from 'lodash'; import _ from 'lodash';
import { nanoid } from 'nanoid/non-secure';
import { clipboard, shell } from 'electron'; import { clipboard, shell } from 'electron';
import settings from 'electron-settings'; import settings from 'electron-settings';
import { ButtonToolbar, Whisper } from 'rsuite'; import { ButtonToolbar, Whisper } from 'rsuite';
@ -51,7 +52,7 @@ import {
PageHeaderSubtitleDataLine, PageHeaderSubtitleDataLine,
} from '../layout/styled'; } from '../layout/styled';
import { apiController } from '../../api/controller'; import { apiController } from '../../api/controller';
import { Server } from '../../types'; import { Artist, Genre, Server } from '../../types';
interface AlbumParams { interface AlbumParams {
id: string; id: string;
@ -290,18 +291,20 @@ const AlbumView = ({ ...rest }: any) => {
Added {formatDate(data.created)} Added {formatDate(data.created)}
</PageHeaderSubtitleDataLine> </PageHeaderSubtitleDataLine>
<PageHeaderSubtitleDataLine> <PageHeaderSubtitleDataLine>
{data.artist && ( {data.artist.map((d: Artist) => {
return (
<StyledTagLink <StyledTagLink
key={nanoid()}
tabIndex={0} tabIndex={0}
tooltip={data.artist} tooltip={d.title}
onClick={() => { onClick={() => {
if (!rest.isModal) { if (!rest.isModal) {
history.push(`/library/artist/${data.artistId}`); history.push(`/library/artist/${d.id}`);
} else { } else {
dispatch( dispatch(
addModalPage({ addModalPage({
pageType: 'artist', pageType: 'artist',
id: data.artistId, id: d.id,
}) })
); );
} }
@ -310,64 +313,52 @@ const AlbumView = ({ ...rest }: any) => {
if (e.key === ' ' || e.key === 'Enter') { if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault(); e.preventDefault();
if (!rest.isModal) { if (!rest.isModal) {
history.push(`/library/artist/${data.artistId}`); history.push(`/library/artist/${d.id}`);
} else { } else {
dispatch( dispatch(
addModalPage({ addModalPage({
pageType: 'artist', pageType: 'artist',
id: data.artistId, id: d.id,
}) })
); );
} }
} }
}} }}
> >
{data.artist} {d.title}
</StyledTagLink> </StyledTagLink>
)} );
{data.genre && ( })}
<> {data.genre.map((d: Genre) => {
return (
<StyledTagLink <StyledTagLink
key={nanoid()}
tabIndex={0} tabIndex={0}
tooltip={data.genre} tooltip={d.title}
onClick={() => { onClick={() => {
if (!rest.isModal) { if (!rest.isModal) {
dispatch(setActive({ ...album.active, filter: data.genre })); dispatch(setActive({ ...album.active, filter: d.id }));
setTimeout(() => { setTimeout(() => {
history.push(`/library/album?sortType=${data.genre}`); history.push(`/library/album?sortType=${d.id}`);
}, 50); }, 50);
} else {
dispatch(
addModalPage({
pageType: 'artist',
id: data.artistId,
})
);
} }
}} }}
onKeyDown={(e: any) => { onKeyDown={(e: any) => {
if (e.key === ' ' || e.key === 'Enter') { if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault(); e.preventDefault();
if (!rest.isModal) { if (!rest.isModal) {
dispatch(setActive({ ...album.active, filter: data.genre })); dispatch(setActive({ ...album.active, filter: d.id }));
setTimeout(() => { setTimeout(() => {
history.push(`/library/album?sortType=${data.genre}`); history.push(`/library/album?sortType=${d.id}`);
}, 50); }, 50);
} else {
dispatch(
addModalPage({
pageType: 'artist',
id: data.artistId,
})
);
} }
} }
}} }}
> >
{data.genre} {d.title}
</StyledTagLink> </StyledTagLink>
</> );
)} })}
</PageHeaderSubtitleDataLine> </PageHeaderSubtitleDataLine>
<div style={{ marginTop: '10px' }}> <div style={{ marginTop: '10px' }}>
<ButtonToolbar> <ButtonToolbar>

11
src/components/player/PlayerBar.tsx

@ -439,7 +439,7 @@ const PlayerBar = () => {
enterable enterable
placement="topStart" placement="topStart"
text={ text={
playQueue[currentEntryList][playQueue.currentIndex]?.artist || playQueue[currentEntryList][playQueue.currentIndex]?.artist[0]?.title ||
'Unknown artist' 'Unknown artist'
} }
> >
@ -454,16 +454,19 @@ const PlayerBar = () => {
tabIndex={0} tabIndex={0}
subtitle="true" subtitle="true"
onClick={() => { onClick={() => {
if (playQueue[currentEntryList][playQueue.currentIndex]?.artistId) { if (
playQueue[currentEntryList][playQueue.currentIndex]?.albumArtist
) {
history.push( history.push(
`/library/artist/${ `/library/artist/${
playQueue[currentEntryList][playQueue.currentIndex]?.artistId playQueue[currentEntryList][playQueue.currentIndex]
?.albumArtistId
}` }`
); );
} }
}} }}
> >
{playQueue[currentEntryList][playQueue.currentIndex]?.artist || {playQueue[currentEntryList][playQueue.currentIndex]?.albumArtist ||
'Unknown artist'} 'Unknown artist'}
</LinkButton> </LinkButton>
</span> </span>

4
src/components/search/SearchView.tsx

@ -218,8 +218,8 @@ const SearchView = () => {
}} }}
cardSubtitle={{ cardSubtitle={{
prefix: '/library/artist', prefix: '/library/artist',
property: 'artist', property: 'albumArtist',
urlProperty: 'artistId', urlProperty: 'albumArtistId',
unit: '', unit: '',
}} }}
cardSize={config.lookAndFeel.gridView.cardSize} cardSize={config.lookAndFeel.gridView.cardSize}

4
src/components/starred/StarredView.tsx

@ -252,8 +252,8 @@ const StarredView = () => {
}} }}
cardSubtitle={{ cardSubtitle={{
prefix: 'artist', prefix: 'artist',
property: 'artist', property: 'albumArtist',
urlProperty: 'artistId', urlProperty: 'albumArtistId',
unit: '', unit: '',
}} }}
playClick={{ type: 'album', idProperty: 'id' }} playClick={{ type: 'album', idProperty: 'id' }}

114
src/components/viewtypes/ListViewTable.tsx

@ -247,9 +247,15 @@ const ListViewTable = ({
if (sortColumn && sortType) { if (sortColumn && sortType) {
// Since the column title(id) won't always match the actual column dataKey, we need to match it // Since the column title(id) won't always match the actual column dataKey, we need to match it
const normalizedSortColumn = columns.find((c: any) => c.id === sortColumn); const actualSortColumn = columns.find((c: any) => c.id === sortColumn);
const sortColumnDataKey = const sortColumnDataKey =
normalizedSortColumn.dataKey === 'combinedtitle' ? 'title' : normalizedSortColumn.dataKey; actualSortColumn.dataKey === 'combinedtitle'
? 'title'
: actualSortColumn.dataKey === 'artist'
? 'albumArtist'
: actualSortColumn.dataKey === 'genre'
? 'albumGenre'
: actualSortColumn.dataKey;
const sortData = _.orderBy( const sortData = _.orderBy(
data, data,
@ -274,7 +280,13 @@ const ListViewTable = ({
if (playQueue.sortColumn && playQueue.sortType) { if (playQueue.sortColumn && playQueue.sortType) {
const actualSortColumn = columns.find((c: any) => c.id === playQueue.sortColumn); const actualSortColumn = columns.find((c: any) => c.id === playQueue.sortColumn);
const sortColumnDataKey = const sortColumnDataKey =
actualSortColumn.dataKey === 'combinedtitle' ? 'title' : actualSortColumn.dataKey; actualSortColumn.dataKey === 'combinedtitle'
? 'title'
: actualSortColumn.dataKey === 'artist'
? 'albumArtist'
: actualSortColumn.dataKey === 'genre'
? 'albumGenre'
: actualSortColumn.dataKey;
dispatch( dispatch(
sortPlayQueue({ sortPlayQueue({
@ -298,7 +310,13 @@ const ListViewTable = ({
if (sortColumn && sortType) { if (sortColumn && sortType) {
const actualSortColumn = columns.find((c: any) => c.id === sortColumn); const actualSortColumn = columns.find((c: any) => c.id === sortColumn);
const sortColumnDataKey = const sortColumnDataKey =
actualSortColumn.dataKey === 'combinedtitle' ? 'title' : actualSortColumn.dataKey; actualSortColumn.dataKey === 'combinedtitle'
? 'title'
: actualSortColumn.dataKey === 'artist'
? 'albumArtist'
: actualSortColumn.dataKey === 'genre'
? 'albumGenre'
: actualSortColumn.dataKey;
dispatch( dispatch(
sortPlaylist({ sortPlaylist({
@ -726,19 +744,19 @@ const ListViewTable = ({
width: '100%', width: '100%',
}} }}
> >
<CustomTooltip text={rowData.artist}> <CustomTooltip text={rowData.albumArtist}>
<RsuiteLinkButton <RsuiteLinkButton
subtitle="true" subtitle="true"
appearance="link" appearance="link"
onClick={(e: any) => { onClick={(e: any) => {
if (!e.ctrlKey && !e.shiftKey) { if (!e.ctrlKey && !e.shiftKey) {
if (rowData.artistId && !isModal) { if (rowData.albumArtistId && !isModal) {
history.push(`/library/artist/${rowData.artistId}`); history.push(`/library/artist/${rowData.albumArtistId}`);
} else if (rowData.artistId && isModal) { } else if (rowData.albumArtistId && isModal) {
dispatch( dispatch(
addModalPage({ addModalPage({
pageType: 'artist', pageType: 'artist',
id: rowData.artistId, id: rowData.albumArtistId,
}) })
); );
} }
@ -757,7 +775,7 @@ const ListViewTable = ({
: 'false' : 'false'
} }
> >
{rowData.artist} {rowData.albumArtist}
</RsuiteLinkButton> </RsuiteLinkButton>
</CustomTooltip> </CustomTooltip>
</span> </span>
@ -881,38 +899,40 @@ const ListViewTable = ({
: undefined, : undefined,
}} }}
> >
{column.dataKey.match(/album|artist|genre/) ? ( {column.dataKey.match(/artist|genre/) ? (
<CustomTooltip text={rowData[column.dataKey]}> <>
{rowData[column.dataKey] && (
<CustomTooltip text={rowData[column.dataKey][0]?.title}>
<RsuiteLinkButton <RsuiteLinkButton
appearance="link" appearance="link"
onClick={(e: any) => { onClick={(e: any) => {
if (!e.ctrlKey && !e.shiftKey) { if (!e.ctrlKey && !e.shiftKey) {
if (column.dataKey === 'album') { if (column.dataKey === 'artist') {
if (rowData.albumId && !isModal) { if (rowData[column.dataKey][0]?.id && !isModal) {
history.push(`/library/album/${rowData.albumId}`); history.push(
} else if (rowData.albumId && isModal) { `/library/artist/${rowData[column.dataKey][0]?.id}`
dispatch(
addModalPage({
pageType: 'album',
id: rowData.albumId,
})
); );
} } else if (rowData[0]?.id && isModal) {
} else if (column.dataKey === 'artist') {
if (rowData.artistId && !isModal) {
history.push(`/library/artist/${rowData.artistId}`);
} else if (rowData.artistId && isModal) {
dispatch( dispatch(
addModalPage({ addModalPage({
pageType: 'artist', pageType: 'artist',
id: rowData.artistId, id: rowData[0]?.id,
}) })
); );
} }
} else if (column.dataKey === 'genre') { } else if (column.dataKey === 'genre') {
dispatch(setActive({ ...album.active, filter: rowData.genre })); dispatch(
setActive({
...album.active,
filter: rowData[column.dataKey][0]?.id,
})
);
setTimeout(() => { setTimeout(() => {
history.push(`/library/album?sortType=${rowData.genre}`); history.push(
`/library/album?sortType=${
rowData[column.dataKey][0]?.id
}`
);
}, 50); }, 50);
} }
} }
@ -929,6 +949,42 @@ const ListViewTable = ({
style={{ style={{
fontSize: `${fontSize}px`, fontSize: `${fontSize}px`,
}} }}
>
{rowData[column.dataKey][0]?.title}
</RsuiteLinkButton>
</CustomTooltip>
)}
</>
) : column.dataKey === 'album' ? (
<CustomTooltip text={rowData[column.dataKey]}>
<RsuiteLinkButton
appearance="link"
onClick={(e: any) => {
if (!e.ctrlKey && !e.shiftKey) {
if (rowData.albumId && !isModal) {
history.push(`/library/album/${rowData.albumId}`);
} else if (rowData.albumId && isModal) {
dispatch(
addModalPage({
pageType: 'album',
id: rowData.albumId,
})
);
}
}
}}
playing={
(rowData.uniqueId === playQueue?.currentSongUniqueId &&
nowPlaying) ||
(!nowPlaying &&
rowData.id === playQueue?.currentSongId &&
playQueue?.currentSongId)
? 'true'
: 'false'
}
style={{
fontSize: `${fontSize}px`,
}}
> >
{rowData[column.dataKey]} {rowData[column.dataKey]}
</RsuiteLinkButton> </RsuiteLinkButton>

10
src/hooks/useSearchQuery.ts

@ -11,6 +11,16 @@ const useSearchQuery = (searchQuery: string, data: any[], filterProperties: stri
const matches: SetStateAction<any[]> = []; const matches: SetStateAction<any[]> = [];
filterProps.map((prop: string) => { filterProps.map((prop: string) => {
const filteredDataByProp = data.filter((entry: any) => { const filteredDataByProp = data.filter((entry: any) => {
if (prop.match('artist')) {
return String(entry.albumArtist)?.toLowerCase().includes(searchQuery.toLowerCase());
}
if (prop.match('genre')) {
return String(entry.genre[0]?.title)
?.toLowerCase()
.includes(searchQuery.toLowerCase());
}
return String(entry[prop])?.toLowerCase().includes(searchQuery.toLowerCase()); return String(entry[prop])?.toLowerCase().includes(searchQuery.toLowerCase());
}); });

2
src/redux/miscSlice.ts

@ -8,7 +8,7 @@ const parsedSettings = process.env.NODE_ENV === 'test' ? mockSettings : settings
export interface ModalPage { export interface ModalPage {
pageType: string; pageType: string;
id: number; id: string;
} }
export interface Modal { export interface Modal {

30
src/types.ts

@ -57,13 +57,15 @@ export interface Album {
title: string; title: string;
isDir?: boolean; isDir?: boolean;
albumId: string; albumId: string;
artist?: string; albumArtist?: string;
artistId?: string; albumArtistId: string;
artist?: Artist[];
songCount: number; songCount: number;
duration: number; duration: number;
created: string; created: string;
year?: number; year?: number;
genre?: string; genre?: Genre[];
albumGenre?: string;
image: string; image: string;
starred?: string; starred?: string;
type: Item.Album; type: Item.Album;
@ -74,12 +76,12 @@ export interface Album {
export interface Artist { export interface Artist {
id: string; id: string;
title: string; title: string;
albumCount: number; albumCount?: number;
image: string; image?: string;
starred?: string; starred?: string;
type: Item.Artist; type?: Item.Artist;
uniqueId: string; uniqueId?: string;
album: Album[]; album?: Album[];
} }
export interface ArtistInfo { export interface ArtistInfo {
@ -102,8 +104,8 @@ export interface Genre {
title: string; title: string;
songCount?: number; songCount?: number;
albumCount?: number; albumCount?: number;
type: Item.Genre; type?: Item.Genre;
uniqueId: string; uniqueId?: string;
} }
export interface Playlist { export interface Playlist {
@ -129,11 +131,13 @@ export interface Song {
isDir?: boolean; isDir?: boolean;
album: string; album: string;
albumId?: string; albumId?: string;
artist: string; albumArtist: string;
artistId?: string; albumArtistId: string;
artist: Artist[];
track?: number; track?: number;
year?: number; year?: number;
genre?: string; genre?: Genre[];
albumGenre?: string;
size: number; size: number;
contentType?: string; contentType?: string;
suffix?: string; suffix?: string;

Loading…
Cancel
Save