Skip to content

Continue progress #32

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 27 commits into from
Sep 23, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
17a71b7
setup storage on server
ShMcK Sep 9, 2019
1b99222
continue tutorial progress
ShMcK Sep 9, 2019
90e5e97
store tutorial id/version on server
ShMcK Sep 9, 2019
e49165c
manage storage in editor Channel
ShMcK Sep 14, 2019
0f992bf
setup storage in editor channel
ShMcK Sep 15, 2019
f174afa
load progress on server and send to client
ShMcK Sep 15, 2019
ecac58b
add continue option to start new
ShMcK Sep 15, 2019
33d854c
load stage (not 100%)
ShMcK Sep 15, 2019
a54f1d5
save commit on successful test
ShMcK Sep 15, 2019
8bea508
setup editor position & progress state
ShMcK Sep 15, 2019
83820ab
cleanup deps
ShMcK Sep 18, 2019
04d3eca
update deps
ShMcK Sep 21, 2019
b1eb6b1
refactor context
ShMcK Sep 21, 2019
d1d5384
setup position/progress/tutorial context
ShMcK Sep 21, 2019
1a9ccf9
refactor setupActions
ShMcK Sep 21, 2019
6217ea6
align channel with context
ShMcK Sep 21, 2019
e7b084d
fix build process
ShMcK Sep 21, 2019
229c6b8
parse webview from jsdom
ShMcK Sep 21, 2019
bab2e54
refactor webview render
ShMcK Sep 21, 2019
7bada36
fix webview after webpack build breakage
ShMcK Sep 22, 2019
22451c7
fix react web view for latest webpack
ShMcK Sep 22, 2019
6f8bee2
add extensive comments to react webview
ShMcK Sep 22, 2019
13a4499
clear up CSP issue with GQL
ShMcK Sep 22, 2019
ccd088b
fix loading tutorial position
ShMcK Sep 22, 2019
30dd21c
fix issues with tutorial init
ShMcK Sep 23, 2019
14d4f56
setup editor sync progress (uninitiated)
ShMcK Sep 23, 2019
4bb125d
clear tutorial on new
ShMcK Sep 23, 2019
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
continue tutorial progress
  • Loading branch information
ShMcK committed Sep 9, 2019
commit 1b9922260373a268cd8c2bd5c551cae08fbe11b4
35 changes: 27 additions & 8 deletions src/Channel.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as CR from 'typings'
import * as vscode from 'vscode'
import * as storage from './services/storage'

import tutorialConfig from './actions/tutorialConfig'
import setupActions from './actions/setupActions'
Expand All @@ -11,10 +10,20 @@ interface Channel {
send(action: CR.Action): Promise<void>
}

interface ChannelProps {
postMessage: (action: CR.Action) => Thenable<boolean>
storage: {
tutorial: any
stepProgress: any
}
}

class Channel implements Channel {
private postMessage: (action: CR.Action) => Thenable<boolean>
constructor(postMessage: (action: CR.Action) => Thenable<boolean>) {
private storage: any
constructor({postMessage, storage}: ChannelProps) {
this.postMessage = postMessage
this.storage = storage
}

// receive from webview
Expand All @@ -24,19 +33,29 @@ class Channel implements Channel {
switch (actionType) {
// continue from tutorial from local storage
case 'TUTORIAL_LOAD_STORED':
const tutorial = storage.tutorial.get()
const stepProgress = storage.stepProgress.get()
this.send({type: 'TUTORIAL_LOADED', payload: {tutorial, stepProgress}})
const tutorial = this.storage.tutorial.get()
const stepProgress = this.storage.stepProgress.get()

console.log('looking at stored')
console.log(JSON.stringify(tutorial))
console.log(JSON.stringify(stepProgress))

if (tutorial && tutorial.id) {
this.send({type: 'CONTINUE_TUTORIAL', payload: {tutorial, stepProgress}})
} else {
this.send({type: 'NEW_TUTORIAL'})
}

return
// clear tutorial local storage
case 'TUTORIAL_CLEAR':
storage.tutorial.set(null)
storage.stepProgress.set({})
this.storage.tutorial.set(null)
this.storage.stepProgress.set({})
return
// configure test runner, language, git
case 'TUTORIAL_CONFIG':
tutorialConfig(action.payload)
storage.tutorial.set(action.payload)
this.storage.tutorial.set(action.payload)
return
// run unit tests on step
case 'TEST_RUN':
Expand Down
17 changes: 14 additions & 3 deletions src/editor/ReactWebView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ const getNonce = (): string => {
return text
}

interface ReactWebViewProps {
extensionPath: string
storage: {
tutorial: any
stepProgress: any
}
}


// Manages webview panel
class ReactWebView {
Expand All @@ -24,7 +32,7 @@ class ReactWebView {
private disposables: vscode.Disposable[] = []
private channel: Channel

public constructor(extensionPath: string) {
public constructor({extensionPath, storage}: ReactWebViewProps) {
this.extensionPath = extensionPath

// Create and show a new webview panel
Expand All @@ -37,8 +45,11 @@ class ReactWebView {
// This happens when the user closes the panel or when the panel is closed programmatically
this.panel.onDidDispose(this.dispose, this, this.disposables)

this.channel = new Channel((action: Action): Thenable<boolean> => {
return this.panel.webview.postMessage(action)
this.channel = new Channel({
storage,
postMessage: (action: Action): Thenable<boolean> => {
return this.panel.webview.postMessage(action)
}
})
// Handle messages from the webview
const receive = this.channel.receive
Expand Down
25 changes: 22 additions & 3 deletions src/editor/commands.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as G from 'typings/graphql'
import * as vscode from 'vscode'
import * as storage from '../services/storage'
import Storage from '../services/storage'
import ReactWebView from './ReactWebView'
import runTest from '../actions/runTest'
import {isEmptyWorkspace} from './workspace'
Expand All @@ -19,6 +20,18 @@ export const createCommands = ({vscodeExt}: CreateCommandProps) => {
// React panel webview
let webview: any
let currentStepId = ''

const tutorial = new Storage<G.Tutorial | null>({
key: 'coderoad:tutorial',
storage: vscodeExt.workspaceState
})

const stepProgress = new Storage<{[stepId: string]: boolean}>({
key: 'coderoad:progress',
storage: vscodeExt.workspaceState
})


return {
// initialize
[COMMANDS.START]: async () => {
Expand All @@ -39,7 +52,13 @@ export const createCommands = ({vscodeExt}: CreateCommandProps) => {
}

// activate machine
webview = new ReactWebView(vscodeExt.extensionPath)
webview = new ReactWebView({
extensionPath: vscodeExt.extensionPath,
storage: {
tutorial,
stepProgress
}
})
},
// open React webview
[COMMANDS.OPEN_WEBVIEW]: (column: number = vscode.ViewColumn.Two) => {
Expand Down Expand Up @@ -68,7 +87,7 @@ export const createCommands = ({vscodeExt}: CreateCommandProps) => {
// send test pass message back to client
webview.send({type: 'TEST_PASS', payload})
// update local storage stepProgress
storage.stepProgress.update({
stepProgress.update({
[payload.stepId]: true
})
vscode.window.showInformationMessage('PASS')
Expand Down
1 change: 1 addition & 0 deletions src/editor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class Editor {
public activate = (vscodeExt: vscode.ExtensionContext): void => {
console.log('ACTIVATE!')
this.vscodeExt = vscodeExt

// commands
this.activateCommands()

Expand Down
28 changes: 11 additions & 17 deletions src/services/storage/index.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
import * as G from 'typings/graphql'
import * as vscode from 'vscode'

// localStorage not available on client
// NOTE: localStorage not available on client
// must be stored in editor
// https://github.com/Microsoft/vscode/issues/52246

// storage is unfortunately bound to the vscode extension context
// forcing it to be passed in through activation and down to other tools
class Storage<T> {
private key: string
// TODO: replace somehow with localStorage
// window.localStorage not working inside of vscode
private storage = localStorage
constructor({key, value}: {key: string, value: T}) {
private storage: vscode.Memento
constructor({key, storage}: {key: string, storage: vscode.Memento}) {
this.storage = storage
this.key = key
this.set(value)
}
public get = async (): Promise<T | null> => {
const value = await this.storage.getItem(this.key)
const value: string | undefined = await this.storage.get(this.key)
if (value) {
return JSON.parse(value)
}
return null
}
public set = (value: T): void => {
const stringValue = JSON.stringify(value)
this.storage.setItem(this.key, stringValue)
this.storage.update(this.key, stringValue)
}
public update = async (value: T): Promise<void> => {
const current = await this.get()
Expand All @@ -32,11 +33,4 @@ class Storage<T> {
}
}

export const tutorial = new Storage<G.Tutorial | null>({
key: 'coderoad:tutorial',
value: null
})
export const stepProgress = new Storage<{[stepId: string]: boolean}>({
key: 'coderoad:progress',
value: {}
})
export default Storage
2 changes: 1 addition & 1 deletion web-app/src/components/Workspace/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const Workspace = ({ children }: Props) => {
// solution for windows getting off size
React.useEffect(() => {
setDimensions(resize())
}, [window.innerHeight, window.innerHeight])
}, [])

const styles = {
page: {
Expand Down
6 changes: 6 additions & 0 deletions web-app/src/services/channel/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ class Channel {
this.machineSend('TUTORIAL_LOADED')
console.log('send action to state machine')
return
case 'NEW_TUTORIAL':
this.machineSend(action)
return
case 'CONTINUE_TUTORIAL':
this.machineSend(action)
return
case 'TEST_PASS':
// { type: 'TEST_PASS', payload: { stepId: string }}
this.machineSend(action)
Expand Down
74 changes: 53 additions & 21 deletions web-app/src/services/state/actions/context.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,72 @@
import {assign, send} from 'xstate'
import * as G from 'typings/graphql'
import * as CR from 'typings'
import * as storage from '../storage'
import * as selectors from '../../selectors'

export default {
continueTutorial: (context: CR.MachineContext, event: CR.MachineEvent) => {

const {tutorial, stepProgress} = event.data.payload

const progress: CR.Progress = {
steps: stepProgress,
stages: {},
levels: {},
complete: false
}

const position: CR.Position = {
stepId: '',
stageId: '',
levelId: '',
}

// calculate progress from tutorial & stepProgress
for (const level of tutorial.version.levels) {
for (const stage of level.stages) {
// set stage progress
const stageComplete: boolean = stage.steps.every((step: G.Step) => {
return stepProgress[step.id]
})
if (stageComplete) {
progress.stages[stage.id] = true
} else if (!position.stageId.length) {
// set stage amd step position
position.stageId = stage.id
position.stepId = stage.steps.find((step: G.Step) => !stepProgress[step.id]).id
}
}
// set level progress
const levelComplete: boolean = level.stages.every((stage: G.Stage) => {
return progress.stages[stage.id]
})
if (levelComplete) {
progress.levels[level.id] = true
} else if (!position.levelId.length) {
position.levelId = level.id
}
}
// set tutorial progress
progress.complete = tutorial.version.levels.every((level: G.Level) => {
return progress.levels[level.id]
})

return assign({
tutorial,
progress,
position,
})
},
setTutorial: assign({
tutorial: (context: CR.MachineContext, event: CR.MachineEvent): any => {
const {tutorial} = event.payload
storage.tutorial.set(tutorial)
return tutorial
},
}),
continueTutorial: assign({
tutorial: (context: CR.MachineContext, event: CR.MachineEvent) => event.data.payload.tutorial,
progress: (context: CR.MachineContext, event: CR.MachineEvent) => event.data.payload.progress,
position: (context: CR.MachineContext, event: CR.MachineEvent) => event.data.payload.position,
}),
// @ts-ignore
initPosition: assign({
position: (context: CR.MachineContext, event: CR.MachineEvent): CR.Position => {
const position: CR.Position = selectors.initialPosition(event.payload)
storage.position.set(position)
return position
},
}),
Expand Down Expand Up @@ -51,8 +96,6 @@ export default {
stepId: step.id
}

storage.position.set(nextPosition)

return nextPosition
},
}),
Expand All @@ -73,8 +116,6 @@ export default {
stepId: stage.steps[0].id,
}

storage.position.set(nextPosition)

return nextPosition
},
}),
Expand All @@ -96,8 +137,6 @@ export default {
stepId: level.stages[0].steps[0].id,
}

storage.position.set(nextPosition)

return nextPosition
},
}),
Expand All @@ -111,8 +150,6 @@ export default {

currentProgress.steps[stepId] = true

storage.progress.set(currentProgress)

return currentProgress
},
}),
Expand All @@ -126,8 +163,6 @@ export default {

progress.stages[stageId] = true

storage.progress.set(progress)

return progress
},
}),
Expand Down Expand Up @@ -228,17 +263,14 @@ export default {
}),
reset: assign({
tutorial() {
storage.tutorial.set(null)
return null
},
progress(): CR.Progress {
const progress: CR.Progress = selectors.defaultProgress()
storage.progress.set(progress)
return progress
},
position(): CR.Position {
const position: CR.Position = selectors.defaultPosition()
storage.position.set(position)
return position
}
})
Expand Down
5 changes: 5 additions & 0 deletions web-app/src/services/state/actions/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import * as selectors from '../../selectors'
import channel from '../../channel'

export default {
loadStoredTutorial() {
channel.editorSend({
type: 'TUTORIAL_LOAD_STORED',
})
},
initializeTutorial(context: CR.MachineContext, event: CR.MachineEvent) {
// setup test runner and git
const {tutorial} = event.data.payload
Expand Down
Loading