fix(channels): drop a stale column filter when sent to "without full history"
A focus-channel deep-link sets (and persists) the channel-name column filter via the DataTable's externalFilter. That persisted value then survived into unrelated visits: clicking the header's "N without full history" link landed on the right tab with the right status chip, but a leftover name filter (e.g. a channel focused in a past session) hid every row — "No channels". Add a resetFiltersToken to DataTable that clears its column filters when it advances, and bump it from the header intent. The token's ref starts at 0 so the clear fires even when the table remounts on navigation, while a plain reload (token still 0) keeps the user's persisted filters.
This commit is contained in:
parent
1bedfca666
commit
7aab0dd047
3 changed files with 26 additions and 0 deletions
|
|
@ -148,6 +148,9 @@ export default function App() {
|
||||||
setChannelsViewState(v);
|
setChannelsViewState(v);
|
||||||
localStorage.setItem(CHANNELS_VIEW_KEY, v);
|
localStorage.setItem(CHANNELS_VIEW_KEY, v);
|
||||||
};
|
};
|
||||||
|
// Bumped to tell the channel manager to drop a stale column filter when we send the user
|
||||||
|
// there to see a specific set (the header's "without full history" link).
|
||||||
|
const [channelsFilterReset, setChannelsFilterReset] = useState(0);
|
||||||
// "Focus this channel in the manager": jump to the Channels page and seed its name filter
|
// "Focus this channel in the manager": jump to the Channels page and seed its name filter
|
||||||
// so the channel is isolated. Cleared when leaving the page so it doesn't re-apply later.
|
// so the channel is isolated. Cleared when leaving the page so it doesn't re-apply later.
|
||||||
const [focusChannelName, setFocusChannelName] = useState<string | null>(null);
|
const [focusChannelName, setFocusChannelName] = useState<string | null>(null);
|
||||||
|
|
@ -423,6 +426,7 @@ export default function App() {
|
||||||
onGoToFullHistory={() => {
|
onGoToFullHistory={() => {
|
||||||
setChannelFilter("needs_full");
|
setChannelFilter("needs_full");
|
||||||
setChannelsView("subscribed"); // the status filter applies to the subscriptions tab
|
setChannelsView("subscribed"); // the status filter applies to the subscriptions tab
|
||||||
|
setChannelsFilterReset((n) => n + 1); // drop any stale column filter hiding the rows
|
||||||
setPage("channels");
|
setPage("channels");
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
@ -449,6 +453,7 @@ export default function App() {
|
||||||
setStatusFilter={setChannelFilter}
|
setStatusFilter={setChannelFilter}
|
||||||
view={channelsView}
|
view={channelsView}
|
||||||
setView={setChannelsView}
|
setView={setChannelsView}
|
||||||
|
filtersResetToken={channelsFilterReset}
|
||||||
onOpenWizard={() => setWizardOpen(true)}
|
onOpenWizard={() => setWizardOpen(true)}
|
||||||
onViewChannel={(id, name) => {
|
onViewChannel={(id, name) => {
|
||||||
setFilters({ ...filters, channelId: id, channelName: name, show: "all" });
|
setFilters({ ...filters, channelId: id, channelName: name, show: "all" });
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ export default function Channels({
|
||||||
setStatusFilter,
|
setStatusFilter,
|
||||||
view,
|
view,
|
||||||
setView,
|
setView,
|
||||||
|
filtersResetToken,
|
||||||
onOpenWizard,
|
onOpenWizard,
|
||||||
}: {
|
}: {
|
||||||
canWrite: boolean;
|
canWrite: boolean;
|
||||||
|
|
@ -66,6 +67,8 @@ export default function Channels({
|
||||||
// (the header's "without full history" link, focus-channel) can force it back here.
|
// (the header's "without full history" link, focus-channel) can force it back here.
|
||||||
view: ChannelsView;
|
view: ChannelsView;
|
||||||
setView: (v: ChannelsView) => void;
|
setView: (v: ChannelsView) => void;
|
||||||
|
// Bumped by the header's "without full history" intent to clear a stale column filter.
|
||||||
|
filtersResetToken: number;
|
||||||
onOpenWizard: () => void;
|
onOpenWizard: () => void;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
@ -510,6 +513,7 @@ export default function Channels({
|
||||||
controlsPosition="top"
|
controlsPosition="top"
|
||||||
controlsLeading={statusChips}
|
controlsLeading={statusChips}
|
||||||
externalFilter={focusChannelName ? { key: "channel", value: focusChannelName } : null}
|
externalFilter={focusChannelName ? { key: "channel", value: focusChannelName } : null}
|
||||||
|
resetFiltersToken={filtersResetToken}
|
||||||
rowClassName={(c) => (c.hidden ? "opacity-60" : "")}
|
rowClassName={(c) => (c.hidden ? "opacity-60" : "")}
|
||||||
emptyText={t("channels.empty")}
|
emptyText={t("channels.empty")}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ export default function DataTable<T>({
|
||||||
controlsPosition = "bottom",
|
controlsPosition = "bottom",
|
||||||
controlsLeading,
|
controlsLeading,
|
||||||
externalFilter,
|
externalFilter,
|
||||||
|
resetFiltersToken,
|
||||||
}: {
|
}: {
|
||||||
rows: T[];
|
rows: T[];
|
||||||
columns: Column<T>[];
|
columns: Column<T>[];
|
||||||
|
|
@ -81,6 +82,10 @@ export default function DataTable<T>({
|
||||||
// Set a column's filter from outside (e.g. "focus this channel" deep-links here). Applied
|
// Set a column's filter from outside (e.g. "focus this channel" deep-links here). Applied
|
||||||
// whenever its value changes; the user can still clear it from the header afterwards.
|
// whenever its value changes; the user can still clear it from the header afterwards.
|
||||||
externalFilter?: { key: string; value: string } | null;
|
externalFilter?: { key: string; value: string } | null;
|
||||||
|
// Bump this to clear all column filters — for navigation intents that mean "show me this
|
||||||
|
// set" (e.g. the header's "without full history" link), so a stale persisted filter can't
|
||||||
|
// hide the rows the user was just sent to see.
|
||||||
|
resetFiltersToken?: number;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const initial = loadPersist(persistKey);
|
const initial = loadPersist(persistKey);
|
||||||
|
|
@ -104,6 +109,18 @@ export default function DataTable<T>({
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [externalFilter?.key, externalFilter?.value]);
|
}, [externalFilter?.key, externalFilter?.value]);
|
||||||
|
|
||||||
|
// Clear column filters when the reset token advances. The ref starts at 0 (not at the prop)
|
||||||
|
// so a navigation that bumped the token still clears even when it remounts this table fresh
|
||||||
|
// (e.g. arriving from another page); a plain reload keeps the token at 0 and the effect is a
|
||||||
|
// no-op, so the user's persisted filters survive.
|
||||||
|
const resetRef = useRef(0);
|
||||||
|
useEffect(() => {
|
||||||
|
if (resetFiltersToken === undefined || resetFiltersToken === resetRef.current) return;
|
||||||
|
resetRef.current = resetFiltersToken;
|
||||||
|
setFilters({});
|
||||||
|
setPage(0);
|
||||||
|
}, [resetFiltersToken]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!openFilter) return;
|
if (!openFilter) return;
|
||||||
function onDown(e: MouseEvent) {
|
function onDown(e: MouseEvent) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue