diff --git a/src/plus/webviews/graph/graphWebview.ts b/src/plus/webviews/graph/graphWebview.ts index b823361..d6c6155 100644 --- a/src/plus/webviews/graph/graphWebview.ts +++ b/src/plus/webviews/graph/graphWebview.ts @@ -101,7 +101,7 @@ import type { UpdateColumnsParams, UpdateRefsVisibilityParams, UpdateSelectedRepositoryParams, - UpdateSelectionParams, + UpdateSelectionParams } from './protocol'; import { DidChangeAvatarsNotificationType, @@ -113,6 +113,7 @@ import { DidChangeRowsNotificationType, DidChangeSelectionNotificationType, DidChangeSubscriptionNotificationType, + DidChangeWindowFocusNotificationType, DidChangeWorkingTreeNotificationType, DidEnsureRowNotificationType, DidFetchNotificationType, @@ -201,6 +202,7 @@ export class GraphWebview extends WebviewBase { private _lastFetchedDisposable: Disposable | undefined; private trialBanner?: boolean; + private isWindowFocused: boolean = true; constructor(container: Container) { super( @@ -256,6 +258,11 @@ export class GraphWebview extends WebviewBase { ); } + protected override onWindowFocusChanged(focused: boolean): void { + this.isWindowFocused = focused; + void this.notifyDidChangeWindowFocus(); + } + protected override get options(): WebviewPanelOptions & WebviewOptions { return { retainContextWhenHidden: true, @@ -936,6 +943,18 @@ export class GraphWebview extends WebviewBase { void this._notifyDidChangeStateDebounced(); } + @debug() + private async notifyDidChangeWindowFocus(): Promise { + if (!this.isReady || !this.visible) { + this.addPendingIpcNotification(DidChangeWindowFocusNotificationType); + return false; + } + + return this.notify(DidChangeWindowFocusNotificationType, { + focused: this.isWindowFocused, + }); + } + private _notifyDidChangeAvatarsDebounced: Deferrable | undefined = undefined; @@ -1135,6 +1154,7 @@ export class GraphWebview extends WebviewBase { [DidChangeSelectionNotificationType, this.notifyDidChangeSelection], [DidChangeSubscriptionNotificationType, this.notifyDidChangeSubscription], [DidChangeWorkingTreeNotificationType, this.notifyDidChangeWorkingTree], + [DidChangeWindowFocusNotificationType, this.notifyDidChangeWindowFocus], ]); private addPendingIpcNotification(type: IpcNotificationType, msg?: IpcMessage) { @@ -1450,6 +1470,7 @@ export class GraphWebview extends WebviewBase { const branch = await this.repository.getBranch(); return { + windowFocused: this.isWindowFocused, trialBanner: this.trialBanner, repositories: formatRepositories(this.container.git.openRepositories), selectedRepository: this.repository.path, diff --git a/src/plus/webviews/graph/protocol.ts b/src/plus/webviews/graph/protocol.ts index a6b6527..bfb7513 100644 --- a/src/plus/webviews/graph/protocol.ts +++ b/src/plus/webviews/graph/protocol.ts @@ -38,6 +38,7 @@ export type GraphMissingRefsMetadata = Record( + 'graph/window/focus/didChange', + true, +); + export interface DidChangeRefsVisibilityParams { hiddenRefs?: GraphHiddenRefs; } diff --git a/src/webviews/apps/plus/graph/GraphWrapper.tsx b/src/webviews/apps/plus/graph/GraphWrapper.tsx index 7061b24..cc2ec09 100644 --- a/src/webviews/apps/plus/graph/GraphWrapper.tsx +++ b/src/webviews/apps/plus/graph/GraphWrapper.tsx @@ -31,8 +31,7 @@ import type { GraphSearchResultsError, InternalNotificationType, State, - UpdateStateCallback, -} from '../../../../plus/webviews/graph/protocol'; + UpdateStateCallback} from '../../../../plus/webviews/graph/protocol'; import { DidChangeAvatarsNotificationType, DidChangeColumnsNotificationType, @@ -42,6 +41,7 @@ import { DidChangeRowsNotificationType, DidChangeSelectionNotificationType, DidChangeSubscriptionNotificationType, + DidChangeWindowFocusNotificationType, DidChangeWorkingTreeNotificationType, DidFetchNotificationType, DidSearchNotificationType, @@ -169,6 +169,7 @@ export function GraphWrapper({ const [styleProps, setStyleProps] = useState(state.theming); const [branchName, setBranchName] = useState(state.branchName); const [lastFetched, setLastFetched] = useState(state.lastFetched); + const [windowFocused, setWindowFocused] = useState(state.windowFocused); // account const [showAccount, setShowAccount] = useState(state.trialBanner); const [isAccessAllowed, setIsAccessAllowed] = useState(state.allowed ?? false); @@ -211,6 +212,9 @@ export function GraphWrapper({ case DidChangeAvatarsNotificationType: setAvatars(state.avatars); break; + case DidChangeWindowFocusNotificationType: + setWindowFocused(state.windowFocused); + break; case DidChangeRefsMetadataNotificationType: setRefsMetadata(state.refsMetadata); break; @@ -836,6 +840,7 @@ export function GraphWrapper({ scrollRowPadding={graphConfig?.scrollRowPadding} showGhostRefsOnRowHover={graphConfig?.showGhostRefsOnRowHover} showRemoteNamesOnRefs={graphConfig?.showRemoteNamesOnRefs} + isContainerWindowFocused={windowFocused} isLoadingRows={isLoading} isSelectedBySha={selectedRows} nonce={nonce} diff --git a/src/webviews/apps/plus/graph/graph.tsx b/src/webviews/apps/plus/graph/graph.tsx index e82cb32..ea52d2a 100644 --- a/src/webviews/apps/plus/graph/graph.tsx +++ b/src/webviews/apps/plus/graph/graph.tsx @@ -13,8 +13,7 @@ import type { GraphRepository, InternalNotificationType, State, - UpdateStateCallback, -} from '../../../../plus/webviews/graph/protocol'; + UpdateStateCallback} from '../../../../plus/webviews/graph/protocol'; import { DidChangeAvatarsNotificationType, DidChangeColumnsNotificationType, @@ -25,6 +24,7 @@ import { DidChangeRowsNotificationType, DidChangeSelectionNotificationType, DidChangeSubscriptionNotificationType, + DidChangeWindowFocusNotificationType, DidChangeWorkingTreeNotificationType, DidEnsureRowNotificationType, DidFetchNotificationType, @@ -144,6 +144,13 @@ export class GraphApp extends App { }); break; + case DidChangeWindowFocusNotificationType.method: + onIpc(DidChangeWindowFocusNotificationType, msg, (params, type) => { + this.state.windowFocused = params.focused; + this.setState(this.state, type); + }); + break; + case DidChangeColumnsNotificationType.method: onIpc(DidChangeColumnsNotificationType, msg, (params, type) => { this.state.columns = params.columns; diff --git a/src/webviews/webviewBase.ts b/src/webviews/webviewBase.ts index 1307019..c001e9c 100644 --- a/src/webviews/webviewBase.ts +++ b/src/webviews/webviewBase.ts @@ -4,6 +4,7 @@ import type { WebviewPanel, WebviewPanelOnDidChangeViewStateEvent, WebviewPanelOptions, + WindowState, } from 'vscode'; import { Disposable, Uri, ViewColumn, window, workspace } from 'vscode'; import { getNonce } from '@env/crypto'; @@ -117,6 +118,7 @@ export abstract class WebviewBase implements Disposable { this._panel.webview.onDidReceiveMessage(this.onMessageReceivedCore, this), ...(this.onInitializing?.() ?? []), ...(this.registerCommands?.() ?? []), + window.onDidChangeWindowState(this.onWindowStateChanged, this), ); this._panel.webview.html = await this.getHtml(this._panel.webview); @@ -137,6 +139,7 @@ export abstract class WebviewBase implements Disposable { protected onActiveChanged?(active: boolean): void; protected onFocusChanged?(focused: boolean): void; protected onVisibilityChanged?(visible: boolean): void; + protected onWindowFocusChanged?(focused: boolean): void; protected registerCommands?(): Disposable[]; @@ -145,6 +148,12 @@ export abstract class WebviewBase implements Disposable { protected includeBody?(): string | Promise; protected includeEndOfBody?(): string | Promise; + private onWindowStateChanged(e: WindowState) { + if (!this.visible) return; + + this.onWindowFocusChanged?.(e.focused); + } + @debug() protected async refresh(force?: boolean): Promise { if (this._panel == null) return;