feat: improve media library layout and loading behavior
All checks were successful
Deploy Development / deploy (push) Successful in 34s
Test Suite / pytest-backend (push) Successful in 24s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 7s
Test Suite / playwright-tests (push) Successful in 28s
All checks were successful
Deploy Development / deploy (push) Successful in 34s
Test Suite / pytest-backend (push) Successful in 24s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 7s
Test Suite / playwright-tests (push) Successful in 28s
- Added a hidden anchor element to the media library for smooth scrolling to the top of the grid after uploads. - Introduced a sequence reference to manage concurrent media item fetch requests, ensuring accurate loading states. - Updated upload summary messaging to include a note about the list being refreshed after uploads. - Enhanced loading state management to prevent unnecessary updates when fetch requests are out of sync.
This commit is contained in:
parent
cc4e621f95
commit
ceef6f09e2
|
|
@ -5563,6 +5563,14 @@ a.analysis-split__nav-item {
|
||||||
color: var(--text2);
|
color: var(--text2);
|
||||||
flex: 1 1 200px;
|
flex: 1 1 200px;
|
||||||
}
|
}
|
||||||
|
.media-library__grid-top-anchor {
|
||||||
|
height: 1px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
.media-library__upload-icon {
|
.media-library__upload-icon {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,8 @@ export default function MediaLibraryPage() {
|
||||||
const [uploadClubId, setUploadClubId] = useState('')
|
const [uploadClubId, setUploadClubId] = useState('')
|
||||||
const [uploadBusy, setUploadBusy] = useState(false)
|
const [uploadBusy, setUploadBusy] = useState(false)
|
||||||
const [uploadSummary, setUploadSummary] = useState('')
|
const [uploadSummary, setUploadSummary] = useState('')
|
||||||
|
const mediaListFetchSeqRef = useRef(0)
|
||||||
|
const gridTopAnchorRef = useRef(null)
|
||||||
|
|
||||||
const loadClubs = useCallback(async () => {
|
const loadClubs = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -277,6 +279,7 @@ export default function MediaLibraryPage() {
|
||||||
}, [loadClubs])
|
}, [loadClubs])
|
||||||
|
|
||||||
const loadItems = useCallback(async () => {
|
const loadItems = useCallback(async () => {
|
||||||
|
const seq = ++mediaListFetchSeqRef.current
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
setError('')
|
setError('')
|
||||||
try {
|
try {
|
||||||
|
|
@ -294,6 +297,7 @@ export default function MediaLibraryPage() {
|
||||||
? { uploaded_by: Number(filterUploaderId) }
|
? { uploaded_by: Number(filterUploaderId) }
|
||||||
: {}),
|
: {}),
|
||||||
})
|
})
|
||||||
|
if (seq !== mediaListFetchSeqRef.current) return
|
||||||
setItems(res.items || [])
|
setItems(res.items || [])
|
||||||
setViewer(res.viewer || null)
|
setViewer(res.viewer || null)
|
||||||
if (res.filter_meta?.uploaders?.length) {
|
if (res.filter_meta?.uploaders?.length) {
|
||||||
|
|
@ -301,9 +305,10 @@ export default function MediaLibraryPage() {
|
||||||
}
|
}
|
||||||
setSelected(new Set())
|
setSelected(new Set())
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (seq !== mediaListFetchSeqRef.current) return
|
||||||
setError(e.message || String(e))
|
setError(e.message || String(e))
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
if (seq === mediaListFetchSeqRef.current) setLoading(false)
|
||||||
}
|
}
|
||||||
}, [lifecycle, q, mediaKind, filterClubId, filterUploaderId, isSuperadmin, viewer?.show_uploader_meta])
|
}, [lifecycle, q, mediaKind, filterClubId, filterUploaderId, isSuperadmin, viewer?.show_uploader_meta])
|
||||||
|
|
||||||
|
|
@ -485,9 +490,15 @@ export default function MediaLibraryPage() {
|
||||||
...(uploadVis === 'club' ? { club_id: Number(uploadClubId) } : {}),
|
...(uploadVis === 'club' ? { club_id: Number(uploadClubId) } : {}),
|
||||||
})
|
})
|
||||||
setUploadSummary(
|
setUploadSummary(
|
||||||
`Archiv-Upload: neu ${res.created_count}, bereits vorhanden ${res.duplicate_count}, fehlgeschlagen ${res.failed_count}.`,
|
`Archiv-Upload: neu ${res.created_count}, bereits vorhanden ${res.duplicate_count}, fehlgeschlagen ${res.failed_count}. Liste aktualisiert.`,
|
||||||
)
|
)
|
||||||
|
if (res.created_count > 0 || res.duplicate_count > 0) {
|
||||||
|
setFilterUploaderId('')
|
||||||
|
}
|
||||||
await loadItems()
|
await loadItems()
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
gridTopAnchorRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||||
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
window.alert(err.message || String(err))
|
window.alert(err.message || String(err))
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -682,6 +693,8 @@ export default function MediaLibraryPage() {
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
<div ref={gridTopAnchorRef} className="media-library__grid-top-anchor" aria-hidden="true" />
|
||||||
|
|
||||||
{loading && !items.length ? <div className="spinner media-library__spinner" /> : null}
|
{loading && !items.length ? <div className="spinner media-library__spinner" /> : null}
|
||||||
|
|
||||||
{!loading && !items.length && !error ? (
|
{!loading && !items.length && !error ? (
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user