mirror of
https://github.com/navidrome/navidrome.git
synced 2025-12-23 23:18:05 -05:00
feat(ui): reset activity panel error icon to normal state when clicked (#4379)
* ui: reset activity icon after viewing error * refactor: improve ActivityPanel error acknowledgment logic Replaced boolean errorAcknowledged state with acknowledgedError string state to track which specific error was acknowledged. This prevents icon flickering when error messages change and simplifies the logic by removing the need for useEffect. Key changes: - Changed from errorAcknowledged boolean to acknowledgedError string state - Added derived isErrorVisible computed value for cleaner logic - Removed useEffect dependency on scanStatus.error changes - Updated handleMenuOpen to store actual error string instead of boolean flag - Fixed test mock to return proper error state matching test expectations This change addresses code review feedback and follows React best practices by using derived state instead of imperative effects.
This commit is contained in:
@@ -75,14 +75,25 @@ const ActivityPanel = () => {
|
||||
scanStatus.scanning,
|
||||
scanStatus.elapsedTime,
|
||||
)
|
||||
const classes = useStyles({ up: up && !scanStatus.error })
|
||||
const [acknowledgedError, setAcknowledgedError] = useState(null)
|
||||
const isErrorVisible =
|
||||
scanStatus.error && scanStatus.error !== acknowledgedError
|
||||
const classes = useStyles({
|
||||
up: up && (!scanStatus.error || !isErrorVisible),
|
||||
})
|
||||
const translate = useTranslate()
|
||||
const notify = useNotify()
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
const open = Boolean(anchorEl)
|
||||
useInitialScanStatus()
|
||||
|
||||
const handleMenuOpen = (event) => setAnchorEl(event.currentTarget)
|
||||
const handleMenuOpen = (event) => {
|
||||
if (scanStatus.error) {
|
||||
setAcknowledgedError(scanStatus.error)
|
||||
}
|
||||
setAnchorEl(event.currentTarget)
|
||||
}
|
||||
|
||||
const handleMenuClose = () => setAnchorEl(null)
|
||||
const triggerScan = (full) => () => subsonic.startScan({ fullScan: full })
|
||||
|
||||
@@ -111,10 +122,10 @@ const ActivityPanel = () => {
|
||||
<div className={classes.wrapper}>
|
||||
<Tooltip title={tooltipTitle}>
|
||||
<IconButton className={classes.button} onClick={handleMenuOpen}>
|
||||
{!up || scanStatus.error ? (
|
||||
<BiError size={'20'} />
|
||||
{!up || isErrorVisible ? (
|
||||
<BiError data-testid="activity-error-icon" size={'20'} />
|
||||
) : (
|
||||
<FiActivity size={'20'} />
|
||||
<FiActivity data-testid="activity-ok-icon" size={'20'} />
|
||||
)}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
61
ui/src/layout/ActivityPanel.test.jsx
Normal file
61
ui/src/layout/ActivityPanel.test.jsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import React from 'react'
|
||||
import { render, screen, fireEvent } from '@testing-library/react'
|
||||
import { Provider } from 'react-redux'
|
||||
import { createStore, combineReducers } from 'redux'
|
||||
import { describe, it, beforeEach } from 'vitest'
|
||||
|
||||
import ActivityPanel from './ActivityPanel'
|
||||
import { activityReducer } from '../reducers'
|
||||
import config from '../config'
|
||||
import subsonic from '../subsonic'
|
||||
|
||||
vi.mock('../subsonic', () => ({
|
||||
default: {
|
||||
getScanStatus: vi.fn(() =>
|
||||
Promise.resolve({
|
||||
json: {
|
||||
'subsonic-response': {
|
||||
status: 'ok',
|
||||
scanStatus: { error: 'Scan failed' },
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
startScan: vi.fn(),
|
||||
},
|
||||
}))
|
||||
|
||||
describe('<ActivityPanel />', () => {
|
||||
let store
|
||||
|
||||
beforeEach(() => {
|
||||
store = createStore(combineReducers({ activity: activityReducer }), {
|
||||
activity: {
|
||||
scanStatus: {
|
||||
scanning: false,
|
||||
folderCount: 0,
|
||||
count: 0,
|
||||
error: 'Scan failed',
|
||||
elapsedTime: 0,
|
||||
},
|
||||
serverStart: { version: config.version, startTime: Date.now() },
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('clears the error icon after opening the panel', () => {
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<ActivityPanel />
|
||||
</Provider>,
|
||||
)
|
||||
|
||||
const button = screen.getByRole('button')
|
||||
expect(screen.getByTestId('activity-error-icon')).toBeInTheDocument()
|
||||
|
||||
fireEvent.click(button)
|
||||
|
||||
expect(screen.getByTestId('activity-ok-icon')).toBeInTheDocument()
|
||||
expect(screen.getByText('Scan failed')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user