feat(managers): table / cards / rows layouts for the channel manager (E4 S3)
A generic ViewSwitcher (reused from the feed) drives a per-account layout shared by the Subscriptions + Discovery tabs. `table` keeps the DataTable (numbered pager); `cards` and `rows` render a virtualized grid over the same client-side list via the new ChannelLayoutGrid, which composes cells straight from each table's Column<T> render closures (cardPrimary/hideInCard/cardLabel), so both tabs — with their different columns — get the views for free. Status chips surface in the fixed band when the table's own controls row is gone.
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
import { getAccountRaw, LS, setAccountRaw } from "../lib/storage";
|
||||
import { parseChannelLayout, type ChannelLayout } from "../lib/channelLayout";
|
||||
import { useNavigation, useNavigationActions } from "./NavigationProvider";
|
||||
import type { ChannelStatusFilter, ChannelsView } from "./Channels";
|
||||
|
||||
@@ -41,6 +42,8 @@ type ChannelsState = {
|
||||
search: string;
|
||||
statusFilter: ChannelStatusFilter;
|
||||
view: ChannelsView;
|
||||
// The table/cards/rows density (shared by the subscribed + discovery tabs).
|
||||
layout: ChannelLayout;
|
||||
// Bumped to tell the manager to drop a stale column filter when an intent sends the user to a
|
||||
// specific set (the header's "without full history" link).
|
||||
filtersResetToken: number;
|
||||
@@ -54,6 +57,7 @@ interface ChannelsActions {
|
||||
setSearch: (q: string) => void;
|
||||
setStatusFilter: (f: ChannelStatusFilter) => void;
|
||||
setView: (v: ChannelsView) => void;
|
||||
setLayout: (l: ChannelLayout) => void;
|
||||
// "Focus this channel in the manager": jump to Channels (subscriptions tab) and seed its name
|
||||
// filter so the channel is isolated. Called from the feed sidebar and the notifications inbox.
|
||||
focusChannel: (name: string) => void;
|
||||
@@ -65,6 +69,7 @@ const StateContext = createContext<ChannelsState>({
|
||||
search: "",
|
||||
statusFilter: "all",
|
||||
view: "subscribed",
|
||||
layout: "table",
|
||||
filtersResetToken: 0,
|
||||
focusName: null,
|
||||
focusToken: 0,
|
||||
@@ -73,6 +78,7 @@ const ActionsContext = createContext<ChannelsActions>({
|
||||
setSearch: () => {},
|
||||
setStatusFilter: () => {},
|
||||
setView: () => {},
|
||||
setLayout: () => {},
|
||||
focusChannel: () => {},
|
||||
goToFullHistory: () => {},
|
||||
});
|
||||
@@ -95,6 +101,9 @@ export function ChannelsProvider({ children }: { children: ReactNode }) {
|
||||
const [view, setViewState] = useState<ChannelsView>(() =>
|
||||
clampView(getAccountRaw(LS.channelsView)),
|
||||
);
|
||||
const [layout, setLayoutState] = useState<ChannelLayout>(() =>
|
||||
parseChannelLayout(getAccountRaw(LS.channelLayout)),
|
||||
);
|
||||
const [filtersResetToken, setFiltersResetToken] = useState(0);
|
||||
const [focusName, setFocusName] = useState<string | null>(null);
|
||||
const [focusToken, setFocusToken] = useState(0);
|
||||
@@ -108,6 +117,10 @@ export function ChannelsProvider({ children }: { children: ReactNode }) {
|
||||
setViewState(v);
|
||||
setAccountRaw(LS.channelsView, v);
|
||||
}, []);
|
||||
const setLayout = useCallback((l: ChannelLayout) => {
|
||||
setLayoutState(l);
|
||||
setAccountRaw(LS.channelLayout, l);
|
||||
}, []);
|
||||
|
||||
const focusChannel = useCallback(
|
||||
(name: string) => {
|
||||
@@ -131,12 +144,12 @@ export function ChannelsProvider({ children }: { children: ReactNode }) {
|
||||
}, [page]);
|
||||
|
||||
const state = useMemo<ChannelsState>(
|
||||
() => ({ search, statusFilter, view, filtersResetToken, focusName, focusToken }),
|
||||
[search, statusFilter, view, filtersResetToken, focusName, focusToken],
|
||||
() => ({ search, statusFilter, view, layout, filtersResetToken, focusName, focusToken }),
|
||||
[search, statusFilter, view, layout, filtersResetToken, focusName, focusToken],
|
||||
);
|
||||
const actions = useMemo<ChannelsActions>(
|
||||
() => ({ setSearch, setStatusFilter, setView, focusChannel, goToFullHistory }),
|
||||
[setSearch, setStatusFilter, setView, focusChannel, goToFullHistory],
|
||||
() => ({ setSearch, setStatusFilter, setView, setLayout, focusChannel, goToFullHistory }),
|
||||
[setSearch, setStatusFilter, setView, setLayout, focusChannel, goToFullHistory],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user