Skip to content

Feature/tutorial setup #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jun 9, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
support passing of messages to view
  • Loading branch information
ShMcK committed Jun 9, 2019
commit b50e2c095bb37b21d05d0bdd3058e71fbe401c3a
93 changes: 47 additions & 46 deletions src/editor/ReactWebView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,10 @@ import * as vscode from 'vscode'
import * as CR from 'typings'
import * as path from 'path'

function getNonce(): string {
let text = ''
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
for (let i = 0; i < 32; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length))
}
return text
}

// TODO: move column into createOrShow


/**
* Manages React webview panels
*/
class ReactWebView {
/**
* Track the currently panel. Only allow a single panel to exist at a time.
*/
public static currentPanel: ReactWebView | undefined = undefined

// @ts-ignore
private panel: vscode.WebviewPanel
private extensionPath: string
Expand All @@ -32,53 +15,71 @@ class ReactWebView {
public constructor(extensionPath: string, onReceive: any) {
this.extensionPath = extensionPath
this.onReceive = onReceive

// Create and show a new webview panel
this.panel = this.createWebviewPanel(vscode.ViewColumn.One)

// Set the webview's initial html content
this.panel.webview.html = this.getHtmlForWebview()

// Listen for when the panel is disposed
// This happens when the user closes the panel or when the panel is closed programatically
this.panel.onDidDispose(() => this.dispose(), null, this.disposables)

// Handle messages from the webview
this.panel.webview.onDidReceiveMessage(this.onReceive, null, this.disposables)
console.log('webview loaded')
}

public async createOrShow(column: number = vscode.ViewColumn.One): Promise<void> {
public async createOrShow(column: number): Promise<void> {
// If we already have a panel, show it.
// Otherwise, create a new panel.
if (ReactWebView.currentPanel && ReactWebView.currentPanel.panel) {
ReactWebView.currentPanel.panel.reveal(column)
if (this.panel && this.panel.webview) {
console.log('reveal')
this.panel.reveal(column)
} else {
const viewType = 'CodeRoad'
const title = 'CodeRoad'
const config = {
// Enable javascript in the webview
enableScripts: true,

// And restric the webview to only loading content from our extension's `media` directory.
localResourceRoots: [vscode.Uri.file(path.join(this.extensionPath, 'build'))],
console.log('make new panel')
this.panel = this.createWebviewPanel(column)

// prevents destroying the window when it is in the background
retainContextWhenHidden: true,
}
// Create and show a new webview panel
this.panel = vscode.window.createWebviewPanel(viewType, title, column, config)

// Set the webview's initial html content
this.panel.webview.html = this.getHtmlForWebview()
}
}

// Listen for when the panel is disposed
// This happens when the user closes the panel or when the panel is closed programatically
this.panel.onDidDispose(() => this.dispose(), null, this.disposables)
private createWebviewPanel(column: number): vscode.WebviewPanel {
const viewType = 'CodeRoad'
const title = 'CodeRoad'
const config = {
// Enable javascript in the webview
enableScripts: true,
// And restric the webview to only loading content from our extension's `media` directory.
localResourceRoots: [vscode.Uri.file(path.join(this.extensionPath, 'build'))],
// prevents destroying the window when it is in the background
retainContextWhenHidden: true,
}
return vscode.window.createWebviewPanel(viewType, title, column, config)
}

// Handle messages from the webview
this.panel.webview.onDidReceiveMessage(this.onReceive, null, this.disposables)
private getNonce(): string {
let text = ''
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
for (let i = 0; i < 32; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length))
}
return text
}

public async postMessage(action: CR.Action): Promise<void> {
console.log('webview postMessage')
console.log(action)
// Send a message to the webview webview.
// You can send any JSON serializable data.
const success = await this.panel.webview.postMessage(action)
if (!success) {
throw new Error(`Message post failure: ${JSON.stringify(action)}`)
}
console.log('postMessage sent')
}

public dispose(): void {
ReactWebView.currentPanel = undefined

// Clean up our resources
this.panel.dispose()

Expand Down Expand Up @@ -108,9 +109,9 @@ class ReactWebView {
const styleUri = stylePathOnDisk.with({ scheme: 'vscode-resource' })

// Use a nonce to whitelist which scripts can be run
const nonce = getNonce()
const nonce2 = getNonce()
const nonce3 = getNonce()
const nonce = this.getNonce()
const nonce2 = this.getNonce()
const nonce3 = this.getNonce()

return `<!DOCTYPE html>
<html lang="en">
Expand Down
53 changes: 49 additions & 4 deletions src/editor/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,65 @@ import * as CR from 'typings'

const COMMANDS = {
START: 'coderoad.start',
NEW_OR_CONTINUE: 'coderoad.new_or_continue',
OPEN_WEBVIEW: 'coderoad.open_webview',
SEND_STATE: 'coderoad.send_state',
OPEN_FILE: 'coderoad.open_file',
RUN_TEST: 'coderoad.test_run',
}

interface CreateCommandProps {
context: vscode.ExtensionContext,
machine: CR.StateMachine
machine: CR.StateMachine,
storage: any,
git: any
}

// React panel webview
let webview: any;
let initialTutorial: CR.Tutorial | undefined
let initialProgress: CR.Progress = {
levels: {},
stages: {},
steps: {},
complete: false,
}

export const createCommands = ({ context, machine }: CreateCommandProps) => ({
export const createCommands = ({ context, machine, storage, git }: CreateCommandProps) => ({
// initialize
[COMMANDS.START]: () => {
// set local storage workspace
setStorage(context.workspaceState)

// activate machine
webview = new ReactWebView(context.extensionPath, machine.onReceive)
console.log('webview', webview.panel.webview.postMessage)
machine.activate()
},
[COMMANDS.OPEN_WEBVIEW]: () => {
webview.createOrShow();
[COMMANDS.NEW_OR_CONTINUE]: async () => {
// verify that the user has a tutorial & progress
// verify git is setup with a coderoad remote
const [tutorial, progress, hasGit, hasGitRemote] = await Promise.all([
storage.getTutorial(),
storage.getProgress(),
git.gitVersion(),
git.gitCheckRemoteExists(),
])
initialTutorial = tutorial
initialProgress = progress
const canContinue = !!(tutorial && progress && hasGit && hasGitRemote)
console.log('canContinue', canContinue)
// if a tutorial exists, "CONTINUE"
// otherwise start from "NEW"
machine.send(canContinue ? 'CONTINUE' : 'NEW')
},
// open React webview
[COMMANDS.OPEN_WEBVIEW]: (column: number = vscode.ViewColumn.One) => {
webview.createOrShow(column);
},
// open a file
[COMMANDS.OPEN_FILE]: async (relativeFilePath: string) => {
console.log(`OPEN_FILE ${JSON.stringify(relativeFilePath)}`)
try {
const workspaceRoot = vscode.workspace.rootPath
if (!workspaceRoot) {
Expand All @@ -43,5 +76,17 @@ export const createCommands = ({ context, machine }: CreateCommandProps) => ({
} catch (error) {
console.log(`Failed to open file ${relativeFilePath}`, error)
}
},
// send messages to webview
[COMMANDS.SEND_STATE]: (action: CR.Action) => {
console.log(`SEND ${JSON.stringify(action)}`)
console.log('webview')
console.log(webview)
// console.log(webview.currentPanel)
// if (!webview || !webview.currentPanel) {
// throw new Error('No valid panel available')
// }
webview.postMessage(action)

}
})
45 changes: 0 additions & 45 deletions src/editor/commands/start.ts

This file was deleted.

9 changes: 9 additions & 0 deletions src/editor/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as vscode from 'vscode'
import * as CR from 'typings'
import { createCommands } from './commands'
import * as storage from '../services/storage'
import * as git from '../services/git'

interface Props {
machine: CR.StateMachine,
Expand Down Expand Up @@ -29,6 +31,8 @@ class Editor {
const commands = createCommands({
context: this.context,
machine: this.machine,
storage,
git,
})
for (const cmd in commands) {
const command: vscode.Disposable = vscode.commands.registerCommand(cmd, commands[cmd])
Expand All @@ -53,6 +57,11 @@ class Editor {
// shut down state machine
this.machine.deactivate()
}

// execute vscode command
public dispatch = (type: string, payload: any) => {
vscode.commands.executeCommand(type, payload)
}
}

export default Editor
1 change: 1 addition & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { setWorkspaceRoot } from './services/node'
import StateMachine from './state'
import Editor from './editor'


// state machine that governs application logic
export const machine = new StateMachine()

Expand Down
2 changes: 1 addition & 1 deletion src/services/git/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export async function gitLoadCommits(actions: CR.TutorialAction): Promise<void>

if (files) {
for (const filePath of files) {
vscode.commands.executeCommand('coderoad.open_webview', filePath)
vscode.commands.executeCommand('coderoad.open_file', filePath)
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/state/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,8 @@ export default {
createWebview() {
console.log('execute coderoad.open_webview')
vscode.commands.executeCommand('coderoad.open_webview')
},
newOrContinue() {
vscode.commands.executeCommand('coderoad.new_or_continue')
}
}
10 changes: 9 additions & 1 deletion src/state/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { interpret, Interpreter } from 'xstate'
import * as CR from '../typings'
import * as CR from 'typings'
import machine from './machine'
import * as vscode from 'vscode'

// machine interpreter
// https://xstate.js.org/docs/guides/interpretation.html
Expand All @@ -17,9 +18,11 @@ class StateMachine {
this.service = interpret(machine, this.machineOptions)
// logging
.onTransition(state => {
console.log('onTransition', state.changed)
if (state.changed) {
console.log('next state')
console.log(state.value)
vscode.commands.executeCommand('coderoad.send_state', state.value)
}
})
}
Expand All @@ -30,6 +33,11 @@ class StateMachine {
deactivate() {
this.service.stop()
}
send(action: string | CR.Action) {
console.log('machine.send')
console.log(action)
this.service.send(action)
}
onReceive(action: CR.Action) {
console.log('RECEIVED ACTION')
console.log(action)
Expand Down
8 changes: 7 additions & 1 deletion src/state/machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,16 @@ export const machine = Machine<
initial: 'SelectTutorial',
states: {
SelectTutorial: {
onEntry: ['createWebview'],
initial: 'Initial',
states: {
Initial: {
onEntry: ['createWebview'],
after: {
1000: 'Startup'
}
},
Startup: {
onEntry: ['newOrContinue'],
on: {
CONTINUE: 'ContinueTutorial',
NEW: 'NewTutorial',
Expand Down
Loading