mirror of
https://github.com/navidrome/navidrome.git
synced 2026-10-08 02:17:25 +02:00
fix(ui): don't crash the playlist list when rows lose their record (#6250)
* fix(ui): don't crash playlist list rows that lost their record react-admin 3 evicts records fetched more than 10 minutes ago whenever another getList for the same resource completes, but the list keeps its cached ids. The Datagrid then renders those rows with an undefined record, and the Public and Auto-import switches crashed reading record.id. This happened when the playlist list was left open and the sidebar or the add to playlist dialog reloaded a smaller set of playlists. Both switches now render nothing when the row has no record; the next list refresh fills the row in again. * refactor(ui): merge playlist list toggles into one ToggleField The Public and Auto-import switches were copies that differed only in the field they flip. ToggleField now flips its source field, and ToggleAutoImport just shows it for playlists that have a file path. The tests render inside TestContext, so they use react-admin's real hooks instead of mocks.
This commit is contained in:
parent
e03ce87177
commit
0e1893530b
2 changed files with 34 additions and 37 deletions
|
|
@ -67,15 +67,15 @@ const PlaylistFilter = (props) => {
|
|||
)
|
||||
}
|
||||
|
||||
const TogglePublicInput = ({ resource, source }) => {
|
||||
export const ToggleField = ({ resource, source }) => {
|
||||
const record = useRecordContext()
|
||||
const notify = useNotify()
|
||||
const [togglePublic] = useUpdate(
|
||||
const [toggle] = useUpdate(
|
||||
resource,
|
||||
record.id,
|
||||
record?.id,
|
||||
{
|
||||
...record,
|
||||
public: !record.public,
|
||||
[source]: !record?.[source],
|
||||
},
|
||||
{
|
||||
undoable: false,
|
||||
|
|
@ -86,10 +86,12 @@ const TogglePublicInput = ({ resource, source }) => {
|
|||
)
|
||||
|
||||
const handleClick = (e) => {
|
||||
togglePublic()
|
||||
toggle()
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
if (!record) return null
|
||||
|
||||
return (
|
||||
<Switch
|
||||
checked={record[source]}
|
||||
|
|
@ -99,35 +101,9 @@ const TogglePublicInput = ({ resource, source }) => {
|
|||
)
|
||||
}
|
||||
|
||||
const ToggleAutoImport = ({ resource, source }) => {
|
||||
export const ToggleAutoImport = (props) => {
|
||||
const record = useRecordContext()
|
||||
const notify = useNotify()
|
||||
const [ToggleAutoImport] = useUpdate(
|
||||
resource,
|
||||
record.id,
|
||||
{
|
||||
...record,
|
||||
sync: !record.sync,
|
||||
},
|
||||
{
|
||||
undoable: false,
|
||||
onFailure: (error) => {
|
||||
notify('ra.page.error', 'warning')
|
||||
},
|
||||
},
|
||||
)
|
||||
const handleClick = (e) => {
|
||||
ToggleAutoImport()
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
return record.path ? (
|
||||
<Switch
|
||||
checked={record[source]}
|
||||
onClick={handleClick}
|
||||
disabled={!isWritable(record.ownerId)}
|
||||
/>
|
||||
) : null
|
||||
return record?.path ? <ToggleField {...props} /> : null
|
||||
}
|
||||
|
||||
const PlaylistListBulkActions = (props) => {
|
||||
|
|
@ -169,9 +145,7 @@ const PlaylistList = (props) => {
|
|||
updatedAt: isDesktop && (
|
||||
<DateField source="updatedAt" sortByOrder={'DESC'} />
|
||||
),
|
||||
public: !isXsmall && (
|
||||
<TogglePublicInput source="public" sortByOrder={'DESC'} />
|
||||
),
|
||||
public: !isXsmall && <ToggleField source="public" sortByOrder={'DESC'} />,
|
||||
comment: <TextField source="comment" />,
|
||||
sync: !isXsmall && (
|
||||
<ToggleAutoImport source="sync" sortByOrder={'DESC'} />
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import React from 'react'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { PlaylistLove } from './PlaylistList'
|
||||
import { TestContext } from 'ra-test'
|
||||
import { PlaylistLove, ToggleField, ToggleAutoImport } from './PlaylistList'
|
||||
|
||||
vi.mock('../config', () => ({
|
||||
default: { enableFavourites: true },
|
||||
|
|
@ -32,3 +33,25 @@ describe('<PlaylistLove />', () => {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
// react-admin evicts records older than 10 minutes while the list still holds
|
||||
// their ids, so rows can render with no record.
|
||||
describe('playlist toggles without a record', () => {
|
||||
it('<ToggleField /> renders nothing', () => {
|
||||
const { container } = render(
|
||||
<TestContext>
|
||||
<ToggleField resource="playlist" source="public" />
|
||||
</TestContext>,
|
||||
)
|
||||
expect(container.innerHTML).toBe('')
|
||||
})
|
||||
|
||||
it('<ToggleAutoImport /> renders nothing', () => {
|
||||
const { container } = render(
|
||||
<TestContext>
|
||||
<ToggleAutoImport resource="playlist" source="sync" />
|
||||
</TestContext>,
|
||||
)
|
||||
expect(container.innerHTML).toBe('')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue