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

16
src/components/dashboard/Dashboard.tsx

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

2
src/components/layout/Titlebar.tsx

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

95
src/components/library/AlbumView.tsx

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

11
src/components/player/PlayerBar.tsx

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

4
src/components/search/SearchView.tsx

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

4
src/components/starred/StarredView.tsx

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

132
src/components/viewtypes/ListViewTable.tsx

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

10
src/hooks/useSearchQuery.ts

@ -11,6 +11,16 @@ const useSearchQuery = (searchQuery: string, data: any[], filterProperties: stri
const matches: SetStateAction<any[]> = [];
filterProps.map((prop: string) => {
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());
});

2
src/redux/miscSlice.ts

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

30
src/types.ts

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

Loading…
Cancel
Save