diff --git a/cmd/wire_injectors.go b/cmd/wire_injectors.go index 6a1f96931..c03d9b804 100644 --- a/cmd/wire_injectors.go +++ b/cmd/wire_injectors.go @@ -1,4 +1,5 @@ -//+build wireinject +//go:build wireinject +// +build wireinject package cmd diff --git a/scanner/metadata/taglib/get_filename.go b/scanner/metadata/taglib/get_filename.go index f4c2307b5..2a74fee40 100644 --- a/scanner/metadata/taglib/get_filename.go +++ b/scanner/metadata/taglib/get_filename.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package taglib diff --git a/scanner/metadata/taglib/get_filename_win.go b/scanner/metadata/taglib/get_filename_win.go index 90bafec5e..5f93c4294 100644 --- a/scanner/metadata/taglib/get_filename_win.go +++ b/scanner/metadata/taglib/get_filename_win.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package taglib diff --git a/ui/src/App.js b/ui/src/App.js index 67d163591..118cdc8ae 100644 --- a/ui/src/App.js +++ b/ui/src/App.js @@ -19,6 +19,7 @@ import customRoutes from './routes' import { themeReducer, addToPlaylistDialogReducer, + expandInfoDialogReducer, playerReducer, albumViewReducer, activityReducer, @@ -52,6 +53,7 @@ const App = () => ( albumView: albumViewReducer, theme: themeReducer, addToPlaylistDialog: addToPlaylistDialogReducer, + expandInfoDialog: expandInfoDialogReducer, activity: activityReducer, settings: settingsReducer, }, diff --git a/ui/src/actions/dialogs.js b/ui/src/actions/dialogs.js index 20c3beaed..10243c45a 100644 --- a/ui/src/actions/dialogs.js +++ b/ui/src/actions/dialogs.js @@ -2,6 +2,9 @@ export const ADD_TO_PLAYLIST_OPEN = 'ADD_TO_PLAYLIST_OPEN' export const ADD_TO_PLAYLIST_CLOSE = 'ADD_TO_PLAYLIST_CLOSE' export const DUPLICATE_SONG_WARNING_OPEN = 'DUPLICATE_SONG_WARNING_OPEN' export const DUPLICATE_SONG_WARNING_CLOSE = 'DUPLICATE_SONG_WARNING_CLOSE' +export const EXTENDED_INFO_OPEN = 'EXTENDED_INFO_OPEN' +export const EXTENDED_INFO_CLOSE = 'EXTENDED_INFO_CLOSE' + export const openAddToPlaylist = ({ selectedIds, onSuccess }) => ({ type: ADD_TO_PLAYLIST_OPEN, selectedIds, @@ -20,3 +23,14 @@ export const openDuplicateSongWarning = (duplicateIds) => ({ export const closeDuplicateSongDialog = () => ({ type: DUPLICATE_SONG_WARNING_CLOSE, }) + +export const openExtendedInfoDialog = (record) => { + return { + type: EXTENDED_INFO_OPEN, + record, + } +} + +export const closeExtendedInfoDialog = () => ({ + type: EXTENDED_INFO_CLOSE, +}) diff --git a/ui/src/album/AlbumInfo.js b/ui/src/album/AlbumInfo.js new file mode 100644 index 000000000..95909f734 --- /dev/null +++ b/ui/src/album/AlbumInfo.js @@ -0,0 +1,77 @@ +import Table from '@material-ui/core/Table' +import TableBody from '@material-ui/core/TableBody' +import inflection from 'inflection' +import TableCell from '@material-ui/core/TableCell' +import TableContainer from '@material-ui/core/TableContainer' +import TableRow from '@material-ui/core/TableRow' +import { + ArrayField, + BooleanField, + ChipField, + DateField, + SingleFieldList, + TextField, + useRecordContext, + useTranslate, +} from 'react-admin' +import { makeStyles } from '@material-ui/core/styles' +import { MultiLineTextField } from '../common' + +const useStyles = makeStyles({ + tableCell: { + width: '17.5%', + }, +}) + +const AlbumInfo = (props) => { + const classes = useStyles() + const translate = useTranslate() + const record = useRecordContext(props) + const data = { + album: , + albumArtist: , + genre: ( + + + + + + ), + compilation: , + updatedAt: , + comment: , + } + + const optionalFields = ['comment', 'genre'] + optionalFields.forEach((field) => { + !record[field] && delete data[field] + }) + + return ( + + + + {Object.keys(data).map((key) => { + return ( + + + {translate(`resources.album.fields.${key}`, { + _: inflection.humanize(inflection.underscore(key)), + })} + : + + {data[key]} + + ) + })} + +
+
+ ) +} + +export default AlbumInfo diff --git a/ui/src/album/AlbumList.js b/ui/src/album/AlbumList.js index 97abb6de8..f3ffcef62 100644 --- a/ui/src/album/AlbumList.js +++ b/ui/src/album/AlbumList.js @@ -27,6 +27,8 @@ import AlbumGridView from './AlbumGridView' import { AddToPlaylistDialog } from '../dialogs' import albumLists, { defaultAlbumList } from './albumLists' import config from '../config' +import AlbumInfo from './AlbumInfo' +import ExpandInfoDialog from '../dialogs/ExpandInfoDialog' const AlbumFilter = (props) => { const translate = useTranslate() @@ -130,6 +132,7 @@ const AlbumList = (props) => { )} + } /> ) } diff --git a/ui/src/album/AlbumSongs.js b/ui/src/album/AlbumSongs.js index b715a0da1..a0f257984 100644 --- a/ui/src/album/AlbumSongs.js +++ b/ui/src/album/AlbumSongs.js @@ -19,7 +19,7 @@ import { SongBulkActions, SongContextMenu, SongDatagrid, - SongDetails, + SongInfo, SongTitleField, RatingField, QualityInfo, @@ -28,6 +28,7 @@ import { } from '../common' import { AddToPlaylistDialog } from '../dialogs' import config from '../config' +import ExpandInfoDialog from '../dialogs/ExpandInfoDialog' const useStyles = makeStyles( (theme) => ({ @@ -85,7 +86,6 @@ const useStyles = makeStyles( const AlbumSongs = (props) => { const { data, ids } = props - const isXsmall = useMediaQuery((theme) => theme.breakpoints.down('xs')) const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md')) const classes = useStyles({ isDesktop }) const dispatch = useDispatch() @@ -157,7 +157,6 @@ const AlbumSongs = (props) => { } rowClick={(id) => dispatch(playTracks(data, ids, id))} {...props} hasBulkActions={true} @@ -183,6 +182,7 @@ const AlbumSongs = (props) => { + } /> ) } diff --git a/ui/src/album/AlbumTableView.js b/ui/src/album/AlbumTableView.js index 7e346f0ec..ad4c52816 100644 --- a/ui/src/album/AlbumTableView.js +++ b/ui/src/album/AlbumTableView.js @@ -1,22 +1,5 @@ import React, { useMemo } from 'react' -import Table from '@material-ui/core/Table' -import TableBody from '@material-ui/core/TableBody' -import inflection from 'inflection' -import TableCell from '@material-ui/core/TableCell' -import TableContainer from '@material-ui/core/TableContainer' -import TableRow from '@material-ui/core/TableRow' -import { - ArrayField, - BooleanField, - ChipField, - Datagrid, - DateField, - NumberField, - SingleFieldList, - TextField, - useRecordContext, - useTranslate, -} from 'react-admin' +import { Datagrid, NumberField, TextField } from 'react-admin' import { useMediaQuery } from '@material-ui/core' import FavoriteBorderIcon from '@material-ui/icons/FavoriteBorder' import { makeStyles } from '@material-ui/core/styles' @@ -25,7 +8,6 @@ import { DurationField, RangeField, SimpleList, - MultiLineTextField, AlbumContextMenu, RatingField, useSelectedFields, @@ -59,56 +41,6 @@ const useStyles = makeStyles({ }, }) -const AlbumDetails = (props) => { - const classes = useStyles() - const translate = useTranslate() - const record = useRecordContext(props) - const data = { - albumArtist: , - genre: ( - - - - - - ), - compilation: , - updatedAt: , - comment: , - } - - const optionalFields = ['comment', 'genre'] - optionalFields.forEach((field) => { - !record[field] && delete data[field] - }) - - return ( - - - - {Object.keys(data).map((key) => { - return ( - - - {translate(`resources.album.fields.${key}`, { - _: inflection.humanize(inflection.underscore(key)), - })} - : - - {data[key]} - - ) - })} - -
-
- ) -} - const AlbumTableView = ({ hasShow, hasEdit, @@ -180,12 +112,7 @@ const AlbumTableView = ({ {...rest} /> ) : ( - } - rowClick={'show'} - classes={{ row: classes.row }} - {...rest} - > + {columns} { const classes = useStyles({ color }) const dataProvider = useDataProvider() @@ -83,6 +85,14 @@ const ContextMenu = ({ )})`, action: () => subsonic.download(record.id), }, + ...(!hideInfo && { + info: { + enabled: true, + needData: true, + label: translate('resources.album.actions.info'), + action: () => dispatch(openExtendedInfoDialog(record)), + }, + }), } const handleClick = (e) => { @@ -195,6 +205,7 @@ export const ArtistContextMenu = (props) => props.record ? ( subsonic.download(record.mediaFileId || record.id), }, + info: { + enabled: true, + label: translate('resources.song.actions.info'), + action: (record) => dispatch(openExtendedInfoDialog(record)), + }, } const handleClick = (e) => { diff --git a/ui/src/common/SongDetails.js b/ui/src/common/SongInfo.js similarity index 98% rename from ui/src/common/SongDetails.js rename to ui/src/common/SongInfo.js index c61772e82..e5d3f4ce6 100644 --- a/ui/src/common/SongDetails.js +++ b/ui/src/common/SongInfo.js @@ -24,7 +24,7 @@ const useStyles = makeStyles({ }, }) -export const SongDetails = (props) => { +export const SongInfo = (props) => { const classes = useStyles() const translate = useTranslate() const record = useRecordContext(props) diff --git a/ui/src/common/index.js b/ui/src/common/index.js index 98135e8ad..ad9bb48c0 100644 --- a/ui/src/common/index.js +++ b/ui/src/common/index.js @@ -17,7 +17,7 @@ export * from './SimpleList' export * from './SizeField' export * from './SongContextMenu' export * from './SongDatagrid' -export * from './SongDetails' +export * from './SongInfo' export * from './SongTitleField' export * from './LoveButton' export * from './Title' diff --git a/ui/src/dialogs/ExpandInfoDialog.js b/ui/src/dialogs/ExpandInfoDialog.js new file mode 100644 index 000000000..40899a2eb --- /dev/null +++ b/ui/src/dialogs/ExpandInfoDialog.js @@ -0,0 +1,57 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { useDispatch, useSelector } from 'react-redux' +import { RecordContextProvider, useTranslate } from 'react-admin' +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, +} from '@material-ui/core' +import { closeExtendedInfoDialog } from '../actions' + +const ExpandInfoDialog = ({ title, content }) => { + const { open, record } = useSelector((state) => state.expandInfoDialog) + const dispatch = useDispatch() + const translate = useTranslate() + + const handleClose = (e) => { + dispatch(closeExtendedInfoDialog()) + e.stopPropagation() + } + + return ( + + + {translate(title || 'resources.song.actions.info')} + + + {record && ( + + {content} + + )} + + + + + + ) +} + +ExpandInfoDialog.propTypes = { + title: PropTypes.string, + content: PropTypes.elementType.isRequired, +} + +export default ExpandInfoDialog diff --git a/ui/src/i18n/en.json b/ui/src/i18n/en.json index 3fa2080e2..baed81a9a 100644 --- a/ui/src/i18n/en.json +++ b/ui/src/i18n/en.json @@ -33,7 +33,8 @@ "addToPlaylist": "Add to Playlist", "shuffleAll": "Shuffle All", "download": "Download", - "playNext": "Play Next" + "playNext": "Play Next", + "info": "Get Info" } }, "album": { @@ -58,7 +59,8 @@ "addToQueue": "Play Later", "shuffle": "Shuffle", "addToPlaylist": "Add to Playlist", - "download": "Download" + "download": "Download", + "info": "Get Info" }, "lists": { "all": "All", diff --git a/ui/src/playlist/PlaylistSongs.js b/ui/src/playlist/PlaylistSongs.js index b8c816ae6..435f494c9 100644 --- a/ui/src/playlist/PlaylistSongs.js +++ b/ui/src/playlist/PlaylistSongs.js @@ -19,7 +19,7 @@ import { makeStyles } from '@material-ui/core/styles' import ReactDragListView from 'react-drag-listview' import { DurationField, - SongDetails, + SongInfo, SongContextMenu, SongDatagrid, SongTitleField, @@ -31,6 +31,7 @@ import { AddToPlaylistDialog } from '../dialogs' import { AlbumLinkField } from '../song/AlbumLinkField' import { playTracks } from '../actions' import PlaylistSongBulkActions from './PlaylistSongBulkActions' +import ExpandInfoDialog from '../dialogs/ExpandInfoDialog' const useStyles = makeStyles( (theme) => ({ @@ -85,7 +86,6 @@ const ReorderableList = ({ readOnly, children, ...rest }) => { const PlaylistSongs = ({ playlistId, readOnly, actions, ...props }) => { const listContext = useListContext() const { data, ids, onUnselectItems } = listContext - const isXsmall = useMediaQuery((theme) => theme.breakpoints.down('xs')) const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md')) const classes = useStyles({ isDesktop }) const dispatch = useDispatch() @@ -186,7 +186,6 @@ const PlaylistSongs = ({ playlistId, readOnly, actions, ...props }) => { nodeSelector={'tr'} > } rowClick={(id) => dispatch(playTracks(data, ids, id))} {...listContext} hasBulkActions={true} @@ -204,6 +203,7 @@ const PlaylistSongs = ({ playlistId, readOnly, actions, ...props }) => { + } /> {React.cloneElement(props.pagination, listContext)} ) diff --git a/ui/src/reducers/dialogReducer.js b/ui/src/reducers/dialogReducer.js index 9d83f2760..f51007bbb 100644 --- a/ui/src/reducers/dialogReducer.js +++ b/ui/src/reducers/dialogReducer.js @@ -3,6 +3,8 @@ import { ADD_TO_PLAYLIST_OPEN, DUPLICATE_SONG_WARNING_OPEN, DUPLICATE_SONG_WARNING_CLOSE, + EXTENDED_INFO_OPEN, + EXTENDED_INFO_CLOSE, } from '../actions' export const addToPlaylistDialogReducer = ( @@ -35,3 +37,27 @@ export const addToPlaylistDialogReducer = ( return previousState } } + +export const expandInfoDialogReducer = ( + previousState = { + open: false, + }, + payload +) => { + const { type } = payload + switch (type) { + case EXTENDED_INFO_OPEN: + return { + ...previousState, + open: true, + record: payload.record, + } + case EXTENDED_INFO_CLOSE: + return { + ...previousState, + open: false, + } + default: + return previousState + } +} diff --git a/ui/src/song/SongList.js b/ui/src/song/SongList.js index c0a4ff2d5..9f99f0940 100644 --- a/ui/src/song/SongList.js +++ b/ui/src/song/SongList.js @@ -17,7 +17,7 @@ import { List, SongContextMenu, SongDatagrid, - SongDetails, + SongInfo, QuickFilter, SongTitleField, SongSimpleList, @@ -33,6 +33,7 @@ import { AlbumLinkField } from './AlbumLinkField' import { AddToPlaylistDialog } from '../dialogs' import { SongBulkActions, QualityInfo, useSelectedFields } from '../common' import config from '../config' +import ExpandInfoDialog from '../dialogs/ExpandInfoDialog' const useStyles = makeStyles({ contextHeader: { @@ -167,7 +168,6 @@ const SongList = (props) => { ) : ( } rowClick={handleRowClick} contextAlwaysVisible={!isDesktop} classes={{ row: classes.row }} @@ -193,6 +193,7 @@ const SongList = (props) => { )} + } /> ) }