soulseek: open a filtered row only when the filter hit something below it

every row in the filtered skeleton was rendered open, so searching a band
name unfolded each of its albums into a wall of tracks that matched nothing.
a row now starts open only when the filter matched a descendant; one that
matched on its own name stays shut, and opening it leaves the filter behind
and browses its real contents.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-30 01:37:25 +00:00
co-authored by Claude Opus 4.8
parent 85b7337b68
commit b94dccd17c
@@ -246,13 +246,17 @@ type TreeNodeProps = {
};
const TreeNode = ({ username, node, filtered, indent, initialOpen }: TreeNodeProps) => {
const [open, setOpen] = useState(initialOpen);
const [limit, setLimit] = useState(LEVEL_LIMIT);
const [revealed, setRevealed] = useState(false);
// Filtering stops at a revealed row: from here down it's ordinary lazy browsing, so one click gets you
// out of the filtered skeleton and into the real folder — which is usually why you searched.
// Filtering stops at a revealed row: from here down it's ordinary lazy browsing, so opening a matched
// folder shows what's really inside it rather than the filtered skeleton — which is why you searched.
const scoped = revealed ? null : filtered;
const scopedChildren = scoped?.childrenOf.get(node.name) ?? [];
// A filtered row starts open only when the filter matched something below it. One that matched on its own
// name has nothing of its own to show, so it stays shut and behaves like any other collapsed folder.
const [open, setOpen] = useState(() => (filtered ? scopedChildren.length > 0 : initialOpen));
const expandable = node.childCount > 0 || node.fileCount > 0;
const level = useSoulseekBrowseLevel({
@@ -262,7 +266,7 @@ const TreeNode = ({ username, node, filtered, indent, initialOpen }: TreeNodePro
enabled: !scoped && open && node.childCount > 0,
});
const children = scoped ? (scoped.childrenOf.get(node.name) ?? []) : (level.data?.nodes ?? []);
const children = scoped ? scopedChildren : (level.data?.nodes ?? []);
// In the filtered skeleton no level was fetched, so the row's own count is what says how many exist.
const total = scoped ? node.childCount : (level.data?.total ?? node.childCount);
const capped = !scoped && limit >= LEVEL_LIMIT_MAX;
@@ -271,11 +275,18 @@ const TreeNode = ({ username, node, filtered, indent, initialOpen }: TreeNodePro
const more = open && total > children.length && (!!scoped || !level.isPending);
const isMatch = filtered?.matched.has(node.name) ?? false;
const toggle = () => {
// Opening a matched row with nothing matched below it leaves the filter behind, rather than opening
// into an empty folder.
if (!open && scoped && !scopedChildren.length) setRevealed(true);
setOpen((v) => !v);
};
return (
<div>
<button
type="button"
onClick={() => expandable && setOpen((v) => !v)}
onClick={() => expandable && toggle()}
style={{ paddingLeft: 12 + indent * 14 }}
className={`flex w-full items-center gap-1.5 py-1.5 pr-4 text-left text-sm transition-colors hover:bg-white/[0.03] ${
expandable ? '' : 'cursor-default'