Skip to content

Commit 227200b

Browse files
Copilotalexr00
andauthored
Add "Mark All as Read" context menu for Copilot on My Behalf PRs (#7859)
* Initial plan * Initial exploration and plan for Copilot PR mark all as read feature Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> * Implement Mark All as Read feature for Copilot notifications * Improve contextValue assignment logic for better readability * Changes before error encountered Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> * Code review fixes: use makeKey, existing getNotificationsCount method, and repo getter Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> * Make it work --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com>
1 parent c991802 commit 227200b

File tree

5 files changed

+77
-1
lines changed

5 files changed

+77
-1
lines changed

package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,12 @@
948948
"title": "%command.pr.dismissNotification.title%",
949949
"category": "%command.pull.request.category%"
950950
},
951+
{
952+
"command": "pr.markAllCopilotNotificationsAsRead",
953+
"title": "Mark All as Read",
954+
"category": "%command.pull.request.category%",
955+
"enablement": "viewItem == copilot-query-with-notifications"
956+
},
951957
{
952958
"command": "pr.merge",
953959
"title": "%command.pr.merge.title%",
@@ -1998,6 +2004,10 @@
19982004
"command": "pr.dismissNotification",
19992005
"when": "false"
20002006
},
2007+
{
2008+
"command": "pr.markAllCopilotNotificationsAsRead",
2009+
"when": "false"
2010+
},
20012011
{
20022012
"command": "pr.resetViewedFiles",
20032013
"when": "github:inReviewMode"
@@ -2786,6 +2796,11 @@
27862796
"when": "view == pr:github && viewItem =~ /pullrequest(.*):notification/",
27872797
"group": "4_pullrequest@5"
27882798
},
2799+
{
2800+
"command": "pr.markAllCopilotNotificationsAsRead",
2801+
"when": "view == pr:github && viewItem =~ /copilot-query/",
2802+
"group": "0_category@1"
2803+
},
27892804
{
27902805
"command": "issue.chatSummarizeIssue",
27912806
"when": "view == pr:github && viewItem =~ /pullrequest/ && github.copilot-chat.activated && config.githubPullRequests.experimental.chat",

src/commands.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,14 @@ export function registerCommands(
917917
}),
918918
);
919919

920+
context.subscriptions.push(
921+
vscode.commands.registerCommand('pr.markAllCopilotNotificationsAsRead', node => {
922+
if (node instanceof CategoryTreeNode && node.isCopilot && node.repo) {
923+
copilotRemoteAgentManager.clearAllNotifications(node.repo.owner, node.repo.repositoryName);
924+
}
925+
}),
926+
);
927+
920928
async function openDescriptionCommand(argument: RepositoryChangesNode | PRNode | IssueModel | ChatSessionWithPR | undefined) {
921929
let issueModel: IssueModel | undefined;
922930
if (!argument) {

src/github/copilotPrWatcher.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,41 @@ export class CopilotStateModel extends Disposable {
107107
}
108108
}
109109

110+
clearAllNotifications(owner?: string, repo?: string): void {
111+
if (this._showNotification.size > 0) {
112+
const items: PullRequestModel[] = [];
113+
114+
// If owner and repo are specified, only clear notifications for that repo
115+
if (owner && repo) {
116+
const keysToRemove: string[] = [];
117+
const prefix = `${this.makeKey(owner, repo)}#`;
118+
for (const key of this._showNotification.keys()) {
119+
if (key.startsWith(prefix)) {
120+
const item = this._states.get(key)?.item;
121+
if (item) {
122+
items.push(item);
123+
}
124+
keysToRemove.push(key);
125+
}
126+
}
127+
keysToRemove.forEach(key => this._showNotification.delete(key));
128+
} else {
129+
// Clear all notifications
130+
for (const key of this._showNotification.keys()) {
131+
const item = this._states.get(key)?.item;
132+
if (item) {
133+
items.push(item);
134+
}
135+
}
136+
this._showNotification.clear();
137+
}
138+
139+
if (items.length > 0) {
140+
this._onDidChangeNotifications.fire(items);
141+
}
142+
}
143+
}
144+
110145
get notifications(): ReadonlySet<string> {
111146
return this._showNotification;
112147
}

src/github/copilotRemoteAgent.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,11 @@ export class CopilotRemoteAgentManager extends Disposable {
881881
return this._stateModel.notifications.size;
882882
}
883883

884+
885+
public clearAllNotifications(owner?: string, repo?: string): void {
886+
this._stateModel.clearAllNotifications(owner, repo);
887+
}
888+
884889
hasNotification(owner: string, repo: string, pullRequestNumber?: number): boolean {
885890
if (pullRequestNumber !== undefined) {
886891
const key = this._stateModel.makeKey(owner, repo, pullRequestNumber);

src/view/treeNodes/categoryNode.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ export class CategoryTreeNode extends TreeNode implements vscode.TreeItem {
179179
}
180180

181181
if (this._categoryQuery) {
182-
this.contextValue = 'query';
182+
this.contextValue = this.isCopilot ? 'copilot-query' : 'query';
183183
}
184184

185185
if (this.isCopilot) {
@@ -195,6 +195,10 @@ export class CategoryTreeNode extends TreeNode implements vscode.TreeItem {
195195
}
196196
}
197197

198+
get repo(): RemoteInfo | undefined {
199+
return this._repo;
200+
}
201+
198202
private _getDescription(): string | undefined {
199203
if (!this.isCopilot || !this._repo) {
200204
return undefined;
@@ -328,6 +332,15 @@ export class CategoryTreeNode extends TreeNode implements vscode.TreeItem {
328332
this._repo = await extractRepoFromQuery(this.folderRepoManager, this._categoryQuery);
329333
}
330334
this.resourceUri = toQueryUri({ remote: this._repo, isCopilot: this.isCopilot });
335+
336+
// Update contextValue based on current notification state
337+
if (this._categoryQuery) {
338+
const hasNotifications = this.isCopilot && this._repo && this._copilotManager.getNotificationsCount(this._repo.owner, this._repo.repositoryName) > 0;
339+
this.contextValue = this.isCopilot ?
340+
(hasNotifications ? 'copilot-query-with-notifications' : 'copilot-query') :
341+
'query';
342+
}
343+
331344
return this;
332345
}
333346
}

0 commit comments

Comments
 (0)