test(channels): cover the tab-history logic; memoize viewTabs (S2b review)

Addresses the 2 applied findings from /code-review high on 735d08c:
- Add ChannelsProvider.dom.test.tsx (4 tests): setView pushes a _chTab history entry +
  persists the tab, re-selecting the current tab pushes nothing, a genuine popstate carrying
  _chTab restores + re-persists it, and a popstate without _chTab is ignored. Real storage
  (jsdom) with the account pinned to 1; only NavigationProvider is mocked. vitest 120->124.
- Memoize viewTabs (keyed on the translation fn) so it isn't rebuilt on every Channels render.

The third finding (consolidate the hand-rolled single-select sync chips with <Tabs>) is
deferred by design into the S2c plan, where MatchToggle will be a shared segmented-control
primitive that can also absorb the sync chips.

gate-green: tsc + vitest 124 + eslint-0 + knip + prettier.
This commit is contained in:
2026-08-14 04:01:38 +02:00
parent 735d08c91d
commit ed1a2b7256
2 changed files with 82 additions and 4 deletions
+9 -4
View File
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { qk } from "../lib/queryKeys";
@@ -432,9 +432,14 @@ export default function Channels() {
}, [filters, search, sort, resetPage]);
const cardPager = <Pager {...pagerProps} />;
const viewTabs: TabDef[] = (
["subscribed", "discovery", "hidden", "blocked"] as ChannelsView[]
).map((v) => ({ id: v, label: t(`channels.tabs.${v}`) }));
const viewTabs: TabDef[] = useMemo(
() =>
(["subscribed", "discovery", "hidden", "blocked"] as ChannelsView[]).map((v) => ({
id: v,
label: t(`channels.tabs.${v}`),
})),
[t],
);
const tabs = (
<div className="px-4 pt-4 max-w-384 mx-auto flex items-center justify-between gap-4 flex-wrap">
<Tabs