Skip to content

Commit 949c66f

Browse files
authored
Restore overviews after reload (#7597)
Fixes #7378
1 parent 1903718 commit 949c66f

5 files changed

Lines changed: 117 additions & 7 deletions

File tree

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@
5757
"onFileSystem:githubpr",
5858
"onFileSystem:githubcommit",
5959
"onFileSystem:review",
60-
"onWebviewPanel:pr.codingAgentSessionLogView"
60+
"onWebviewPanel:pr.codingAgentSessionLogView",
61+
"onWebviewPanel:IssueOverview",
62+
"onWebviewPanel:PullRequestOverview"
6163
],
6264
"browser": "./dist/browser/extension",
6365
"l10n": "./dist/browser/extension",

src/extension.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { createExperimentationService, ExperimentationTelemetry } from './experi
2525
import { CopilotRemoteAgentManager } from './github/copilotRemoteAgent';
2626
import { CredentialStore } from './github/credentials';
2727
import { FolderRepositoryManager } from './github/folderRepositoryManager';
28+
import { OverviewRestorer } from './github/overviewRestorer';
2829
import { RepositoriesManager } from './github/repositoriesManager';
2930
import { registerBuiltinGitProvider, registerLiveShareGitProvider } from './gitProviders/api';
3031
import { GitHubContactServiceProvider } from './gitProviders/GitHubContactServiceProvider';
@@ -237,6 +238,8 @@ async function init(
237238
const sessionLogViewManager = new SessionLogViewManager(credentialStore, context, reposManager, telemetry, copilotRemoteAgentManager);
238239
context.subscriptions.push(sessionLogViewManager);
239240

241+
context.subscriptions.push(new OverviewRestorer(reposManager, telemetry, context.extensionUri, credentialStore));
242+
240243
await vscode.commands.executeCommand('setContext', 'github:initialized', true);
241244

242245
registerPostCommitCommandsProvider(reposManager, git);

src/github/issueOverview.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export class IssueOverviewPanel<TItem extends IssueModel = IssueModel> extends W
2929
*/
3030
public static currentPanel?: IssueOverviewPanel;
3131

32-
private static readonly _viewType: string = 'IssueOverview';
32+
public static readonly viewType: string = 'IssueOverview';
3333

3434
protected readonly _panel: vscode.WebviewPanel;
3535
protected _item: TItem;
@@ -42,6 +42,8 @@ export class IssueOverviewPanel<TItem extends IssueModel = IssueModel> extends W
4242
folderRepositoryManager: FolderRepositoryManager,
4343
issue: IssueModel,
4444
toTheSide: Boolean = false,
45+
_preserveFocus: boolean = true,
46+
existingPanel?: vscode.WebviewPanel
4547
) {
4648
await ensureEmojis(folderRepositoryManager.context);
4749
const activeColumn = toTheSide
@@ -62,6 +64,9 @@ export class IssueOverviewPanel<TItem extends IssueModel = IssueModel> extends W
6264
activeColumn || vscode.ViewColumn.Active,
6365
title,
6466
folderRepositoryManager,
67+
undefined,
68+
existingPanel,
69+
undefined
6570
);
6671
}
6772

@@ -89,7 +94,8 @@ export class IssueOverviewPanel<TItem extends IssueModel = IssueModel> extends W
8994
column: vscode.ViewColumn,
9095
title: string,
9196
folderRepositoryManager: FolderRepositoryManager,
92-
private readonly type: string = IssueOverviewPanel._viewType,
97+
private readonly type: string = IssueOverviewPanel.viewType,
98+
existingPanel?: vscode.WebviewPanel,
9399
iconSubpath?: {
94100
light: string,
95101
dark: string,
@@ -99,7 +105,7 @@ export class IssueOverviewPanel<TItem extends IssueModel = IssueModel> extends W
99105
this._folderRepositoryManager = folderRepositoryManager;
100106

101107
// Create and show a new webview panel
102-
this._panel = this._register(vscode.window.createWebviewPanel(type, title, column, {
108+
this._panel = existingPanel ?? this._register(vscode.window.createWebviewPanel(type, title, column, {
103109
// Enable javascript in the webview
104110
enableScripts: true,
105111
retainContextWhenHidden: true,

src/github/overviewRestorer.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as vscode from 'vscode';
7+
import { Disposable } from '../common/lifecycle';
8+
import Logger from '../common/logger';
9+
import { ITelemetry } from '../common/telemetry';
10+
import { CredentialStore } from './credentials';
11+
import { FolderRepositoryManager } from './folderRepositoryManager';
12+
import { GitHubRepository } from './githubRepository';
13+
import { IssueOverviewPanel } from './issueOverview';
14+
import { PullRequestOverviewPanel } from './pullRequestOverview';
15+
import { RepositoriesManager } from './repositoriesManager';
16+
import { PullRequest } from './views';
17+
18+
export class OverviewRestorer extends Disposable implements vscode.WebviewPanelSerializer {
19+
private static ID = 'OverviewRestorer';
20+
21+
constructor(private readonly _repositoriesManager: RepositoriesManager,
22+
private readonly _telemetry: ITelemetry,
23+
private readonly _extensionUri: vscode.Uri,
24+
private readonly _credentialStore: CredentialStore
25+
) {
26+
super();
27+
this._register(vscode.window.registerWebviewPanelSerializer(IssueOverviewPanel.viewType, this));
28+
this._register(vscode.window.registerWebviewPanelSerializer(PullRequestOverviewPanel.viewType, this));
29+
}
30+
31+
async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, state: PullRequest): Promise<void> {
32+
await this.waitForAuth();
33+
await this.waitForAnyGitHubRepos(this._repositoriesManager);
34+
35+
if (!state || !state.number || this._repositoriesManager.folderManagers.length === 0) {
36+
webviewPanel.dispose();
37+
return;
38+
}
39+
40+
let repo: GitHubRepository | undefined;
41+
let folderManager: FolderRepositoryManager | undefined;
42+
for (const manager of this._repositoriesManager.folderManagers) {
43+
const githubRepository = manager.findExistingGitHubRepository({ owner: state.owner, repositoryName: state.repo });
44+
if (githubRepository) {
45+
repo = githubRepository;
46+
folderManager = manager;
47+
break;
48+
}
49+
}
50+
51+
if (!repo || !folderManager) {
52+
folderManager = this._repositoriesManager.folderManagers[0];
53+
repo = await folderManager.createGitHubRepositoryFromOwnerName(state.owner, state.repo);
54+
}
55+
56+
if (state.isIssue) {
57+
const issueModel = await repo.getIssue(state.number, true);
58+
if (!issueModel) {
59+
webviewPanel.dispose();
60+
return;
61+
}
62+
return IssueOverviewPanel.createOrShow(this._telemetry, this._extensionUri, folderManager, issueModel, undefined, true, webviewPanel);
63+
} else {
64+
const pullRequestModel = await repo.getPullRequest(state.number, true);
65+
if (!pullRequestModel) {
66+
webviewPanel.dispose();
67+
return;
68+
}
69+
return PullRequestOverviewPanel.createOrShow(this._telemetry, this._extensionUri, folderManager, pullRequestModel, undefined, true, webviewPanel);
70+
}
71+
}
72+
73+
protected async waitForAuth(): Promise<void> {
74+
if (this._credentialStore.isAnyAuthenticated()) {
75+
return;
76+
}
77+
return new Promise(resolve => this._credentialStore.onDidGetSession(() => resolve()));
78+
}
79+
80+
protected async waitForAnyGitHubRepos(reposManager: RepositoriesManager): Promise<void> {
81+
// Check if any folder manager already has GitHub repositories
82+
if (reposManager.folderManagers.some(manager => manager.gitHubRepositories.length > 0)) {
83+
return;
84+
}
85+
86+
Logger.appendLine('Waiting for GitHub repositories.', OverviewRestorer.ID);
87+
return new Promise(resolve => {
88+
const disposable = reposManager.onDidChangeAnyGitHubRepository(() => {
89+
Logger.appendLine('Found GitHub repositories.', OverviewRestorer.ID);
90+
disposable.dispose();
91+
resolve();
92+
});
93+
});
94+
}
95+
}

src/github/pullRequestOverview.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import { CancelCodingAgentReply, MergeArguments, MergeResult, PullRequest, Revie
4141

4242
export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestModel> {
4343
public static override ID: string = 'PullRequestOverviewPanel';
44+
public static override readonly viewType = PULL_REQUEST_OVERVIEW_VIEW_TYPE;
4445
/**
4546
* Track the currently panel. Only allow a single panel to exist at a time.
4647
*/
@@ -65,7 +66,8 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
6566
folderRepositoryManager: FolderRepositoryManager,
6667
issue: PullRequestModel,
6768
toTheSide: boolean = false,
68-
preserveFocus: boolean = true
69+
preserveFocus: boolean = true,
70+
existingPanel?: vscode.WebviewPanel
6971
) {
7072

7173
/* __GDPR__
@@ -92,7 +94,8 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
9294
extensionUri,
9395
activeColumn || vscode.ViewColumn.Active,
9496
title,
95-
folderRepositoryManager
97+
folderRepositoryManager,
98+
existingPanel
9699
);
97100
}
98101

@@ -128,8 +131,9 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
128131
column: vscode.ViewColumn,
129132
title: string,
130133
folderRepositoryManager: FolderRepositoryManager,
134+
existingPanel?: vscode.WebviewPanel
131135
) {
132-
super(telemetry, extensionUri, column, title, folderRepositoryManager, PULL_REQUEST_OVERVIEW_VIEW_TYPE, {
136+
super(telemetry, extensionUri, column, title, folderRepositoryManager, PullRequestOverviewPanel.viewType, existingPanel, {
133137
light: 'resources/icons/pr_webview.svg',
134138
dark: 'resources/icons/dark/pr_webview.svg'
135139
});

0 commit comments

Comments
 (0)