Skip to content

Commit deffa1b

Browse files
committed
refactor: refactor webview
1 parent 21a0a8f commit deffa1b

File tree

5 files changed

+137
-128
lines changed

5 files changed

+137
-128
lines changed

src/commands/index.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,15 @@ import refreshCommand from './refresh'
44
import diffCommand from './diff'
55

66
import type { DiffTreeView } from '@/views/diff'
7-
import type { GitPanelViewProvider } from '@/views/webview'
87
import { EXTENSION_SYMBOL } from '@/constant'
98

109
interface CommandProvider {
1110
diffProvider: DiffTreeView
12-
provider: GitPanelViewProvider
1311
}
1412

15-
export function initCommands({ diffProvider, provider }: CommandProvider) {
13+
export function initCommands({ diffProvider }: CommandProvider) {
1614
useCommands({
17-
[`${EXTENSION_SYMBOL}.history.refresh`]: refreshCommand(provider),
15+
[`${EXTENSION_SYMBOL}.history.refresh`]: refreshCommand,
1816
[`${EXTENSION_SYMBOL}.openDiff`]: diffCommand(diffProvider),
1917
})
2018
}

src/commands/refresh.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import type { GitPanelViewProvider } from '@/views/webview'
1+
import { useGitPanelView } from '@/views/webview'
22

3-
export default function refreshCommand(provider: GitPanelViewProvider) {
4-
return () => {
5-
provider.refreshHistory(true)
6-
}
3+
export default function refreshCommand() {
4+
useGitPanelView().forceRefresh()
75
}

src/git/GitChangeMonitor.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import { type Disposable, extensions } from 'vscode'
2-
import { computed, createSingletonComposable, ref, useFsWatcher } from 'reactive-vscode'
2+
import { createSingletonComposable, ref, useFsWatcher } from 'reactive-vscode'
3+
import { useGitPanelView } from '@/views/webview'
34

45
export const useGitChangeMonitor = createSingletonComposable(() => {
6+
const webview = useGitPanelView()
7+
58
const disposables = ref<Disposable[]>([])
69
const retryCount = ref(0)
7-
let onGitChange = () => { }
810

9-
const filesToWatch = computed(() => ['**/.git/index'])
10-
const fsWatcher = useFsWatcher(filesToWatch)
11+
const onGitChange = () => {
12+
webview.refreshHistory(true)
13+
}
14+
15+
const fsWatcher = useFsWatcher('**/.git/index')
1116

1217
fsWatcher.onDidChange(onGitChange)
1318
fsWatcher.onDidCreate(onGitChange)
@@ -45,24 +50,24 @@ export const useGitChangeMonitor = createSingletonComposable(() => {
4550
}
4651
}
4752

48-
function initializeError(fn: () => void) {
53+
function initializeError() {
4954
console.error('Failed to get Git extension API, will retry after 5 seconds...')
50-
retryCount.value = 0
51-
setTimeout(() => initialize(fn), 5000)
55+
if (retryCount.value === 0) {
56+
retryCount.value++
57+
setTimeout(initialize, 5000)
58+
}
5259
}
5360

54-
async function initialize(fn: () => void) {
61+
async function initialize() {
5562
try {
5663
const git = await getGetInstance()
5764

58-
if (!git && retryCount.value === 0) {
59-
initializeError(fn)
65+
if (!git) {
66+
initializeError()
6067
return
6168
}
6269

6370
retryCount.value = 0
64-
onGitChange = fn
65-
6671
if (!git.repositories || git.repositories.length === 0) {
6772
disposables.value.push(git.onDidOpenRepository(setupRepository))
6873
}
@@ -71,7 +76,7 @@ export const useGitChangeMonitor = createSingletonComposable(() => {
7176
}
7277
}
7378
catch {
74-
initializeError(fn)
79+
initializeError()
7580
}
7681
}
7782

@@ -81,8 +86,9 @@ export const useGitChangeMonitor = createSingletonComposable(() => {
8186
disposables.value = []
8287
}
8388

89+
initialize()
90+
8491
return {
8592
dispose,
86-
initialize,
8793
}
8894
})

src/index.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
1-
import { Uri, window } from 'vscode'
2-
import { defineExtension } from 'reactive-vscode'
1+
import { Disposable, window } from 'vscode'
2+
import { defineExtension, useDisposable } from 'reactive-vscode'
33

4-
import { GitPanelViewProvider } from './views/webview'
4+
import { useGitPanelView } from './views/webview'
55
import { DiffTreeView } from './views/diff'
66
import { logger } from './utils'
77
import { initCommands } from './commands'
88
import { EXTENSION_SYMBOL } from './constant'
99
import { initDecoration } from './decoration'
1010
import { useStorage } from './storage'
11+
import { useGitChangeMonitor } from './git/GitChangeMonitor'
1112

1213
const { activate, deactivate } = defineExtension(() => {
1314
logger.info('Git Panel Activated')
1415

15-
const storage = useStorage()
16-
storage.clearCommits()
17-
18-
const provider = new GitPanelViewProvider(Uri.file(__dirname))
19-
window.registerWebviewViewProvider(GitPanelViewProvider.viewType, provider)
16+
useStorage().clearCommits()
17+
useGitPanelView()
18+
const gitChangeMonitor = useGitChangeMonitor()
2019

2120
const diffProvider = DiffTreeView.getInstance()
2221
window.createTreeView(`${EXTENSION_SYMBOL}.changes`, {
@@ -25,7 +24,9 @@ const { activate, deactivate } = defineExtension(() => {
2524
})
2625

2726
initDecoration()
28-
initCommands({ diffProvider, provider })
27+
initCommands({ diffProvider })
28+
29+
useDisposable(new Disposable(gitChangeMonitor.dispose))
2930
})
3031

3132
export { activate, deactivate }

src/views/webview.ts

Lines changed: 102 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
import type { Webview, WebviewView, WebviewViewProvider } from 'vscode'
21
import { ExtensionMode, Uri } from 'vscode'
3-
import { extensionContext as context, executeCommand } from 'reactive-vscode'
2+
import {
3+
computed,
4+
extensionContext as context,
5+
createSingletonComposable,
6+
executeCommand,
7+
ref,
8+
useWebviewView,
9+
} from 'reactive-vscode'
410

511
import { DiffTreeView } from './diff/DiffTreeView'
612

@@ -10,103 +16,36 @@ import { CHANNEL, EXTENSION_SYMBOL, WEBVIEW_CHANNEL } from '@/constant'
1016

1117
import type { Commit } from '@/git'
1218

13-
export class GitPanelViewProvider implements WebviewViewProvider {
14-
private git = useGitService()
15-
private storage = useStorage()
16-
private gitChangesProvider: DiffTreeView
17-
public static readonly viewType = `${EXTENSION_SYMBOL}.history`
18-
private _commits: Commit[] = []
19-
private _view?: WebviewView
20-
21-
constructor(
22-
private readonly _extensionUri: Uri,
23-
) {
24-
this.gitChangesProvider = DiffTreeView.getInstance()
25-
this._commits = this.storage.getCommits()
26-
}
27-
28-
public async refreshHistory(forceRefresh: boolean = false) {
29-
if (!this._view)
30-
return
31-
32-
try {
33-
if (this._commits.length === 0 || forceRefresh) {
34-
const history = await this.git.getHistory()
35-
this._commits = Array.from(history.all)
36-
37-
this.storage.saveCommits(this._commits)
38-
}
19+
function getNonce() {
20+
let text = ''
21+
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
22+
for (let i = 0; i < 32; i++)
23+
text += possible.charAt(Math.floor(Math.random() * possible.length))
24+
return text
25+
}
3926

40-
this._view.webview.postMessage({
41-
command: CHANNEL.HISTORY,
42-
commits: this._commits,
43-
})
44-
}
45-
catch (error) {
46-
this._view.webview.postMessage({
47-
command: 'Failed to get git history',
48-
message: `${error}`,
49-
})
50-
}
51-
}
27+
export const useGitPanelView = createSingletonComposable(() => {
28+
const git = useGitService()
29+
const storage = useStorage()
5230

53-
public resolveWebviewView(
54-
webviewView: WebviewView,
55-
) {
56-
this._view = webviewView
57-
webviewView.webview.options = {
58-
enableScripts: true,
59-
localResourceRoots: [
60-
Uri.joinPath(this._extensionUri, '../'),
61-
Uri.joinPath(this._extensionUri),
62-
],
63-
}
31+
const extensionUri = context.value?.extensionUri || Uri.file(context.value?.extensionPath || '')
6432

65-
webviewView.webview.html = this._getHtmlForWebview(webviewView.webview)
66-
67-
// Handle messages from webview
68-
webviewView.webview.onDidReceiveMessage(async (message) => {
69-
switch (message.command) {
70-
case WEBVIEW_CHANNEL.GET_HISTORY:
71-
await this.refreshHistory(message.forceRefresh)
72-
break
73-
74-
case WEBVIEW_CHANNEL.SHOW_COMMIT_DETAILS:
75-
try {
76-
this.gitChangesProvider.refresh(message.commitHash)
77-
}
78-
catch (error) {
79-
webviewView.webview.postMessage({
80-
command: 'Failed to show commit details',
81-
message: `${error}`,
82-
})
83-
}
84-
break
85-
case WEBVIEW_CHANNEL.SHOW_CHANGES_PANEL:
86-
await executeCommand('git-panel.changes.focus')
87-
break
88-
89-
case 'clearHistory':
90-
this.storage.clearCommits()
91-
break
92-
}
93-
})
33+
if (!extensionUri) {
34+
throw new Error('Extension context not initialized')
9435
}
9536

96-
private _getHtmlForWebview(webview: Webview) {
97-
const isDev = context.value?.extensionMode === ExtensionMode.Development
37+
const gitChangesProvider = DiffTreeView.getInstance()
38+
const commits = ref<Commit[]>(storage.getCommits())
9839

40+
const isDev = context.value?.extensionMode === ExtensionMode.Development
41+
const html = computed(() => {
9942
const scriptUri = isDev
10043
? 'http://localhost:5173/src/views/history/index.ts'
101-
: webview.asWebviewUri(
102-
Uri.joinPath(this._extensionUri, 'views.es.js'),
103-
)
44+
: Uri.joinPath(extensionUri, 'views.es.js')
10445

10546
const styleUri = isDev
10647
? null
107-
: webview.asWebviewUri(
108-
Uri.joinPath(this._extensionUri, 'views.css'),
109-
)
48+
: Uri.joinPath(extensionUri, 'views.css')
11049

11150
const nonce = getNonce()
11251

@@ -135,13 +74,80 @@ export class GitPanelViewProvider implements WebviewViewProvider {
13574
<script type="module" nonce="${nonce}" src="${scriptUri}"></script>
13675
</body>
13776
</html>`
77+
})
78+
79+
const { forceRefresh: refreshWebview, postMessage } = useWebviewView(
80+
`${EXTENSION_SYMBOL}.history`,
81+
html,
82+
{
83+
retainContextWhenHidden: true,
84+
webviewOptions: {
85+
enableCommandUris: true,
86+
enableScripts: true,
87+
localResourceRoots: [
88+
Uri.joinPath(extensionUri, '../'),
89+
Uri.joinPath(extensionUri),
90+
],
91+
},
92+
onDidReceiveMessage: async (message) => {
93+
switch (message.command) {
94+
case WEBVIEW_CHANNEL.GET_HISTORY:
95+
await refreshHistory(message.forceRefresh)
96+
break
97+
98+
case WEBVIEW_CHANNEL.SHOW_COMMIT_DETAILS:
99+
try {
100+
gitChangesProvider.refresh(message.commitHash)
101+
}
102+
catch (error) {
103+
postMessage({
104+
command: 'Failed to show commit details',
105+
message: `${error}`,
106+
})
107+
}
108+
break
109+
110+
case WEBVIEW_CHANNEL.SHOW_CHANGES_PANEL:
111+
await executeCommand('git-panel.changes.focus')
112+
break
113+
114+
case 'clearHistory':
115+
storage.clearCommits()
116+
break
117+
}
118+
},
119+
},
120+
)
121+
122+
async function refreshHistory(forceRefresh: boolean = false) {
123+
try {
124+
if (commits.value.length === 0 || forceRefresh) {
125+
const history = await git.getHistory()
126+
commits.value = Array.from(history.all)
127+
storage.saveCommits(commits.value)
128+
}
129+
130+
postMessage({
131+
command: CHANNEL.HISTORY,
132+
commits: commits.value,
133+
})
134+
}
135+
catch (error) {
136+
postMessage({
137+
command: 'Failed to get git history',
138+
message: `${error}`,
139+
})
140+
}
138141
}
139-
}
140142

141-
function getNonce() {
142-
let text = ''
143-
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
144-
for (let i = 0; i < 32; i++)
145-
text += possible.charAt(Math.floor(Math.random() * possible.length))
146-
return text
147-
}
143+
function forceRefresh() {
144+
refreshHistory(true)
145+
refreshWebview()
146+
}
147+
148+
return {
149+
viewType: `${EXTENSION_SYMBOL}.history` as const,
150+
refreshHistory,
151+
forceRefresh,
152+
}
153+
})

0 commit comments

Comments
 (0)