Skip to content

Commit cab3d73

Browse files
authored
fix(scope): show tracked user items in default involves_me scope (#61)
* fix(scope): shows tracked user items in default involves_me scope The scope filter's isUserInvolved() tier 1 checked whether the main user's login was in surfacedBy, hiding items surfaced only by tracked users. Since surfacedBy is exclusively populated by involves: searches (main user or tracked users), any surfacedBy presence means someone the user chose to track is involved — always relevant. Changes tier 1 from surfacedBy.includes(login) to surfacedBy.length > 0. Monitored repo items (no surfacedBy) still use field-based fallback. Updates 4 tests that modeled tracked user items as community items by using surfacedBy with a non-main-user login — an unrealistic fixture since surfacedBy is only set for tracked user search results. * docs: updates scope filter description to reflect tracked user behavior * test: adds tier-ordering and border accent tests for tracked user items * docs: fixes border accent and monitored repo scope filter wording * docs: fixes incorrect monitored repo claim in scope filter description * docs: fixes duplicate monitored repo claim in monitor-all section * docs: removes theme-specific color from border accent description
1 parent 97a0517 commit cab3d73

File tree

5 files changed

+66
-19
lines changed

5 files changed

+66
-19
lines changed

docs/USER_GUIDE.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ When a group is collapsed, a brief preview of any status change detected by the
110110

111111
The **Scope** filter chip appears on the Issues and Pull Requests tabs when you have tracked users configured or monitor-all repos enabled. It has two options:
112112

113-
- **Involves me** (default) — shows only items where you (the signed-in user) are the author, assignee, reviewer, or mentioned. For monitored repos, all activity in that repo is always shown regardless of scope.
114-
- **All activity** — shows every open item across your selected repos. Items that involve you are highlighted with a blue left border.
113+
- **Involves me** (default) — shows items where you or any of your tracked users are involved (author, assignee, reviewer, or mentioned). For monitored repos, only items where you are the author, assignee, or reviewer are shown.
114+
- **All activity** — shows every open item across your selected repos. Items involving you or your tracked users are highlighted with a colored left border.
115115

116116
The scope filter is hidden (and always set to "Involves me") when you have no tracked users and no monitor-all repos, because in that configuration all fetched data already involves you.
117117

@@ -251,7 +251,7 @@ Normally, the dashboard shows only issues and PRs that involve you (or a tracked
251251

252252
**How to enable:** In **Settings > Repositories**, expand the repo panel. Each repo has an eye icon toggle. Enabling it adds the repo to the monitored list (maximum 10 monitored repos).
253253

254-
**Effect on display:** Repo groups for monitored repos show a **Monitoring all** badge in their header. Items from monitored repos are always visible even when the Scope filter is set to "Involves me", and they bypass the User filter.
254+
**Effect on display:** Repo groups for monitored repos show a **Monitoring all** badge in their header. In "Involves me" scope, monitored repo items are shown when you are the author, assignee, or reviewer; switch to "All activity" to see all monitored repo items. Monitored repo items bypass the User filter.
255255

256256
Upstream repos cannot be monitored (only selected repos are eligible).
257257

@@ -447,7 +447,7 @@ These are UI preferences that persist across sessions but are not included in th
447447

448448
**Items I expect to see are not showing up.**
449449

450-
- Check that the Scope filter is set correctly. "Involves me" hides items where you have no direct involvement. Switch to "All activity" to see everything.
450+
- Check that the Scope filter is set correctly. "Involves me" shows items involving you or your tracked users; items from monitored repos where you are not directly involved (author, assignee, or reviewer) are hidden. Switch to "All activity" to see everything.
451451
- Verify the repo is in your selected repo list (Settings > Repositories).
452452
- Check if the item was accidentally ignored (toolbar Ignored badge).
453453
- If you recently added the repo, wait for the next full refresh or click the manual refresh button.

src/app/lib/grouping.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ export function orderRepoGroups<G extends { repoFullName: string }>(
7777
* Three-tier involvement check for scope filtering.
7878
* Shared by IssuesTab and PullRequestsTab — keep both call sites in sync.
7979
*
80-
* Tier 1: surfacedBy annotation present → check if user is included
80+
* Tier 1: surfacedBy annotation present → pass (item was found via an involves:
81+
* search for the main user or a tracked user — always relevant)
8182
* Tier 2: monitored repo (no surfacedBy) → field-based fallback (author/assignee)
8283
* Pass reviewerLogins for PRs (only when enriched — unenriched PRs have [])
8384
* Tier 3: non-monitored, no surfacedBy → pass (fetched via involves:{user})
@@ -89,7 +90,7 @@ export function isUserInvolved(
8990
reviewerLogins?: string[],
9091
): boolean {
9192
const surfacedBy = item.surfacedBy ?? [];
92-
if (surfacedBy.length > 0) return surfacedBy.includes(login);
93+
if (surfacedBy.length > 0) return true;
9394
if (monitoredRepos.has(item.repoFullName)) {
9495
return item.userLogin.toLowerCase() === login ||
9596
item.assigneeLogins.some(a => a.toLowerCase() === login) ||

tests/components/dashboard/IssuesTab.test.tsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -322,10 +322,10 @@ describe("IssuesTab — monitored repos filter bypass", () => {
322322
// ── IssuesTab — scope filter ───────────────────────────────────────────────────
323323

324324
describe("IssuesTab — scope filter", () => {
325-
it("default scope shows only items involving the user (surfacedBy includes userLogin)", () => {
325+
it("default scope shows items surfaced by tracked users (surfacedBy present)", () => {
326326
const issues = [
327327
makeIssue({ id: 1, title: "My issue", repoFullName: "org/repo", surfacedBy: ["me"] }),
328-
makeIssue({ id: 2, title: "Community issue", repoFullName: "org/repo", surfacedBy: ["other"] }),
328+
makeIssue({ id: 2, title: "Tracked User issue", repoFullName: "org/repo", surfacedBy: ["other"] }),
329329
];
330330
setAllExpanded("issues", ["org/repo"], true);
331331

@@ -339,7 +339,7 @@ describe("IssuesTab — scope filter", () => {
339339
));
340340

341341
screen.getByText("My issue");
342-
expect(screen.queryByText("Community issue")).toBeNull();
342+
screen.getByText("Tracked User issue");
343343
});
344344

345345
it("scope 'all' shows all items including community items", () => {
@@ -437,9 +437,9 @@ describe("IssuesTab — left border accent in 'all' scope", () => {
437437
expect(listitem?.className).toContain("border-l-primary");
438438
});
439439

440-
it("does not add border-l-2 to community items in 'all' scope", () => {
440+
it("does not add border-l-2 to untracked monitored repo items in 'all' scope", () => {
441441
const issues = [
442-
makeIssue({ id: 1, title: "Community issue", repoFullName: "org/monitored", surfacedBy: ["other"], userLogin: "other", assigneeLogins: [] }),
442+
makeIssue({ id: 1, title: "Community issue", repoFullName: "org/monitored", userLogin: "other", assigneeLogins: [] }),
443443
];
444444
setTabFilter("issues", "scope", "all");
445445
setAllExpanded("issues", ["org/monitored"], true);
@@ -456,6 +456,25 @@ describe("IssuesTab — left border accent in 'all' scope", () => {
456456
expect(listitem?.className).not.toContain("border-l-primary");
457457
});
458458

459+
it("adds border-l-primary to tracked user issue in monitored repo in 'all' scope", () => {
460+
const issues = [
461+
makeIssue({ id: 1, title: "Bot issue", repoFullName: "org/monitored", surfacedBy: ["tracked-bot[bot]"] }),
462+
];
463+
setTabFilter("issues", "scope", "all");
464+
setAllExpanded("issues", ["org/monitored"], true);
465+
466+
const { container } = render(() => (
467+
<IssuesTab
468+
issues={issues}
469+
userLogin="me"
470+
monitoredRepos={[{ owner: "org", name: "monitored", fullName: "org/monitored" }]}
471+
/>
472+
));
473+
474+
const listitem = container.querySelector('[role="listitem"]');
475+
expect(listitem?.className).toContain("border-l-primary");
476+
});
477+
459478
it("does not add border-l-2 in default 'involves_me' scope", () => {
460479
const issues = [
461480
makeIssue({ id: 1, title: "My issue", repoFullName: "org/repo", surfacedBy: ["me"] }),

tests/components/dashboard/PullRequestsTab.test.tsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,10 @@ describe("PullRequestsTab — monitored repos filter bypass", () => {
272272
// ── PullRequestsTab — scope filter ────────────────────────────────────────────
273273

274274
describe("PullRequestsTab — scope filter", () => {
275-
it("default scope shows only items involving the user (surfacedBy includes userLogin)", () => {
275+
it("default scope shows items surfaced by tracked users (surfacedBy present)", () => {
276276
const prs = [
277277
makePullRequest({ id: 1, title: "My PR", repoFullName: "org/repo", surfacedBy: ["me"] }),
278-
makePullRequest({ id: 2, title: "Community PR", repoFullName: "org/repo", surfacedBy: ["other"] }),
278+
makePullRequest({ id: 2, title: "Tracked User PR", repoFullName: "org/repo", surfacedBy: ["other"] }),
279279
];
280280
setAllExpanded("pullRequests", ["org/repo"], true);
281281

@@ -289,7 +289,7 @@ describe("PullRequestsTab — scope filter", () => {
289289
));
290290

291291
screen.getByText("My PR");
292-
expect(screen.queryByText("Community PR")).toBeNull();
292+
screen.getByText("Tracked User PR");
293293
});
294294

295295
it("scope 'all' shows all PRs including community items", () => {
@@ -387,9 +387,9 @@ describe("PullRequestsTab — left border accent in 'all' scope", () => {
387387
expect(listitem?.className).toContain("border-l-primary");
388388
});
389389

390-
it("does not add border-l-primary to community PRs in 'all' scope", () => {
390+
it("does not add border-l-primary to untracked monitored repo PRs in 'all' scope", () => {
391391
const prs = [
392-
makePullRequest({ id: 1, title: "Community PR", repoFullName: "org/monitored", surfacedBy: ["other"], userLogin: "other", assigneeLogins: [], reviewerLogins: [] }),
392+
makePullRequest({ id: 1, title: "Community PR", repoFullName: "org/monitored", userLogin: "other", assigneeLogins: [], reviewerLogins: [] }),
393393
];
394394
setTabFilter("pullRequests", "scope", "all");
395395
setAllExpanded("pullRequests", ["org/monitored"], true);
@@ -406,6 +406,25 @@ describe("PullRequestsTab — left border accent in 'all' scope", () => {
406406
expect(listitem?.className).not.toContain("border-l-primary");
407407
});
408408

409+
it("adds border-l-primary to tracked user PR in monitored repo in 'all' scope", () => {
410+
const prs = [
411+
makePullRequest({ id: 1, title: "Bot PR", repoFullName: "org/monitored", surfacedBy: ["tracked-bot[bot]"] }),
412+
];
413+
setTabFilter("pullRequests", "scope", "all");
414+
setAllExpanded("pullRequests", ["org/monitored"], true);
415+
416+
const { container } = render(() => (
417+
<PullRequestsTab
418+
pullRequests={prs}
419+
userLogin="me"
420+
monitoredRepos={[{ owner: "org", name: "monitored", fullName: "org/monitored" }]}
421+
/>
422+
));
423+
424+
const listitem = container.querySelector('[role="listitem"]');
425+
expect(listitem?.className).toContain("border-l-primary");
426+
});
427+
409428
it("does not add border-l-primary in default 'involves_me' scope", () => {
410429
const prs = [
411430
makePullRequest({ id: 1, title: "My PR", repoFullName: "org/repo", surfacedBy: ["me"] }),

tests/lib/grouping.test.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,20 @@ describe("isUserInvolved", () => {
6363
const base = { repoFullName: "org/repo", userLogin: "author", assigneeLogins: [] as string[] };
6464
const monitored = new Set(["org/monitored"]);
6565

66-
it("returns true when surfacedBy includes user", () => {
66+
it("returns true when surfacedBy is non-empty (main user)", () => {
6767
expect(isUserInvolved({ ...base, surfacedBy: ["me"] }, "me", monitored)).toBe(true);
6868
});
6969

70-
it("returns false when surfacedBy excludes user", () => {
71-
expect(isUserInvolved({ ...base, surfacedBy: ["other"] }, "me", monitored)).toBe(false);
70+
it("returns true when surfacedBy contains only a tracked user (not the main user)", () => {
71+
expect(isUserInvolved({ ...base, surfacedBy: ["tracked-bot[bot]"] }, "me", monitored)).toBe(true);
72+
});
73+
74+
it("returns true when surfacedBy contains multiple tracked users but not the main user", () => {
75+
expect(isUserInvolved({ ...base, surfacedBy: ["bot1[bot]", "bot2"] }, "me", monitored)).toBe(true);
76+
});
77+
78+
it("returns true for monitored repo item with surfacedBy (tier 1 before tier 2)", () => {
79+
expect(isUserInvolved({ ...base, repoFullName: "org/monitored", surfacedBy: ["tracked-bot[bot]"] }, "me", monitored)).toBe(true);
7280
});
7381

7482
it("returns true for non-monitored item with no surfacedBy (fetched via involves:{user})", () => {

0 commit comments

Comments
 (0)