fix(nav,plex,playlists): extend account-pin re-hydrate to every affected provider
The fresh-tab restore bug wasn't Channels-specific: any provider that mounts above <App> and reads per-account state at useState-init hit it, since <App> pins the tab's account only after /api/me. Audited all of them — FeedFilters/FeedView/Prefs/App already self-correct via their own me?.id server-prefs adopt effects, so the still-broken ones were the pure-localStorage providers with no server adopt: NavigationProvider (landing page), PlexProvider (scope/show/sort/filters/q), PlaylistsProvider (selected playlist). Each now adopts the shared useAccountRehydrate hook (markDirty in the setters; page uses replaceState so the restore doesn't add a phantom Back step). Also baked re-hydrate into useAccountPersistedState/Object so their consumers (persisted tabs, player prefs) are robust even if one ever mounts before the pin. Self-E2E'd on a genuinely fresh tab: it lands on Plex with Movies/In-progress/Year/search all restored on the FIRST load.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { createContext, useCallback, useContext, useMemo, useState, type ReactNode } from "react";
|
||||
import { getAccountRaw, LS, setAccountRaw } from "../lib/storage";
|
||||
import { useAccountRehydrate } from "../lib/useAccountRehydrate";
|
||||
|
||||
// Owns the Playlists module's shared local state — the rail's name filter and the selected playlist
|
||||
// — lifted out of the App god component so the header search box, the PlaylistsRail, and the
|
||||
@@ -35,18 +36,28 @@ export function usePlaylistsActions(): PlaylistsActions {
|
||||
return useContext(ActionsContext);
|
||||
}
|
||||
|
||||
function readSelectedId(): number | null {
|
||||
const s = getAccountRaw(LS.playlist);
|
||||
return s ? Number(s) : null;
|
||||
}
|
||||
|
||||
export function PlaylistsProvider({ children }: { children: ReactNode }) {
|
||||
const [search, setSearchState] = useState("");
|
||||
const [selectedId, setSelectedIdState] = useState<number | null>(() => {
|
||||
const s = getAccountRaw(LS.playlist);
|
||||
return s ? Number(s) : null;
|
||||
});
|
||||
const [selectedId, setSelectedIdState] = useState<number | null>(readSelectedId);
|
||||
|
||||
// Re-read the selected playlist once the account is pinned after mount (first load on a fresh tab):
|
||||
// this provider mounts above <App>, which pins the account only after /api/me.
|
||||
const markDirty = useAccountRehydrate(() => setSelectedIdState(readSelectedId()));
|
||||
|
||||
const setSearch = useCallback((q: string) => setSearchState(q), []);
|
||||
const setSelectedId = useCallback((id: number | null) => {
|
||||
setSelectedIdState(id);
|
||||
if (id != null) setAccountRaw(LS.playlist, String(id));
|
||||
}, []);
|
||||
const setSelectedId = useCallback(
|
||||
(id: number | null) => {
|
||||
markDirty();
|
||||
setSelectedIdState(id);
|
||||
if (id != null) setAccountRaw(LS.playlist, String(id));
|
||||
},
|
||||
[markDirty],
|
||||
);
|
||||
|
||||
const state = useMemo<PlaylistsState>(() => ({ search, selectedId }), [search, selectedId]);
|
||||
const actions = useMemo<PlaylistsActions>(
|
||||
|
||||
Reference in New Issue
Block a user