- Notifications
You must be signed in to change notification settings - Fork39
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
17a71b7
1b99222
90e5e97
e49165c
0f992bf
f174afa
ecac58b
33d854c
a54f1d5
8bea508
83820ab
04d3eca
b1eb6b1
d1d5384
1a9ccf9
6217ea6
e7b084d
229c6b8
bab2e54
7bada36
22451c7
6f8bee2
13a4499
ccd088b
30dd21c
14d4f56
4bb125d
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
node_modules | ||
.vscode-test/ | ||
*.vsix | ||
.DS_Store | ||
# local | ||
.env | ||
Large diffs are not rendered by default.
Uh oh!
There was an error while loading.Please reload this page.
This file was deleted.
Uh oh!
There was an error while loading.Please reload this page.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import * as git from '../services/git' | ||
async function saveCommit() { | ||
console.log('committing progress') | ||
git.saveCommit('Save progress') | ||
} | ||
export default saveCommit |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import * as CR from 'typings' | ||
import * as G from 'typings/graphql' | ||
import * as vscode from 'vscode' | ||
import Position from './state/Position' | ||
import Progress from './state/Progress' | ||
import Tutorial from './state/Tutorial' | ||
class Context { | ||
public tutorial: Tutorial | ||
public position: Position | ||
public progress: Progress | ||
constructor(workspaceState: vscode.Memento) { | ||
// state held in one place | ||
this.tutorial = new Tutorial(workspaceState) | ||
this.position = new Position() | ||
this.progress = new Progress() | ||
} | ||
public setTutorial = async (workspaceState: vscode.Memento, tutorial: G.Tutorial): Promise<{progress: CR.Progress, position: CR.Position}> => { | ||
this.tutorial.set(tutorial) | ||
const progress: CR.Progress = await this.progress.setTutorial(workspaceState, tutorial) | ||
const position: CR.Position = this.position.setPositionFromProgress(tutorial, progress) | ||
return {progress, position} | ||
} | ||
} | ||
export default Context |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import * as CR from 'typings' | ||
import * as G from 'typings/graphql' | ||
import * as vscode from 'vscode' | ||
import Context from './context' | ||
import tutorialConfig from '../actions/tutorialConfig' | ||
import setupActions from '../actions/setupActions' | ||
import solutionActions from '../actions/solutionActions' | ||
import saveCommit from '../actions/saveCommit' | ||
interface Channel { | ||
receive(action: CR.Action): Promise<void> | ||
send(action: CR.Action): Promise<void> | ||
} | ||
interface ChannelProps { | ||
postMessage: (action: CR.Action) => Thenable<boolean> | ||
workspaceState: vscode.Memento | ||
} | ||
class Channel implements Channel { | ||
private postMessage: (action: CR.Action) => Thenable<boolean> | ||
private workspaceState: vscode.Memento | ||
private context: Context | ||
constructor({postMessage, workspaceState}: ChannelProps) { | ||
// workspaceState used for local storage | ||
this.workspaceState = workspaceState | ||
this.postMessage = postMessage | ||
this.context = new Context(workspaceState) | ||
} | ||
// receive from webview | ||
public receive = async (action: CR.Action) => { | ||
// action may be an object.type or plain string | ||
const actionType: string = typeof action === 'string' ? action : action.type | ||
console.log('EDITOR RECEIVED:', actionType) | ||
switch (actionType) { | ||
// continue from tutorial from local storage | ||
case 'EDITOR_TUTORIAL_LOAD': | ||
const tutorial: G.Tutorial | null = this.context.tutorial.get() | ||
// new tutorial | ||
if (!tutorial || !tutorial.id || !tutorial.version) { | ||
this.send({type: 'NEW_TUTORIAL'}) | ||
return | ||
} | ||
// set tutorial | ||
const {position, progress} = await this.context.setTutorial(this.workspaceState, tutorial) | ||
if (progress.complete) { | ||
// tutorial is already complete | ||
this.send({type: 'NEW_TUTORIAL'}) | ||
return | ||
} | ||
// communicate to client the tutorial & stepProgress state | ||
this.send({type: 'CONTINUE_TUTORIAL', payload: {tutorial, progress, position}}) | ||
return | ||
// clear tutorial local storage | ||
case 'TUTORIAL_CLEAR': | ||
// clear current progress/position/tutorial | ||
this.context = new Context(this.workspaceState) | ||
return | ||
// configure test runner, language, git | ||
case 'EDITOR_TUTORIAL_CONFIG': | ||
const tutorialData = action.payload.tutorial | ||
this.context.setTutorial(this.workspaceState, tutorialData) | ||
tutorialConfig(tutorialData) | ||
return | ||
case 'EDITOR_SYNC_PROGRESS': | ||
// sync client progress on server | ||
this.context.position.set(action.payload.position) | ||
this.context.progress.set(action.payload.progress) | ||
return | ||
// run unit tests on step | ||
case 'TEST_RUN': | ||
vscode.commands.executeCommand('coderoad.run_test', action.payload) | ||
return | ||
// load step actions (git commits, commands, open files) | ||
case 'SETUP_ACTIONS': | ||
vscode.commands.executeCommand('coderoad.set_current_step', action.payload) | ||
setupActions(action.payload) | ||
return | ||
// load solution step actions (git commits, commands, open files) | ||
case 'SOLUTION_ACTIONS': | ||
solutionActions(action.payload) | ||
return | ||
default: | ||
console.log(`No match for action type: ${actionType}`) | ||
return | ||
} | ||
} | ||
// send to webview | ||
public send = async (action: CR.Action) => { | ||
switch (action.type) { | ||
case 'TEST_PASS': | ||
// update local storage stepProgress | ||
const progress = this.context.progress.setStepComplete(action.payload.stepId) | ||
const tutorial = this.context.tutorial.get() | ||
if (!tutorial) { | ||
throw new Error('Error with current tutorial') | ||
} | ||
this.context.position.setPositionFromProgress(tutorial, progress) | ||
saveCommit() | ||
} | ||
const success = await this.postMessage(action) | ||
if (!success) { | ||
throw new Error(`Message post failure: ${JSON.stringify(action)}`) | ||
} | ||
} | ||
} | ||
export default Channel | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import * as CR from 'typings' | ||
import * as G from 'typings/graphql' | ||
// position | ||
class Position { | ||
private value: CR.Position | ||
constructor() { | ||
this.value = { | ||
levelId: '', | ||
stageId: '', | ||
stepId: '', | ||
} | ||
} | ||
public get = () => { | ||
return this.value | ||
} | ||
public set = (value: CR.Position) => { | ||
this.value = value | ||
} | ||
// calculate the current position based on the saved progress | ||
public setPositionFromProgress = (tutorial: G.Tutorial, progress: CR.Progress): CR.Position => { | ||
// tutorial already completed | ||
// TODO: handle start again? | ||
if (progress.complete) { | ||
return this.value | ||
} | ||
const {levels} = tutorial.version | ||
const lastLevelIndex: number | undefined = levels.findIndex((l: G.Level) => progress.levels[l.id]) | ||
// TODO: consider all levels complete as progress.complete | ||
if (lastLevelIndex >= levels.length) { | ||
throw new Error('Error setting progress level') | ||
} | ||
const currentLevel: G.Level = levels[lastLevelIndex + 1] | ||
const {stages} = currentLevel | ||
const lastStageIndex: number | undefined = stages.findIndex((s: G.Stage) => progress.stages[s.id]) | ||
if (lastStageIndex >= stages.length) { | ||
throw new Error('Error setting progress stage') | ||
} | ||
const currentStage: G.Stage = stages[lastStageIndex + 1] | ||
const {steps} = currentStage | ||
const lastStepIndex: number | undefined = steps.findIndex((s: G.Step) => progress.steps[s.id]) | ||
if (lastStepIndex >= steps.length) { | ||
throw new Error('Error setting progress step') | ||
} | ||
const currentStep: G.Step = steps[lastStepIndex + 1] | ||
this.value = { | ||
levelId: currentLevel.id, | ||
stageId: currentStage.id, | ||
stepId: currentStep.id, | ||
} | ||
return this.value | ||
} | ||
} | ||
export default Position |
Uh oh!
There was an error while loading.Please reload this page.