diff --git a/conf/configuration.go b/conf/configuration.go index 185c1e3e5..606493800 100644 --- a/conf/configuration.go +++ b/conf/configuration.go @@ -73,6 +73,7 @@ type configOptions struct { DevEnableShare bool DevSidebarPlaylists bool DevEnableBufferedScrobble bool + DevShowArtistPage bool } type scannerOptions struct { @@ -238,6 +239,7 @@ func init() { viper.SetDefault("devenableshare", false) viper.SetDefault("devenablebufferedscrobble", true) viper.SetDefault("devsidebarplaylists", false) + viper.SetDefault("devshowartistpage", false) } func InitConfig(cfgFile string) { diff --git a/server/serve_index.go b/server/serve_index.go index e3fc15b29..6946b4828 100644 --- a/server/serve_index.go +++ b/server/serve_index.go @@ -48,6 +48,7 @@ func serveIndex(ds model.DataStore, fs fs.FS) http.HandlerFunc { "devSidebarPlaylists": conf.Server.DevSidebarPlaylists, "lastFMEnabled": conf.Server.LastFM.Enabled, "lastFMApiKey": conf.Server.LastFM.ApiKey, + "devShowArtistPage": conf.Server.DevShowArtistPage, } auth := handleLoginFromHeaders(ds, r) if auth != nil { diff --git a/server/serve_index_test.go b/server/serve_index_test.go index 72a81037b..ec6893e65 100644 --- a/server/serve_index_test.go +++ b/server/serve_index_test.go @@ -243,6 +243,17 @@ var _ = Describe("serveIndex", func() { config := extractAppConfig(w.Body.String()) Expect(config).To(HaveKeyWithValue("lastFMApiKey", "APIKEY-123")) }) + It("sets the devShowArtistPage", func() { + conf.Server.DevShowArtistPage = true + r := httptest.NewRequest("GET", "/index.html", nil) + w := httptest.NewRecorder() + + serveIndex(ds, fs)(w, r) + + config := extractAppConfig(w.Body.String()) + Expect(config).To(HaveKeyWithValue("devShowArtistPage", true)) + }) + }) var appConfigRegex = regexp.MustCompile(`(?m)window.__APP_CONFIG__="([^"]*)`) diff --git a/ui/src/album/AlbumGridView.js b/ui/src/album/AlbumGridView.js index 0c919375e..4807d7bdf 100644 --- a/ui/src/album/AlbumGridView.js +++ b/ui/src/album/AlbumGridView.js @@ -113,7 +113,7 @@ const Cover = withContentRect('bounds')( } ) -const AlbumGridTile = ({ showArtist, record, basePath }) => { +const AlbumGridTile = ({ showArtist, record, basePath, ...props }) => { const classes = useStyles() const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md'), { noSsr: true, @@ -121,7 +121,6 @@ const AlbumGridTile = ({ showArtist, record, basePath }) => { if (!record) { return null } - return (
{ const classes = useStyles() const { filterValues } = useListContext() const isArtistView = !!(filterValues && filterValues.artist_id) - return (
({ + root: { + display: 'flex', + padding: '1em', + '& .MuiTypography-h5': { + wordBreak: 'break-word', + }, + [theme.breakpoints.down('xs')]: { + padding: 'unset', + background: ({ img }) => `url(${img})`, + }, + }, + bgContainer: { + display: 'flex', + width: '100%', + [theme.breakpoints.down('xs')]: { + height: '15rem', + width: '100vw', + padding: 'unset', + backdropFilter: 'blur(1px)', + backgroundPosition: '50% 30%', + background: `linear-gradient(to bottom, rgba(52 52 52 / 72%), rgba(21 21 21))`, + }, + }, + albumList: { + margin: '20px', + display: 'grid', + }, + details: { + display: 'flex', + flex: '1', + flexDirection: 'column', + }, + bioBlock: { + display: 'inline-block', + marginTop: '1em', + float: 'left', + wordBreak: 'break-all', + cursor: 'pointer', + }, + link: { + margin: '1px', + }, + mdetails: { + display: 'none', + [theme.breakpoints.down('xs')]: { + display: 'flex', + alignItems: 'center', + width: '7rem', + marginLeft: '0.5rem', + flex: '1', + }, + }, + mbio: { + display: 'none', + [theme.breakpoints.down('xs')]: { + display: 'flex', + marginLeft: '3%', + marginRight: '3%', + zIndex: '1', + '& p': { + whiteSpace: ({ expanded }) => (expanded ? 'unset' : 'nowrap'), + overflow: 'hidden', + width: '95vw', + textOverflow: 'ellipsis', + }, + }, + }, + content: { + flex: '1 0 auto', + '& .MuiTypography-root': { + display: ({ expanded }) => (expanded ? 'block' : '-webkit-inline-box'), + boxOrient: 'vertical', + lineClamp: '3', + }, + }, + cover: { + width: 151, + boxShadow: '0px 0px 6px 0px #565656', + borderRadius: '5px', + [theme.breakpoints.up('sm')]: { + borderRadius: '7em', + }, + }, + martImage: { + marginLeft: '1em', + maxHeight: '10rem', + backgroundColor: 'inherit', + display: 'none', + [theme.breakpoints.down('xs')]: { + marginTop: '4rem', + maxHeight: '7rem', + width: '7rem', + display: 'flex', + }, + }, + artImage: { + maxHeight: '9.5rem', + backgroundColor: 'inherit', + display: 'flex', + [theme.breakpoints.down('xs')]: { + marginTop: '4rem', + maxHeight: '7rem', + width: '7rem', + }, + }, + artDetail: { + flex: '1', + padding: '3%', + display: 'flex', + minHeight: '10rem', + '& .MuiPaper-elevation1': { + boxShadow: 'none', + padding: '4px', + }, + [theme.breakpoints.down('xs')]: { + display: 'none', + }, + }, + artistSummary: { + marginBottom: '1em', + }, + }), + { name: 'NDArtistPage' } +) + +const ArtistDetails = () => { + const [artistInfo, setArtistInfo] = useState() + const [expanded, setExpanded] = useState(false) + const record = useRecordContext() + const artistId = record?.id + + const title = record.name + let completeBioLink = '' + const link = artistInfo?.biography?.match( + /]*?\s+)?href=(["'])(.*?)\1/ + ) + if (link) { + completeBioLink = link[2] + } + const biography = artistInfo?.biography?.replace(new RegExp('<.*>', 'g'), '') + const translate = useTranslate() + + const img = artistInfo?.largeImageUrl + const classes = useStyles({ img, expanded }) + + useEffect(() => { + subsonic + .getArtistInfo(artistId) + .then((resp) => resp.json['subsonic-response']) + .then((data) => { + if (data.status === 'ok') { + setArtistInfo(data.artistInfo) + } + }) + .catch((e) => { + console.error('error on artist page', e) + }) + }, [artistId, record]) + + const handleExpandClick = useCallback(() => { + setExpanded(!expanded) + }, [expanded, setExpanded]) + + return ( + <> +
+
+ + + +
+ + {title} + +
+ + + + +
+ + + {title} + + + + + {completeBioLink !== '' && ( + + {translate('message.lastfmLink')} + + )} + + + +
+
+
+
+
+ + + {biography} + + {translate('message.lastfmLink')} + + + +
+ + ) +} + +const ArtistAlbums = ({ ...props }) => { + const { ids } = props + const classes = useStyles() + const translate = useTranslate() + + return ( +
+
+ {ids.length + + ' ' + + translate('resources.album.name', { smart_count: ids.length })} +
+ +
+ ) +} + +const AlbumShowLayout = (props) => { + const showContext = useShowContext(props) + const record = useRecordContext() + + return ( + <> + {record && } + {record && ( + + + + )} + + ) +} + +const ArtistShow = (props) => { + const controllerProps = useShowController(props) + return ( + + + + ) +} + +export default ArtistShow diff --git a/ui/src/artist/index.js b/ui/src/artist/index.js index 206bfb6cd..6b2011452 100644 --- a/ui/src/artist/index.js +++ b/ui/src/artist/index.js @@ -1,11 +1,13 @@ import React from 'react' import ArtistList from './ArtistList' +import ArtistShow from './ArtistShow' import DynamicMenuIcon from '../layout/DynamicMenuIcon' import MicNoneOutlinedIcon from '@material-ui/icons/MicNoneOutlined' import MicIcon from '@material-ui/icons/Mic' export default { list: ArtistList, + show: ArtistShow, icon: ( { const [perPage] = useAlbumsPerPage(width) - return (id) => { - return `/album?filter={"artist_id":"${id}"}&order=ASC&sort=maxYear&displayedFilters={"compilation":true}&perPage=${perPage}` + return config.devShowArtistPage + ? `/artist/${id}/show` + : `/album?filter={"artist_id":"${id}"}&order=ASC&sort=maxYear&displayedFilters={"compilation":true}&perPage=${perPage}` } } export const ArtistLinkField = withWidth()(({ record, className, width }) => { const artistLink = useGetHandleArtistClick(width) + return ( { ...(record.updatedAt && { _: record.updatedAt }), ...(size && { size }), } + if (record.coverArtId) { return baseUrl(url('getCoverArt', record.coverArtId, options)) } else { @@ -56,6 +57,10 @@ const getCoverArtUrl = (record, size) => { } } +const getArtistInfo = (id) => { + return httpClient(url('getArtistInfo', id)) +} + const streamUrl = (id) => { return baseUrl(url('stream', id, { ts: true })) } @@ -72,4 +77,5 @@ export default { getScanStatus, getCoverArtUrl, streamUrl, + getArtistInfo, } diff --git a/ui/src/themes/dark.js b/ui/src/themes/dark.js index 24fb46793..c293cd18a 100644 --- a/ui/src/themes/dark.js +++ b/ui/src/themes/dark.js @@ -32,6 +32,15 @@ export default { boxShadow: '3px 3px 5px #000000a3', }, }, + NDArtistPage: { + bgContainer: { + background: + 'linear-gradient(to bottom, rgba(52 52 52 / 72%), rgb(48 48 48))!important', + }, + more: { + boxShadow: '-10px 0px 18px 5px #303030!important', + }, + }, }, player: { theme: 'dark', diff --git a/ui/src/themes/extradark.js b/ui/src/themes/extradark.js index ebb50d11c..04cdc3c76 100644 --- a/ui/src/themes/extradark.js +++ b/ui/src/themes/extradark.js @@ -28,7 +28,14 @@ export default { color: '#eee', }, }, + NDArtistPage: { + bgContainer: { + background: + 'linear-gradient(to bottom, rgba(52 52 52 / 72%), rgb(0 0 0))!important', + }, + }, }, + player: { theme: 'dark', stylesheet: require('./dark.css.js'), diff --git a/ui/src/themes/green.js b/ui/src/themes/green.js index a0991c73f..c414833d8 100644 --- a/ui/src/themes/green.js +++ b/ui/src/themes/green.js @@ -27,6 +27,15 @@ export default { color: '#eee', }, }, + NDArtistPage: { + bgContainer: { + background: + 'linear-gradient(to bottom, rgba(52 52 52 / 72%), rgb(48 48 48))!important', + }, + more: { + boxShadow: '-10px 0px 18px 5px #303030!important', + }, + }, }, player: { theme: 'dark', diff --git a/ui/src/themes/ligera.js b/ui/src/themes/ligera.js index 3f1104e87..6afc5bb5d 100644 --- a/ui/src/themes/ligera.js +++ b/ui/src/themes/ligera.js @@ -359,6 +359,15 @@ export default { marginTop: '-50px', }, }, + NDArtistPage: { + bgContainer: { + background: + 'linear-gradient(to bottom, rgb(255 255 255 / 51%), rgb(240 242 245))!important', + }, + more: { + boxShadow: '-10px 0px 18px 5px #f0f2f5!important', + }, + }, RaLayout: { content: { padding: '0 !important', diff --git a/ui/src/themes/light.js b/ui/src/themes/light.js index e846a3f63..c29d51171 100644 --- a/ui/src/themes/light.js +++ b/ui/src/themes/light.js @@ -46,6 +46,15 @@ export default { color: '#0085ff', }, }, + NDArtistPage: { + bgContainer: { + background: + 'linear-gradient(to bottom, rgb(255 255 255 / 51%), rgb(250 250 250))!important', + }, + more: { + boxShadow: '-10px 0px 18px 5px #fafafa!important', + }, + }, }, player: { theme: 'light',