diff --git a/ui/src/album/AlbumList.js b/ui/src/album/AlbumList.js
index dd90fce8..10ed0860 100644
--- a/ui/src/album/AlbumList.js
+++ b/ui/src/album/AlbumList.js
@@ -17,7 +17,7 @@ import { List, QuickFilter, Title, useAlbumsPerPage } from '../common'
import AlbumListActions from './AlbumListActions'
import AlbumListView from './AlbumListView'
import AlbumGridView from './AlbumGridView'
-import AddToPlaylistDialog from '../dialogs/AddToPlaylistDialog'
+import { AddToPlaylistDialog } from '../dialogs'
import albumLists, { defaultAlbumList } from './albumLists'
const AlbumFilter = (props) => {
diff --git a/ui/src/album/AlbumSongs.js b/ui/src/album/AlbumSongs.js
index bb34f658..8bcac53a 100644
--- a/ui/src/album/AlbumSongs.js
+++ b/ui/src/album/AlbumSongs.js
@@ -20,7 +20,7 @@ import {
SongDetails,
SongTitleField,
} from '../common'
-import AddToPlaylistDialog from '../dialogs/AddToPlaylistDialog'
+import { AddToPlaylistDialog } from '../dialogs'
const useStyles = makeStyles(
(theme) => ({
diff --git a/ui/src/artist/ArtistList.js b/ui/src/artist/ArtistList.js
index 20600a79..1e59bc45 100644
--- a/ui/src/artist/ArtistList.js
+++ b/ui/src/artist/ArtistList.js
@@ -10,7 +10,7 @@ import {
import { useMediaQuery, withWidth } from '@material-ui/core'
import StarIcon from '@material-ui/icons/Star'
import StarBorderIcon from '@material-ui/icons/StarBorder'
-import AddToPlaylistDialog from '../dialogs/AddToPlaylistDialog'
+import { AddToPlaylistDialog } from '../dialogs'
import {
ArtistContextMenu,
List,
diff --git a/ui/src/layout/AboutDialog.js b/ui/src/dialogs/AboutDialog.js
similarity index 74%
rename from ui/src/layout/AboutDialog.js
rename to ui/src/dialogs/AboutDialog.js
index 00e59caf..fa2be42c 100644
--- a/ui/src/layout/AboutDialog.js
+++ b/ui/src/dialogs/AboutDialog.js
@@ -1,13 +1,8 @@
import React from 'react'
import PropTypes from 'prop-types'
-import { withStyles } from '@material-ui/core/styles'
import Link from '@material-ui/core/Link'
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 CloseIcon from '@material-ui/icons/Close'
-import Typography from '@material-ui/core/Typography'
import TableContainer from '@material-ui/core/TableContainer'
import Table from '@material-ui/core/Table'
import TableBody from '@material-ui/core/TableBody'
@@ -18,19 +13,8 @@ import FavoriteBorderIcon from '@material-ui/icons/FavoriteBorder'
import inflection from 'inflection'
import { useTranslate } from 'react-admin'
import config from '../config'
-
-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],
- },
-})
+import { DialogTitle } from './DialogTitle'
+import { DialogContent } from './DialogContent'
const links = {
homepage: 'navidrome.org',
@@ -41,30 +25,6 @@ const links = {
featureRequests: 'github.com/deluan/navidrome/issues',
}
-const DialogTitle = withStyles(styles)((props) => {
- const { children, classes, onClose, ...other } = props
- return (
-
- {children}
- {onClose ? (
-
-
-
- ) : null}
-
- )
-})
-
-const DialogContent = withStyles((theme) => ({
- root: {
- padding: theme.spacing(2),
- },
-}))(MuiDialogContent)
-
const AboutDialog = ({ open, onClose }) => {
const translate = useTranslate()
return (
@@ -79,7 +39,7 @@ const AboutDialog = ({ open, onClose }) => {
-
+
@@ -143,4 +103,4 @@ AboutDialog.propTypes = {
onClose: PropTypes.func.isRequired,
}
-export default AboutDialog
+export { AboutDialog }
diff --git a/ui/src/dialogs/AddToPlaylistDialog.js b/ui/src/dialogs/AddToPlaylistDialog.js
index de0d9a44..00dbec9b 100644
--- a/ui/src/dialogs/AddToPlaylistDialog.js
+++ b/ui/src/dialogs/AddToPlaylistDialog.js
@@ -14,9 +14,9 @@ import {
DialogTitle,
} from '@material-ui/core'
import { closeAddToPlaylist } from '../actions'
-import SelectPlaylistInput from './SelectPlaylistInput'
+import { SelectPlaylistInput } from './SelectPlaylistInput'
-const AddToPlaylistDialog = () => {
+export const AddToPlaylistDialog = () => {
const { open, selectedIds, onSuccess } = useSelector(
(state) => state.addToPlaylistDialog
)
@@ -96,5 +96,3 @@ const AddToPlaylistDialog = () => {
)
}
-
-export default AddToPlaylistDialog
diff --git a/ui/src/dialogs/DialogContent.js b/ui/src/dialogs/DialogContent.js
new file mode 100644
index 00000000..cc650611
--- /dev/null
+++ b/ui/src/dialogs/DialogContent.js
@@ -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)
diff --git a/ui/src/dialogs/DialogTitle.js b/ui/src/dialogs/DialogTitle.js
new file mode 100644
index 00000000..959031e7
--- /dev/null
+++ b/ui/src/dialogs/DialogTitle.js
@@ -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 (
+
+ {children}
+
+
+
+
+ )
+})
diff --git a/ui/src/dialogs/HelpDialog.js b/ui/src/dialogs/HelpDialog.js
new file mode 100644
index 00000000..0e7621fe
--- /dev/null
+++ b/ui/src/dialogs/HelpDialog.js
@@ -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(
+ ,
+ 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 (
+ <>
+
+
+ >
+ )
+}
diff --git a/ui/src/dialogs/SelectPlaylistInput.js b/ui/src/dialogs/SelectPlaylistInput.js
index 2e4e5312..97569484 100644
--- a/ui/src/dialogs/SelectPlaylistInput.js
+++ b/ui/src/dialogs/SelectPlaylistInput.js
@@ -6,11 +6,11 @@ import Autocomplete, {
} from '@material-ui/lab/Autocomplete'
import { useGetList, useTranslate } from 'react-admin'
import PropTypes from 'prop-types'
-import { isWritable } from '../common/Writable'
+import { isWritable } from '../common'
const filter = createFilterOptions()
-const SelectPlaylistInput = ({ onChange }) => {
+export const SelectPlaylistInput = ({ onChange }) => {
const translate = useTranslate()
const { ids, data } = useGetList(
'playlist',
@@ -94,5 +94,3 @@ const SelectPlaylistInput = ({ onChange }) => {
SelectPlaylistInput.propTypes = {
onChange: PropTypes.func.isRequired,
}
-
-export default SelectPlaylistInput
diff --git a/ui/src/dialogs/index.js b/ui/src/dialogs/index.js
new file mode 100644
index 00000000..79c43130
--- /dev/null
+++ b/ui/src/dialogs/index.js
@@ -0,0 +1,4 @@
+export * from './AboutDialog'
+export * from './AddToPlaylistDialog'
+export * from './SelectPlaylistInput'
+export * from './HelpDialog'
diff --git a/ui/src/hotkeys.js b/ui/src/hotkeys.js
index 5086afa5..8ad02e06 100644
--- a/ui/src/hotkeys.js
+++ b/ui/src/hotkeys.js
@@ -1,12 +1,12 @@
const keyMap = {
- TOGGLE_MENU: { name: 'Toggle Menu Side Bar', sequence: 'm', group: 'Global' },
-
- TOGGLE_PLAY: { name: 'Play / Pause', sequence: 'p', group: 'Player' },
- PREV_SONG: { name: 'Previous Songs', sequence: 'left', group: 'Player' },
- NEXT_SONG: { name: 'Next Song', sequence: 'right', group: 'Player' },
- VOL_UP: { name: 'Volume Up', sequence: '=', group: 'Player' },
- VOL_DOWN: { name: 'Volume Down', sequence: '-', group: 'Player' },
- TOGGLE_STAR: { name: 'Toggle Star', sequence: 's', group: 'Player' },
+ SHOW_HELP: { name: 'show_help', sequence: 'shift+?', group: 'Global' },
+ TOGGLE_MENU: { name: 'toggle_menu', sequence: 'm', group: 'Global' },
+ TOGGLE_PLAY: { name: 'toggle_play', sequence: 'p', group: 'Player' },
+ PREV_SONG: { name: 'prev_song', sequence: 'left', group: 'Player' },
+ NEXT_SONG: { name: 'next_song', sequence: 'right', group: 'Player' },
+ VOL_UP: { name: 'vol_up', sequence: '=', group: 'Player' },
+ VOL_DOWN: { name: 'vol_down', sequence: '-', group: 'Player' },
+ TOGGLE_STAR: { name: 'toggle_star', sequence: 's', group: 'Player' },
}
export { keyMap }
diff --git a/ui/src/i18n/en.json b/ui/src/i18n/en.json
index aefac515..b01377d3 100644
--- a/ui/src/i18n/en.json
+++ b/ui/src/i18n/en.json
@@ -324,5 +324,18 @@
"fullScan": "Full Scan",
"serverUptime": "Server Uptime",
"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"
+ }
}
}
diff --git a/ui/src/layout/AppBar.js b/ui/src/layout/AppBar.js
index 353fbd52..ccbc9402 100644
--- a/ui/src/layout/AppBar.js
+++ b/ui/src/layout/AppBar.js
@@ -10,7 +10,7 @@ import { useSelector } from 'react-redux'
import { makeStyles, MenuItem, ListItemIcon, Divider } from '@material-ui/core'
import ViewListIcon from '@material-ui/icons/ViewList'
import InfoIcon from '@material-ui/icons/Info'
-import AboutDialog from './AboutDialog'
+import { AboutDialog } from '../dialogs'
import PersonalMenu from './PersonalMenu'
import ActivityPanel from './ActivityPanel'
import UserMenu from './UserMenu'
diff --git a/ui/src/layout/Menu.js b/ui/src/layout/Menu.js
index 2ef57cff..9aae9c05 100644
--- a/ui/src/layout/Menu.js
+++ b/ui/src/layout/Menu.js
@@ -9,6 +9,7 @@ import AlbumIcon from '@material-ui/icons/Album'
import SubMenu from './SubMenu'
import inflection from 'inflection'
import albumLists from '../album/albumLists'
+import { HelpDialog } from '../dialogs'
const translatedResourceName = (resource, translate) =>
translate(`resources.${resource.name}.name`, {
@@ -108,6 +109,7 @@ const Menu = ({ onMenuClick, dense, logout }) => {
{resources.filter(subItems(undefined)).map(renderResourceMenuItemLink)}
{isXsmall && logout}
+
)
}
diff --git a/ui/src/playlist/PlaylistSongs.js b/ui/src/playlist/PlaylistSongs.js
index 2be7f823..9bf302cd 100644
--- a/ui/src/playlist/PlaylistSongs.js
+++ b/ui/src/playlist/PlaylistSongs.js
@@ -21,7 +21,7 @@ import {
SongDatagrid,
SongTitleField,
} from '../common'
-import AddToPlaylistDialog from '../dialogs/AddToPlaylistDialog'
+import { AddToPlaylistDialog } from '../dialogs'
import { AlbumLinkField } from '../song/AlbumLinkField'
import { playTracks } from '../actions'
import PlaylistSongBulkActions from './PlaylistSongBulkActions'
diff --git a/ui/src/song/SongList.js b/ui/src/song/SongList.js
index f4c8b32a..59dec1f0 100644
--- a/ui/src/song/SongList.js
+++ b/ui/src/song/SongList.js
@@ -23,7 +23,7 @@ import { setTrack } from '../actions'
import { SongBulkActions } from '../common'
import { SongListActions } from './SongListActions'
import { AlbumLinkField } from './AlbumLinkField'
-import AddToPlaylistDialog from '../dialogs/AddToPlaylistDialog'
+import { AddToPlaylistDialog } from '../dialogs'
import { makeStyles } from '@material-ui/core/styles'
import StarBorderIcon from '@material-ui/icons/StarBorder'