Add Desktop Notifications

This commit is contained in:
Steve Richter 2020-11-19 23:06:09 -05:00 committed by Deluan Quintão
parent b8d47d1db4
commit 2397a7e464
12 changed files with 99 additions and 4 deletions

View File

@ -22,6 +22,7 @@ import {
playQueueReducer,
albumViewReducer,
activityReducer,
settingsReducer,
} from './reducers'
import createAdminStore from './store/createAdminStore'
import { i18nProvider } from './i18n'
@ -50,6 +51,7 @@ const App = () => (
theme: themeReducer,
addToPlaylistDialog: addToPlaylistDialogReducer,
activity: activityReducer,
settings: settingsReducer,
},
})}
>

View File

@ -3,3 +3,4 @@ export * from './themes'
export * from './albumView'
export * from './dialogs'
export * from './serverEvents'
export * from './settings'

View File

@ -0,0 +1,6 @@
export const SET_NOTIFICATIONS_STATE = 'SET_NOTIFICATIONS_STATE'
export const setNotificationsState = (enabled) => ({
type: SET_NOTIFICATIONS_STATE,
data: enabled,
})

View File

@ -18,6 +18,7 @@ import themes from '../themes'
import config from '../config'
import PlayerToolbar from './PlayerToolbar'
import { useHotkeys } from 'react-hotkeys-hook'
import { sendNotification, baseUrl } from '../utils'
const useStyle = makeStyles((theme) => ({
audioTitle: {
@ -54,6 +55,7 @@ const Player = () => {
const queue = useSelector((state) => state.queue)
const current = queue.current || {}
const { authenticated } = useAuthState()
const showNotifications = useSelector((state) => state.settings.notifications || false)
const visible = authenticated && queue.queue.length > 0
const classes = useStyle({ visible })
@ -230,9 +232,12 @@ const Player = () => {
label: `${info.name} - ${info.singer}`,
})
}
if (showNotifications) {
sendNotification(info.name, `${info.singer} - ${info.album}`, baseUrl(info.cover))
}
}
},
[dispatch]
[dispatch, showNotifications]
)
const onAudioPause = useCallback(

View File

@ -262,7 +262,8 @@
"songsAddedToPlaylist": "Added 1 song to playlist |||| Added %{smart_count} songs to playlist",
"noPlaylistsAvailable": "None available",
"delete_user_title": "Delete user '%{name}'",
"delete_user_content": "Are you sure you want to delete this user and all their data (including playlists and preferences)?"
"delete_user_content": "Are you sure you want to delete this user and all their data (including playlists and preferences)?",
"notifications_blocked": "You have blocked Notifications for this site in your browser's settings or your browser does not support notifications."
},
"menu": {
"library": "Library",
@ -274,7 +275,8 @@
"options": {
"theme": "Theme",
"language": "Language",
"defaultView": "Default View"
"defaultView": "Default View",
"desktop_notifications": "Desktop Notifications"
}
},
"albumList": "Albums",

View File

@ -8,10 +8,15 @@ import {
useLocale,
useSetLocale,
useTranslate,
BooleanInput,
useNotify,
} from 'react-admin'
import { makeStyles } from '@material-ui/core/styles'
import HelpOutlineIcon from '@material-ui/icons/HelpOutline'
import { changeTheme } from '../actions'
import {
changeTheme,
setNotificationsState,
} from '../actions'
import themes from '../themes'
import { docsUrl } from '../utils'
import { useGetLanguageChoices } from '../i18n'
@ -119,6 +124,45 @@ const SelectDefaultView = (props) => {
)
}
const NotificationsToggle = (props) => {
const translate = useTranslate()
const dispatch = useDispatch()
const notify = useNotify()
const currentSetting = useSelector((state) => state.settings.notifications)
const current = (() => {
if (!("Notification" in window) || Notification.permission !== 'granted') {
return false
}
return currentSetting
})()
return (
<BooleanInput
{...props}
source='notifications'
label={translate('menu.personal.options.desktop_notifications')}
defaultValue={current}
onChange={async (notificationsEnabled) => {
if (notificationsEnabled) {
if (!('Notification' in window) || Notification.permission === 'denied') {
notify(translate('message.notifications_blocked'), 'warning')
notificationsEnabled = false
} else {
const response = await Notification.requestPermission()
if (response !== 'granted') {
notificationsEnabled = false
}
}
if (!notificationsEnabled) {
// Need to turn switch off
}
}
dispatch(setNotificationsState(notificationsEnabled))
}}
/>
)
}
const Personal = () => {
const translate = useTranslate()
const classes = useStyles()
@ -130,6 +174,7 @@ const Personal = () => {
<SelectTheme />
<SelectLanguage />
<SelectDefaultView />
<NotificationsToggle />
</SimpleForm>
</Card>
)

View File

@ -3,3 +3,4 @@ export * from './dialogReducer'
export * from './playQueue'
export * from './albumView'
export * from './activityReducer'
export * from './settingsReducer'

View File

@ -23,6 +23,7 @@ const mapToAudioLists = (item) => {
trackId: id,
name: item.title,
singer: item.artist,
album: item.album,
albumId: item.albumId,
artistId: item.albumArtistId,
duration: item.duration,

View File

@ -0,0 +1,18 @@
import { SET_NOTIFICATIONS_STATE } from '../actions'
const initialState = {
notifications: false,
}
export const settingsReducer = (previousState = initialState, payload) => {
const { type, data } = payload
switch (type) {
case SET_NOTIFICATIONS_STATE:
return {
...previousState,
notifications: data
}
default:
return previousState
}
}

View File

@ -50,6 +50,7 @@ export default ({
theme: state.theme,
queue: pick(state.queue, ['queue', 'volume']),
albumView: state.albumView,
settings: state.settings,
})
}),
1000

View File

@ -1,3 +1,4 @@
export * from './baseUrl'
export * from './docsUrl'
export * from './formatters'
export * from './notifications'

View File

@ -0,0 +1,12 @@
export const sendNotification = (title, body = '', image = '') => {
checkForNotificationPermission()
new Notification(title, {
body: body,
icon: image,
silent: true
})
}
const checkForNotificationPermission = () => {
return 'Notification' in window && Notification.permission === 'granted'
}