fix(scanner): store scan errors in the database and update UI error handling
Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
@@ -15,7 +15,7 @@ import {
|
||||
Typography,
|
||||
} from '@material-ui/core'
|
||||
import { FiActivity } from 'react-icons/fi'
|
||||
import { BiError } from 'react-icons/bi'
|
||||
import { BiError, BiMessageError } from 'react-icons/bi'
|
||||
import { VscSync } from 'react-icons/vsc'
|
||||
import { GiMagnifyingGlass } from 'react-icons/gi'
|
||||
import subsonic from '../subsonic'
|
||||
@@ -28,7 +28,12 @@ import config from '../config'
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
wrapper: {
|
||||
position: 'relative',
|
||||
color: (props) => (props.up ? null : 'orange'),
|
||||
color: (props) =>
|
||||
props.serverDown
|
||||
? theme.palette.error.main
|
||||
: props.hasWarning
|
||||
? theme.palette.warning.main
|
||||
: null,
|
||||
},
|
||||
progress: {
|
||||
color: theme.palette.primary.light,
|
||||
@@ -75,12 +80,10 @@ const ActivityPanel = () => {
|
||||
scanStatus.scanning,
|
||||
scanStatus.elapsedTime,
|
||||
)
|
||||
const [acknowledgedError, setAcknowledgedError] = useState(null)
|
||||
const isErrorVisible =
|
||||
scanStatus.error && scanStatus.error !== acknowledgedError
|
||||
const classes = useStyles({
|
||||
up: up && (!scanStatus.error || !isErrorVisible),
|
||||
})
|
||||
// Determine icon state: error (server down), warning (scan error), or normal
|
||||
const serverDown = !up
|
||||
const hasWarning = Boolean(scanStatus.error)
|
||||
const classes = useStyles({ serverDown, hasWarning })
|
||||
const translate = useTranslate()
|
||||
const notify = useNotify()
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
@@ -88,13 +91,12 @@ const ActivityPanel = () => {
|
||||
useInitialScanStatus()
|
||||
|
||||
const handleMenuOpen = (event) => {
|
||||
if (scanStatus.error) {
|
||||
setAcknowledgedError(scanStatus.error)
|
||||
}
|
||||
setAnchorEl(event.currentTarget)
|
||||
}
|
||||
|
||||
const handleMenuClose = () => setAnchorEl(null)
|
||||
const handleMenuClose = () => {
|
||||
setAnchorEl(null)
|
||||
}
|
||||
const triggerScan = (full) => () => subsonic.startScan({ fullScan: full })
|
||||
|
||||
useEffect(() => {
|
||||
@@ -125,8 +127,10 @@ const ActivityPanel = () => {
|
||||
<div className={classes.wrapper}>
|
||||
<Tooltip title={tooltipTitle}>
|
||||
<IconButton className={classes.button} onClick={handleMenuOpen}>
|
||||
{!up || isErrorVisible ? (
|
||||
{serverDown ? (
|
||||
<BiError data-testid="activity-error-icon" size={'20'} />
|
||||
) : hasWarning ? (
|
||||
<BiMessageError data-testid="activity-warning-icon" size={'20'} />
|
||||
) : (
|
||||
<FiActivity data-testid="activity-ok-icon" size={'20'} />
|
||||
)}
|
||||
@@ -155,7 +159,11 @@ const ActivityPanel = () => {
|
||||
<Box component="span" flex={2}>
|
||||
{translate('activity.serverUptime')}:
|
||||
</Box>
|
||||
<Box component="span" flex={1}>
|
||||
<Box
|
||||
component="span"
|
||||
flex={1}
|
||||
className={!up ? classes.error : null}
|
||||
>
|
||||
{up ? <Uptime /> : translate('activity.serverDown')}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@@ -43,19 +43,47 @@ describe('<ActivityPanel />', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('clears the error icon after opening the panel', () => {
|
||||
it('shows warning icon when server reports a scan error', () => {
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<ActivityPanel />
|
||||
</Provider>,
|
||||
)
|
||||
|
||||
// Warning icon should be visible when there's a scan error
|
||||
expect(screen.getByTestId('activity-warning-icon')).toBeInTheDocument()
|
||||
|
||||
// Open the panel - warning icon should still be visible
|
||||
const button = screen.getByRole('button')
|
||||
expect(screen.getByTestId('activity-error-icon')).toBeInTheDocument()
|
||||
|
||||
fireEvent.click(button)
|
||||
|
||||
expect(screen.getByTestId('activity-ok-icon')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('activity-warning-icon')).toBeInTheDocument()
|
||||
expect(screen.getByText('Scan failed')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows error icon when server is down', () => {
|
||||
const downStore = createStore(
|
||||
combineReducers({ activity: activityReducer }),
|
||||
{
|
||||
activity: {
|
||||
scanStatus: {
|
||||
scanning: false,
|
||||
folderCount: 0,
|
||||
count: 0,
|
||||
error: '',
|
||||
elapsedTime: 0,
|
||||
},
|
||||
serverStart: { version: config.version, startTime: null }, // null startTime = server down
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
render(
|
||||
<Provider store={downStore}>
|
||||
<ActivityPanel />
|
||||
</Provider>,
|
||||
)
|
||||
|
||||
// Error icon should be visible when server is down
|
||||
expect(screen.getByTestId('activity-error-icon')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user