Browse Source

Moves pending ipc handling to controller

main
Eric Amodio 1 year ago
parent
commit
b82080db60
7 changed files with 112 additions and 132 deletions
  1. +0
    -1
      src/plus/webviews/focus/protocol.ts
  2. +48
    -101
      src/plus/webviews/graph/graphWebview.ts
  3. +2
    -11
      src/plus/webviews/graph/protocol.ts
  4. +0
    -4
      src/webviews/commitDetails/commitDetailsWebview.ts
  5. +1
    -1
      src/webviews/commitDetails/protocol.ts
  6. +1
    -1
      src/webviews/protocol.ts
  7. +60
    -13
      src/webviews/webviewController.ts

+ 0
- 1
src/plus/webviews/focus/protocol.ts View File

@ -63,5 +63,4 @@ export interface DidChangeSubscriptionParams {
} }
export const DidChangeSubscriptionNotificationType = new IpcNotificationType<DidChangeSubscriptionParams>( export const DidChangeSubscriptionNotificationType = new IpcNotificationType<DidChangeSubscriptionParams>(
'graph/subscription/didChange', 'graph/subscription/didChange',
true,
); );

+ 48
- 101
src/plus/webviews/graph/graphWebview.ts View File

@ -76,7 +76,7 @@ import { getSettledValue } from '../../../system/promise';
import { isDarkTheme, isLightTheme } from '../../../system/utils'; import { isDarkTheme, isLightTheme } from '../../../system/utils';
import { isWebviewItemContext, isWebviewItemGroupContext, serializeWebviewItemContext } from '../../../system/webview'; import { isWebviewItemContext, isWebviewItemGroupContext, serializeWebviewItemContext } from '../../../system/webview';
import { RepositoryFolderNode } from '../../../views/nodes/viewNode'; import { RepositoryFolderNode } from '../../../views/nodes/viewNode';
import type { IpcMessage, IpcMessageParams, IpcNotificationType } from '../../../webviews/protocol';
import type { IpcMessage, IpcNotificationType } from '../../../webviews/protocol';
import { onIpc } from '../../../webviews/protocol'; import { onIpc } from '../../../webviews/protocol';
import type { WebviewController, WebviewProvider } from '../../../webviews/webviewController'; import type { WebviewController, WebviewProvider } from '../../../webviews/webviewController';
import type { SubscriptionChangeEvent } from '../../subscription/subscriptionService'; import type { SubscriptionChangeEvent } from '../../subscription/subscriptionService';
@ -205,7 +205,17 @@ export class GraphWebviewProvider implements WebviewProvider {
private _etagRepository?: number; private _etagRepository?: number;
private _firstSelection = true; private _firstSelection = true;
private _graph?: GitGraph; private _graph?: GitGraph;
private _pendingIpcNotifications = new Map<IpcNotificationType, IpcMessage | (() => Promise<boolean>)>();
private readonly _ipcNotificationMap = new Map<IpcNotificationType<any>, () => Promise<boolean>>([
[DidChangeColumnsNotificationType, this.notifyDidChangeColumns],
[DidChangeGraphConfigurationNotificationType, this.notifyDidChangeConfiguration],
[DidChangeNotificationType, this.notifyDidChangeState],
[DidChangeRefsVisibilityNotificationType, this.notifyDidChangeRefsVisibility],
[DidChangeSelectionNotificationType, this.notifyDidChangeSelection],
[DidChangeSubscriptionNotificationType, this.notifyDidChangeSubscription],
[DidChangeWorkingTreeNotificationType, this.notifyDidChangeWorkingTree],
[DidChangeWindowFocusNotificationType, this.notifyDidChangeWindowFocus],
[DidFetchNotificationType, this.notifyDidFetch],
]);
private _refsMetadata: Map<string, GraphRefMetadata | null> | null | undefined; private _refsMetadata: Map<string, GraphRefMetadata | null> | null | undefined;
private _search: GitSearch | undefined; private _search: GitSearch | undefined;
private _searchCancellation: CancellationTokenSource | undefined; private _searchCancellation: CancellationTokenSource | undefined;
@ -294,7 +304,6 @@ export class GraphWebviewProvider implements WebviewProvider {
onRefresh(force?: boolean) { onRefresh(force?: boolean) {
if (force) { if (force) {
this.resetRepositoryState(); this.resetRepositoryState();
this._pendingIpcNotifications.clear();
} }
} }
@ -388,10 +397,6 @@ export class GraphWebviewProvider implements WebviewProvider {
void this.notifyDidChangeWindowFocus(); void this.notifyDidChangeWindowFocus();
} }
onReady(): void {
this.sendPendingIpcNotifications();
}
onFocusChanged(focused: boolean): void { onFocusChanged(focused: boolean): void {
if (!focused || this.activeSelection == null || !this.container.commitDetailsView.visible) { if (!focused || this.activeSelection == null || !this.container.commitDetailsView.visible) {
this._showActiveSelectionDetailsDebounced?.cancel(); this._showActiveSelectionDetailsDebounced?.cancel();
@ -417,7 +422,7 @@ export class GraphWebviewProvider implements WebviewProvider {
if (visible) { if (visible) {
if (this.host.ready) { if (this.host.ready) {
this.sendPendingIpcNotifications();
this.host.sendPendingIpcNotifications();
} }
const { activeSelection } = this; const { activeSelection } = this;
@ -734,7 +739,7 @@ export class GraphWebviewProvider implements WebviewProvider {
} }
} }
void this.notify(DidEnsureRowNotificationType, { id: id }, completionId);
void this.host.notify(DidEnsureRowNotificationType, { id: id }, completionId);
} }
private async onGetMissingAvatars(e: GetMissingAvatarsParams) { private async onGetMissingAvatars(e: GetMissingAvatarsParams) {
@ -910,7 +915,7 @@ export class GraphWebviewProvider implements WebviewProvider {
this._search = search; this._search = search;
void (await this.ensureSearchStartsInRange(this._graph!, search)); void (await this.ensureSearchStartsInRange(this._graph!, search));
void this.notify(
void this.host.notify(
DidSearchNotificationType, DidSearchNotificationType,
{ {
results: results:
@ -953,7 +958,7 @@ export class GraphWebviewProvider implements WebviewProvider {
} catch (ex) { } catch (ex) {
this._search = undefined; this._search = undefined;
void this.notify(
void this.host.notify(
DidSearchNotificationType, DidSearchNotificationType,
{ {
results: { results: {
@ -967,7 +972,7 @@ export class GraphWebviewProvider implements WebviewProvider {
if (cancellation.token.isCancellationRequested) { if (cancellation.token.isCancellationRequested) {
if (completionId != null) { if (completionId != null) {
void this.notify(DidSearchNotificationType, { results: undefined }, completionId);
void this.host.notify(DidSearchNotificationType, { results: undefined }, completionId);
} }
return; return;
} }
@ -985,7 +990,7 @@ export class GraphWebviewProvider implements WebviewProvider {
this.setSelectedRows(firstResult); this.setSelectedRows(firstResult);
} }
void this.notify(
void this.host.notify(
DidSearchNotificationType, DidSearchNotificationType,
{ {
results: results:
@ -1103,7 +1108,7 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug() @debug()
private updateState(immediate: boolean = false) { private updateState(immediate: boolean = false) {
this._pendingIpcNotifications.clear();
this.host.clearPendingIpcNotifications();
if (immediate) { if (immediate) {
void this.notifyDidChangeState(); void this.notifyDidChangeState();
@ -1120,11 +1125,11 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug() @debug()
private async notifyDidChangeWindowFocus(): Promise<boolean> { private async notifyDidChangeWindowFocus(): Promise<boolean> {
if (!this.host.ready || !this.host.visible) { if (!this.host.ready || !this.host.visible) {
this.addPendingIpcNotification(DidChangeWindowFocusNotificationType);
this.host.addPendingIpcNotification(DidChangeWindowFocusNotificationType, this._ipcNotificationMap, this);
return false; return false;
} }
return this.notify(DidChangeWindowFocusNotificationType, {
return this.host.notify(DidChangeWindowFocusNotificationType, {
focused: this.isWindowFocused, focused: this.isWindowFocused,
}); });
} }
@ -1151,7 +1156,7 @@ export class GraphWebviewProvider implements WebviewProvider {
if (this._graph == null) return; if (this._graph == null) return;
const data = this._graph; const data = this._graph;
return this.notify(DidChangeAvatarsNotificationType, {
return this.host.notify(DidChangeAvatarsNotificationType, {
avatars: Object.fromEntries(data.avatars), avatars: Object.fromEntries(data.avatars),
}); });
} }
@ -1176,7 +1181,7 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug() @debug()
private async notifyDidChangeRefsMetadata() { private async notifyDidChangeRefsMetadata() {
return this.notify(DidChangeRefsMetadataNotificationType, {
return this.host.notify(DidChangeRefsMetadataNotificationType, {
metadata: this._refsMetadata != null ? Object.fromEntries(this._refsMetadata) : this._refsMetadata, metadata: this._refsMetadata != null ? Object.fromEntries(this._refsMetadata) : this._refsMetadata,
}); });
} }
@ -1184,13 +1189,13 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug() @debug()
private async notifyDidChangeColumns() { private async notifyDidChangeColumns() {
if (!this.host.ready || !this.host.visible) { if (!this.host.ready || !this.host.visible) {
this.addPendingIpcNotification(DidChangeColumnsNotificationType);
this.host.addPendingIpcNotification(DidChangeColumnsNotificationType, this._ipcNotificationMap, this);
return false; return false;
} }
const columns = this.getColumns(); const columns = this.getColumns();
const columnSettings = this.getColumnSettings(columns); const columnSettings = this.getColumnSettings(columns);
return this.notify(DidChangeColumnsNotificationType, {
return this.host.notify(DidChangeColumnsNotificationType, {
columns: columnSettings, columns: columnSettings,
context: this.getColumnHeaderContext(columnSettings), context: this.getColumnHeaderContext(columnSettings),
}); });
@ -1199,11 +1204,15 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug() @debug()
private async notifyDidChangeRefsVisibility() { private async notifyDidChangeRefsVisibility() {
if (!this.host.ready || !this.host.visible) { if (!this.host.ready || !this.host.visible) {
this.addPendingIpcNotification(DidChangeRefsVisibilityNotificationType);
this.host.addPendingIpcNotification(
DidChangeRefsVisibilityNotificationType,
this._ipcNotificationMap,
this,
);
return false; return false;
} }
return this.notify(DidChangeRefsVisibilityNotificationType, {
return this.host.notify(DidChangeRefsVisibilityNotificationType, {
excludeRefs: this.getExcludedRefs(this._graph), excludeRefs: this.getExcludedRefs(this._graph),
excludeTypes: this.getExcludedTypes(this._graph), excludeTypes: this.getExcludedTypes(this._graph),
includeOnlyRefs: this.getIncludeOnlyRefs(this._graph), includeOnlyRefs: this.getIncludeOnlyRefs(this._graph),
@ -1213,11 +1222,15 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug() @debug()
private async notifyDidChangeConfiguration() { private async notifyDidChangeConfiguration() {
if (!this.host.ready || !this.host.visible) { if (!this.host.ready || !this.host.visible) {
this.addPendingIpcNotification(DidChangeGraphConfigurationNotificationType);
this.host.addPendingIpcNotification(
DidChangeGraphConfigurationNotificationType,
this._ipcNotificationMap,
this,
);
return false; return false;
} }
return this.notify(DidChangeGraphConfigurationNotificationType, {
return this.host.notify(DidChangeGraphConfigurationNotificationType, {
config: this.getComponentConfig(), config: this.getComponentConfig(),
}); });
} }
@ -1225,12 +1238,12 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug() @debug()
private async notifyDidFetch() { private async notifyDidFetch() {
if (!this.host.ready || !this.host.visible) { if (!this.host.ready || !this.host.visible) {
this.addPendingIpcNotification(DidFetchNotificationType);
this.host.addPendingIpcNotification(DidFetchNotificationType, this._ipcNotificationMap, this);
return false; return false;
} }
const lastFetched = await this.repository!.getLastFetched(); const lastFetched = await this.repository!.getLastFetched();
return this.notify(DidFetchNotificationType, {
return this.host.notify(DidFetchNotificationType, {
lastFetched: new Date(lastFetched), lastFetched: new Date(lastFetched),
}); });
} }
@ -1240,7 +1253,7 @@ export class GraphWebviewProvider implements WebviewProvider {
if (this._graph == null) return; if (this._graph == null) return;
const data = this._graph; const data = this._graph;
return this.notify(
return this.host.notify(
DidChangeRowsNotificationType, DidChangeRowsNotificationType,
{ {
rows: data.rows, rows: data.rows,
@ -1260,11 +1273,11 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug() @debug()
private async notifyDidChangeWorkingTree() { private async notifyDidChangeWorkingTree() {
if (!this.host.ready || !this.host.visible) { if (!this.host.ready || !this.host.visible) {
this.addPendingIpcNotification(DidChangeWorkingTreeNotificationType);
this.host.addPendingIpcNotification(DidChangeWorkingTreeNotificationType, this._ipcNotificationMap, this);
return false; return false;
} }
return this.notify(DidChangeWorkingTreeNotificationType, {
return this.host.notify(DidChangeWorkingTreeNotificationType, {
stats: (await this.getWorkingTreeStats()) ?? { added: 0, deleted: 0, modified: 0 }, stats: (await this.getWorkingTreeStats()) ?? { added: 0, deleted: 0, modified: 0 },
}); });
} }
@ -1272,11 +1285,11 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug() @debug()
private async notifyDidChangeSelection() { private async notifyDidChangeSelection() {
if (!this.host.ready || !this.host.visible) { if (!this.host.ready || !this.host.visible) {
this.addPendingIpcNotification(DidChangeSelectionNotificationType);
this.host.addPendingIpcNotification(DidChangeSelectionNotificationType, this._ipcNotificationMap, this);
return false; return false;
} }
return this.notify(DidChangeSelectionNotificationType, {
return this.host.notify(DidChangeSelectionNotificationType, {
selection: this._selectedRows ?? {}, selection: this._selectedRows ?? {},
}); });
} }
@ -1284,12 +1297,12 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug() @debug()
private async notifyDidChangeSubscription() { private async notifyDidChangeSubscription() {
if (!this.host.ready || !this.host.visible) { if (!this.host.ready || !this.host.visible) {
this.addPendingIpcNotification(DidChangeSubscriptionNotificationType);
this.host.addPendingIpcNotification(DidChangeSubscriptionNotificationType, this._ipcNotificationMap, this);
return false; return false;
} }
const [access] = await this.getGraphAccess(); const [access] = await this.getGraphAccess();
return this.notify(DidChangeSubscriptionNotificationType, {
return this.host.notify(DidChangeSubscriptionNotificationType, {
subscription: access.subscription.current, subscription: access.subscription.current,
allowed: access.allowed !== false, allowed: access.allowed !== false,
}); });
@ -1298,77 +1311,11 @@ export class GraphWebviewProvider implements WebviewProvider {
@debug() @debug()
private async notifyDidChangeState() { private async notifyDidChangeState() {
if (!this.host.ready || !this.host.visible) { if (!this.host.ready || !this.host.visible) {
this.addPendingIpcNotification(DidChangeNotificationType);
this.host.addPendingIpcNotification(DidChangeNotificationType, this._ipcNotificationMap, this);
return false; return false;
} }
return this.notify(DidChangeNotificationType, { state: await this.getState() });
}
private async notify<T extends IpcNotificationType<any>>(
type: T,
params: IpcMessageParams<T>,
completionId?: string,
): Promise<boolean> {
const msg: IpcMessage = {
id: this.host.nextIpcId(),
method: type.method,
params: params,
completionId: completionId,
};
const success = await this.host.postMessage(msg);
if (success) {
this._pendingIpcNotifications.clear();
} else {
this.addPendingIpcNotification(type, msg);
}
return success;
}
private readonly _ipcNotificationMap = new Map<IpcNotificationType<any>, () => Promise<boolean>>([
[DidChangeColumnsNotificationType, this.notifyDidChangeColumns],
[DidChangeGraphConfigurationNotificationType, this.notifyDidChangeConfiguration],
[DidChangeNotificationType, this.notifyDidChangeState],
[DidChangeRefsVisibilityNotificationType, this.notifyDidChangeRefsVisibility],
[DidChangeSelectionNotificationType, this.notifyDidChangeSelection],
[DidChangeSubscriptionNotificationType, this.notifyDidChangeSubscription],
[DidChangeWorkingTreeNotificationType, this.notifyDidChangeWorkingTree],
[DidChangeWindowFocusNotificationType, this.notifyDidChangeWindowFocus],
[DidFetchNotificationType, this.notifyDidFetch],
]);
private addPendingIpcNotification(type: IpcNotificationType<any>, msg?: IpcMessage) {
if (type === DidChangeNotificationType) {
this._pendingIpcNotifications.clear();
} else if (type.overwriteable) {
this._pendingIpcNotifications.delete(type);
}
let msgOrFn: IpcMessage | (() => Promise<boolean>) | undefined;
if (msg == null) {
msgOrFn = this._ipcNotificationMap.get(type)?.bind(this);
if (msgOrFn == null) {
debugger;
return;
}
} else {
msgOrFn = msg;
}
this._pendingIpcNotifications.set(type, msgOrFn);
}
private sendPendingIpcNotifications() {
if (this._pendingIpcNotifications.size === 0) return;
const ipcs = new Map(this._pendingIpcNotifications);
this._pendingIpcNotifications.clear();
for (const msgOrFn of ipcs.values()) {
if (typeof msgOrFn === 'function') {
void msgOrFn();
} else {
void this.host.postMessage(msgOrFn);
}
}
return this.host.notify(DidChangeNotificationType, { state: await this.getState() });
} }
private ensureRepositorySubscriptions(force?: boolean) { private ensureRepositorySubscriptions(force?: boolean) {

+ 2
- 11
src/plus/webviews/graph/protocol.ts View File

@ -308,7 +308,6 @@ export interface DidChangeGraphConfigurationParams {
} }
export const DidChangeGraphConfigurationNotificationType = new IpcNotificationType<DidChangeGraphConfigurationParams>( export const DidChangeGraphConfigurationNotificationType = new IpcNotificationType<DidChangeGraphConfigurationParams>(
'graph/configuration/didChange', 'graph/configuration/didChange',
true,
); );
export interface DidChangeSubscriptionParams { export interface DidChangeSubscriptionParams {
@ -317,7 +316,6 @@ export interface DidChangeSubscriptionParams {
} }
export const DidChangeSubscriptionNotificationType = new IpcNotificationType<DidChangeSubscriptionParams>( export const DidChangeSubscriptionNotificationType = new IpcNotificationType<DidChangeSubscriptionParams>(
'graph/subscription/didChange', 'graph/subscription/didChange',
true,
); );
export interface DidChangeAvatarsParams { export interface DidChangeAvatarsParams {
@ -325,7 +323,6 @@ export interface DidChangeAvatarsParams {
} }
export const DidChangeAvatarsNotificationType = new IpcNotificationType<DidChangeAvatarsParams>( export const DidChangeAvatarsNotificationType = new IpcNotificationType<DidChangeAvatarsParams>(
'graph/avatars/didChange', 'graph/avatars/didChange',
true,
); );
export interface DidChangeRefsMetadataParams { export interface DidChangeRefsMetadataParams {
@ -333,7 +330,6 @@ export interface DidChangeRefsMetadataParams {
} }
export const DidChangeRefsMetadataNotificationType = new IpcNotificationType<DidChangeRefsMetadataParams>( export const DidChangeRefsMetadataNotificationType = new IpcNotificationType<DidChangeRefsMetadataParams>(
'graph/refs/didChangeMetadata', 'graph/refs/didChangeMetadata',
true,
); );
export interface DidChangeColumnsParams { export interface DidChangeColumnsParams {
@ -342,7 +338,6 @@ export interface DidChangeColumnsParams {
} }
export const DidChangeColumnsNotificationType = new IpcNotificationType<DidChangeColumnsParams>( export const DidChangeColumnsNotificationType = new IpcNotificationType<DidChangeColumnsParams>(
'graph/columns/didChange', 'graph/columns/didChange',
true,
); );
export interface DidChangeWindowFocusParams { export interface DidChangeWindowFocusParams {
@ -350,7 +345,6 @@ export interface DidChangeWindowFocusParams {
} }
export const DidChangeWindowFocusNotificationType = new IpcNotificationType<DidChangeWindowFocusParams>( export const DidChangeWindowFocusNotificationType = new IpcNotificationType<DidChangeWindowFocusParams>(
'graph/window/focus/didChange', 'graph/window/focus/didChange',
true,
); );
export interface DidChangeRefsVisibilityParams { export interface DidChangeRefsVisibilityParams {
@ -360,7 +354,6 @@ export interface DidChangeRefsVisibilityParams {
} }
export const DidChangeRefsVisibilityNotificationType = new IpcNotificationType<DidChangeRefsVisibilityParams>( export const DidChangeRefsVisibilityNotificationType = new IpcNotificationType<DidChangeRefsVisibilityParams>(
'graph/refs/didChangeVisibility', 'graph/refs/didChangeVisibility',
true,
); );
export interface DidChangeRowsParams { export interface DidChangeRowsParams {
@ -378,7 +371,6 @@ export interface DidChangeSelectionParams {
} }
export const DidChangeSelectionNotificationType = new IpcNotificationType<DidChangeSelectionParams>( export const DidChangeSelectionNotificationType = new IpcNotificationType<DidChangeSelectionParams>(
'graph/selection/didChange', 'graph/selection/didChange',
true,
); );
export interface DidChangeWorkingTreeParams { export interface DidChangeWorkingTreeParams {
@ -386,7 +378,6 @@ export interface DidChangeWorkingTreeParams {
} }
export const DidChangeWorkingTreeNotificationType = new IpcNotificationType<DidChangeWorkingTreeParams>( export const DidChangeWorkingTreeNotificationType = new IpcNotificationType<DidChangeWorkingTreeParams>(
'graph/workingTree/didChange', 'graph/workingTree/didChange',
true,
); );
export interface DidEnsureRowParams { export interface DidEnsureRowParams {
@ -408,12 +399,12 @@ export interface DidSearchParams {
results: GraphSearchResults | GraphSearchResultsError | undefined; results: GraphSearchResults | GraphSearchResultsError | undefined;
selectedRows?: GraphSelectedRows; selectedRows?: GraphSelectedRows;
} }
export const DidSearchNotificationType = new IpcNotificationType<DidSearchParams>('graph/didSearch', true);
export const DidSearchNotificationType = new IpcNotificationType<DidSearchParams>('graph/didSearch');
export interface DidFetchParams { export interface DidFetchParams {
lastFetched: Date; lastFetched: Date;
} }
export const DidFetchNotificationType = new IpcNotificationType<DidFetchParams>('graph/didFetch', true);
export const DidFetchNotificationType = new IpcNotificationType<DidFetchParams>('graph/didFetch');
export interface ShowInCommitGraphCommandArgs { export interface ShowInCommitGraphCommandArgs {
ref: GitReference; ref: GitReference;

+ 0
- 4
src/webviews/commitDetails/commitDetailsWebview.ts View File

@ -653,8 +653,6 @@ export class CommitDetailsWebviewProvider implements WebviewProvider
private _notifyDidChangeStateDebounced: Deferrable<() => void> | undefined = undefined; private _notifyDidChangeStateDebounced: Deferrable<() => void> | undefined = undefined;
private updateState(immediate: boolean = false) { private updateState(immediate: boolean = false) {
if (!this.host.ready || !this.host.visible) return;
if (immediate) { if (immediate) {
void this.notifyDidChangeState(); void this.notifyDidChangeState();
return; return;
@ -668,8 +666,6 @@ export class CommitDetailsWebviewProvider implements WebviewProvider
} }
private async notifyDidChangeState() { private async notifyDidChangeState() {
if (!this.host.ready || !this.host.visible) return false;
const scope = getLogScope(); const scope = getLogScope();
this._notifyDidChangeStateDebounced?.cancel(); this._notifyDidChangeStateDebounced?.cancel();

+ 1
- 1
src/webviews/commitDetails/protocol.ts View File

@ -104,7 +104,7 @@ export const PreferencesCommandType = new IpcCommandType('comm
export interface DidChangeParams { export interface DidChangeParams {
state: Serialized<State>; state: Serialized<State>;
} }
export const DidChangeNotificationType = new IpcNotificationType<DidChangeParams>('commit/didChange');
export const DidChangeNotificationType = new IpcNotificationType<DidChangeParams>('commit/didChange', true);
export type DidChangeRichStateParams = { export type DidChangeRichStateParams = {
formattedMessage?: string; formattedMessage?: string;

+ 1
- 1
src/webviews/protocol.ts View File

@ -10,7 +10,7 @@ export interface IpcMessage {
abstract class IpcMessageType<Params = void> { abstract class IpcMessageType<Params = void> {
_?: Params; // Required for type inferencing to work properly _?: Params; // Required for type inferencing to work properly
constructor(public readonly method: string, public readonly overwriteable: boolean = false) {}
constructor(public readonly method: string, public readonly reset: boolean = false) {}
} }
export type IpcMessageParams<T> = T extends IpcMessageType<infer P> ? P : never; export type IpcMessageParams<T> = T extends IpcMessageType<infer P> ? P : never;

+ 60
- 13
src/webviews/webviewController.ts View File

@ -117,7 +117,6 @@ export class WebviewController<
public readonly id: Descriptor['id']; public readonly id: Descriptor['id'];
private _ready: boolean = false; private _ready: boolean = false;
private _suspended: boolean = false;
get ready() { get ready() {
return this._ready; return this._ready;
} }
@ -174,7 +173,6 @@ export class WebviewController<
this.provider?.onVisibilityChanged?.(false); this.provider?.onVisibilityChanged?.(false);
this._ready = false; this._ready = false;
this._suspended = false;
this._onDidDispose.fire(); this._onDidDispose.fire();
this.disposable?.dispose(); this.disposable?.dispose();
@ -271,11 +269,13 @@ export class WebviewController<
@debug() @debug()
async refresh(force?: boolean): Promise<void> { async refresh(force?: boolean): Promise<void> {
if (force) {
this.clearPendingIpcNotifications();
}
this.provider.onRefresh?.(force); this.provider.onRefresh?.(force);
// Mark the webview as not ready, until we know if we are changing the html // Mark the webview as not ready, until we know if we are changing the html
this._ready = false; this._ready = false;
this._suspended = false;
const html = await this.getHtml(this.webview); const html = await this.getHtml(this.webview);
if (force) { if (force) {
@ -307,7 +307,7 @@ export class WebviewController<
case WebviewReadyCommandType.method: case WebviewReadyCommandType.method:
onIpc(WebviewReadyCommandType, e, () => { onIpc(WebviewReadyCommandType, e, () => {
this._ready = true; this._ready = true;
this._suspended = false;
this.sendPendingIpcNotifications();
this.provider.onReady?.(); this.provider.onReady?.();
}); });
@ -348,14 +348,11 @@ export class WebviewController<
private onParentVisibilityChanged(visible: boolean, active?: boolean) { private onParentVisibilityChanged(visible: boolean, active?: boolean) {
if (this.descriptor.webviewHostOptions?.retainContextWhenHidden !== true) { if (this.descriptor.webviewHostOptions?.retainContextWhenHidden !== true) {
if (visible) { if (visible) {
if (this._suspended) {
this._ready = true;
this._suspended = false;
this.provider.onReady?.();
if (this._ready) {
this.sendPendingIpcNotifications();
} }
} else { } else {
this._ready = false; this._ready = false;
this._suspended = true;
} }
} }
@ -438,17 +435,24 @@ export class WebviewController<
return nextIpcId(); return nextIpcId();
} }
notify<T extends IpcNotificationType<any>>(
async notify<T extends IpcNotificationType<any>>(
type: T, type: T,
params: IpcMessageParams<T>, params: IpcMessageParams<T>,
completionId?: string, completionId?: string,
): Promise<boolean> { ): Promise<boolean> {
return this.postMessage({
const msg: IpcMessage = {
id: this.nextIpcId(), id: this.nextIpcId(),
method: type.method, method: type.method,
params: params, params: params,
completionId: completionId, completionId: completionId,
});
};
const success = await this.postMessage(msg);
if (success) {
this._pendingIpcNotifications.clear();
} else {
this.addPendingIpcNotificationCore(type, msg);
}
return success;
} }
@serialize() @serialize()
@ -457,7 +461,7 @@ export class WebviewController<
0: m => `{"id":${m.id},"method":${m.method}${m.completionId ? `,"completionId":${m.completionId}` : ''}}`, 0: m => `{"id":${m.id},"method":${m.method}${m.completionId ? `,"completionId":${m.completionId}` : ''}}`,
}, },
}) })
async postMessage(message: IpcMessage): Promise<boolean> {
private async postMessage(message: IpcMessage): Promise<boolean> {
if (!this._ready) return Promise.resolve(false); if (!this._ready) return Promise.resolve(false);
// It looks like there is a bug where `postMessage` can sometimes just hang infinitely. Not sure why, but ensure we don't hang // It looks like there is a bug where `postMessage` can sometimes just hang infinitely. Not sure why, but ensure we don't hang
@ -467,6 +471,49 @@ export class WebviewController<
]); ]);
return success; return success;
} }
private _pendingIpcNotifications = new Map<IpcNotificationType, IpcMessage | (() => Promise<boolean>)>();
addPendingIpcNotification(
type: IpcNotificationType<any>,
mapping: Map<IpcNotificationType<any>, () => Promise<boolean>>,
thisArg: any,
) {
this.addPendingIpcNotificationCore(type, mapping.get(type)?.bind(thisArg));
}
private addPendingIpcNotificationCore(
type: IpcNotificationType<any>,
msgOrFn: IpcMessage | (() => Promise<boolean>) | undefined,
) {
if (type.reset) {
this._pendingIpcNotifications.clear();
}
if (msgOrFn == null) {
debugger;
return;
}
this._pendingIpcNotifications.set(type, msgOrFn);
}
clearPendingIpcNotifications() {
this._pendingIpcNotifications.clear();
}
sendPendingIpcNotifications() {
if (this._pendingIpcNotifications.size === 0) return;
const ipcs = new Map(this._pendingIpcNotifications);
this._pendingIpcNotifications.clear();
for (const msgOrFn of ipcs.values()) {
if (typeof msgOrFn === 'function') {
void msgOrFn();
} else {
void this.postMessage(msgOrFn);
}
}
}
} }
export function replaceWebviewHtmlTokens<SerializedState>( export function replaceWebviewHtmlTokens<SerializedState>(

Loading…
Cancel
Save