diff --git a/src/constants.commands.ts b/src/constants.commands.ts index d8ef3630da63e..944458c3519f3 100644 --- a/src/constants.commands.ts +++ b/src/constants.commands.ts @@ -147,6 +147,7 @@ export type CoreCommands = | 'workbench.action.closeActiveEditor' | 'workbench.action.closeAllEditors' | 'workbench.action.closePanel' + | 'workbench.action.closeWindow' | 'workbench.action.focusRightGroup' | 'workbench.action.nextEditor' | 'workbench.action.newGroupRight' diff --git a/src/constants.storage.ts b/src/constants.storage.ts index 4440b82c3043f..47045ace7fe35 100644 --- a/src/constants.storage.ts +++ b/src/constants.storage.ts @@ -14,7 +14,8 @@ import type { DeepLinkServiceState } from './uris/deepLinks/deepLink'; export type SecretKeys = | IntegrationAuthenticationKeys | `gitlens.${AIProviders}.key` - | `gitlens.plus.auth:${Environment}`; + | `gitlens.plus.auth:${Environment}` + | 'deepLinks:pending'; export type IntegrationAuthenticationKeys = | `gitlens.integration.auth:${IntegrationId}|${string}` @@ -64,7 +65,6 @@ export type GlobalStorage = { avatars: [string, StoredAvatar][]; 'confirm:ai:tos': boolean; repoVisibility: [string, StoredRepoVisibilityInfo][]; - 'deepLinks:pending': StoredDeepLinkContext; pendingWhatsNewOnFocus: boolean; // Don't change this key name ('premium`) as its the stored subscription 'premium:subscription': Stored; diff --git a/src/git/models/repository.ts b/src/git/models/repository.ts index 446922f0f165c..8c26e119afd56 100644 --- a/src/git/models/repository.ts +++ b/src/git/models/repository.ts @@ -625,11 +625,11 @@ export class Repository implements Disposable { @gate() @log({ exit: true }) async getCommonRepository(): Promise { - const gitDir = await this.git.config().getGitDir?.(); - if (gitDir?.commonUri == null) return this; + const uri = await this.getCommonRepositoryUri(); + if (uri == null) return this; // If the repository isn't already opened, then open it as a "closed" repo (won't show up in the UI) - return this.container.git.getOrOpenRepository(gitDir.commonUri, { + return this.container.git.getOrOpenRepository(uri, { detectNested: false, force: true, closeOnOpen: true, diff --git a/src/uris/deepLinks/deepLinkService.ts b/src/uris/deepLinks/deepLinkService.ts index 474c49cb8ec93..f6f5e66e5ecae 100644 --- a/src/uris/deepLinks/deepLinkService.ts +++ b/src/uris/deepLinks/deepLinkService.ts @@ -1,4 +1,4 @@ -import type { QuickPickItem } from 'vscode'; +import type { QuickPickItem, SecretStorageChangeEvent } from 'vscode'; import { Disposable, env, EventEmitter, ProgressLocation, Range, Uri, window, workspace } from 'vscode'; import type { OpenCloudPatchCommandArgs } from '../../commands/patches'; import type { StoredDeepLinkContext, StoredNamedRef } from '../../constants.storage'; @@ -72,16 +72,43 @@ export class DeepLinkService implements Disposable { this._disposables.push( this._onDeepLinkProgressUpdated, container.uri.onDidReceiveUri(async (uri: Uri) => this.processDeepLinkUri(uri)), + container.storage.onDidChangeSecrets(this.onDidChangeStorage, this), ); - const pendingDeepLink = this.container.storage.get('deepLinks:pending'); - void this.processPendingDeepLink(pendingDeepLink); + void this.container.storage.getSecret('deepLinks:pending').then(pendingDeepLink => { + if (pendingDeepLink != null) { + const link = JSON.parse(pendingDeepLink) as StoredDeepLinkContext; + void this.processPendingDeepLink(link); + } + }); } dispose(): void { Disposable.from(...this._disposables).dispose(); } + private async onDidChangeStorage(e: SecretStorageChangeEvent): Promise { + if (e.key === 'deepLinks:pending') { + const pendingDeepLinkStored = await this.container.storage.getSecret('deepLinks:pending'); + if (pendingDeepLinkStored == null) return; + + const pendingDeepLink = JSON.parse(pendingDeepLinkStored) as StoredDeepLinkContext; + if (pendingDeepLink?.url == null) return; + + const link = parseDeepLinkUri(Uri.parse(pendingDeepLink.url)); + if (link == null) return; + + // TODO: See if we can remove this condition without breaking other link flows + if (link.action !== DeepLinkActionType.DeleteBranch) return; + + // see if there is a matching repo in the current window + await this.findMatchingRepositoryFromCurrentWindow(link.repoPath, link.remoteUrl, link.mainId, true); + if (this._context.repo != null) { + void this.processPendingDeepLink(pendingDeepLink); + } + } + } + private resetContext() { this._context = { state: DeepLinkServiceState.Idle, @@ -203,14 +230,24 @@ export class DeepLinkService implements Disposable { repoPath: string | undefined, remoteUrl: string | undefined, repoId: string | undefined, + openOnly: boolean = false, ): Promise { if (repoPath != null) { const repoOpenUri = maybeUri(repoPath) ? Uri.parse(repoPath) : repoPath; try { - const openRepo = await this.container.git.getOrOpenRepository(repoOpenUri, { detectNested: false }); - if (openRepo != null) { - this._context.repo = openRepo; - return; + if (openOnly) { + for (const repo of this.container.git.openRepositories) { + if (repo.path === repoPath || repo.uri.fsPath === repoPath) { + this._context.repo = repo; + return; + } + } + } else { + const openRepo = await this.container.git.getOrOpenRepository(repoOpenUri, { detectNested: false }); + if (openRepo != null) { + this._context.repo = openRepo; + return; + } } } catch {} } @@ -223,7 +260,7 @@ export class DeepLinkService implements Disposable { // Try to match a repo using the remote URL first, since that saves us some steps. // As a fallback, try to match using the repo id. - for (const repo of this.container.git.repositories) { + for (const repo of openOnly ? this.container.git.openRepositories : this.container.git.repositories) { if (repoPath != null && normalizePath(repo.path.toLowerCase()) === normalizePath(repoPath.toLowerCase())) { this._context.repo = repo; return; @@ -254,7 +291,7 @@ export class DeepLinkService implements Disposable { @debug() private async processPendingDeepLink(pendingDeepLink: StoredDeepLinkContext | undefined) { if (pendingDeepLink == null) return; - void this.container.storage.delete('deepLinks:pending'); + void this.container.storage.deleteSecret('deepLinks:pending'); if (pendingDeepLink?.url == null) return; const link = parseDeepLinkUri(Uri.parse(pendingDeepLink.url)); if (link == null) return; @@ -1061,13 +1098,16 @@ export class DeepLinkService implements Disposable { action = DeepLinkServiceAction.RepoOpening; if (!(repoOpenLocation === 'addToWorkspace' && (workspace.workspaceFolders?.length || 0) > 1)) { // Deep link will resolve in a different service instance - await this.container.storage.store('deepLinks:pending', { - url: this._context.url, - repoPath: repoOpenUri.toString(), - targetSha: this._context.targetSha, - secondaryTargetSha: this._context.secondaryTargetSha, - useProgress: useProgress, - }); + await this.container.storage.storeSecret( + 'deepLinks:pending', + JSON.stringify({ + url: this._context.url, + repoPath: repoOpenUri.toString(), + targetSha: this._context.targetSha, + secondaryTargetSha: this._context.secondaryTargetSha, + useProgress: useProgress, + }), + ); action = DeepLinkServiceAction.DeepLinkStored; } @@ -1349,9 +1389,11 @@ export class DeepLinkService implements Disposable { // Storing link info in case the switch causes a new window to open const onWorkspaceChanging = async (isNewWorktree?: boolean) => - this.container.storage.store( + this.container.storage.storeSecret( 'deepLinks:pending', - isNewWorktree ? pendingDeepLink : { ...pendingDeepLink, url: nonPrUrl }, + isNewWorktree + ? JSON.stringify(pendingDeepLink) + : JSON.stringify({ ...pendingDeepLink, url: nonPrUrl }), ); await executeGitCommand({ @@ -1406,7 +1448,7 @@ export class DeepLinkService implements Disposable { } case DeepLinkServiceState.OpenInspect: { // If we arrive at this step, clear any stored data used for the "new window" option - await this.container.storage.delete('deepLinks:pending'); + await this.container.storage.deleteSecret('deepLinks:pending'); if (!repo) { action = DeepLinkServiceAction.DeepLinkErrored; message = 'Missing repository.'; diff --git a/src/webviews/home/homeWebview.ts b/src/webviews/home/homeWebview.ts index e28f86e8cdc98..5e61fa35f7481 100644 --- a/src/webviews/home/homeWebview.ts +++ b/src/webviews/home/homeWebview.ts @@ -1236,8 +1236,17 @@ export class HomeWebviewProvider implements WebviewProvider - this.container.storage.store('deepLinks:pending', deleteBranchDeepLink), + onWorkspaceChanging: async (_isNewWorktree?: boolean) => { + await this.container.storage.storeSecret( + 'deepLinks:pending', + JSON.stringify(deleteBranchDeepLink), + ); + // Close the current window. This should only occur if there was already a different + // window open for the default worktree. + setTimeout(() => { + void executeCoreCommand('workbench.action.closeWindow'); + }, 2000); + }, worktreeDefaultOpen: 'current', }, });