Skip to content

Commit b9a7c59

Browse files
committed
feat: add filter by reviewer # 39
1 parent 3024bc1 commit b9a7c59

4 files changed

Lines changed: 127 additions & 2 deletions

File tree

src/filter-manager.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ export class FilterManager {
3838
filteredPullRequests = this.applyAssigneeFilter(filteredPullRequests, repo.prAssigneeFilterMode ?? "assigned-to-me", repo.prAssigneeFilters ?? []);
3939
}
4040

41+
// Apply reviewer filtering
42+
if ((repo.enablePrReviewerFilter ?? false)) {
43+
filteredPullRequests = this.applyReviewerFilter(filteredPullRequests, repo.prReviewerFilterMode ?? "review-requested-from-me", repo.prReviewerFilters ?? []);
44+
}
45+
4146
return filteredPullRequests;
4247
}
4348

@@ -91,6 +96,31 @@ export class FilterManager {
9196
});
9297
}
9398

99+
private applyReviewerFilter(items: any[], filterMode: "review-requested-from-me" | "review-requested-from-specific" | "no-review-requested" | "any-review-requested", reviewerFilters: string[]): any[] {
100+
return items.filter((item) => {
101+
const reviewers = item.requested_reviewers || [];
102+
const reviewerUsernames = reviewers.map((reviewer: any) => reviewer.login || reviewer);
103+
104+
switch (filterMode) {
105+
case "review-requested-from-me":
106+
const currentUser = this.getCurrentUser();
107+
return reviewerUsernames.includes(currentUser);
108+
109+
case "review-requested-from-specific":
110+
return reviewerFilters.some(filterUser => reviewerUsernames.includes(filterUser));
111+
112+
case "no-review-requested":
113+
return reviewerUsernames.length === 0;
114+
115+
case "any-review-requested":
116+
return reviewerUsernames.length > 0;
117+
118+
default:
119+
return true;
120+
}
121+
});
122+
}
123+
94124
private getCurrentUser(): string {
95125
// Access the current user from the GitHubClient through the main plugin
96126
return this.gitHubClient ? this.gitHubClient.getCurrentUser() : "";

src/settings/modal-manager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ export class ModalManager {
240240
async fetchAndShowRepositoryCollaborators(
241241
repositoryName: string,
242242
repo: RepositoryTracking,
243-
filterType: 'assigneeFilters' | 'prAssigneeFilters',
243+
filterType: 'assigneeFilters' | 'prAssigneeFilters' | 'prReviewerFilters',
244244
textAreaElement: HTMLTextAreaElement,
245245
): Promise<void> {
246246
if (!this.plugin.gitHubClient?.isReady()) {

src/settings/repository-renderer.ts

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export class RepositoryRenderer {
77
private app: App,
88
private plugin: GitHubTrackerPlugin,
99
private fetchLabels?: (repo: string, repoObj: RepositoryTracking, filterType: 'labelFilters' | 'prLabelFilters', textArea: HTMLTextAreaElement) => Promise<void>,
10-
private fetchCollaborators?: (repo: string, repoObj: RepositoryTracking, filterType: 'assigneeFilters' | 'prAssigneeFilters', textArea: HTMLTextAreaElement) => Promise<void>,
10+
private fetchCollaborators?: (repo: string, repoObj: RepositoryTracking, filterType: 'assigneeFilters' | 'prAssigneeFilters' | 'prReviewerFilters', textArea: HTMLTextAreaElement) => Promise<void>,
1111
) {}
1212

1313
renderIssueSettings(
@@ -46,6 +46,9 @@ export class RepositoryRenderer {
4646

4747
// Assignee filtering settings for pull requests
4848
this.renderAssigneeFilter(container, repo, 'pr');
49+
50+
// Reviewer filtering settings for pull requests
51+
this.renderReviewerFilter(container, repo);
4952
}
5053

5154
private renderLabelFilter(
@@ -126,6 +129,92 @@ export class RepositoryRenderer {
126129
);
127130
}
128131

132+
private renderReviewerFilter(
133+
container: HTMLElement,
134+
repo: RepositoryTracking,
135+
): void {
136+
new Setting(container)
137+
.setName("Filter pull requests by reviewers")
138+
.setDesc("Enable filtering pull requests based on requested reviewers")
139+
.addToggle((toggle) =>
140+
toggle
141+
.setValue(repo.enablePrReviewerFilter ?? false)
142+
.onChange(async (value) => {
143+
repo.enablePrReviewerFilter = value;
144+
reviewerFilterContainer.classList.toggle(
145+
"github-issues-hidden",
146+
!value,
147+
);
148+
await this.plugin.saveSettings();
149+
}),
150+
);
151+
152+
const reviewerFilterContainer = container.createDiv(
153+
"github-issues-settings-group github-issues-nested",
154+
);
155+
reviewerFilterContainer.classList.toggle(
156+
"github-issues-hidden",
157+
!(repo.enablePrReviewerFilter ?? false),
158+
);
159+
160+
new Setting(reviewerFilterContainer)
161+
.setName("Reviewer filter mode")
162+
.setDesc("Choose how to filter pull requests by reviewers")
163+
.addDropdown((dropdown) =>
164+
dropdown
165+
.addOption("review-requested-from-me", "Review requested from me")
166+
.addOption("review-requested-from-specific", "Review requested from specific users")
167+
.addOption("no-review-requested", "No review requested")
168+
.addOption("any-review-requested", "Any review requested")
169+
.setValue(repo.prReviewerFilterMode ?? "review-requested-from-me")
170+
.onChange(async (value) => {
171+
repo.prReviewerFilterMode = value as "review-requested-from-me" | "review-requested-from-specific" | "no-review-requested" | "any-review-requested";
172+
reviewerSpecificContainer.classList.toggle(
173+
"github-issues-hidden",
174+
value !== "review-requested-from-specific",
175+
);
176+
await this.plugin.saveSettings();
177+
}),
178+
);
179+
180+
const reviewerSpecificContainer = reviewerFilterContainer.createDiv(
181+
"github-issues-settings-group github-issues-nested",
182+
);
183+
reviewerSpecificContainer.classList.toggle(
184+
"github-issues-hidden",
185+
(repo.prReviewerFilterMode ?? "review-requested-from-me") !== "review-requested-from-specific",
186+
);
187+
188+
new Setting(reviewerSpecificContainer)
189+
.setName("Specific reviewers")
190+
.setDesc("Comma-separated list of GitHub usernames to filter by")
191+
.addTextArea((text) => {
192+
text
193+
.setPlaceholder("username1, username2, username3")
194+
.setValue((repo.prReviewerFilters || []).join(", "))
195+
.onChange(async (value) => {
196+
repo.prReviewerFilters = value
197+
.split(",")
198+
.map(username => username.trim())
199+
.filter(username => username.length > 0);
200+
await this.plugin.saveSettings();
201+
});
202+
203+
return text;
204+
})
205+
.addButton((button) =>
206+
button
207+
.setButtonText("Fetch collaborators")
208+
.setTooltip("Load collaborators from this repository to help with configuration")
209+
.onClick(async () => {
210+
const textArea = button.buttonEl.closest('.setting-item')?.querySelector('textarea');
211+
if (textArea && this.fetchCollaborators) {
212+
await this.fetchCollaborators(repo.repository, repo, 'prReviewerFilters', textArea as HTMLTextAreaElement);
213+
}
214+
}),
215+
);
216+
}
217+
129218
private renderAssigneeFilter(
130219
container: HTMLElement,
131220
repo: RepositoryTracking,

src/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ export interface RepositoryTracking {
6363
enablePrAssigneeFilter: boolean;
6464
prAssigneeFilterMode: "assigned-to-me" | "assigned-to-specific" | "unassigned" | "any-assigned";
6565
prAssigneeFilters: string[];
66+
enablePrReviewerFilter: boolean;
67+
prReviewerFilterMode: "review-requested-from-me" | "review-requested-from-specific" | "no-review-requested" | "any-review-requested";
68+
prReviewerFilters: string[];
6669
escapeHashTags: boolean;
6770

6871
// Profile-managed fields (optional - hydrated from profile at runtime)
@@ -311,5 +314,8 @@ export const DEFAULT_REPOSITORY_TRACKING: RepositoryTracking = {
311314
enablePrAssigneeFilter: false,
312315
prAssigneeFilterMode: "assigned-to-me",
313316
prAssigneeFilters: [],
317+
enablePrReviewerFilter: false,
318+
prReviewerFilterMode: "review-requested-from-me",
319+
prReviewerFilters: [],
314320
escapeHashTags: false,
315321
};

0 commit comments

Comments
 (0)