Option to toggle fields in songs, albums & artists (#923)
* Add toggleColumns - Add logic for toggling columns - Add MenuComponent + useSelectedFields hook * Refactoring * eslint-fixes * Typo * skip menu in albumGridView * add omittedFields * Add toggling for playlists and albumSong * Refactoring * defaultProps - fix * Add toggling for PlaylistSongs * remove accidental console log * Refactoring for future compatibility * Hide ToggleMenu in albumGridView * Add TopBarComponent in ToggleFieldsMenu * Add defaultOff for useSelectedFields * Fix edge case * eslint fix * Refactoring * Add propType for forwardRef * Fix issues * add translation for grid and table * add translation for grid and table * Ignore menuBtn for spotify-ish and Ligera themes * hide bpm by default in playlistSongs * Add memoization * Default album view must be Grid Co-authored-by: Deluan <deluan@navidrome.org>
This commit is contained in:
@@ -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 (
|
||||
<TopToolbar className={className} {...sanitizeListRestProps(rest)}>
|
||||
<Button
|
||||
onClick={handlePlay}
|
||||
label={translate('resources.album.actions.playAll')}
|
||||
>
|
||||
<PlayArrowIcon />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleShuffle}
|
||||
label={translate('resources.album.actions.shuffle')}
|
||||
>
|
||||
<ShuffleIcon />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handlePlayNext}
|
||||
label={translate('resources.album.actions.playNext')}
|
||||
>
|
||||
<RiPlayList2Fill />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handlePlayLater}
|
||||
label={translate('resources.album.actions.addToQueue')}
|
||||
>
|
||||
<RiPlayListAddFill />
|
||||
</Button>
|
||||
{config.enableDownloads && (
|
||||
<Button
|
||||
onClick={handleDownload}
|
||||
label={
|
||||
translate('resources.album.actions.download') +
|
||||
(isDesktop ? ` (${formatBytes(record.size)})` : '')
|
||||
}
|
||||
>
|
||||
<CloudDownloadOutlinedIcon />
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
onClick={handleExport}
|
||||
label={translate('resources.playlist.actions.export')}
|
||||
>
|
||||
<QueueMusicIcon />
|
||||
</Button>
|
||||
<div className={classes.toolbar}>
|
||||
<div>
|
||||
<Button
|
||||
onClick={handlePlay}
|
||||
label={translate('resources.album.actions.playAll')}
|
||||
>
|
||||
<PlayArrowIcon />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleShuffle}
|
||||
label={translate('resources.album.actions.shuffle')}
|
||||
>
|
||||
<ShuffleIcon />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handlePlayNext}
|
||||
label={translate('resources.album.actions.playNext')}
|
||||
>
|
||||
<RiPlayList2Fill />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handlePlayLater}
|
||||
label={translate('resources.album.actions.addToQueue')}
|
||||
>
|
||||
<RiPlayListAddFill />
|
||||
</Button>
|
||||
{config.enableDownloads && (
|
||||
<Button
|
||||
onClick={handleDownload}
|
||||
label={
|
||||
translate('resources.album.actions.download') +
|
||||
(isDesktop ? ` (${formatBytes(record.size)})` : '')
|
||||
}
|
||||
>
|
||||
<CloudDownloadOutlinedIcon />
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
onClick={handleExport}
|
||||
label={translate('resources.playlist.actions.export')}
|
||||
>
|
||||
<QueueMusicIcon />
|
||||
</Button>
|
||||
</div>
|
||||
<div>{isNotSmall && <ToggleFieldsMenu resource="playlistTrack" />}</div>
|
||||
</div>
|
||||
</TopToolbar>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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) => (
|
||||
<Filter {...props} variant={'outlined'}>
|
||||
@@ -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: <TextField source="owner" />,
|
||||
songCount: isDesktop && <NumberField source="songCount" />,
|
||||
duration: isDesktop && <DurationField source="duration" />,
|
||||
updatedAt: isDesktop && (
|
||||
<DateField source="updatedAt" sortByOrder={'DESC'} />
|
||||
),
|
||||
public: !isXsmall && (
|
||||
<TogglePublicInput
|
||||
source="public"
|
||||
permissions={permissions}
|
||||
sortByOrder={'DESC'}
|
||||
/>
|
||||
),
|
||||
}
|
||||
}, [isDesktop, isXsmall, permissions])
|
||||
|
||||
const columns = useSelectedFields({
|
||||
resource: 'playlist',
|
||||
columns: toggleableFields,
|
||||
})
|
||||
|
||||
return (
|
||||
<List {...props} exporter={false} filters={<PlaylistFilter />}>
|
||||
<List
|
||||
{...props}
|
||||
exporter={false}
|
||||
filters={<PlaylistFilter />}
|
||||
actions={<PlaylistListActions />}
|
||||
>
|
||||
<Datagrid
|
||||
rowClick="show"
|
||||
isRowSelectable={(r) => isWritable(r && r.owner)}
|
||||
>
|
||||
<TextField source="name" />
|
||||
<TextField source="owner" />
|
||||
{isDesktop && <NumberField source="songCount" />}
|
||||
{isDesktop && <DurationField source="duration" />}
|
||||
{isDesktop && <DateField source="updatedAt" sortByOrder={'DESC'} />}
|
||||
{!isXsmall && (
|
||||
<TogglePublicInput
|
||||
source="public"
|
||||
permissions={permissions}
|
||||
sortByOrder={'DESC'}
|
||||
/>
|
||||
)}
|
||||
{columns}
|
||||
<Writable>
|
||||
<EditButton />
|
||||
</Writable>
|
||||
|
||||
@@ -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 (
|
||||
<TopToolbar className={className} {...sanitizeListRestProps(rest)}>
|
||||
<CreateButton basePath="/playlist">
|
||||
{translate('ra.action.create')}
|
||||
</CreateButton>
|
||||
{isNotSmall && <ToggleFieldsMenu resource="playlist" />}
|
||||
</TopToolbar>
|
||||
)
|
||||
}
|
||||
|
||||
export default PlaylistListActions
|
||||
@@ -13,7 +13,9 @@ import PlaylistActions from './PlaylistActions'
|
||||
import { Title, isReadOnly } from '../common'
|
||||
const useStyles = makeStyles(
|
||||
(theme) => ({
|
||||
playlistActions: {},
|
||||
playlistActions: {
|
||||
width: '100%',
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: 'NDPlaylistShow',
|
||||
|
||||
@@ -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 && <TextField source="id" label={'#'} />,
|
||||
title: <SongTitleField source="title" showTrackNumbers={false} />,
|
||||
album: isDesktop && <AlbumLinkField source="album" />,
|
||||
artist: isDesktop && <TextField source="artist" />,
|
||||
duration: (
|
||||
<DurationField source="duration" className={classes.draggable} />
|
||||
),
|
||||
quality: isDesktop && <QualityInfo source="quality" sortable={false} />,
|
||||
bpm: isDesktop && <NumberField source="bpm" />,
|
||||
}
|
||||
}, [isDesktop, classes.draggable])
|
||||
|
||||
const columns = useSelectedFields({
|
||||
resource: 'playlistTrack',
|
||||
columns: toggleableFields,
|
||||
defaultOff: ['bpm'],
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<ListToolbar
|
||||
@@ -161,13 +182,7 @@ const PlaylistSongs = ({ playlistId, readOnly, actions, ...props }) => {
|
||||
contextAlwaysVisible={!isDesktop}
|
||||
classes={{ row: classes.row }}
|
||||
>
|
||||
{isDesktop && <TextField source="id" label={'#'} />}
|
||||
<SongTitleField source="title" showTrackNumbers={false} />
|
||||
{isDesktop && <AlbumLinkField source="album" />}
|
||||
{isDesktop && <TextField source="artist" />}
|
||||
<DurationField source="duration" className={classes.draggable} />
|
||||
{isDesktop && <QualityInfo source="quality" sortable={false} />}
|
||||
{isDesktop && <NumberField source="bpm" />}
|
||||
{columns}
|
||||
<SongContextMenu
|
||||
onAddToPlaylist={onAddToPlaylist}
|
||||
showLove={false}
|
||||
|
||||
Reference in New Issue
Block a user