Skip to content

Commit d57159e

Browse files
authored
Switch to decoration for the padawan query notification (#7071)
* Switch to decoration for the padawan query notification * Clear notification timeout
1 parent 383829a commit d57159e

File tree

5 files changed

+61
-16
lines changed

5 files changed

+61
-16
lines changed

src/common/uri.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,8 +576,11 @@ export enum Schemes {
576576
NewIssue = 'newissue', // New issue file
577577
Repo = 'repo', // New issue file for passing data
578578
Git = 'git', // File content from the git extension
579+
PRQuery = 'prquery', // PR query tree item
579580
}
580581

582+
export const COPILOT_QUERY = vscode.Uri.from({ scheme: Schemes.PRQuery, path: 'copilot' });
583+
581584
export function resolvePath(from: vscode.Uri, to: string) {
582585
if (from.scheme === Schemes.File) {
583586
return pathUtils.resolve(from.fsPath, to);

src/github/copilotPrWatcher.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ export class CopilotStateModel extends Disposable {
4141
private _isInitialized = false;
4242
private readonly _states: Map<string, CopilotPRStatus> = new Map();
4343
private readonly _showNotification: Set<string> = new Set();
44-
private readonly _onDidChange = this._register(new vscode.EventEmitter<void>());
45-
readonly onDidChange = this._onDidChange.event;
44+
private readonly _onDidChangeStates = this._register(new vscode.EventEmitter<void>());
45+
readonly onDidChangeStates = this._onDidChangeStates.event;
46+
private readonly _onDidChangeNotifications = this._register(new vscode.EventEmitter<void>());
47+
readonly onDidChangeNotifications = this._onDidChangeNotifications.event;
4648

4749
makeKey(owner: string, repo: string, prNumber: number): string {
4850
return `${owner}/${repo}#${prNumber}`;
@@ -57,8 +59,9 @@ export class CopilotStateModel extends Disposable {
5759
this._states.delete(key);
5860
if (this._showNotification.has(key)) {
5961
this._showNotification.delete(key);
62+
this._onDidChangeNotifications.fire();
6063
}
61-
this._onDidChange.fire();
64+
this._onDidChangeStates.fire();
6265
}
6366
}
6467

@@ -71,8 +74,9 @@ export class CopilotStateModel extends Disposable {
7174
this._states.set(key, status);
7275
if (this._isInitialized) {
7376
this._showNotification.add(key);
77+
this._onDidChangeNotifications.fire();
7478
}
75-
this._onDidChange.fire();
79+
this._onDidChangeStates.fire();
7680
}
7781

7882
get(owner: string, repo: string, prNumber: number): CopilotPRStatus {
@@ -86,6 +90,7 @@ export class CopilotStateModel extends Disposable {
8690

8791
clearNotifications(): void {
8892
this._showNotification.clear();
93+
this._onDidChangeNotifications.fire();
8994
}
9095

9196
get notifications(): ReadonlySet<string> {

src/view/prStatusDecorationProvider.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
import * as vscode from 'vscode';
77
import { Disposable } from '../common/lifecycle';
8-
import { createPRNodeUri, fromPRNodeUri, Schemes } from '../common/uri';
8+
import { COPILOT_QUERY, createPRNodeUri, fromPRNodeUri, Schemes } from '../common/uri';
9+
import { CopilotStateModel } from '../github/copilotPrWatcher';
910
import { getStatusDecoration } from '../github/markdownUtils';
1011
import { PrsTreeModel } from './prsTreeModel';
1112

@@ -16,20 +17,28 @@ export class PRStatusDecorationProvider extends Disposable implements vscode.Fil
1617
>();
1718
onDidChangeFileDecorations: vscode.Event<vscode.Uri | vscode.Uri[]> = this._onDidChangeFileDecorations.event;
1819

19-
constructor(private readonly _prsTreeModel: PrsTreeModel) {
20+
constructor(private readonly _prsTreeModel: PrsTreeModel, private readonly _copilotStateModel: CopilotStateModel) {
2021
super();
2122
this._register(vscode.window.registerFileDecorationProvider(this));
2223
this._register(
2324
this._prsTreeModel.onDidChangePrStatus(identifiers => {
2425
this._onDidChangeFileDecorations.fire(identifiers.map(id => createPRNodeUri(id)));
2526
})
2627
);
28+
29+
this._register(this._copilotStateModel.onDidChangeNotifications(() => {
30+
this._onDidChangeFileDecorations.fire(COPILOT_QUERY);
31+
}));
2732
}
2833

2934
provideFileDecoration(
3035
uri: vscode.Uri,
3136
_token: vscode.CancellationToken,
3237
): vscode.ProviderResult<vscode.FileDecoration> {
38+
if (uri.scheme === Schemes.PRQuery) {
39+
return this._queryDecoration(uri);
40+
}
41+
3342
if (uri.scheme !== Schemes.PRNode) {
3443
return;
3544
}
@@ -44,4 +53,16 @@ export class PRStatusDecorationProvider extends Disposable implements vscode.Fil
4453

4554
return getStatusDecoration(status.status) as vscode.FileDecoration;
4655
}
56+
57+
private _queryDecoration(uri: vscode.Uri): vscode.ProviderResult<vscode.FileDecoration> {
58+
if (uri.path === 'copilot') {
59+
if (this._copilotStateModel.notifications.size > 0) {
60+
return {
61+
tooltip: vscode.l10n.t('Coding agent has made changes', this._copilotStateModel.notifications.size),
62+
badge: new vscode.ThemeIcon('copilot') as any,
63+
color: new vscode.ThemeColor('pullRequests.notification'),
64+
};
65+
}
66+
}
67+
}
4768
}

src/view/prsTreeDataProvider.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export class PullRequestsTreeDataProvider extends Disposable implements vscode.T
4545
private _initialized: boolean = false;
4646
public notificationProvider: NotificationProvider;
4747
public readonly prsTreeModel: PrsTreeModel;
48+
private _notificationClearTimeout: NodeJS.Timeout | undefined;
4849

4950
get view(): vscode.TreeView<TreeNode> {
5051
return this._view;
@@ -54,7 +55,7 @@ export class PullRequestsTreeDataProvider extends Disposable implements vscode.T
5455
super();
5556
this.prsTreeModel = this._register(new PrsTreeModel(this._telemetry, this._reposManager, _context));
5657
this._register(this.prsTreeModel.onDidChangeData(folderManager => folderManager ? this.refreshRepo(folderManager) : this.refresh()));
57-
this._register(new PRStatusDecorationProvider(this.prsTreeModel));
58+
this._register(new PRStatusDecorationProvider(this.prsTreeModel, this._copilotStateModel));
5859
this._register(vscode.commands.registerCommand('pr.refreshList', _ => {
5960
this.refresh(undefined, true);
6061
}));
@@ -71,12 +72,28 @@ export class PullRequestsTreeDataProvider extends Disposable implements vscode.T
7172

7273
this._register(this._view.onDidChangeVisibility(e => {
7374
if (e.visible) {
74-
_copilotStateModel.clearNotifications();
75-
this._view.badge = undefined;
75+
// Clear notifications with a delay of 5 seconds
76+
if (this._notificationClearTimeout) {
77+
clearTimeout(this._notificationClearTimeout);
78+
}
79+
this._notificationClearTimeout = setTimeout(() => {
80+
_copilotStateModel.clearNotifications();
81+
this._view.badge = undefined;
82+
this._notificationClearTimeout = undefined;
83+
}, 5000);
7684
}
7785
}));
7886

79-
this._register(_copilotStateModel.onDidChange(() => {
87+
this._register({
88+
dispose: () => {
89+
if (this._notificationClearTimeout) {
90+
clearTimeout(this._notificationClearTimeout);
91+
this._notificationClearTimeout = undefined;
92+
}
93+
}
94+
});
95+
96+
this._register(_copilotStateModel.onDidChangeStates(() => {
8097
if (_copilotStateModel.notifications.size > 0) {
8198
this._view.badge = {
8299
tooltip: _copilotStateModel.notifications.size === 1 ? vscode.l10n.t('Coding agent has 1 change to view') : vscode.l10n.t('Coding agent has {0} changes to view', _copilotStateModel.notifications.size),

src/view/treeNodes/categoryNode.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import * as vscode from 'vscode';
77
import { AuthenticationError } from '../../common/authentication';
88
import { ITelemetry } from '../../common/telemetry';
9+
import { COPILOT_QUERY, Schemes } from '../../common/uri';
910
import { formatError } from '../../common/utils';
1011
import { CopilotStateModel, isCopilotQuery } from '../../github/copilotPrWatcher';
1112
import { FolderRepositoryManager, ItemsResponseResult } from '../../github/folderRepositoryManager';
@@ -127,7 +128,7 @@ export class CategoryTreeNode extends TreeNode implements vscode.TreeItem {
127128
public fetchNextPage: boolean = false;
128129
public repositoryPageInformation: Map<string, PageInformation> = new Map<string, PageInformation>();
129130
public contextValue: string;
130-
public iconPath: vscode.ThemeIcon | undefined;
131+
public resourceUri: vscode.Uri;
131132

132133
constructor(
133134
parent: TreeNodeParent,
@@ -151,11 +152,6 @@ export class CategoryTreeNode extends TreeNode implements vscode.TreeItem {
151152
this.label = vscode.l10n.t('All Open');
152153
break;
153154
case PRType.Query:
154-
if (hasCopilotChanges) {
155-
this.iconPath = new vscode.ThemeIcon('copilot');
156-
} else {
157-
this.iconPath = undefined;
158-
}
159155
this.label = _categoryLabel!;
160156
break;
161157
case PRType.LocalPullRequest:
@@ -168,8 +164,11 @@ export class CategoryTreeNode extends TreeNode implements vscode.TreeItem {
168164

169165
this.id = parent instanceof TreeNode ? `${parent.id ?? parent.label}/${this.label}` : this.label;
170166

167+
this.resourceUri = vscode.Uri.parse(Schemes.PRQuery);
168+
171169
if (hasCopilotChanges) {
172170
this.collapsibleState = vscode.TreeItemCollapsibleState.Expanded;
171+
this.resourceUri = COPILOT_QUERY;
173172
} else if ((this._prsTreeModel.expandedQueries.size === 0) && (this.type === PRType.All)) {
174173
this.collapsibleState = vscode.TreeItemCollapsibleState.Expanded;
175174
} else {

0 commit comments

Comments
 (0)