From 7dd04b00cf1fc2684ac73afe67acaa8e0c664aed Mon Sep 17 00:00:00 2001 From: Inverle Date: Thu, 11 Dec 2025 18:33:39 +0100 Subject: [PATCH] Scroll into filtered feed/category on page load (#8281) Previously if you were to go to for example *Subscription management* and filter a feed, the feed wouldn't be visible in a sidebar with lots of feeds, since you'd have to scroll to it first. Now, this is no longer the case. Note that if the navigation comes from the sidebar itself, the original behavior remains. (scroll into previous `scrollTop` value of sidebar) Also improves experience of using shift+j/k (see https://github.com/FreshRSS/FreshRSS/pull/8057) --- p/scripts/main.js | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/p/scripts/main.js b/p/scripts/main.js index b3724ca76..b8af7e1a5 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -1018,9 +1018,6 @@ function init_column_categories() { return; } - // Restore sidebar scroll position - document.getElementById('sidebar').scrollTop = +sessionStorage.getItem('FreshRSS_sidebar_scrollTop'); - // Restore open categories if (context.display_categories === 'remember') { const open_categories = JSON.parse(localStorage.getItem('FreshRSS_open_categories') || '{}'); @@ -1029,6 +1026,23 @@ function init_column_categories() { }); } + const sidebar_scrollTop = sessionStorage.getItem('FreshRSS_sidebar_scrollTop'); + if (sidebar_scrollTop) { + // Restore sidebar scroll position (navigated from sidebar) + document.querySelector('ul#sidebar').scrollTop = +sidebar_scrollTop; + sessionStorage.removeItem('FreshRSS_sidebar_scrollTop'); + } else { + // Scroll into filtered feed/category on sidebar (navigated from anywhere) + const params = new URLSearchParams(location.search); + const get = params.get('get'); + if (params.has('get') && (get.startsWith('f_') || get.startsWith('c_'))) { + const selectedEl = document.getElementById(get); + if (selectedEl) { + selectedEl.scrollIntoView({ block: get.startsWith('f_') ? 'center' : 'start' }); + } + } + } + document.getElementById('aside_feed').onclick = function (ev) { let a = ev.target.closest('.tree-folder > .tree-folder-title > button.dropdown-toggle'); if (a) { @@ -2237,12 +2251,23 @@ function init_normal() { init_actualize(); faviconNbUnread(); - document.addEventListener("visibilitychange", () => { - if (document.visibilityState === "hidden") { - const sidebar = document.getElementById('sidebar'); - if (sidebar) { // Save sidebar scroll position + const sidebar = document.querySelector('ul#sidebar'); + if (sidebar) { + sidebar.addEventListener('click', (e) => { + if (!e.isTrusted) { + // Event was likely called via a keyboard shortcut shift+j/k using click() + return; + } + const target = e.target.closest('a.item-title, a.tree-folder-title'); + if (target) { + // A page navigation should occur now, save the sidebar scroll position sessionStorage.setItem('FreshRSS_sidebar_scrollTop', sidebar.scrollTop); } + }); + } + + document.addEventListener("visibilitychange", () => { + if (document.visibilityState === "hidden") { if (mark_read_queue && mark_read_queue.length > 0) { clearTimeout(send_mark_read_queue_timeout); send_mark_queue_tick(null);