diff --git a/ui/src/actions/settings.js b/ui/src/actions/settings.js index bb8942a6b..e62ecde8f 100644 --- a/ui/src/actions/settings.js +++ b/ui/src/actions/settings.js @@ -1,6 +1,18 @@ export const SET_NOTIFICATIONS_STATE = 'SET_NOTIFICATIONS_STATE' +export const SET_TOGGLEABLE_FIELDS = 'SET_TOGGLEABLE_FIELDS' +export const SET_OMITTED_FIELDS = 'SET_OMITTED_FIELDS' export const setNotificationsState = (enabled) => ({ type: SET_NOTIFICATIONS_STATE, data: enabled, }) + +export const setToggleableFields = (obj) => ({ + type: SET_TOGGLEABLE_FIELDS, + data: obj, +}) + +export const setOmittedFields = (obj) => ({ + type: SET_OMITTED_FIELDS, + data: obj, +}) diff --git a/ui/src/album/AlbumActions.js b/ui/src/album/AlbumActions.js index 6f2b0576d..95ad15363 100644 --- a/ui/src/album/AlbumActions.js +++ b/ui/src/album/AlbumActions.js @@ -14,8 +14,13 @@ import { RiPlayListAddFill, RiPlayList2Fill } from 'react-icons/ri' import { playNext, addTracks, playTracks, shuffleTracks } from '../actions' import subsonic from '../subsonic' import { formatBytes } from '../utils' -import { useMediaQuery } from '@material-ui/core' +import { useMediaQuery, makeStyles } from '@material-ui/core' import config from '../config' +import ToggleFieldsMenu from '../common/ToggleFieldsMenu' + +const useStyles = makeStyles({ + toolbar: { display: 'flex', justifyContent: 'space-between', width: '100%' }, +}) const AlbumActions = ({ className, @@ -27,7 +32,9 @@ const AlbumActions = ({ }) => { const dispatch = useDispatch() const translate = useTranslate() + const classes = useStyles() const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md')) + const isNotSmall = useMediaQuery((theme) => theme.breakpoints.up('sm')) const handlePlay = React.useCallback(() => { dispatch(playTracks(data, ids)) @@ -51,41 +58,46 @@ const AlbumActions = ({ return ( - - - - - {config.enableDownloads && ( - - )} +
+
+ + + + + {config.enableDownloads && ( + + )} +
+
{isNotSmall && }
+
) } diff --git a/ui/src/album/AlbumList.js b/ui/src/album/AlbumList.js index 91008e550..0707358c7 100644 --- a/ui/src/album/AlbumList.js +++ b/ui/src/album/AlbumList.js @@ -6,9 +6,9 @@ import { Filter, NullableBooleanInput, NumberInput, + Pagination, ReferenceInput, SearchInput, - Pagination, useTranslate, } from 'react-admin' import FavoriteIcon from '@material-ui/icons/Favorite' @@ -20,6 +20,7 @@ import AlbumGridView from './AlbumGridView' import { AddToPlaylistDialog } from '../dialogs' import albumLists, { defaultAlbumList } from './albumLists' import config from '../config' +import useSelectedFields from '../common/useSelectedFields' const AlbumFilter = (props) => { const translate = useTranslate() @@ -70,6 +71,21 @@ const AlbumList = (props) => { .replace(/^\/album/, '') .replace(/^\//, '') + // Workaround to force album columns to appear the first time. + // See https://github.com/navidrome/navidrome/pull/923#issuecomment-833004842 + // TODO: Find a better solution + useSelectedFields({ + resource: 'album', + columns: { + artist: 'artist', + songCount: 'songCount', + playCount: 'playCount', + year: 'year', + duration: 'duration', + rating: 'rating', + }, + }) + // If it does not have filter/sort params (usually coming from Menu), // reload with correct filter/sort params if (!location.search) { diff --git a/ui/src/album/AlbumListActions.js b/ui/src/album/AlbumListActions.js index 09135bb65..97ce05de8 100644 --- a/ui/src/album/AlbumListActions.js +++ b/ui/src/album/AlbumListActions.js @@ -1,10 +1,71 @@ import React, { cloneElement } from 'react' -import { Button, sanitizeListRestProps, TopToolbar } from 'react-admin' -import { ButtonGroup } from '@material-ui/core' +import { + Button, + sanitizeListRestProps, + TopToolbar, + useTranslate, +} from 'react-admin' +import { + ButtonGroup, + useMediaQuery, + Typography, + makeStyles, +} from '@material-ui/core' import ViewHeadlineIcon from '@material-ui/icons/ViewHeadline' import ViewModuleIcon from '@material-ui/icons/ViewModule' import { useDispatch, useSelector } from 'react-redux' import { albumViewGrid, albumViewList } from '../actions' +import ToggleFieldsMenu from '../common/ToggleFieldsMenu' + +const useStyles = makeStyles({ + title: { margin: '1rem' }, + buttonGroup: { width: '100%', justifyContent: 'center' }, + leftButton: { paddingRight: '0.5rem' }, + rightButton: { paddingLeft: '0.5rem' }, +}) + +const AlbumViewToggler = React.forwardRef( + ({ showTitle = true, disableElevation, fullWidth }, ref) => { + const dispatch = useDispatch() + const albumView = useSelector((state) => state.albumView) + const classes = useStyles() + const translate = useTranslate() + return ( +
+ {showTitle && ( + + {translate('ra.toggleFieldsMenu.layout')} + + )} + + + + +
+ ) + } +) const AlbumListActions = ({ currentSort, @@ -24,9 +85,7 @@ const AlbumListActions = ({ fullWidth, ...rest }) => { - const dispatch = useDispatch() - const albumView = useSelector((state) => state.albumView) - + const isNotSmall = useMediaQuery((theme) => theme.breakpoints.up('sm')) return ( {filters && @@ -37,26 +96,11 @@ const AlbumListActions = ({ filterValues, context: 'button', })} - - - - + {isNotSmall ? ( + + ) : ( + + )} ) } diff --git a/ui/src/album/AlbumListView.js b/ui/src/album/AlbumListView.js index 99ef1e5b4..487970388 100644 --- a/ui/src/album/AlbumListView.js +++ b/ui/src/album/AlbumListView.js @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useMemo } from 'react' import Paper from '@material-ui/core/Paper' import Table from '@material-ui/core/Table' import TableBody from '@material-ui/core/TableBody' @@ -28,6 +28,7 @@ import { RatingField, } from '../common' import config from '../config' +import useSelectedFields from '../common/useSelectedFields' const useStyles = makeStyles({ columnIcon: { @@ -109,6 +110,36 @@ const AlbumListView = ({ const classes = useStyles() const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md')) const isXsmall = useMediaQuery((theme) => theme.breakpoints.down('xs')) + + const toggleableFields = useMemo(() => { + return { + artist: , + songCount: isDesktop && ( + + ), + playCount: isDesktop && ( + + ), + year: ( + + ), + duration: isDesktop && , + rating: config.enableStarRating && ( + + ), + } + }, [classes.ratingField, isDesktop]) + + const columns = useSelectedFields({ + resource: 'album', + columns: toggleableFields, + }) + return isXsmall ? ( r.name} @@ -147,19 +178,7 @@ const AlbumListView = ({ {...rest} > - - {isDesktop && } - {isDesktop && } - - {isDesktop && } - {config.enableStarRating && ( - - )} + {columns} ({ - albumActions: {}, + albumActions: { + width: '100%', + }, }), { name: 'NDAlbumShow', diff --git a/ui/src/album/AlbumSongs.js b/ui/src/album/AlbumSongs.js index 9c5210478..307992d9d 100644 --- a/ui/src/album/AlbumSongs.js +++ b/ui/src/album/AlbumSongs.js @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useMemo } from 'react' import { BulkActionsToolbar, ListToolbar, @@ -24,6 +24,7 @@ import { } from '../common' import { AddToPlaylistDialog } from '../dialogs' import { QualityInfo } from '../common/QualityInfo' +import useSelectedFields from '../common/useSelectedFields' import config from '../config' const useStyles = makeStyles( @@ -87,6 +88,46 @@ const AlbumSongs = (props) => { const classes = useStyles({ isDesktop }) const dispatch = useDispatch() const version = useVersion() + + const toggleableFields = useMemo(() => { + return { + trackNumber: isDesktop && ( + + ), + title: ( + + ), + artist: isDesktop && , + duration: , + quality: isDesktop && , + bpm: isDesktop && , + rating: isDesktop && config.enableStarRating && ( + + ), + } + }, [isDesktop, classes.ratingField]) + + const columns = useSelectedFields({ + resource: 'albumSong', + columns: toggleableFields, + omittedColumns: ['title'], + defaultOff: ['bpm'], + }) + return ( <> { contextAlwaysVisible={!isDesktop} classes={{ row: classes.row }} > - {isDesktop && ( - - )} - - {isDesktop && } - - {isDesktop && } - {isDesktop && } - {isDesktop && config.enableStarRating && ( - - )} + {columns} { const handleArtistLink = useGetHandleArtistClick(width) const history = useHistory() const isXsmall = useMediaQuery((theme) => theme.breakpoints.down('xs')) + + const toggleableFields = useMemo(() => { + return { + albumCount: , + songCount: , + playCount: , + rating: config.enableStarRating && ( + + ), + } + }, [classes.ratingField]) + + const columns = useSelectedFields({ + resource: 'artist', + columns: toggleableFields, + }) + return isXsmall ? ( history.push(handleArtistLink(id))} @@ -72,17 +96,7 @@ const ArtistListView = ({ hasShow, hasEdit, hasList, width, ...rest }) => { ) : ( - - - - {config.enableStarRating && ( - - )} + {columns} { exporter={false} bulkActionButtons={false} filters={} + actions={} > diff --git a/ui/src/artist/ArtistListActions.js b/ui/src/artist/ArtistListActions.js new file mode 100644 index 000000000..a1aa4c089 --- /dev/null +++ b/ui/src/artist/ArtistListActions.js @@ -0,0 +1,16 @@ +import React from 'react' +import { sanitizeListRestProps, TopToolbar } from 'react-admin' +import { useMediaQuery } from '@material-ui/core' +import ToggleFieldsMenu from '../common/ToggleFieldsMenu' + +const ArtistListActions = ({ className, ...rest }) => { + const isNotSmall = useMediaQuery((theme) => theme.breakpoints.up('sm')) + + return ( + + {isNotSmall && } + + ) +} + +export default ArtistListActions diff --git a/ui/src/common/ToggleFieldsMenu.js b/ui/src/common/ToggleFieldsMenu.js new file mode 100644 index 000000000..cdae143e9 --- /dev/null +++ b/ui/src/common/ToggleFieldsMenu.js @@ -0,0 +1,109 @@ +import React, { useState } from 'react' +import PropTypes from 'prop-types' +import IconButton from '@material-ui/core/IconButton' +import Menu from '@material-ui/core/Menu' +import MenuItem from '@material-ui/core/MenuItem' +import { makeStyles, Typography } from '@material-ui/core' +import MoreVertIcon from '@material-ui/icons/MoreVert' +import Checkbox from '@material-ui/core/Checkbox' +import { useDispatch, useSelector } from 'react-redux' +import { useTranslate } from 'react-admin' +import { setToggleableFields } from '../actions' + +const useStyles = makeStyles({ + menuIcon: { + position: 'relative', + top: '-0.5em', + }, + menu: { + width: '24ch', + }, + columns: { + maxHeight: '21rem', + overflow: 'auto', + }, + title: { + margin: '1rem', + }, +}) + +const ToggleFieldsMenu = ({ resource, topbarComponent: TopBarComponent }) => { + const [anchorEl, setAnchorEl] = useState(null) + const dispatch = useDispatch() + const translate = useTranslate() + const toggleableColumns = useSelector( + (state) => state.settings.toggleableFields[resource] + ) + const omittedColumns = + useSelector((state) => state.settings.omittedFields[resource]) || [] + + const classes = useStyles() + const open = Boolean(anchorEl) + + const handleOpen = (event) => { + setAnchorEl(event.currentTarget) + } + const handleClose = () => { + setAnchorEl(null) + } + + const handleClick = (selectedColumn) => { + dispatch( + setToggleableFields({ + [resource]: { + ...toggleableColumns, + [selectedColumn]: !toggleableColumns[selectedColumn], + }, + }) + ) + } + + return ( +
+ + + + + {TopBarComponent && } + {toggleableColumns ? ( +
+ + {translate('ra.toggleFieldsMenu.columnsToDisplay')} + +
+ {Object.entries(toggleableColumns).map(([key, val]) => + !omittedColumns.includes(key) ? ( + handleClick(key)}> + + {translate(`resources.${resource}.fields.${key}`)} + + ) : null + )} +
+
+ ) : null} +
+
+ ) +} + +export default ToggleFieldsMenu + +ToggleFieldsMenu.propTypes = { + resource: PropTypes.string.isRequired, + topbarComponent: PropTypes.elementType, +} diff --git a/ui/src/common/useSelectedFields.js b/ui/src/common/useSelectedFields.js new file mode 100644 index 000000000..6e3d40c74 --- /dev/null +++ b/ui/src/common/useSelectedFields.js @@ -0,0 +1,79 @@ +import React, { useState, useEffect } from 'react' +import PropTypes from 'prop-types' +import { useDispatch, useSelector } from 'react-redux' +import { setOmittedFields, setToggleableFields } from '../actions' + +const useSelectedFields = ({ + resource, + columns, + omittedColumns = [], + defaultOff = [], +}) => { + const dispatch = useDispatch() + const resourceFields = useSelector( + (state) => state.settings.toggleableFields + )?.[resource] + const omittedFields = useSelector((state) => state.settings.omittedFields)?.[ + resource + ] + + const [filteredComponents, setFilteredComponents] = useState([]) + + useEffect(() => { + if ( + !resourceFields || + Object.keys(resourceFields).length !== Object.keys(columns).length + ) { + const obj = {} + for (const key of Object.keys(columns)) { + obj[key] = !defaultOff.includes(key) + } + dispatch(setToggleableFields({ [resource]: obj })) + } + if (!omittedFields) { + dispatch(setOmittedFields({ [resource]: omittedColumns })) + } + }, [ + columns, + defaultOff, + dispatch, + omittedColumns, + omittedFields, + resource, + resourceFields, + ]) + + useEffect(() => { + if (resourceFields) { + const filtered = [] + const omitted = omittedColumns + for (const [key, val] of Object.entries(columns)) { + if (!val) omitted.push(key) + else if (resourceFields[key]) filtered.push(val) + } + if (filteredComponents.length !== filtered.length) + setFilteredComponents(filtered) + if (omittedFields.length !== omitted.length) + dispatch(setOmittedFields({ [resource]: omitted })) + } + }, [ + resourceFields, + columns, + dispatch, + omittedColumns, + omittedFields, + resource, + filteredComponents.length, + ]) + + return React.Children.toArray(filteredComponents) +} + +export default useSelectedFields + +useSelectedFields.propTypes = { + resource: PropTypes.string, + columns: PropTypes.object, + omittedColumns: PropTypes.arrayOf(PropTypes.string), + defaultOff: PropTypes.arrayOf(PropTypes.string), +} diff --git a/ui/src/i18n/en.json b/ui/src/i18n/en.json index 2ec45ce3e..aef6d4284 100644 --- a/ui/src/i18n/en.json +++ b/ui/src/i18n/en.json @@ -278,6 +278,12 @@ "i18n_error": "Cannot load the translations for the specified language", "canceled": "Action cancelled", "logged_out": "Your session has ended, please reconnect." + }, + "toggleFieldsMenu": { + "columnsToDisplay": "Columns To Display", + "layout": "Layout", + "grid": "Grid", + "table": "Table" } }, "message": { diff --git a/ui/src/playlist/PlaylistActions.js b/ui/src/playlist/PlaylistActions.js index 24cfd0b25..3987e4180 100644 --- a/ui/src/playlist/PlaylistActions.js +++ b/ui/src/playlist/PlaylistActions.js @@ -19,15 +19,22 @@ import { M3U_MIME_TYPE, REST_URL } from '../consts' import subsonic from '../subsonic' import PropTypes from 'prop-types' import { formatBytes } from '../utils' -import { useMediaQuery } from '@material-ui/core' +import { useMediaQuery, makeStyles } from '@material-ui/core' import config from '../config' +import ToggleFieldsMenu from '../common/ToggleFieldsMenu' + +const useStyles = makeStyles({ + toolbar: { display: 'flex', justifyContent: 'space-between', width: '100%' }, +}) const PlaylistActions = ({ className, ids, data, record, ...rest }) => { const dispatch = useDispatch() const translate = useTranslate() + const classes = useStyles() const dataProvider = useDataProvider() const notify = useNotify() const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md')) + const isNotSmall = useMediaQuery((theme) => theme.breakpoints.up('sm')) const getAllSongsAndDispatch = React.useCallback( (action) => { @@ -94,47 +101,52 @@ const PlaylistActions = ({ className, ids, data, record, ...rest }) => { return ( - - - - - {config.enableDownloads && ( - - )} - +
+
+ + + + + {config.enableDownloads && ( + + )} + +
+
{isNotSmall && }
+
) } diff --git a/ui/src/playlist/PlaylistList.js b/ui/src/playlist/PlaylistList.js index 434c8cd20..320f16c5e 100644 --- a/ui/src/playlist/PlaylistList.js +++ b/ui/src/playlist/PlaylistList.js @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useMemo } from 'react' import { Datagrid, DateField, @@ -11,8 +11,10 @@ import { useNotify, } from 'react-admin' import Switch from '@material-ui/core/Switch' -import { DurationField, List, Writable, isWritable } from '../common' import { useMediaQuery } from '@material-ui/core' +import { DurationField, List, Writable, isWritable } from '../common' +import useSelectedFields from '../common/useSelectedFields' +import PlaylistListActions from './PlaylistListActions' const PlaylistFilter = (props) => ( @@ -60,24 +62,42 @@ const PlaylistList = ({ permissions, ...props }) => { const isXsmall = useMediaQuery((theme) => theme.breakpoints.down('xs')) const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md')) + const toggleableFields = useMemo(() => { + return { + owner: , + songCount: isDesktop && , + duration: isDesktop && , + updatedAt: isDesktop && ( + + ), + public: !isXsmall && ( + + ), + } + }, [isDesktop, isXsmall, permissions]) + + const columns = useSelectedFields({ + resource: 'playlist', + columns: toggleableFields, + }) + return ( - }> + } + actions={} + > isWritable(r && r.owner)} > - - {isDesktop && } - {isDesktop && } - {isDesktop && } - {!isXsmall && ( - - )} + {columns} diff --git a/ui/src/playlist/PlaylistListActions.js b/ui/src/playlist/PlaylistListActions.js new file mode 100644 index 000000000..3717dfac7 --- /dev/null +++ b/ui/src/playlist/PlaylistListActions.js @@ -0,0 +1,25 @@ +import React from 'react' +import { + sanitizeListRestProps, + TopToolbar, + CreateButton, + useTranslate, +} from 'react-admin' +import { useMediaQuery } from '@material-ui/core' +import ToggleFieldsMenu from '../common/ToggleFieldsMenu' + +const PlaylistListActions = ({ className, ...rest }) => { + const isNotSmall = useMediaQuery((theme) => theme.breakpoints.up('sm')) + const translate = useTranslate() + + return ( + + + {translate('ra.action.create')} + + {isNotSmall && } + + ) +} + +export default PlaylistListActions diff --git a/ui/src/playlist/PlaylistShow.js b/ui/src/playlist/PlaylistShow.js index 7cb705ef7..8fb1e216e 100644 --- a/ui/src/playlist/PlaylistShow.js +++ b/ui/src/playlist/PlaylistShow.js @@ -13,7 +13,9 @@ import PlaylistActions from './PlaylistActions' import { Title, isReadOnly } from '../common' const useStyles = makeStyles( (theme) => ({ - playlistActions: {}, + playlistActions: { + width: '100%', + }, }), { name: 'NDPlaylistShow', diff --git a/ui/src/playlist/PlaylistSongs.js b/ui/src/playlist/PlaylistSongs.js index fbf6c901a..8a4556864 100644 --- a/ui/src/playlist/PlaylistSongs.js +++ b/ui/src/playlist/PlaylistSongs.js @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react' +import React, { useCallback, useMemo } from 'react' import { BulkActionsToolbar, ListToolbar, @@ -28,6 +28,7 @@ import { AlbumLinkField } from '../song/AlbumLinkField' import { playTracks } from '../actions' import PlaylistSongBulkActions from './PlaylistSongBulkActions' import { QualityInfo } from '../common/QualityInfo' +import useSelectedFields from '../common/useSelectedFields' const useStyles = makeStyles( (theme) => ({ @@ -127,6 +128,26 @@ const PlaylistSongs = ({ playlistId, readOnly, actions, ...props }) => { [playlistId, reorder, ids] ) + const toggleableFields = useMemo(() => { + return { + trackNumber: isDesktop && , + title: , + album: isDesktop && , + artist: isDesktop && , + duration: ( + + ), + quality: isDesktop && , + bpm: isDesktop && , + } + }, [isDesktop, classes.draggable]) + + const columns = useSelectedFields({ + resource: 'playlistTrack', + columns: toggleableFields, + defaultOff: ['bpm'], + }) + return ( <> { contextAlwaysVisible={!isDesktop} classes={{ row: classes.row }} > - {isDesktop && } - - {isDesktop && } - {isDesktop && } - - {isDesktop && } - {isDesktop && } + {columns} { @@ -12,6 +18,22 @@ export const settingsReducer = (previousState = initialState, payload) => { ...previousState, notifications: data, } + case SET_TOGGLEABLE_FIELDS: + return { + ...previousState, + toggleableFields: { + ...previousState.toggleableFields, + ...data, + }, + } + case SET_OMITTED_FIELDS: + return { + ...previousState, + omittedFields: { + ...previousState.omittedFields, + ...data, + }, + } default: return previousState } diff --git a/ui/src/song/SongList.js b/ui/src/song/SongList.js index 9aabf325d..37017bcb5 100644 --- a/ui/src/song/SongList.js +++ b/ui/src/song/SongList.js @@ -28,6 +28,7 @@ import { AddToPlaylistDialog } from '../dialogs' import { makeStyles } from '@material-ui/core/styles' import FavoriteBorderIcon from '@material-ui/icons/FavoriteBorder' import config from '../config' +import useSelectedFields from '../common/useSelectedFields' import { QualityInfo } from '../common/QualityInfo' const useStyles = makeStyles({ @@ -77,6 +78,49 @@ const SongList = (props) => { dispatch(setTrack(record)) } + const toggleableFields = React.useMemo(() => { + return { + album: isDesktop && ( + + ), + artist: , + trackNumber: isDesktop && , + playCount: isDesktop && ( + + ), + year: isDesktop && ( + r.year || ''} + sortByOrder={'DESC'} + /> + ), + quality: isDesktop && , + duration: , + rating: config.enableStarRating && ( + + ), + bpm: isDesktop && , + } + }, [isDesktop, classes.ratingField]) + + const columns = useSelectedFields({ + resource: 'song', + columns: toggleableFields, + defaultOff: ['bpm'], + }) + return ( <> { classes={{ row: classes.row }} > - {isDesktop && ( - - )} - - {isDesktop && } - {isDesktop && ( - - )} - {isDesktop && ( - r.year || ''} - sortByOrder={'DESC'} - /> - )} - {isDesktop && } - - {isDesktop && } - {config.enableStarRating && ( - - )} + {columns} { + const isNotSmall = useMediaQuery((theme) => theme.breakpoints.up('sm')) return ( {filters && @@ -31,6 +34,7 @@ export const SongListActions = ({ context: 'button', })} + {isNotSmall && } ) } diff --git a/ui/src/themes/ligera.js b/ui/src/themes/ligera.js index 1b6790279..8ba96418f 100644 --- a/ui/src/themes/ligera.js +++ b/ui/src/themes/ligera.js @@ -16,7 +16,7 @@ const musicListActions = { backgroundColor: 'inherit !important', }, }, - 'button:first-child': { + 'button:first-child:not(:only-child)': { '@media screen and (max-width: 720px)': { transform: 'scale(1.5)', margin: '1rem', @@ -40,6 +40,9 @@ const musicListActions = { boxShadow: '0px 0px 4px 0px #5656567d', }, }, + 'button:only-child': { + margin: '1.5rem', + }, 'button:first-child>span:first-child': { padding: 0, color: bLight['300'], diff --git a/ui/src/themes/spotify.js b/ui/src/themes/spotify.js index 8f49f6394..c7ad01345 100644 --- a/ui/src/themes/spotify.js +++ b/ui/src/themes/spotify.js @@ -19,7 +19,7 @@ const musicListActions = { backgroundColor: 'inherit !important', }, }, - 'button:first-child': { + 'button:first-child:not(:only-child)': { '@media screen and (max-width: 720px)': { transform: 'scale(1.5)', margin: '1rem', @@ -42,6 +42,9 @@ const musicListActions = { border: 0, }, }, + 'button:only-child': { + margin: '1.5rem', + }, 'button:first-child>span:first-child': { padding: 0, },