fix(feed): round-2 deep-review remediations (incl. a real coerce-on-seed bug)
The re-review of the fix round surfaced 4 findings; fixing the coverage one
exposed that the C1 migration was ineffective:
- FeedFiltersProvider.loadAccountFilters: run coerceTagModes on the RAW stored
blob, not the object merged over DEFAULT_FILTERS. The merge injects
DEFAULT_TAG_MODES (all "or"), which coerceTagModes prefers over the legacy
scalar `tagMode` — so the migration never fired. Now a persisted legacy
tagMode:'and' actually migrates into tagModes.topic (proven by a new test).
- test_feed_views: the tag_mode_other=and case was vacuous (only chC carried
either "other" tag, so AND == OR). Give chA an "other" tag too so OR-other
{chA,chC} differs from AND-other {chC} — the conjunctive path now narrows.
- DateFilterSelect: reset the custom reveal in useLayoutEffect (not useEffect)
so the correction commits before paint — no one-frame "Custom range" flicker
on the trigger after an external clear.
- SavedViewsProvider: extract the sticky-view tie-break to a pure, tested
lib/savedViews.ts (preferSticky).
New tests: savedViews (preferSticky), FeedFiltersProvider.dom (legacy tagMode
migration on the localStorage/default-view seed).
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
type SavedView,
|
||||
} from "../lib/api";
|
||||
import { filtersToParams } from "../lib/urlState";
|
||||
import { preferSticky } from "../lib/savedViews";
|
||||
import { notifyActionError } from "../lib/actionErrors";
|
||||
import { accountKey, LS, writeJSON } from "../lib/storage";
|
||||
import { useConfirm } from "./ConfirmProvider";
|
||||
@@ -85,12 +86,16 @@ export function SavedViewsProvider({ children }: { children: ReactNode }) {
|
||||
}, [viewsQuery.data]);
|
||||
|
||||
const currentKey = keyOf(filters);
|
||||
const activeView = useMemo(() => {
|
||||
const matches = views.filter((v) => keyOf(v.filters) === currentKey);
|
||||
const activeView = useMemo(
|
||||
// When several views capture identical filters, prefer the one the user explicitly applied
|
||||
// (sticky) so apply(B) isn't silently re-attributed to an earlier duplicate A; else the first.
|
||||
return matches.find((v) => v.id === stickyId) ?? matches[0] ?? null;
|
||||
}, [views, currentKey, stickyId]);
|
||||
() =>
|
||||
preferSticky(
|
||||
views.filter((v) => keyOf(v.filters) === currentKey),
|
||||
stickyId,
|
||||
),
|
||||
[views, currentKey, stickyId],
|
||||
);
|
||||
// An exact match becomes the sticky selection, so filters that happen to equal a view (deep-link,
|
||||
// manual, or an applied view) name that view in the chip and keep naming it after a later tweak.
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user