diff --git a/CHANGELOG.md b/CHANGELOG.md index cdf59871..57fe01a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -127,6 +127,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +### Playlists — column sorting keeps the viewport in place + +**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#848](https://github.com/Psychotoxical/psysonic/pull/848)** + +* Sorting a playlist column no longer snaps the viewport down to the list when scrolled to the top. + + + ## [1.46.0] - 2026-05-18 > **🙏 Special thanks to [@zz5zz](https://github.com/zz5zz)** for his tireless quirk-spotting and bug reports on the [Psysonic Discord](https://discord.gg/AMnDRErm4u) — several of the polish fixes in this release landed directly off the back of his messages. diff --git a/src/components/playlist/PlaylistTracklist.tsx b/src/components/playlist/PlaylistTracklist.tsx index 558ce166..c544a11c 100644 --- a/src/components/playlist/PlaylistTracklist.tsx +++ b/src/components/playlist/PlaylistTracklist.tsx @@ -41,6 +41,10 @@ interface Props { displayedSongs: SubsonicSong[]; displayedTracks: Track[]; isFiltered: boolean; + /** True only while a filter text is active — distinct from `isFiltered`, + * which also goes true while sorting (displayedSongs !== songs). Drives the + * scroll-to-list effect so sorting doesn't snap the viewport (issue #840). */ + hasActiveFilter: boolean; id: string | undefined; // Sort @@ -82,7 +86,7 @@ interface Props { export default function PlaylistTracklist({ allColumns, visibleCols, gridStyle, colVisible, toggleColumn, resetColumns, pickerOpen, setPickerOpen, pickerRef, startResize, tracklistRef, - songs, displayedSongs, displayedTracks, isFiltered, id, + songs, displayedSongs, displayedTracks, isFiltered, hasActiveFilter, id, sortKey, setSortKey, sortDir, setSortDir, sortClickCount, setSortClickCount, selectedIds, setSelectedIds, allSelected, toggleAll, toggleSelect, showBulkPlPicker, setShowBulkPlPicker, bulkRemove, @@ -190,7 +194,7 @@ export default function PlaylistTracklist({ const sc = document.getElementById(APP_MAIN_SCROLL_VIEWPORT_ID); if (sc) sc.scrollTop = scrollMargin; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [id, isFiltered]); + }, [id, hasActiveFilter]); const autoScrollRef = useRef(0); const pointerYRef = useRef(0); diff --git a/src/pages/PlaylistDetail.tsx b/src/pages/PlaylistDetail.tsx index e48027e2..97c6db2f 100644 --- a/src/pages/PlaylistDetail.tsx +++ b/src/pages/PlaylistDetail.tsx @@ -349,6 +349,7 @@ export default function PlaylistDetail() { displayedSongs={displayedSongs} displayedTracks={displayedTracks} isFiltered={isFiltered} + hasActiveFilter={filterText.trim().length > 0} id={id} sortKey={sortKey} setSortKey={setSortKey}