Enable turn on/off Favorites/Loved feature. (#917)

* Added option to enable/disable favorites in Song

* Added option to enable/disable favorites in Artist

* Added option to enable/disable favorites in Albums and Favorites tab in sidebar

* Added option to enable/disable favorites in Player

* Set default value of enableFavourites as true

* Improved the functionality to enable/disable Favorites

* Add `EnableFavourites` config option

Co-authored-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Neil Chauhan 2021-03-29 06:05:49 +05:30 committed by GitHub
parent 5dfcb316cf
commit 404253a881
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 106 additions and 52 deletions

View file

@ -40,6 +40,7 @@ type configOptions struct {
CoverJpegQuality int CoverJpegQuality int
UIWelcomeMessage string UIWelcomeMessage string
EnableGravatar bool EnableGravatar bool
EnableFavourites bool
GATrackingID string GATrackingID string
AuthRequestLimit int AuthRequestLimit int
AuthWindowLength time.Duration AuthWindowLength time.Duration
@ -143,6 +144,7 @@ func init() {
viper.SetDefault("coverjpegquality", 75) viper.SetDefault("coverjpegquality", 75)
viper.SetDefault("uiwelcomemessage", "") viper.SetDefault("uiwelcomemessage", "")
viper.SetDefault("enablegravatar", false) viper.SetDefault("enablegravatar", false)
viper.SetDefault("enablefavourites", true)
viper.SetDefault("gatrackingid", "") viper.SetDefault("gatrackingid", "")
viper.SetDefault("authrequestlimit", 5) viper.SetDefault("authrequestlimit", 5)
viper.SetDefault("authwindowlength", 20*time.Second) viper.SetDefault("authwindowlength", 20*time.Second)

View file

@ -36,6 +36,7 @@ func serveIndex(ds model.DataStore, fs fs.FS) http.HandlerFunc {
"enableTranscodingConfig": conf.Server.EnableTranscodingConfig, "enableTranscodingConfig": conf.Server.EnableTranscodingConfig,
"gaTrackingId": conf.Server.GATrackingID, "gaTrackingId": conf.Server.GATrackingID,
"enableDownloads": conf.Server.EnableDownloads, "enableDownloads": conf.Server.EnableDownloads,
"enableFavourites": conf.Server.EnableFavourites,
"devActivityPanel": conf.Server.DevActivityPanel, "devActivityPanel": conf.Server.DevActivityPanel,
"devFastAccessCoverArt": conf.Server.DevFastAccessCoverArt, "devFastAccessCoverArt": conf.Server.DevFastAccessCoverArt,
} }

View file

@ -114,6 +114,17 @@ var _ = Describe("serveIndex", func() {
Expect(config).To(HaveKeyWithValue("enableDownloads", true)) Expect(config).To(HaveKeyWithValue("enableDownloads", true))
}) })
It("sets the enableLoved", func() {
conf.Server.EnableFavourites = true
r := httptest.NewRequest("GET", "/index.html", nil)
w := httptest.NewRecorder()
serveIndex(ds, fs)(w, r)
config := extractAppConfig(w.Body.String())
Expect(config).To(HaveKeyWithValue("enableFavourites", true))
})
It("sets the gaTrackingId", func() { It("sets the gaTrackingId", func() {
conf.Server.GATrackingID = "UA-12345" conf.Server.GATrackingID = "UA-12345"
r := httptest.NewRequest("GET", "/index.html", nil) r := httptest.NewRequest("GET", "/index.html", nil)

View file

@ -20,6 +20,7 @@ import {
SizeField, SizeField,
LoveButton, LoveButton,
} from '../common' } from '../common'
import config from '../config'
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
root: { root: {
@ -160,14 +161,16 @@ const AlbumDetails = ({ record }) => {
<CardContent className={classes.content}> <CardContent className={classes.content}>
<Typography variant="h5"> <Typography variant="h5">
{record.name} {record.name}
<LoveButton {config.enableFavourites && (
className={classes.loveButton} <LoveButton
record={record} className={classes.loveButton}
resource={'album'} record={record}
size={isDesktop ? 'default' : 'small'} resource={'album'}
aria-label="love" size={isDesktop ? 'default' : 'small'}
color="primary" aria-label="love"
/> color="primary"
/>
)}
</Typography> </Typography>
<Typography component="h6"> <Typography component="h6">
<ArtistLinkField record={record} /> <ArtistLinkField record={record} />

View file

@ -19,6 +19,7 @@ import AlbumListView from './AlbumListView'
import AlbumGridView from './AlbumGridView' import AlbumGridView from './AlbumGridView'
import { AddToPlaylistDialog } from '../dialogs' import { AddToPlaylistDialog } from '../dialogs'
import albumLists, { defaultAlbumList } from './albumLists' import albumLists, { defaultAlbumList } from './albumLists'
import config from '../config'
const AlbumFilter = (props) => { const AlbumFilter = (props) => {
const translate = useTranslate() const translate = useTranslate()
@ -36,11 +37,13 @@ const AlbumFilter = (props) => {
</ReferenceInput> </ReferenceInput>
<NullableBooleanInput source="compilation" /> <NullableBooleanInput source="compilation" />
<NumberInput source="year" /> <NumberInput source="year" />
<QuickFilter {config.enableFavourites && (
source="starred" <QuickFilter
label={<FavoriteIcon fontSize={'small'} />} source="starred"
defaultValue={true} label={<FavoriteIcon fontSize={'small'} />}
/> defaultValue={true}
/>
)}
</Filter> </Filter>
) )
} }

View file

@ -20,6 +20,7 @@ import {
MultiLineTextField, MultiLineTextField,
AlbumContextMenu, AlbumContextMenu,
} from '../common' } from '../common'
import config from '../config'
const useStyles = makeStyles({ const useStyles = makeStyles({
columnIcon: { columnIcon: {
@ -91,12 +92,15 @@ const AlbumListView = ({ hasShow, hasEdit, hasList, ...rest }) => {
source={'starred'} source={'starred'}
sortBy={'starred ASC, starredAt ASC'} sortBy={'starred ASC, starredAt ASC'}
sortByOrder={'DESC'} sortByOrder={'DESC'}
sortable={config.enableFavourites}
className={classes.contextMenu} className={classes.contextMenu}
label={ label={
<FavoriteBorderIcon config.enableFavourites && (
fontSize={'small'} <FavoriteBorderIcon
className={classes.columnIcon} fontSize={'small'}
/> className={classes.columnIcon}
/>
)
} }
/> />
</Datagrid> </Datagrid>

View file

@ -21,6 +21,7 @@ import {
SongTitleField, SongTitleField,
} from '../common' } from '../common'
import { AddToPlaylistDialog } from '../dialogs' import { AddToPlaylistDialog } from '../dialogs'
import config from '../config'
const useStyles = makeStyles( const useStyles = makeStyles(
(theme) => ({ (theme) => ({
@ -123,10 +124,12 @@ const AlbumSongs = (props) => {
sortable={false} sortable={false}
className={classes.contextMenu} className={classes.contextMenu}
label={ label={
<FavoriteBorderIcon config.enableFavourites && (
fontSize={'small'} <FavoriteBorderIcon
className={classes.columnIcon} fontSize={'small'}
/> className={classes.columnIcon}
/>
)
} }
/> />
</SongDatagrid> </SongDatagrid>

View file

@ -4,6 +4,7 @@ import VideoLibraryIcon from '@material-ui/icons/VideoLibrary'
import RepeatIcon from '@material-ui/icons/Repeat' import RepeatIcon from '@material-ui/icons/Repeat'
import AlbumIcon from '@material-ui/icons/Album' import AlbumIcon from '@material-ui/icons/Album'
import FavoriteIcon from '@material-ui/icons/Favorite' import FavoriteIcon from '@material-ui/icons/Favorite'
import config from '../config'
export default { export default {
all: { all: {
@ -11,10 +12,12 @@ export default {
params: 'sort=name&order=ASC', params: 'sort=name&order=ASC',
}, },
random: { icon: ShuffleIcon, params: 'sort=random' }, random: { icon: ShuffleIcon, params: 'sort=random' },
starred: { ...(config.enableFavourites && {
icon: FavoriteIcon, starred: {
params: 'sort=starred_at&order=DESC&filter={"starred":true}', icon: FavoriteIcon,
}, params: 'sort=starred_at&order=DESC&filter={"starred":true}',
},
}),
recentlyAdded: { recentlyAdded: {
icon: LibraryAddIcon, icon: LibraryAddIcon,
params: 'sort=recently_added&order=DESC', params: 'sort=recently_added&order=DESC',

View file

@ -19,6 +19,7 @@ import {
ArtistSimpleList, ArtistSimpleList,
} from '../common' } from '../common'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import config from '../config'
const useStyles = makeStyles({ const useStyles = makeStyles({
contextHeader: { contextHeader: {
@ -41,11 +42,13 @@ const useStyles = makeStyles({
const ArtistFilter = (props) => ( const ArtistFilter = (props) => (
<Filter {...props} variant={'outlined'}> <Filter {...props} variant={'outlined'}>
<SearchInput source="name" alwaysOn /> <SearchInput source="name" alwaysOn />
<QuickFilter {config.enableFavourites && (
source="starred" <QuickFilter
label={<FavoriteIcon fontSize={'small'} />} source="starred"
defaultValue={true} label={<FavoriteIcon fontSize={'small'} />}
/> defaultValue={true}
/>
)}
</Filter> </Filter>
) )
@ -69,12 +72,15 @@ const ArtistListView = ({ hasShow, hasEdit, hasList, width, ...rest }) => {
source={'starred'} source={'starred'}
sortBy={'starred ASC, starredAt ASC'} sortBy={'starred ASC, starredAt ASC'}
sortByOrder={'DESC'} sortByOrder={'DESC'}
sortable={config.enableFavourites}
className={classes.contextMenu} className={classes.contextMenu}
label={ label={
<FavoriteBorderIcon config.enableFavourites && (
fontSize={'small'} <FavoriteBorderIcon
className={classes.contextHeader} fontSize={'small'}
/> className={classes.contextHeader}
/>
)
} }
/> />
</Datagrid> </Datagrid>

View file

@ -7,8 +7,10 @@ import { keyMap } from '../hotkeys'
import { ThemeProvider } from '@material-ui/styles' import { ThemeProvider } from '@material-ui/styles'
import { createMuiTheme } from '@material-ui/core/styles' import { createMuiTheme } from '@material-ui/core/styles'
import useCurrentTheme from '../themes/useCurrentTheme' import useCurrentTheme from '../themes/useCurrentTheme'
import config from '../config'
const Placeholder = () => <LoveButton disabled={true} resource={'song'} /> const Placeholder = () =>
config.enableFavourites && <LoveButton disabled={true} resource={'song'} />
const Toolbar = ({ id }) => { const Toolbar = ({ id }) => {
const location = useLocation() const location = useLocation()
@ -24,11 +26,13 @@ const Toolbar = ({ id }) => {
return ( return (
<ThemeProvider theme={createMuiTheme(theme)}> <ThemeProvider theme={createMuiTheme(theme)}>
<GlobalHotKeys keyMap={keyMap} handlers={handlers} allowChanges /> <GlobalHotKeys keyMap={keyMap} handlers={handlers} allowChanges />
<LoveButton {config.enableFavourites && (
record={data} <LoveButton
resource={resource} record={data}
disabled={loading || toggling} resource={resource}
/> disabled={loading || toggling}
/>
)}
</ThemeProvider> </ThemeProvider>
) )
} }

View file

@ -133,7 +133,7 @@ const ContextMenu = ({
<LoveButton <LoveButton
record={record} record={record}
resource={resource} resource={resource}
visible={showLove} visible={config.enableFavourites && showLove}
color={color} color={color}
/> />
<IconButton <IconButton

View file

@ -87,7 +87,11 @@ export const SongContextMenu = ({
return ( return (
<span className={clsx(classes.noWrap, className)}> <span className={clsx(classes.noWrap, className)}>
<LoveButton record={record} resource={resource} visible={showLove} /> <LoveButton
record={record}
resource={resource}
visible={config.enableFavourites && showLove}
/>
<IconButton onClick={handleClick} size={'small'}> <IconButton onClick={handleClick} size={'small'}>
<MoreVertIcon fontSize={'small'} /> <MoreVertIcon fontSize={'small'} />
</IconButton> </IconButton>

View file

@ -12,6 +12,7 @@ const defaultConfig = {
gaTrackingId: '', gaTrackingId: '',
devActivityPanel: true, devActivityPanel: true,
devFastAccessCoverArt: false, devFastAccessCoverArt: false,
enableFavourites: true,
} }
let config let config

View file

@ -1,3 +1,4 @@
import config from './config'
const keyMap = { const keyMap = {
SHOW_HELP: { name: 'show_help', sequence: 'shift+?', group: 'Global' }, SHOW_HELP: { name: 'show_help', sequence: 'shift+?', group: 'Global' },
TOGGLE_MENU: { name: 'toggle_menu', sequence: 'm', group: 'Global' }, TOGGLE_MENU: { name: 'toggle_menu', sequence: 'm', group: 'Global' },
@ -6,7 +7,9 @@ const keyMap = {
NEXT_SONG: { name: 'next_song', sequence: 'right', group: 'Player' }, NEXT_SONG: { name: 'next_song', sequence: 'right', group: 'Player' },
VOL_UP: { name: 'vol_up', sequence: '=', group: 'Player' }, VOL_UP: { name: 'vol_up', sequence: '=', group: 'Player' },
VOL_DOWN: { name: 'vol_down', sequence: '-', group: 'Player' }, VOL_DOWN: { name: 'vol_down', sequence: '-', group: 'Player' },
TOGGLE_LOVE: { name: 'toggle_love', sequence: 'l', group: 'Player' }, ...(config.enableFavourites && {
TOGGLE_LOVE: { name: 'toggle_love', sequence: 'l', group: 'Player' },
}),
} }
export { keyMap } export { keyMap }

View file

@ -26,6 +26,7 @@ import { AlbumLinkField } from './AlbumLinkField'
import { AddToPlaylistDialog } from '../dialogs' import { AddToPlaylistDialog } from '../dialogs'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import FavoriteBorderIcon from '@material-ui/icons/FavoriteBorder' import FavoriteBorderIcon from '@material-ui/icons/FavoriteBorder'
import config from '../config'
const useStyles = makeStyles({ const useStyles = makeStyles({
contextHeader: { contextHeader: {
@ -48,11 +49,13 @@ const useStyles = makeStyles({
const SongFilter = (props) => ( const SongFilter = (props) => (
<Filter {...props} variant={'outlined'}> <Filter {...props} variant={'outlined'}>
<SearchInput source="title" alwaysOn /> <SearchInput source="title" alwaysOn />
<QuickFilter {config.enableFavourites && (
source="starred" <QuickFilter
label={<FavoriteIcon fontSize={'small'} />} source="starred"
defaultValue={true} label={<FavoriteIcon fontSize={'small'} />}
/> defaultValue={true}
/>
)}
</Filter> </Filter>
) )
@ -113,12 +116,15 @@ const SongList = (props) => {
source={'starred'} source={'starred'}
sortBy={'starred ASC, starredAt ASC'} sortBy={'starred ASC, starredAt ASC'}
sortByOrder={'DESC'} sortByOrder={'DESC'}
sortable={config.enableFavourites}
className={classes.contextMenu} className={classes.contextMenu}
label={ label={
<FavoriteBorderIcon config.enableFavourites && (
fontSize={'small'} <FavoriteBorderIcon
className={classes.contextHeader} fontSize={'small'}
/> className={classes.contextHeader}
/>
)
} }
/> />
</SongDatagrid> </SongDatagrid>