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 }