From e52ca9bc1e24a0f9c6cab50d75600739d84e8797 Mon Sep 17 00:00:00 2001 From: jeffvli Date: Thu, 18 Nov 2021 22:48:43 -0800 Subject: [PATCH] Support types for multiple artists, genres --- src/api/api.ts | 16 ++- src/components/dashboard/Dashboard.tsx | 16 +-- src/components/layout/Titlebar.tsx | 2 +- src/components/library/AlbumView.tsx | 95 +++++++-------- src/components/player/PlayerBar.tsx | 11 +- src/components/search/SearchView.tsx | 4 +- src/components/starred/StarredView.tsx | 4 +- src/components/viewtypes/ListViewTable.tsx | 132 +++++++++++++++------ src/hooks/useSearchQuery.ts | 10 ++ src/redux/miscSlice.ts | 2 +- src/types.ts | 30 +++-- 11 files changed, 195 insertions(+), 127 deletions(-) diff --git a/src/api/api.ts b/src/api/api.ts index 5a7bda0..4efccde 100644 --- a/src/api/api.ts +++ b/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, diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index 546fe45..3993e38 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/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={() => { diff --git a/src/components/layout/Titlebar.tsx b/src/components/layout/Titlebar.tsx index 1361632..17accb1 100644 --- a/src/components/layout/Titlebar.tsx +++ b/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}`); diff --git a/src/components/library/AlbumView.tsx b/src/components/library/AlbumView.tsx index 277c75c..efc1bac 100644 --- a/src/components/library/AlbumView.tsx +++ b/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)} - {data.artist && ( - { - 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} - - )} - {data.genre && ( - <> + {data.artist.map((d: Artist) => { + return ( { 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} - - )} + ); + })} + {data.genre.map((d: Genre) => { + return ( + { + 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} + + ); + })}
diff --git a/src/components/player/PlayerBar.tsx b/src/components/player/PlayerBar.tsx index e13f4ee..668090d 100644 --- a/src/components/player/PlayerBar.tsx +++ b/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'} diff --git a/src/components/search/SearchView.tsx b/src/components/search/SearchView.tsx index 7b6dffc..908891c 100644 --- a/src/components/search/SearchView.tsx +++ b/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} diff --git a/src/components/starred/StarredView.tsx b/src/components/starred/StarredView.tsx index 568390b..063ed77 100644 --- a/src/components/starred/StarredView.tsx +++ b/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' }} diff --git a/src/components/viewtypes/ListViewTable.tsx b/src/components/viewtypes/ListViewTable.tsx index 4592f32..568ec9f 100644 --- a/src/components/viewtypes/ListViewTable.tsx +++ b/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%', }} > - + { 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} @@ -881,39 +899,77 @@ const ListViewTable = ({ : undefined, }} > - {column.dataKey.match(/album|artist|genre/) ? ( + {column.dataKey.match(/artist|genre/) ? ( + <> + {rowData[column.dataKey] && ( + + { + 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} + + + )} + + ) : column.dataKey === 'album' ? ( { 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, + }) + ); } } }} diff --git a/src/hooks/useSearchQuery.ts b/src/hooks/useSearchQuery.ts index e65afdb..4629eb1 100644 --- a/src/hooks/useSearchQuery.ts +++ b/src/hooks/useSearchQuery.ts @@ -11,6 +11,16 @@ const useSearchQuery = (searchQuery: string, data: any[], filterProperties: stri const matches: SetStateAction = []; 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()); }); diff --git a/src/redux/miscSlice.ts b/src/redux/miscSlice.ts index e1e7a78..a3ecb0a 100644 --- a/src/redux/miscSlice.ts +++ b/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 { diff --git a/src/types.ts b/src/types.ts index cd9c79c..69705c3 100644 --- a/src/types.ts +++ b/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;