feat(ui): reflect filters/sort/search in the URL
Serialize the active filters to a compact, readable query string (only non-default values) and parse them back on load, with URL taking precedence over localStorage. Uses history.replaceState so it never spams browser history. A pasted link now reproduces the exact view.
This commit is contained in:
parent
3e1823b08b
commit
98025d8fab
2 changed files with 97 additions and 2 deletions
|
|
@ -8,6 +8,7 @@ import {
|
|||
saveLocalTheme,
|
||||
type ThemePrefs,
|
||||
} from "./lib/theme";
|
||||
import { hasFilterParams, paramsToFilters, syncUrl } from "./lib/urlState";
|
||||
import Login from "./components/Login";
|
||||
import Header from "./components/Header";
|
||||
import Sidebar from "./components/Sidebar";
|
||||
|
|
@ -27,7 +28,7 @@ const DEFAULT_FILTERS: FeedFilters = {
|
|||
|
||||
const FILTERS_KEY = "subfeed.filters";
|
||||
|
||||
function loadFilters(): FeedFilters {
|
||||
function loadStoredFilters(): FeedFilters {
|
||||
try {
|
||||
return { ...DEFAULT_FILTERS, ...JSON.parse(localStorage.getItem(FILTERS_KEY) || "{}") };
|
||||
} catch {
|
||||
|
|
@ -35,18 +36,30 @@ function loadFilters(): FeedFilters {
|
|||
}
|
||||
}
|
||||
|
||||
// URL wins over localStorage so a pasted link reproduces the exact view.
|
||||
function loadInitialFilters(): FeedFilters {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
if (hasFilterParams(params)) return paramsToFilters(params, DEFAULT_FILTERS);
|
||||
return loadStoredFilters();
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const [theme, setThemeState] = useState<ThemePrefs>(() => loadLocalTheme());
|
||||
const [filters, setFiltersState] = useState<FeedFilters>(loadFilters);
|
||||
const [filters, setFiltersState] = useState<FeedFilters>(loadInitialFilters);
|
||||
const [view, setView] = useState<"grid" | "list">("grid");
|
||||
|
||||
function setFilters(next: FeedFilters) {
|
||||
setFiltersState(next);
|
||||
localStorage.setItem(FILTERS_KEY, JSON.stringify(next));
|
||||
syncUrl(next);
|
||||
}
|
||||
|
||||
useEffect(() => applyTheme(theme), [theme]);
|
||||
|
||||
// Reflect the initial filters (from localStorage or URL) into the address bar
|
||||
// so the URL is always shareable, even before the first filter change.
|
||||
useEffect(() => syncUrl(filters), []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const meQuery = useQuery({ queryKey: ["me"], queryFn: api.me });
|
||||
|
||||
// On login, adopt server-stored preferences.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue