Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,12 @@
"default": "off",
"description": "%githubPullRequests.notifications.description%"
},
"githubPullRequests.notificationsRefreshInterval": {
"type": "number",
"default": 600,
"minimum": 60,
"description": "%githubPullRequests.notificationsRefreshInterval.description%"
},
"githubPullRequests.fileListLayout": {
"type": "string",
"enum": [
Expand Down
1 change: 1 addition & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"githubPullRequests.queries.createdByMe": "Created By Me",
"githubPullRequests.defaultMergeMethod.description": "The method to use when merging pull requests.",
"githubPullRequests.notifications.description": "If GitHub notifications should be shown to the user.",
"githubPullRequests.notificationsRefreshInterval.description": "The interval in seconds at which to refresh GitHub notifications. Default is 600 seconds (10 minutes). Minimum is 60 seconds.",
"githubPullRequests.fileListLayout.description": "The layout to use when displaying changed files list.",
"githubPullRequests.hideViewedFiles.description": "Hide files that have been marked as viewed in the pull request changes tree.",
"githubPullRequests.defaultDeletionMethod.selectLocalBranch.description": "When true, the option to delete the local branch will be selected by default when deleting a branch from a pull request.",
Expand Down
1 change: 1 addition & 0 deletions src/@types/vscode.proposed.chatParticipantAdditions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ declare module 'vscode' {
isComplete?: boolean;
toolSpecificData?: ChatTerminalToolInvocationData;
fromSubAgent?: boolean;
presentation?: 'hidden' | 'hiddenAfterComplete' | undefined;

constructor(toolName: string, toolCallId: string, isError?: boolean);
}
Expand Down
1 change: 1 addition & 0 deletions src/common/settingKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export const EXPERIMENTAL_CHAT = 'experimental.chat';
export const EXPERIMENTAL_USE_QUICK_CHAT = 'experimental.useQuickChat';
export const EXPERIMENTAL_NOTIFICATIONS_PAGE_SIZE = 'experimental.notificationsViewPageSize';
export const EXPERIMENTAL_NOTIFICATIONS_SCORE = 'experimental.notificationsScore';
export const NOTIFICATIONS_REFRESH_INTERVAL = 'notificationsRefreshInterval';
export const WEBVIEW_REFRESH_INTERVAL = 'webviewRefreshInterval';

// git
Expand Down
55 changes: 40 additions & 15 deletions src/notifications/notificationsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { NotificationsProvider } from './notificationsProvider';
import { commands, contexts } from '../common/executeCommands';
import { Disposable } from '../common/lifecycle';
import Logger from '../common/logger';
import { NOTIFICATION_SETTING, NotificationVariants, PR_SETTINGS_NAMESPACE } from '../common/settingKeys';
import { NOTIFICATION_SETTING, NOTIFICATIONS_REFRESH_INTERVAL, NotificationVariants, PR_SETTINGS_NAMESPACE } from '../common/settingKeys';
import { EventType, TimelineEvent } from '../common/timelineEvent';
import { toNotificationUri } from '../common/uri';
import { CredentialStore } from '../github/credentials';
Expand Down Expand Up @@ -47,8 +47,8 @@ export class NotificationsManager extends Disposable implements vscode.TreeDataP
private _fetchNotifications: boolean = false;
private _notifications = new Map<string, NotificationTreeItem>();

private _pollingDuration: number = 60; // Default polling duration
private _pollingHandler: NodeJS.Timeout | null;
private _pollingDisposable: vscode.Disposable | null;
private _pollingLastModified: string;

private _sortingMethod: NotificationsSortMethod = NotificationsSortMethod.Timestamp;
Expand All @@ -70,6 +70,12 @@ export class NotificationsManager extends Disposable implements vscode.TreeDataP
this._startPolling();
}
}
if (e.affectsConfiguration(`${PR_SETTINGS_NAMESPACE}.${NOTIFICATIONS_REFRESH_INTERVAL}`)) {
if (this.isPRNotificationsOn()) {
// Restart polling with new interval (stopPolling is called inside startPolling)
this._startPolling();
}
}
}));
this._register(PullRequestOverviewPanel.onVisible(e => {
this.markPrNotificationsAsRead(e);
Expand Down Expand Up @@ -195,7 +201,7 @@ export class NotificationsManager extends Disposable implements vscode.TreeDataP
}

public async getNotifications(): Promise<INotificationTreeItems | undefined> {
let pollInterval = this._pollingDuration;
let pollInterval = this._getRefreshInterval();
let lastModified = this._pollingLastModified;
if (this._fetchNotifications) {
// Get raw notifications
Expand Down Expand Up @@ -412,6 +418,10 @@ export class NotificationsManager extends Disposable implements vscode.TreeDataP
return (vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get<NotificationVariants>(NOTIFICATION_SETTING) === 'pullRequests');
}

private _getRefreshInterval(): number {
return vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get<number>(NOTIFICATIONS_REFRESH_INTERVAL, 600);
}

private async _pollForNewNotifications() {
this._pageCount = 1;
this._dateTime = new Date();
Expand All @@ -423,36 +433,51 @@ export class NotificationsManager extends Disposable implements vscode.TreeDataP
return;
}

// Adapt polling interval if it has changed.
if (response.pollInterval !== this._pollingDuration) {
this._pollingDuration = response.pollInterval;
if (this._pollingHandler && this.isPRNotificationsOn()) {
Logger.appendLine('Notifications: Clearing interval', NotificationsManager.ID);
clearInterval(this._pollingHandler);
Logger.appendLine(`Notifications: Starting new polling interval with ${this._pollingDuration}`, NotificationsManager.ID);
this._startPolling();
}
}
// Use the user-configured interval instead of GitHub's poll interval to respect user preferences.
// This allows users to control notification refresh frequency for managing notification volume and API rate limits.
if (response.lastModified !== this._pollingLastModified) {
this._pollingLastModified = response.lastModified;
this._onDidChangeTreeData.fire();
}
// this._onDidChangeNotifications.fire(oldPRNodesToUpdate);
}

private _stopPolling() {
if (this._pollingDisposable) {
this._pollingDisposable.dispose();
this._pollingDisposable = null;
}
if (this._pollingHandler) {
Logger.appendLine('Notifications: Clearing interval', NotificationsManager.ID);
clearInterval(this._pollingHandler);
this._pollingHandler = null;
}
}

private _startPolling() {
if (!this.isPRNotificationsOn()) {
return;
}
// Stop any existing polling to prevent resource leaks
this._stopPolling();

const refreshInterval = this._getRefreshInterval();
Logger.appendLine(`Notifications: Starting polling with interval ${refreshInterval} seconds`, NotificationsManager.ID);
this._pollForNewNotifications();
this._pollingHandler = setInterval(
function (notificationProvider: NotificationsManager) {
notificationProvider._pollForNewNotifications();
},
this._pollingDuration * 1000,
refreshInterval * 1000,
this
);
this._register({ dispose: () => clearInterval(this._pollingHandler!) });
this._pollingDisposable = this._register({
dispose: () => {
if (this._pollingHandler) {
clearInterval(this._pollingHandler);
}
}
});
}

private _findNotificationKeyForIssueModel(issueModel: IssueModel | PullRequestModel | { owner: string; repo: string; number: number }): string | undefined {
Expand Down