diff --git a/package.json b/package.json index bc34ccc..0bc1dbe 100644 --- a/package.json +++ b/package.json @@ -9086,14 +9086,14 @@ "scm/sourceControl": [ { "command": "gitlens.showGraphPage", - "when": "gitlens:enabled && config.gitlens.menus.scm.graph && gitlens:plus:enabled && scmProvider == git", + "when": "gitlens:enabled && config.gitlens.menus.scm.graph && gitlens:plus:enabled && scmProvider == git && scmProviderRootUri not in gitlens:plus:disallowedRepos", "group": "6_gitlens@1" } ], "scm/title": [ { "command": "gitlens.showGraphPage", - "when": "gitlens:enabled && config.gitlens.menus.scmRepositoryInline.graph && gitlens:plus:enabled && scmProvider == git", + "when": "gitlens:enabled && config.gitlens.menus.scmRepositoryInline.graph && gitlens:plus:enabled && scmProvider == git && scmProviderRootUri not in gitlens:plus:disallowedRepos", "group": "navigation@-1000" }, { @@ -9103,7 +9103,7 @@ }, { "command": "gitlens.showGraphPage", - "when": "gitlens:enabled && config.gitlens.menus.scmRepository.graph && gitlens:plus:enabled && scmProvider == git", + "when": "gitlens:enabled && config.gitlens.menus.scmRepository.graph && gitlens:plus:enabled && scmProvider == git && scmProviderRootUri not in gitlens:plus:disallowedRepos", "group": "2_z_gitlens@2" } ], diff --git a/src/constants.ts b/src/constants.ts index 5616d18..d09a538 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -270,6 +270,7 @@ export const enum ContextKeys { Vsls = 'gitlens:vsls', Plus = 'gitlens:plus', + PlusDisallowedRepos = 'gitlens:plus:disallowedRepos', PlusEnabled = 'gitlens:plus:enabled', PlusRequired = 'gitlens:plus:required', PlusState = 'gitlens:plus:state', diff --git a/src/plus/subscription/subscriptionService.ts b/src/plus/subscription/subscriptionService.ts index 62ad6dd..44ef9b8 100644 --- a/src/plus/subscription/subscriptionService.ts +++ b/src/plus/subscription/subscriptionService.ts @@ -1,12 +1,14 @@ import type { AuthenticationProviderAuthenticationSessionsChangeEvent, AuthenticationSession, + CancellationToken, Event, MessageItem, StatusBarItem, } from 'vscode'; import { authentication, + CancellationTokenSource, version as codeVersion, Disposable, env, @@ -46,7 +48,8 @@ import { createFromDateDelta } from '../../system/date'; import { gate } from '../../system/decorators/gate'; import { debug, getLogScope, log } from '../../system/decorators/log'; import { memoize } from '../../system/decorators/memoize'; -import { once } from '../../system/function'; +import type { Deferrable } from '../../system/function'; +import { debounce, once } from '../../system/function'; import { flatten } from '../../system/object'; import { pluralize } from '../../system/string'; import { openWalkthrough } from '../../system/utils'; @@ -946,18 +949,23 @@ export class SubscriptionService implements Disposable { }); } + private _cancellationSource: CancellationTokenSource | undefined; + private _updateAccessContextDebounced: Deferrable | undefined; + private updateContext(): void { - this.updateStatusBar(); + this._updateAccessContextDebounced?.cancel(); + if (this._updateAccessContextDebounced == null) { + this._updateAccessContextDebounced = debounce(this.updateAccessContext.bind(this), 500); + } - queueMicrotask(async () => { - let allowed: boolean | 'mixed' = false; - // For performance reasons, only check if we have any repositories - if (this.container.git.repositoryCount !== 0) { - ({ allowed } = await this.container.git.access()); - } - void setContext(ContextKeys.PlusEnabled, Boolean(allowed) || configuration.get('plusFeatures.enabled')); - void setContext(ContextKeys.PlusRequired, allowed === false); - }); + if (this._cancellationSource != null) { + this._cancellationSource.cancel(); + this._cancellationSource.dispose(); + } + this._cancellationSource = new CancellationTokenSource(); + + void this._updateAccessContextDebounced(this._cancellationSource.token); + this.updateStatusBar(); const { plan: { actual }, @@ -968,6 +976,37 @@ export class SubscriptionService implements Disposable { void setContext(ContextKeys.PlusState, state); } + private async updateAccessContext(cancellation: CancellationToken): Promise { + let allowed: boolean | 'mixed' = false; + // For performance reasons, only check if we have any repositories + if (this.container.git.repositoryCount !== 0) { + ({ allowed } = await this.container.git.access()); + if (cancellation.isCancellationRequested) return; + } + + const plusFeatures = configuration.get('plusFeatures.enabled') ?? true; + + let disallowedRepos: string[] | undefined; + + if (!plusFeatures && allowed === 'mixed') { + disallowedRepos = []; + for (const repo of this.container.git.repositories) { + if (repo.closed) continue; + + const access = await this.container.git.access(undefined, repo.uri); + if (cancellation.isCancellationRequested) return; + + if (!access.allowed) { + disallowedRepos.push(repo.uri.toString()); + } + } + } + + void setContext(ContextKeys.PlusEnabled, Boolean(allowed) || plusFeatures); + void setContext(ContextKeys.PlusRequired, allowed === false); + void setContext(ContextKeys.PlusDisallowedRepos, disallowedRepos); + } + private updateStatusBar(): void { const { account,