diff --git a/ui/src/App.js b/ui/src/App.js
index 52ea5a0ff..eae809684 100644
--- a/ui/src/App.js
+++ b/ui/src/App.js
@@ -4,7 +4,7 @@ import { createHashHistory } from 'history'
import { Admin, Resource } from 'react-admin'
import dataProvider from './dataProvider'
import authProvider from './authProvider'
-import { Layout, Login } from './layout'
+import { Layout, Login, Logout } from './layout'
import transcoding from './transcoding'
import player from './player'
import user from './user'
@@ -44,6 +44,7 @@ const App = () => (
history={history}
layout={Layout}
loginPage={Login}
+ logoutButton={Logout}
>
{(permissions) => [
,
diff --git a/ui/src/audioplayer/index.js b/ui/src/audioplayer/index.js
index c7cf0aebd..1434dbb07 100644
--- a/ui/src/audioplayer/index.js
+++ b/ui/src/audioplayer/index.js
@@ -5,6 +5,7 @@ import {
playQueueReducer,
playTracks,
shuffleTracks,
+ clearQueue,
} from './queue'
export {
@@ -14,4 +15,5 @@ export {
playTracks,
playQueueReducer,
shuffleTracks,
+ clearQueue,
}
diff --git a/ui/src/audioplayer/queue.js b/ui/src/audioplayer/queue.js
index 251030987..da4a2ab3d 100644
--- a/ui/src/audioplayer/queue.js
+++ b/ui/src/audioplayer/queue.js
@@ -4,6 +4,7 @@ import subsonic from '../subsonic'
const PLAYER_ADD_TRACKS = 'PLAYER_ADD_TRACKS'
const PLAYER_SET_TRACK = 'PLAYER_SET_TRACK'
const PLAYER_SYNC_QUEUE = 'PLAYER_SYNC_QUEUE'
+const PLAYER_CLEAR_QUEUE = 'PLAYER_CLEAR_QUEUE'
const PLAYER_SCROBBLE = 'PLAYER_SCROBBLE'
const PLAYER_PLAY_TRACKS = 'PLAYER_PLAY_TRACKS'
const PLAYER_CURRENT = 'PLAYER_CURRENT'
@@ -83,6 +84,10 @@ const syncQueue = (id, data) => ({
data,
})
+const clearQueue = () => ({
+ type: PLAYER_CLEAR_QUEUE,
+})
+
const scrobble = (id, submit) => ({
type: PLAYER_SCROBBLE,
id,
@@ -94,13 +99,14 @@ const currentPlaying = (audioInfo) => ({
data: audioInfo,
})
-const playQueueReducer = (
- previousState = { queue: [], clear: true, playing: false, current: {} },
- payload
-) => {
+const initialState = { queue: [], clear: true, playing: false, current: {} }
+
+const playQueueReducer = (previousState = initialState, payload) => {
let queue, current
const { type, data } = payload
switch (type) {
+ case PLAYER_CLEAR_QUEUE:
+ return initialState
case PLAYER_CURRENT:
queue = previousState.queue
current = data.ended
@@ -175,6 +181,7 @@ export {
setTrack,
playTracks,
syncQueue,
+ clearQueue,
scrobble,
currentPlaying,
shuffleTracks,
diff --git a/ui/src/layout/Login.js b/ui/src/layout/Login.js
index 925c6547d..1a3014a56 100644
--- a/ui/src/layout/Login.js
+++ b/ui/src/layout/Login.js
@@ -1,7 +1,9 @@
-import React, { useState } from 'react'
+import React, { useState, useCallback } from 'react'
import PropTypes from 'prop-types'
import { Field, Form } from 'react-final-form'
+import { useDispatch } from 'react-redux'
+
import Avatar from '@material-ui/core/Avatar'
import Button from '@material-ui/core/Button'
import Card from '@material-ui/core/Card'
@@ -16,6 +18,7 @@ import { Notification, useLogin, useNotify, useTranslate } from 'react-admin'
import LightTheme from '../themes/light'
import config from '../config'
+import { clearQueue } from '../audioplayer'
const useStyles = makeStyles((theme) => ({
main: {
@@ -210,49 +213,60 @@ const Login = ({ location }) => {
const translate = useTranslate()
const notify = useNotify()
const login = useLogin()
+ const dispatch = useDispatch()
- const handleSubmit = (auth) => {
- setLoading(true)
- login(auth, location.state ? location.state.nextPathname : '/').catch(
- (error) => {
- setLoading(false)
- notify(
- typeof error === 'string'
- ? error
- : typeof error === 'undefined' || !error.message
- ? 'ra.auth.sign_in_error'
- : error.message,
- 'warning'
- )
+ const handleSubmit = useCallback(
+ (auth) => {
+ setLoading(true)
+ dispatch(clearQueue())
+ login(auth, location.state ? location.state.nextPathname : '/').catch(
+ (error) => {
+ setLoading(false)
+ notify(
+ typeof error === 'string'
+ ? error
+ : typeof error === 'undefined' || !error.message
+ ? 'ra.auth.sign_in_error'
+ : error.message,
+ 'warning'
+ )
+ }
+ )
+ },
+ [dispatch, login, notify, setLoading, location]
+ )
+
+ const validateLogin = useCallback(
+ (values) => {
+ const errors = {}
+ if (!values.username) {
+ errors.username = translate('ra.validation.required')
}
- )
- }
+ if (!values.password) {
+ errors.password = translate('ra.validation.required')
+ }
+ return errors
+ },
+ [translate]
+ )
- const validateLogin = (values) => {
- const errors = {}
- if (!values.username) {
- errors.username = translate('ra.validation.required')
- }
- if (!values.password) {
- errors.password = translate('ra.validation.required')
- }
- return errors
- }
-
- const validateSignup = (values) => {
- const errors = validateLogin(values)
- const regex = /^\w+$/g
- if (values.username && !values.username.match(regex)) {
- errors.username = translate('ra.validation.invalidChars')
- }
- if (!values.confirmPassword) {
- errors.confirmPassword = translate('ra.validation.required')
- }
- if (values.confirmPassword !== values.password) {
- errors.confirmPassword = translate('ra.validation.passwordDoesNotMatch')
- }
- return errors
- }
+ const validateSignup = useCallback(
+ (values) => {
+ const errors = validateLogin(values)
+ const regex = /^\w+$/g
+ if (values.username && !values.username.match(regex)) {
+ errors.username = translate('ra.validation.invalidChars')
+ }
+ if (!values.confirmPassword) {
+ errors.confirmPassword = translate('ra.validation.required')
+ }
+ if (values.confirmPassword !== values.password) {
+ errors.confirmPassword = translate('ra.validation.passwordDoesNotMatch')
+ }
+ return errors
+ },
+ [translate, validateLogin]
+ )
if (config.firstTime) {
return (
diff --git a/ui/src/layout/Logout.js b/ui/src/layout/Logout.js
new file mode 100644
index 000000000..9b9a871ae
--- /dev/null
+++ b/ui/src/layout/Logout.js
@@ -0,0 +1,15 @@
+import React, { useCallback } from 'react'
+import { useDispatch } from 'react-redux'
+import { Logout } from 'react-admin'
+import { clearQueue } from '../audioplayer'
+
+export default (props) => {
+ const dispatch = useDispatch()
+ const handleClick = useCallback(() => dispatch(clearQueue()), [dispatch])
+
+ return (
+
+
+
+ )
+}
diff --git a/ui/src/layout/index.js b/ui/src/layout/index.js
index ebfb0760a..c04e6f30a 100644
--- a/ui/src/layout/index.js
+++ b/ui/src/layout/index.js
@@ -1,4 +1,5 @@
import Login from './Login'
+import Logout from './Logout'
import Layout from './Layout'
-export { Layout, Login }
+export { Layout, Login, Logout }