Help dialog with available hotkeys
This commit is contained in:
@@ -17,7 +17,7 @@ import { List, QuickFilter, Title, useAlbumsPerPage } from '../common'
|
|||||||
import AlbumListActions from './AlbumListActions'
|
import AlbumListActions from './AlbumListActions'
|
||||||
import AlbumListView from './AlbumListView'
|
import AlbumListView from './AlbumListView'
|
||||||
import AlbumGridView from './AlbumGridView'
|
import AlbumGridView from './AlbumGridView'
|
||||||
import AddToPlaylistDialog from '../dialogs/AddToPlaylistDialog'
|
import { AddToPlaylistDialog } from '../dialogs'
|
||||||
import albumLists, { defaultAlbumList } from './albumLists'
|
import albumLists, { defaultAlbumList } from './albumLists'
|
||||||
|
|
||||||
const AlbumFilter = (props) => {
|
const AlbumFilter = (props) => {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
SongDetails,
|
SongDetails,
|
||||||
SongTitleField,
|
SongTitleField,
|
||||||
} from '../common'
|
} from '../common'
|
||||||
import AddToPlaylistDialog from '../dialogs/AddToPlaylistDialog'
|
import { AddToPlaylistDialog } from '../dialogs'
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
(theme) => ({
|
(theme) => ({
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
import { useMediaQuery, withWidth } from '@material-ui/core'
|
import { useMediaQuery, withWidth } from '@material-ui/core'
|
||||||
import StarIcon from '@material-ui/icons/Star'
|
import StarIcon from '@material-ui/icons/Star'
|
||||||
import StarBorderIcon from '@material-ui/icons/StarBorder'
|
import StarBorderIcon from '@material-ui/icons/StarBorder'
|
||||||
import AddToPlaylistDialog from '../dialogs/AddToPlaylistDialog'
|
import { AddToPlaylistDialog } from '../dialogs'
|
||||||
import {
|
import {
|
||||||
ArtistContextMenu,
|
ArtistContextMenu,
|
||||||
List,
|
List,
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { withStyles } from '@material-ui/core/styles'
|
|
||||||
import Link from '@material-ui/core/Link'
|
import Link from '@material-ui/core/Link'
|
||||||
import Dialog from '@material-ui/core/Dialog'
|
import Dialog from '@material-ui/core/Dialog'
|
||||||
import MuiDialogTitle from '@material-ui/core/DialogTitle'
|
|
||||||
import MuiDialogContent from '@material-ui/core/DialogContent'
|
|
||||||
import IconButton from '@material-ui/core/IconButton'
|
import IconButton from '@material-ui/core/IconButton'
|
||||||
import CloseIcon from '@material-ui/icons/Close'
|
|
||||||
import Typography from '@material-ui/core/Typography'
|
|
||||||
import TableContainer from '@material-ui/core/TableContainer'
|
import TableContainer from '@material-ui/core/TableContainer'
|
||||||
import Table from '@material-ui/core/Table'
|
import Table from '@material-ui/core/Table'
|
||||||
import TableBody from '@material-ui/core/TableBody'
|
import TableBody from '@material-ui/core/TableBody'
|
||||||
@@ -18,19 +13,8 @@ import FavoriteBorderIcon from '@material-ui/icons/FavoriteBorder'
|
|||||||
import inflection from 'inflection'
|
import inflection from 'inflection'
|
||||||
import { useTranslate } from 'react-admin'
|
import { useTranslate } from 'react-admin'
|
||||||
import config from '../config'
|
import config from '../config'
|
||||||
|
import { DialogTitle } from './DialogTitle'
|
||||||
const styles = (theme) => ({
|
import { DialogContent } from './DialogContent'
|
||||||
root: {
|
|
||||||
margin: 0,
|
|
||||||
padding: theme.spacing(2),
|
|
||||||
},
|
|
||||||
closeButton: {
|
|
||||||
position: 'absolute',
|
|
||||||
right: theme.spacing(1),
|
|
||||||
top: theme.spacing(1),
|
|
||||||
color: theme.palette.grey[500],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const links = {
|
const links = {
|
||||||
homepage: 'navidrome.org',
|
homepage: 'navidrome.org',
|
||||||
@@ -41,30 +25,6 @@ const links = {
|
|||||||
featureRequests: 'github.com/deluan/navidrome/issues',
|
featureRequests: 'github.com/deluan/navidrome/issues',
|
||||||
}
|
}
|
||||||
|
|
||||||
const DialogTitle = withStyles(styles)((props) => {
|
|
||||||
const { children, classes, onClose, ...other } = props
|
|
||||||
return (
|
|
||||||
<MuiDialogTitle disableTypography className={classes.root} {...other}>
|
|
||||||
<Typography variant="h5">{children}</Typography>
|
|
||||||
{onClose ? (
|
|
||||||
<IconButton
|
|
||||||
aria-label="close"
|
|
||||||
className={classes.closeButton}
|
|
||||||
onClick={onClose}
|
|
||||||
>
|
|
||||||
<CloseIcon />
|
|
||||||
</IconButton>
|
|
||||||
) : null}
|
|
||||||
</MuiDialogTitle>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const DialogContent = withStyles((theme) => ({
|
|
||||||
root: {
|
|
||||||
padding: theme.spacing(2),
|
|
||||||
},
|
|
||||||
}))(MuiDialogContent)
|
|
||||||
|
|
||||||
const AboutDialog = ({ open, onClose }) => {
|
const AboutDialog = ({ open, onClose }) => {
|
||||||
const translate = useTranslate()
|
const translate = useTranslate()
|
||||||
return (
|
return (
|
||||||
@@ -79,7 +39,7 @@ const AboutDialog = ({ open, onClose }) => {
|
|||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
<TableContainer component={Paper}>
|
<TableContainer component={Paper}>
|
||||||
<Table aria-label="song details" size="small">
|
<Table aria-label={translate('menu.about')} size="small">
|
||||||
<TableBody>
|
<TableBody>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell align="right" component="th" scope="row">
|
<TableCell align="right" component="th" scope="row">
|
||||||
@@ -143,4 +103,4 @@ AboutDialog.propTypes = {
|
|||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AboutDialog
|
export { AboutDialog }
|
||||||
@@ -14,9 +14,9 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from '@material-ui/core'
|
} from '@material-ui/core'
|
||||||
import { closeAddToPlaylist } from '../actions'
|
import { closeAddToPlaylist } from '../actions'
|
||||||
import SelectPlaylistInput from './SelectPlaylistInput'
|
import { SelectPlaylistInput } from './SelectPlaylistInput'
|
||||||
|
|
||||||
const AddToPlaylistDialog = () => {
|
export const AddToPlaylistDialog = () => {
|
||||||
const { open, selectedIds, onSuccess } = useSelector(
|
const { open, selectedIds, onSuccess } = useSelector(
|
||||||
(state) => state.addToPlaylistDialog
|
(state) => state.addToPlaylistDialog
|
||||||
)
|
)
|
||||||
@@ -96,5 +96,3 @@ const AddToPlaylistDialog = () => {
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AddToPlaylistDialog
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { withStyles } from '@material-ui/core/styles'
|
||||||
|
import MuiDialogContent from '@material-ui/core/DialogContent'
|
||||||
|
|
||||||
|
export const DialogContent = withStyles((theme) => ({
|
||||||
|
root: {
|
||||||
|
padding: theme.spacing(2),
|
||||||
|
},
|
||||||
|
}))(MuiDialogContent)
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { withStyles } from '@material-ui/core/styles'
|
||||||
|
import MuiDialogTitle from '@material-ui/core/DialogTitle'
|
||||||
|
import Typography from '@material-ui/core/Typography'
|
||||||
|
import IconButton from '@material-ui/core/IconButton'
|
||||||
|
import CloseIcon from '@material-ui/icons/Close'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const styles = (theme) => ({
|
||||||
|
root: {
|
||||||
|
margin: 0,
|
||||||
|
padding: theme.spacing(2),
|
||||||
|
},
|
||||||
|
closeButton: {
|
||||||
|
position: 'absolute',
|
||||||
|
right: theme.spacing(1),
|
||||||
|
top: theme.spacing(1),
|
||||||
|
color: theme.palette.grey[500],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const DialogTitle = withStyles(styles)((props) => {
|
||||||
|
const { children, classes, onClose, ...other } = props
|
||||||
|
return (
|
||||||
|
<MuiDialogTitle disableTypography className={classes.root} {...other}>
|
||||||
|
<Typography variant="h5">{children}</Typography>
|
||||||
|
<IconButton
|
||||||
|
aria-label="close"
|
||||||
|
className={classes.closeButton}
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
</MuiDialogTitle>
|
||||||
|
)
|
||||||
|
})
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import React, { useCallback, useState } from 'react'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
import { Dialog } from '@material-ui/core'
|
||||||
|
import { getApplicationKeyMap, GlobalHotKeys } from 'react-hotkeys'
|
||||||
|
import TableContainer from '@material-ui/core/TableContainer'
|
||||||
|
import Paper from '@material-ui/core/Paper'
|
||||||
|
import Table from '@material-ui/core/Table'
|
||||||
|
import TableBody from '@material-ui/core/TableBody'
|
||||||
|
import TableRow from '@material-ui/core/TableRow'
|
||||||
|
import TableCell from '@material-ui/core/TableCell'
|
||||||
|
import { useTranslate } from 'react-admin'
|
||||||
|
import inflection from 'inflection'
|
||||||
|
import { keyMap } from '../hotkeys'
|
||||||
|
import { DialogTitle } from './DialogTitle'
|
||||||
|
import { DialogContent } from './DialogContent'
|
||||||
|
|
||||||
|
const HelpTable = (props) => {
|
||||||
|
const keyMap = getApplicationKeyMap()
|
||||||
|
const translate = useTranslate()
|
||||||
|
return ReactDOM.createPortal(
|
||||||
|
<Dialog {...props}>
|
||||||
|
<DialogTitle onClose={props.onClose}>
|
||||||
|
{translate('help.title')}
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent dividers>
|
||||||
|
<TableContainer component={Paper}>
|
||||||
|
<Table size="small">
|
||||||
|
<TableBody>
|
||||||
|
{Object.keys(keyMap).map((key) => {
|
||||||
|
const { sequences, name } = keyMap[key]
|
||||||
|
const description = translate(`help.hotkeys.${name}`, {
|
||||||
|
_: inflection.humanize(name),
|
||||||
|
})
|
||||||
|
return (
|
||||||
|
<TableRow key={key}>
|
||||||
|
<TableCell align="right" component="th" scope="row">
|
||||||
|
{description}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell align="left">
|
||||||
|
{sequences.map(({ sequence }) => (
|
||||||
|
<span key={sequence}>{sequence}</span>
|
||||||
|
))}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>,
|
||||||
|
document.body
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HelpDialog = (props) => {
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
|
||||||
|
const handleClickClose = (e) => {
|
||||||
|
setOpen(false)
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlers = {
|
||||||
|
SHOW_HELP: useCallback(() => setOpen(true), [setOpen]),
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<GlobalHotKeys keyMap={keyMap} handlers={handlers} allowChanges />
|
||||||
|
<HelpTable
|
||||||
|
open={open}
|
||||||
|
onClose={handleClickClose}
|
||||||
|
onBackdropClick={handleClickClose}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -6,11 +6,11 @@ import Autocomplete, {
|
|||||||
} from '@material-ui/lab/Autocomplete'
|
} from '@material-ui/lab/Autocomplete'
|
||||||
import { useGetList, useTranslate } from 'react-admin'
|
import { useGetList, useTranslate } from 'react-admin'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { isWritable } from '../common/Writable'
|
import { isWritable } from '../common'
|
||||||
|
|
||||||
const filter = createFilterOptions()
|
const filter = createFilterOptions()
|
||||||
|
|
||||||
const SelectPlaylistInput = ({ onChange }) => {
|
export const SelectPlaylistInput = ({ onChange }) => {
|
||||||
const translate = useTranslate()
|
const translate = useTranslate()
|
||||||
const { ids, data } = useGetList(
|
const { ids, data } = useGetList(
|
||||||
'playlist',
|
'playlist',
|
||||||
@@ -94,5 +94,3 @@ const SelectPlaylistInput = ({ onChange }) => {
|
|||||||
SelectPlaylistInput.propTypes = {
|
SelectPlaylistInput.propTypes = {
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectPlaylistInput
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export * from './AboutDialog'
|
||||||
|
export * from './AddToPlaylistDialog'
|
||||||
|
export * from './SelectPlaylistInput'
|
||||||
|
export * from './HelpDialog'
|
||||||
+8
-8
@@ -1,12 +1,12 @@
|
|||||||
const keyMap = {
|
const keyMap = {
|
||||||
TOGGLE_MENU: { name: 'Toggle Menu Side Bar', sequence: 'm', group: 'Global' },
|
SHOW_HELP: { name: 'show_help', sequence: 'shift+?', group: 'Global' },
|
||||||
|
TOGGLE_MENU: { name: 'toggle_menu', sequence: 'm', group: 'Global' },
|
||||||
TOGGLE_PLAY: { name: 'Play / Pause', sequence: 'p', group: 'Player' },
|
TOGGLE_PLAY: { name: 'toggle_play', sequence: 'p', group: 'Player' },
|
||||||
PREV_SONG: { name: 'Previous Songs', sequence: 'left', group: 'Player' },
|
PREV_SONG: { name: 'prev_song', sequence: 'left', group: 'Player' },
|
||||||
NEXT_SONG: { name: 'Next Song', sequence: 'right', group: 'Player' },
|
NEXT_SONG: { name: 'next_song', sequence: 'right', group: 'Player' },
|
||||||
VOL_UP: { name: 'Volume Up', sequence: '=', group: 'Player' },
|
VOL_UP: { name: 'vol_up', sequence: '=', group: 'Player' },
|
||||||
VOL_DOWN: { name: 'Volume Down', sequence: '-', group: 'Player' },
|
VOL_DOWN: { name: 'vol_down', sequence: '-', group: 'Player' },
|
||||||
TOGGLE_STAR: { name: 'Toggle Star', sequence: 's', group: 'Player' },
|
TOGGLE_STAR: { name: 'toggle_star', sequence: 's', group: 'Player' },
|
||||||
}
|
}
|
||||||
|
|
||||||
export { keyMap }
|
export { keyMap }
|
||||||
|
|||||||
@@ -324,5 +324,18 @@
|
|||||||
"fullScan": "Full Scan",
|
"fullScan": "Full Scan",
|
||||||
"serverUptime": "Server Uptime",
|
"serverUptime": "Server Uptime",
|
||||||
"serverDown": "OFFLINE"
|
"serverDown": "OFFLINE"
|
||||||
|
},
|
||||||
|
"help": {
|
||||||
|
"title": "Navidrome Hotkeys",
|
||||||
|
"hotkeys": {
|
||||||
|
"show_help": "Show This Help",
|
||||||
|
"toggle_menu": "Toggle Menu Side Bar",
|
||||||
|
"toggle_play": "Play / Pause",
|
||||||
|
"prev_song": "Previous Song",
|
||||||
|
"next_song": "Next Song",
|
||||||
|
"vol_up": "Volume Up",
|
||||||
|
"vol_down": "Volume Down",
|
||||||
|
"toggle_star": "Toggle Current Song's Star"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { useSelector } from 'react-redux'
|
|||||||
import { makeStyles, MenuItem, ListItemIcon, Divider } from '@material-ui/core'
|
import { makeStyles, MenuItem, ListItemIcon, Divider } from '@material-ui/core'
|
||||||
import ViewListIcon from '@material-ui/icons/ViewList'
|
import ViewListIcon from '@material-ui/icons/ViewList'
|
||||||
import InfoIcon from '@material-ui/icons/Info'
|
import InfoIcon from '@material-ui/icons/Info'
|
||||||
import AboutDialog from './AboutDialog'
|
import { AboutDialog } from '../dialogs'
|
||||||
import PersonalMenu from './PersonalMenu'
|
import PersonalMenu from './PersonalMenu'
|
||||||
import ActivityPanel from './ActivityPanel'
|
import ActivityPanel from './ActivityPanel'
|
||||||
import UserMenu from './UserMenu'
|
import UserMenu from './UserMenu'
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import AlbumIcon from '@material-ui/icons/Album'
|
|||||||
import SubMenu from './SubMenu'
|
import SubMenu from './SubMenu'
|
||||||
import inflection from 'inflection'
|
import inflection from 'inflection'
|
||||||
import albumLists from '../album/albumLists'
|
import albumLists from '../album/albumLists'
|
||||||
|
import { HelpDialog } from '../dialogs'
|
||||||
|
|
||||||
const translatedResourceName = (resource, translate) =>
|
const translatedResourceName = (resource, translate) =>
|
||||||
translate(`resources.${resource.name}.name`, {
|
translate(`resources.${resource.name}.name`, {
|
||||||
@@ -108,6 +109,7 @@ const Menu = ({ onMenuClick, dense, logout }) => {
|
|||||||
</SubMenu>
|
</SubMenu>
|
||||||
{resources.filter(subItems(undefined)).map(renderResourceMenuItemLink)}
|
{resources.filter(subItems(undefined)).map(renderResourceMenuItemLink)}
|
||||||
{isXsmall && logout}
|
{isXsmall && logout}
|
||||||
|
<HelpDialog />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
SongDatagrid,
|
SongDatagrid,
|
||||||
SongTitleField,
|
SongTitleField,
|
||||||
} from '../common'
|
} from '../common'
|
||||||
import AddToPlaylistDialog from '../dialogs/AddToPlaylistDialog'
|
import { AddToPlaylistDialog } from '../dialogs'
|
||||||
import { AlbumLinkField } from '../song/AlbumLinkField'
|
import { AlbumLinkField } from '../song/AlbumLinkField'
|
||||||
import { playTracks } from '../actions'
|
import { playTracks } from '../actions'
|
||||||
import PlaylistSongBulkActions from './PlaylistSongBulkActions'
|
import PlaylistSongBulkActions from './PlaylistSongBulkActions'
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { setTrack } from '../actions'
|
|||||||
import { SongBulkActions } from '../common'
|
import { SongBulkActions } from '../common'
|
||||||
import { SongListActions } from './SongListActions'
|
import { SongListActions } from './SongListActions'
|
||||||
import { AlbumLinkField } from './AlbumLinkField'
|
import { AlbumLinkField } from './AlbumLinkField'
|
||||||
import AddToPlaylistDialog from '../dialogs/AddToPlaylistDialog'
|
import { AddToPlaylistDialog } from '../dialogs'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import StarBorderIcon from '@material-ui/icons/StarBorder'
|
import StarBorderIcon from '@material-ui/icons/StarBorder'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user