Implement Last.fm account linking UI

This commit is contained in:
Steve Richter 2021-06-21 21:20:44 -04:00 committed by Deluan Quintão
parent 8b62a58b4c
commit 5fbfd9c81e
6 changed files with 149 additions and 89 deletions

View File

@ -1 +1,17 @@
Success! Your account is linked to Last.FM. You can close this tab now.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Account Linking Success</title>
</head>
<body>
<h2>
Success! Your account is linked to Last.fm. You can close this tab now.
</h2>
<script>
document.addEventListener("DOMContentLoaded", () => {
window.close();
});
</script>
</body>
</html>

View File

@ -297,7 +297,11 @@
"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)?",
"notifications_blocked": "You have blocked Notifications for this site in your browser's settings",
"notifications_not_available": "This browser does not support desktop notifications or you are not accessing Navidrome over https"
"notifications_not_available": "This browser does not support desktop notifications or you are not accessing Navidrome over https",
"lastfmLinkSuccess": "Last.fm successfully linked and scrobbling enabled",
"lastfmLinkFailure": "Last.fm could not be linked",
"lastfmUnlinkSuccess": "Last.fm unlinked and scrobbling disabled",
"lastfmUnlinkFailure": "Last.fm could not unlinked"
},
"menu": {
"library": "Library",
@ -310,7 +314,8 @@
"theme": "Theme",
"language": "Language",
"defaultView": "Default View",
"desktop_notifications": "Desktop Notifications"
"desktop_notifications": "Desktop Notifications",
"lastfmScrobbling": "Scrobble to Last.fm"
}
},
"albumList": "Albums",

View File

@ -0,0 +1,122 @@
import { useEffect, useRef, useState } from 'react'
import { useNotify, useTranslate } from 'react-admin'
import {
FormControl,
FormControlLabel,
LinearProgress,
Switch,
} from '@material-ui/core'
import { useInterval } from '../common'
import config from '../config'
import { baseUrl, openInNewTab } from '../utils'
import { httpClient } from '../dataProvider'
const Progress = (props) => {
const { setLinked, setCheckingLink } = props
const notify = useNotify()
let linkCheckDelay = 2000
let linkChecks = 30
const openedTab = useRef()
useEffect(() => {
const callbackEndpoint = baseUrl(
`/api/lastfm/link/callback?uid=${localStorage.getItem('userId')}`
)
const callbackUrl = `${window.location.origin}${callbackEndpoint}`
openedTab.current = openInNewTab(
`https://www.last.fm/api/auth/?api_key=${config.lastFMApiKey}&cb=${callbackUrl}`
)
}, [])
const endChecking = (success) => {
linkCheckDelay = null
setCheckingLink(false)
if (success) {
notify('message.lastfmLinkSuccess', 'success')
} else {
notify('message.lastfmLinkFailure', 'warning')
}
setLinked(success)
}
useInterval(() => {
httpClient('/api/lastfm/link')
.then((response) => {
let result = false
if (response.json.status === true) {
result = true
endChecking(true)
}
return result
})
.then((result) => {
if (!result && openedTab.current?.closed === true) {
endChecking(false)
result = true
}
return result
})
.then((result) => {
if (!result && --linkChecks === 0) {
endChecking(false)
}
})
.catch(() => {
endChecking(false)
})
}, linkCheckDelay)
return <LinearProgress />
}
export const LastfmScrobbleToggle = (props) => {
const notify = useNotify()
const translate = useTranslate()
const [linked, setLinked] = useState(null)
const [checkingLink, setCheckingLink] = useState(false)
const toggleScrobble = () => {
if (!linked) {
setCheckingLink(true)
} else {
httpClient('/api/lastfm/link', { method: 'DELETE' })
.then(() => {
setLinked(false)
notify('message.lastfmUnlinkSuccess', 'success')
})
.catch(() => notify('message.lastfmUnlinkFailure', 'warning'))
}
}
useEffect(() => {
httpClient('/api/lastfm/link')
.then((response) => {
setLinked(response.json.status === true)
})
.catch(() => {
setLinked(false)
})
}, [])
return (
<FormControl>
<FormControlLabel
control={
<Switch
id={'lastfm'}
color="primary"
checked={linked || checkingLink}
disabled={linked === null || checkingLink}
onChange={toggleScrobble}
/>
}
label={
<span>{translate('menu.personal.options.lastfmScrobbling')}</span>
}
/>
{checkingLink && (
<Progress setLinked={setLinked} setCheckingLink={setCheckingLink} />
)}
</FormControl>
)
}

View File

@ -5,7 +5,7 @@ import { SelectLanguage } from './SelectLanguage'
import { SelectTheme } from './SelectTheme'
import { SelectDefaultView } from './SelectDefaultView'
import { NotificationsToggle } from './NotificationsToggle'
import { ScrobbleToggle } from './ScrobbleToggle'
import { LastfmScrobbleToggle } from './LastfmScrobbleToggle'
import config from '../config'
const useStyles = makeStyles({
@ -24,7 +24,7 @@ const Personal = () => {
<SelectLanguage />
<SelectDefaultView />
<NotificationsToggle />
{config.devEnableScrobble && <ScrobbleToggle />}
{config.devEnableScrobble && <LastfmScrobbleToggle />}
</SimpleForm>
</Card>
)

View File

@ -1,84 +0,0 @@
import { useState } from 'react'
import { useNotify, useTranslate } from 'react-admin'
import {
FormControl,
FormControlLabel,
LinearProgress,
Switch,
} from '@material-ui/core'
import { useInterval } from '../common'
import { baseUrl } from '../utils'
const Progress = (props) => {
const { setLinked, setCheckingLink } = props
const translate = useTranslate()
const notify = useNotify()
let linkCheckDelay = 2000
let linkChecks = 5
// openInNewTab(
// '/api/lastfm/link'
// )
const endChecking = (success) => {
linkCheckDelay = null
setCheckingLink(false)
if (success) {
notify(translate('Last.fm successfully linked!'), 'success')
} else {
notify(translate('Last.fm could not be linked'), 'warning')
}
setLinked(success)
}
useInterval(() => {
fetch(baseUrl(`/api/lastfm/link/status?c=${linkChecks}`))
.then((response) => response.text())
.then((response) => {
if (response === 'true') {
endChecking(true)
}
})
.catch((error) => {
endChecking(false)
throw new Error(error)
})
if (--linkChecks === 0) {
endChecking(false)
}
}, linkCheckDelay)
return linkChecks > 0 && <LinearProgress />
}
export const ScrobbleToggle = (props) => {
const translate = useTranslate()
const [linked, setLinked] = useState(false)
const [checkingLink, setCheckingLink] = useState(false)
const toggleScrobble = () => {
if (!linked) {
return setCheckingLink(true)
}
setLinked(!linked)
}
return (
<FormControl>
<FormControlLabel
control={
<Switch
id={'notifications'}
color="primary"
checked={linked || checkingLink}
disabled={checkingLink}
onChange={toggleScrobble}
/>
}
label={<span>{translate('Scrobble to Last.FM')}</span>}
/>
{checkingLink && (
<Progress setLinked={setLinked} setCheckingLink={setCheckingLink} />
)}
</FormControl>
)
}

View File

@ -1,4 +1,5 @@
export const openInNewTab = (url) => {
const win = window.open(url, '_blank')
win.focus()
return win
}