Skip to content

Commit 9011721

Browse files
authored
Merge pull request #4 from ShMcK/feature/tutorial-setup
Feature/tutorial setup
2 parents ca9901b + 99372b4 commit 9011721

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+427
-327
lines changed

src/editor/views/createWebview.ts renamed to src/editor/ReactWebView.ts

+58-58
Original file line numberDiff line numberDiff line change
@@ -2,116 +2,118 @@ import * as vscode from 'vscode'
22
import * as CR from 'typings'
33
import * as path from 'path'
44

5-
import getNonce from './utils/nonce'
6-
import onReceive from './onReceive'
7-
85
/**
96
* Manages React webview panels
107
*/
11-
class ReactPanel {
12-
/**
13-
* Track the currently panel. Only allow a single panel to exist at a time.
14-
*/
15-
public static currentPanel: ReactPanel | undefined = undefined
8+
class ReactWebView {
9+
// @ts-ignore
10+
private panel: vscode.WebviewPanel
11+
private extensionPath: string
12+
private disposables: vscode.Disposable[] = []
13+
private onReceive: any // TODO: properly type
14+
15+
public constructor(extensionPath: string) {
16+
this.extensionPath = extensionPath
17+
18+
// Create and show a new webview panel
19+
this.panel = this.createWebviewPanel(vscode.ViewColumn.One)
1620

17-
private readonly _panel: vscode.WebviewPanel
18-
private readonly _extensionPath: string
19-
private _disposables: vscode.Disposable[] = []
21+
// Set the webview's initial html content
22+
this.panel.webview.html = this.getHtmlForWebview()
2023

21-
public static async createOrShow(extensionPath: string): Promise<void> {
22-
// const hasActiveEditor = vscode.window.activeTextEditor
24+
// Listen for when the panel is disposed
25+
// This happens when the user closes the panel or when the panel is closed programatically
26+
this.panel.onDidDispose(() => this.dispose(), null, this.disposables)
2327

24-
// if (!hasActiveEditor) {
25-
// throw new Error('Should have an open file on launch')
26-
// }
27-
const column = vscode.ViewColumn.One
28+
// Handle messages from the webview
29+
const onReceive = (action: string | CR.Action) => vscode.commands.executeCommand('coderoad.receive_action', action)
30+
this.panel.webview.onDidReceiveMessage(onReceive, null, this.disposables)
31+
console.log('webview loaded')
32+
}
2833

34+
public async createOrShow(column: number): Promise<void> {
2935
// If we already have a panel, show it.
3036
// Otherwise, create a new panel.
31-
if (ReactPanel.currentPanel) {
32-
console.log('--- HAS CURRENT PANEL ---')
33-
ReactPanel.currentPanel._panel.reveal(column)
37+
if (this.panel && this.panel.webview) {
38+
console.log('reveal')
39+
this.panel.reveal(column)
3440
} else {
35-
ReactPanel.currentPanel = new ReactPanel(extensionPath, column)
41+
console.log('make new panel')
42+
this.panel = this.createWebviewPanel(column)
43+
3644
}
3745
}
3846

39-
private constructor(extensionPath: string, column: vscode.ViewColumn) {
40-
this._extensionPath = extensionPath
41-
47+
private createWebviewPanel(column: number): vscode.WebviewPanel {
4248
const viewType = 'CodeRoad'
4349
const title = 'CodeRoad'
4450
const config = {
4551
// Enable javascript in the webview
4652
enableScripts: true,
47-
4853
// And restric the webview to only loading content from our extension's `media` directory.
49-
localResourceRoots: [vscode.Uri.file(path.join(this._extensionPath, 'build'))],
50-
54+
localResourceRoots: [vscode.Uri.file(path.join(this.extensionPath, 'build'))],
5155
// prevents destroying the window when it is in the background
5256
retainContextWhenHidden: true,
5357
}
58+
return vscode.window.createWebviewPanel(viewType, title, column, config)
59+
}
5460

55-
// Create and show a new webview panel
56-
this._panel = vscode.window.createWebviewPanel(viewType, title, column, config)
57-
58-
// Set the webview's initial html content
59-
this._panel.webview.html = this._getHtmlForWebview()
60-
61-
// Listen for when the panel is disposed
62-
// This happens when the user closes the panel or when the panel is closed programatically
63-
this._panel.onDidDispose(() => this.dispose(), null, this._disposables)
64-
65-
// Handle messages from the webview
66-
this._panel.webview.onDidReceiveMessage(onReceive, null, this._disposables)
61+
private getNonce(): string {
62+
let text = ''
63+
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
64+
for (let i = 0; i < 32; i++) {
65+
text += possible.charAt(Math.floor(Math.random() * possible.length))
66+
}
67+
return text
6768
}
6869

6970
public async postMessage(action: CR.Action): Promise<void> {
71+
console.log('webview postMessage')
72+
console.log(action)
7073
// Send a message to the webview webview.
7174
// You can send any JSON serializable data.
72-
const success = await this._panel.webview.postMessage(action)
75+
const success = await this.panel.webview.postMessage(action)
7376
if (!success) {
7477
throw new Error(`Message post failure: ${JSON.stringify(action)}`)
7578
}
79+
console.log('postMessage sent')
7680
}
7781

7882
public dispose(): void {
79-
ReactPanel.currentPanel = undefined
80-
8183
// Clean up our resources
82-
this._panel.dispose()
84+
this.panel.dispose()
8385

84-
while (this._disposables.length) {
85-
const x = this._disposables.pop()
86+
while (this.disposables.length) {
87+
const x = this.disposables.pop()
8688
if (x) {
8789
x.dispose()
8890
}
8991
}
9092
}
9193

92-
private _getHtmlForWebview(): string {
94+
private getHtmlForWebview(): string {
9395

9496
// eslint-disable-next-line
95-
const manifest = require(path.join(this._extensionPath, 'build', 'asset-manifest.json'))
97+
const manifest = require(path.join(this.extensionPath, 'build', 'asset-manifest.json'))
9698
const mainScript = manifest.files['main.js']
9799
// grab first chunk
98100
const chunk = Object.keys(manifest.files).filter(f => f.match(/^static\/js\/.+\.js$/))[0]
99101
const chunkScript = manifest.files[chunk]
100102
const mainStyle = manifest.files['main.css']
101103

102-
const scriptPathOnDisk = vscode.Uri.file(path.join(this._extensionPath, 'build', mainScript))
104+
const scriptPathOnDisk = vscode.Uri.file(path.join(this.extensionPath, 'build', mainScript))
103105
const scriptUri = scriptPathOnDisk.with({ scheme: 'vscode-resource' })
104-
const chunkPathOnDisk = vscode.Uri.file(path.join(this._extensionPath, 'build', chunkScript))
106+
const chunkPathOnDisk = vscode.Uri.file(path.join(this.extensionPath, 'build', chunkScript))
105107
const chunkUri = chunkPathOnDisk.with({ scheme: 'vscode-resource' })
106-
const stylePathOnDisk = vscode.Uri.file(path.join(this._extensionPath, 'build', mainStyle))
108+
const stylePathOnDisk = vscode.Uri.file(path.join(this.extensionPath, 'build', mainStyle))
107109
const styleUri = stylePathOnDisk.with({ scheme: 'vscode-resource' })
108110

109111
// Use a nonce to whitelist which scripts can be run
110-
const nonce = getNonce()
111-
const nonce2 = getNonce()
112-
const nonce3 = getNonce()
112+
const nonce = this.getNonce()
113+
const nonce2 = this.getNonce()
114+
const nonce3 = this.getNonce()
113115

114-
const output = `<!DOCTYPE html>
116+
return `<!DOCTYPE html>
115117
<html lang="en">
116118
<head>
117119
<meta charset="utf-8">
@@ -121,7 +123,7 @@ class ReactPanel {
121123
<link rel="manifest" href="./manifest.json" />
122124
<link rel="stylesheet" type="text/css" href="${styleUri}">
123125
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-resource: https:; script-src 'nonce-${nonce}' 'nonce-${nonce2}' 'nonce-${nonce3}'; style-src vscode-resource: 'unsafe-inline' http: https: data:;">
124-
<base href="https://github.com/%3Cspan%20class="pl-s1">${vscode.Uri.file(path.join(this._extensionPath, 'build')).with({ scheme: 'vscode-resource' })}/">
126+
<base href="https://github.com/%3Cspan%20class="pl-s1">${vscode.Uri.file(path.join(this.extensionPath, 'build')).with({ scheme: 'vscode-resource' })}/">
125127
<style></style>
126128
</head>
127129
@@ -133,9 +135,7 @@ class ReactPanel {
133135
<script nonce="${nonce3}" src="${scriptUri}"></script>
134136
</body>
135137
</html>`
136-
console.log(output)
137-
return output
138138
}
139139
}
140140

141-
export default ReactPanel
141+
export default ReactWebView

src/editor/commands/index.ts

+76-28
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,84 @@
11
import * as vscode from 'vscode'
2-
import start from './start'
3-
import ReactPanel from '../views/createWebview'
4-
5-
// import runTest from './runTest'
6-
// import loadSolution from './loadSolution'
7-
// import quit from './quit'
2+
import { join } from 'path'
3+
import { setStorage } from '../storage'
4+
import ReactWebView from '../ReactWebView'
5+
import * as CR from 'typings'
86

97
const COMMANDS = {
10-
// TUTORIAL_SETUP: 'coderoad.tutorial_setup',
11-
START: 'coderoad.start',
12-
OPEN_WEBVIEW: 'coderoad.open_webview'
13-
// RUN_TEST: 'coderoad.test_run',
14-
// LOAD_SOLUTION: 'coderoad.solution_load',
15-
// QUIT: 'coderoad.quit',
8+
START: 'coderoad.start',
9+
NEW_OR_CONTINUE: 'coderoad.new_or_continue',
10+
OPEN_WEBVIEW: 'coderoad.open_webview',
11+
SEND_STATE: 'coderoad.send_state',
12+
SEND_DATA: 'coderoad.send_data',
13+
RECEIVE_ACTION: 'coderoad.receive_action',
14+
OPEN_FILE: 'coderoad.open_file',
15+
RUN_TEST: 'coderoad.test_run',
16+
}
17+
18+
interface CreateCommandProps {
19+
context: vscode.ExtensionContext,
20+
machine: CR.StateMachine,
21+
storage: any,
22+
git: any
1623
}
1724

25+
// React panel webview
26+
let webview: any;
1827

19-
export default (context: vscode.ExtensionContext): void => {
20-
const commands = {
21-
[COMMANDS.START]: async function startCommand(): Promise<void> {
22-
return start(context)
28+
export const createCommands = ({ context, machine, storage, git }: CreateCommandProps) => ({
29+
// initialize
30+
[COMMANDS.START]: () => {
31+
// set local storage workspace
32+
setStorage(context.workspaceState)
33+
34+
// activate machine
35+
webview = new ReactWebView(context.extensionPath)
36+
console.log('webview', webview.panel.webview.postMessage)
37+
machine.activate()
2338
},
24-
[COMMANDS.OPEN_WEBVIEW]: () => {
25-
ReactPanel.createOrShow(context.extensionPath);
39+
[COMMANDS.NEW_OR_CONTINUE]: async () => {
40+
// verify that the user has a tutorial & progress
41+
// verify git is setup with a coderoad remote
42+
const [tutorial, progress, hasGit, hasGitRemote] = await Promise.all([
43+
storage.getTutorial(),
44+
storage.getProgress(),
45+
git.gitVersion(),
46+
git.gitCheckRemoteExists(),
47+
])
48+
const canContinue = !!(tutorial && progress && hasGit && hasGitRemote)
49+
console.log('canContinue', canContinue)
50+
// if a tutorial exists, 'CONTINUE'
51+
// otherwise start from 'NEW'
52+
machine.send(canContinue ? 'CONTINUE' : 'NEW')
2653
},
27-
// [COMMANDS.RUN_TEST]: runTest,
28-
// [COMMANDS.LOAD_SOLUTION]: loadSolution,
29-
// [COMMANDS.QUIT]: () => quit(context.subscriptions),
30-
}
31-
32-
for (const cmd in commands) {
33-
const command: vscode.Disposable = vscode.commands.registerCommand(cmd, commands[cmd])
34-
context.subscriptions.push(command)
35-
}
36-
}
54+
// open React webview
55+
[COMMANDS.OPEN_WEBVIEW]: (column: number = vscode.ViewColumn.One) => {
56+
webview.createOrShow(column);
57+
},
58+
// open a file
59+
[COMMANDS.OPEN_FILE]: async (relativeFilePath: string) => {
60+
console.log(`OPEN_FILE ${JSON.stringify(relativeFilePath)}`)
61+
try {
62+
const workspaceRoot = vscode.workspace.rootPath
63+
if (!workspaceRoot) {
64+
throw new Error('No workspace root path')
65+
}
66+
const absoluteFilePath = join(workspaceRoot, relativeFilePath)
67+
const doc = await vscode.workspace.openTextDocument(absoluteFilePath)
68+
await vscode.window.showTextDocument(doc, vscode.ViewColumn.One)
69+
} catch (error) {
70+
console.log(`Failed to open file ${relativeFilePath}`, error)
71+
}
72+
},
73+
// send messages to webview
74+
[COMMANDS.SEND_STATE]: (payload: { data: any, state: any }) => {
75+
webview.postMessage({ type: 'SET_STATE', payload })
76+
},
77+
[COMMANDS.SEND_DATA]: (payload: { data: any }) => {
78+
webview.postMessage({ type: 'SET_DATA', payload })
79+
},
80+
[COMMANDS.RECEIVE_ACTION]: (action: string | CR.Action) => {
81+
console.log('onReceiveAction', action)
82+
machine.onReceive(action)
83+
}
84+
})

src/editor/commands/quit.ts

-5
This file was deleted.

src/editor/commands/start.ts

-45
This file was deleted.

0 commit comments

Comments
 (0)