diff --git a/ui/src/album/AlbumSongs.js b/ui/src/album/AlbumSongs.js index 04eee761..2a804df9 100644 --- a/ui/src/album/AlbumSongs.js +++ b/ui/src/album/AlbumSongs.js @@ -14,8 +14,12 @@ import { useDispatch } from 'react-redux' import { Card, useMediaQuery } from '@material-ui/core' import { makeStyles } from '@material-ui/core/styles' import { playTracks } from '../audioplayer' -import { DurationField, SongDetails, SongDatagridRow } from '../common' -import { SongContextMenu } from '../song/SongContextMenu' +import { + DurationField, + SongDetails, + SongDatagridRow, + SongContextMenu, +} from '../common' const useStyles = makeStyles( (theme) => ({ diff --git a/ui/src/song/SongContextMenu.js b/ui/src/common/SongContextMenu.js similarity index 78% rename from ui/src/song/SongContextMenu.js rename to ui/src/common/SongContextMenu.js index 40d7a56e..9fe35c72 100644 --- a/ui/src/song/SongContextMenu.js +++ b/ui/src/common/SongContextMenu.js @@ -9,17 +9,24 @@ import StarIcon from '@material-ui/icons/Star' import StarBorderIcon from '@material-ui/icons/StarBorder' import NestedMenuItem from 'material-ui-nested-menu-item' import { addTracks, setTrack } from '../audioplayer' -import { AddToPlaylistMenu } from '../common' +import AddToPlaylistMenu from './AddToPlaylistMenu' import config from '../config' const useStyles = makeStyles({ noWrap: { whiteSpace: 'nowrap', }, + menu: { + visibility: (props) => (props.visible ? 'visible' : 'hidden'), + }, + star: { + visibility: (props) => + props.visible || props.starred ? 'visible' : 'hidden', + }, }) -export const SongContextMenu = ({ className, record, onAddToPlaylist }) => { - const classes = useStyles() +const SongContextMenu = ({ record, onAddToPlaylist, visible }) => { + const classes = useStyles({ visible, starred: record.starred }) const dispatch = useDispatch() const translate = useTranslate() const notify = useNotify() @@ -54,7 +61,7 @@ export const SongContextMenu = ({ className, record, onAddToPlaylist }) => { e.stopPropagation() } - const [toggleStar, { toggling: loading }] = useUpdate( + const [updateRecord, { loading: updating }] = useUpdate( 'albumSong', record.id, record, @@ -68,26 +75,36 @@ export const SongContextMenu = ({ className, record, onAddToPlaylist }) => { } ) - const handleToggleStar = (e, record) => { + const toggleStar = (record) => { record.starred = !record.starred - toggleStar() + updateRecord() + } + + const handleToggleStar = (e, record) => { + toggleStar(record) e.stopPropagation() } const open = Boolean(anchorEl) return ( - + {config.enableStarred && !onAddToPlaylist && ( handleToggleStar(e, record)} size={'small'} - disabled={loading} + disabled={updating} + className={classes.star} > {record.starred ? : } )} - + { SongContextMenu.propTypes = { record: PropTypes.object, onAddToPlaylist: PropTypes.func, + visible: PropTypes.bool, } + +export default SongContextMenu diff --git a/ui/src/common/SongDatagridRow.js b/ui/src/common/SongDatagridRow.js index 7a3d61a7..6b20569a 100644 --- a/ui/src/common/SongDatagridRow.js +++ b/ui/src/common/SongDatagridRow.js @@ -1,17 +1,18 @@ -import React from 'react' +import React, { useState, isValidElement, cloneElement } from 'react' import { DatagridRow, useTranslate } from 'react-admin' -import { TableRow, TableCell, Typography } from '@material-ui/core' +import { TableCell, TableRow, Typography } from '@material-ui/core' import PropTypes from 'prop-types' import RangeField from './RangeField' const SongDatagridRow = ({ record, children, multiDisc, ...rest }) => { const translate = useTranslate() + const [visible, setVisible] = useState(false) return ( <> {multiDisc && ( {record.trackNumber === 1 && ( - + {record.discSubtitle ? translate('message.discSubtitle', { @@ -26,8 +27,19 @@ const SongDatagridRow = ({ record, children, multiDisc, ...rest }) => { )} )} - - {children} + setVisible(true)} + onMouseLeave={() => setVisible(false)} + {...rest} + > + {React.Children.map(children, (child) => + child && + isValidElement(child) && + child.type.name === 'SongContextMenu' + ? cloneElement(child, { visible, ...rest }) + : child + )} ) diff --git a/ui/src/common/index.js b/ui/src/common/index.js index 14f3399c..04a74fd8 100644 --- a/ui/src/common/index.js +++ b/ui/src/common/index.js @@ -12,6 +12,7 @@ import DocLink from './DocLink' import List from './List' import SongDatagridRow from './SongDatagridRow' import AddToPlaylistMenu from './AddToPlaylistMenu' +import SongContextMenu from './SongContextMenu' export { Title, @@ -30,4 +31,5 @@ export { ArtistLinkField, artistLink, AddToPlaylistMenu, + SongContextMenu, } diff --git a/ui/src/playlist/PlaylistSongs.js b/ui/src/playlist/PlaylistSongs.js index edfaa4db..c995149f 100644 --- a/ui/src/playlist/PlaylistSongs.js +++ b/ui/src/playlist/PlaylistSongs.js @@ -2,6 +2,7 @@ import React from 'react' import { BulkActionsToolbar, Datagrid, + DatagridBody, DatagridLoading, ListToolbar, TextField, @@ -11,8 +12,12 @@ import { import classnames from 'classnames' import { Card, useMediaQuery } from '@material-ui/core' import { makeStyles } from '@material-ui/core/styles' -import { DurationField, SongDetails } from '../common' -import { SongContextMenu } from '../song/SongContextMenu' +import { + DurationField, + SongDetails, + SongContextMenu, + SongDatagridRow, +} from '../common' const useStyles = makeStyles( (theme) => ({ @@ -50,6 +55,13 @@ const useStylesListToolbar = makeStyles({ }, }) +const SongsDatagridBody = (props) => ( + } /> +) +const SongsDatagrid = (props) => ( + } /> +) + const PlaylistSongs = (props) => { const classes = useStyles(props) const classesToolbar = useStylesListToolbar(props) @@ -106,7 +118,7 @@ const PlaylistSongs = (props) => { size={'small'} /> ) : ( - } rowClick={null} {...controllerProps} @@ -117,7 +129,7 @@ const PlaylistSongs = (props) => { {isDesktop && } - + )} diff --git a/ui/src/song/SongList.js b/ui/src/song/SongList.js index 4eea4af6..d7a55f1a 100644 --- a/ui/src/song/SongList.js +++ b/ui/src/song/SongList.js @@ -1,6 +1,7 @@ import React from 'react' import { Datagrid, + DatagridBody, Filter, FunctionField, NumberField, @@ -8,12 +9,18 @@ import { TextField, } from 'react-admin' import { useMediaQuery } from '@material-ui/core' -import { DurationField, SimpleList, List, SongDetails } from '../common' +import { + DurationField, + SimpleList, + List, + SongDetails, + SongDatagridRow, + SongContextMenu, +} from '../common' import { useDispatch } from 'react-redux' import { setTrack } from '../audioplayer' import { SongBulkActions } from './SongBulkActions' import { AlbumLinkField } from './AlbumLinkField' -import { SongContextMenu } from './SongContextMenu' const SongFilter = (props) => ( @@ -21,6 +28,13 @@ const SongFilter = (props) => ( ) +const SongsDatagridBody = (props) => ( + } /> +) +const SongsDatagrid = (props) => ( + } /> +) + const SongList = (props) => { const dispatch = useDispatch() const isXsmall = useMediaQuery((theme) => theme.breakpoints.down('xs')) @@ -48,7 +62,7 @@ const SongList = (props) => { rightIcon={(r) => } /> ) : ( - } rowClick={(id, basePath, record) => dispatch(setTrack(record))} > @@ -62,7 +76,7 @@ const SongList = (props) => { )} - + )} )