-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBaseWebviewViewProvider.ts
More file actions
127 lines (111 loc) · 3.76 KB
/
BaseWebviewViewProvider.ts
File metadata and controls
127 lines (111 loc) · 3.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import * as vscode from 'vscode';
import type { HostCalls } from '../types';
import {
isMyActionMessage,
PATCH,
type ActionDelegate,
type FnKeys,
type Patch,
type Patches,
type WebviewKey,
} from '../types/ipcReducer';
import { LogLevel, type ILogger } from './ILogger';
import { getLogger } from './logger';
import { isLogMessage, type LogMessage } from './WebviewLogger';
import type { WebviewApiProvider } from './WebviewApiProvider';
export abstract class BaseWebviewViewProvider<A extends object>
implements vscode.WebviewViewProvider
{
protected _view?: vscode.WebviewView;
protected readonly logger: ILogger;
protected abstract readonly webviewActionDelegate: ActionDelegate<A>;
constructor(
private readonly providerId: WebviewKey,
private readonly extensionUri: vscode.Uri,
private readonly apiProvider?: WebviewApiProvider<HostCalls>
) {
this.logger = getLogger(providerId.split('.')[1] ?? providerId);
}
public resolveWebviewView(
webviewView: vscode.WebviewView,
_context: vscode.WebviewViewResolveContext,
_token: vscode.CancellationToken
): Thenable<void> | void {
this.logger.debug('Resolving webview view');
this._view = webviewView;
webviewView.webview.options = {
enableScripts: true,
localResourceRoots: [
vscode.Uri.joinPath(this.extensionUri, 'dist'),
vscode.Uri.joinPath(this.extensionUri, 'src', 'assets'),
],
};
webviewView.webview.html = this.generateWebviewHtml(webviewView.webview, this.extensionUri);
this.apiProvider?.registerView(this.providerId, webviewView);
const messageListener = webviewView.webview.onDidReceiveMessage(async (message) => {
if (isLogMessage(message)) {
this.handleLogMessage(message);
return;
}
if (isMyActionMessage<A>(message, this.providerId)) {
this.logger.debug('Received action message from webview', { message });
const delegateFn = this.webviewActionDelegate[message.key];
if (typeof delegateFn !== 'function') {
throw new TypeError(`Unknown action key: ${String(message.key)}`);
}
const patch = await delegateFn(...message.params);
this._view?.webview.postMessage({
type: PATCH,
providerId: this.providerId,
key: message.key,
patch,
});
return;
}
await this.handleMessage(message, webviewView.webview);
});
// Dispose of the message listener when webview is disposed
webviewView.onDidDispose(() => {
messageListener.dispose();
this.onWebviewDispose();
});
}
public postPatch<K extends FnKeys<A> = FnKeys<A>>(key: K, patch: Patches<A>[K]) {
this._view?.webview.postMessage({
type: PATCH,
providerId: this.providerId,
key,
patch,
} satisfies Patch<A>);
}
/**
* Called when the webview is disposed
* Override this method to clean up resources
*/
protected onWebviewDispose(): void {
// Default implementation does nothing
// Subclasses can override to clean up resources
}
protected handleLogMessage(message: LogMessage): void {
switch (message.level) {
case LogLevel.DEBUG: {
this.logger.debug(message.message, message.data);
break;
}
case LogLevel.INFO: {
this.logger.info(message.message, message.data);
break;
}
case LogLevel.WARN: {
this.logger.warn(message.message, message.data);
break;
}
case LogLevel.ERROR: {
this.logger.error(message.message, message.data);
break;
}
}
}
protected abstract generateWebviewHtml(webview: vscode.Webview, extensionUri: vscode.Uri): string;
protected abstract handleMessage(message: unknown, webview: vscode.Webview): Promise<void>;
}