Skip to content

In progress: Feature/api #28

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

Closed
wants to merge 64 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
9004e35
setup server api
ShMcK Aug 14, 2019
4bd3fad
setup apollo on web
ShMcK Aug 14, 2019
4456261
setup new page query
ShMcK Aug 14, 2019
d0f984b
npm updates
ShMcK Aug 16, 2019
a591a2d
sort-package-json
ShMcK Aug 18, 2019
5a060fd
migrate codegen graphql typings
ShMcK Aug 18, 2019
f3f9765
cleanup routes
ShMcK Aug 18, 2019
2d84fa8
setup apollo decorator
ShMcK Aug 18, 2019
1114d13
new queryTutorials loaded
ShMcK Aug 18, 2019
4b1d714
migrate latestVersion to version
ShMcK Aug 18, 2019
5d38263
remove data from version, schema change
ShMcK Aug 18, 2019
8d4d43f
ckean up new container loading
ShMcK Aug 18, 2019
64837c0
load continue container
ShMcK Aug 18, 2019
b2fcd7a
add error component
ShMcK Aug 18, 2019
0c9b0b9
clenaup level summary progress
ShMcK Aug 18, 2019
f57b367
apollo decorator with local cache
ShMcK Aug 18, 2019
2b16a0b
working LevelStage container
ShMcK Aug 18, 2019
204840f
update level stories & structure
ShMcK Aug 18, 2019
2bc96a1
cleanup gql paths
ShMcK Aug 18, 2019
372658d
add level/stage/step to gql
ShMcK Aug 18, 2019
44613b6
migrate stage page
ShMcK Aug 18, 2019
df11100
fix typing paths for storybook
ShMcK Aug 18, 2019
7bab14b
cleanup Stage story
ShMcK Aug 18, 2019
c69b868
working stage container
ShMcK Aug 18, 2019
00b3550
cleanup storybook summary
ShMcK Aug 18, 2019
223019e
cleanup step stories
ShMcK Aug 18, 2019
d0d2f07
restructure single use components into containers
ShMcK Aug 18, 2019
b022179
remove old web api
ShMcK Aug 19, 2019
24eb6a6
absolute paths not allowed
ShMcK Aug 19, 2019
be46fad
create setStatus mutation
ShMcK Aug 19, 2019
26ae887
resolve build issues
ShMcK Aug 19, 2019
c39666a
update deps
ShMcK Aug 20, 2019
6c73657
fix react webview script issue
ShMcK Aug 22, 2019
36af369
create script for styles
ShMcK Aug 22, 2019
a058eff
update deps
ShMcK Aug 22, 2019
8c72738
cleanup logs
ShMcK Aug 22, 2019
d784c57
fix use effects
ShMcK Aug 22, 2019
f4e9877
fix no route unclear object
ShMcK Aug 22, 2019
6a13fcd
update api url for src
ShMcK Aug 22, 2019
39af55d
cleanup apollo server
ShMcK Aug 22, 2019
8740c1d
migrate to graphql-request on server
ShMcK Aug 23, 2019
5776877
tutorial command progress
ShMcK Aug 23, 2019
47224c4
update deps
ShMcK Aug 25, 2019
7bbacd6
update typings
ShMcK Aug 25, 2019
0dd047f
create Tutorial model
ShMcK Aug 25, 2019
97b878a
initialize state machine with tutorial model
ShMcK Aug 25, 2019
57675e5
tutorialModel progress
ShMcK Aug 25, 2019
a92d52f
heavy refactoring
ShMcK Aug 25, 2019
46d1e27
fix launch errors
ShMcK Aug 25, 2019
98c9cc9
remove unusued loading tutorial
ShMcK Aug 25, 2019
b358130
fix send data to client
ShMcK Aug 25, 2019
b6ec3e8
cleanup env vars & fix request
ShMcK Aug 25, 2019
bf0ad9b
clenaup typings
ShMcK Aug 25, 2019
0ad9d2c
add additional logging
ShMcK Aug 26, 2019
f462b39
heavy refactor
ShMcK Aug 26, 2019
1253177
Refactor editor dispatch uses
ShMcK Sep 1, 2019
c32d670
add jest
ShMcK Sep 1, 2019
3886921
fix stateToString function
ShMcK Sep 1, 2019
63f28db
fix jest mismatch build errors
ShMcK Sep 1, 2019
0d50ab6
update deps
ShMcK Sep 1, 2019
44cf7d7
cleanup client state
ShMcK Sep 1, 2019
957d05b
rename select to start
ShMcK Sep 1, 2019
0133ab8
migrate git into tutorial model
ShMcK Sep 1, 2019
50cad58
setup initial start/loaded actions
ShMcK Sep 1, 2019
File filter

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
tutorialModel progress
  • Loading branch information
ShMcK committed Aug 25, 2019
commit 57675e5a964ecf886d4e740ca7f1d39a79a8fc32
24 changes: 10 additions & 14 deletions src/editor/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@ const COMMANDS = {
}

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

const resetLayout = () => {
Expand All @@ -36,7 +34,7 @@ const resetLayout = () => {
})
}

export const createCommands = ({context, machine, storage, git, position}: CreateCommandProps) => {
export const createCommands = ({vscodeExt, machine, git}: CreateCommandProps) => {
// React panel webview
let webview: any

Expand All @@ -55,10 +53,10 @@ export const createCommands = ({context, machine, storage, git, position}: Creat
webviewState = 'RESTARTING'
}

setStorage(context.workspaceState)
setStorage(vscodeExt.workspaceState)

// activate machine
webview = new ReactWebView(context.extensionPath)
webview = new ReactWebView(vscodeExt.extensionPath)
if (webviewState === 'INITIALIZING') {
machine.activate()
} else if (webviewState === 'RESTARTING') {
Expand All @@ -80,32 +78,30 @@ export const createCommands = ({context, machine, storage, git, position}: Creat
},
// launch a new tutorial
// NOTE: may be better to move into action as logic is primarily non-vscode
[COMMANDS.TUTORIAL_LAUNCH]: async (tutorial: G.Tutorial) => {
[COMMANDS.TUTORIAL_LAUNCH]: async (repo: G.TutorialRepo) => {
console.log('launch tutorial')

await isEmptyWorkspace()

await git.gitInitIfNotExists()

if (!tutorial.repo || !tutorial.repo.uri) {
if (!repo || !repo.uri) {
throw new Error('Tutorial repo uri not found')
}

// TODO: use actual tutorial repo
await Promise.all([git.gitSetupRemote(tutorial.repo.uri), storage.setTutorial(tutorial), storage.resetProgress()])
await git.gitSetupRemote(repo.uri)

machine.send('TUTORIAL_LOADED')
},
[COMMANDS.TUTORIAL_SETUP]: async (tutorial: G.Tutorial) => {
[COMMANDS.TUTORIAL_SETUP]: async (codingLanguage: G.EnumCodingLanguage) => {

// TODO: allow multiple coding languages in a tutorial
console.log('tutorial setup', tutorial)

// setup onSave hook
const languageId = tutorial.codingLanguage
// console.log(`languageIds: ${languageIds.join(', ')}`)
vscode.workspace.onDidSaveTextDocument((document: vscode.TextDocument) => {
console.log('save document', document)
if (document.uri.scheme === 'file' && languageId === document.languageId) {
if (document.uri.scheme === 'file' && codingLanguage === document.languageId) {
// do work
machine.send('TEST_RUN')
}
Expand Down
41 changes: 26 additions & 15 deletions src/editor/commands/loadSolution.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
import * as CR from 'typings'
import * as G from 'typings/graphql'
import * as storage from '../../services/storage'
import { gitLoadCommits, gitClear } from '../../services/git'
import {gitLoadCommits, gitClear} from '../../services/git'

export default async function loadSolution(dispatch: CR.EditorDispatch): Promise<void> {
const [position, tutorial]: [CR.Position, CR.Tutorial | undefined] = await Promise.all([
storage.getPosition(),
storage.getTutorial(),
])
if (!position) {
throw new Error('No tutorial position state found')
}
if (!tutorial) {
throw new Error('Local tutorial not found')
}
// eslint-disable-next-line
const { solution } = tutorial.data.steps[position.stepId].actions
const [position, tutorial]: [CR.Position, G.Tutorial | undefined] = await Promise.all([
storage.getPosition(),
storage.getTutorial(),
])
if (!position) {
throw new Error('No tutorial position state found')
}
if (!tutorial || !tutorial.version || !tutorial.version.levels) {
throw new Error('Local tutorial not found')
}
// eslint-disable-next-line

try {
const solution = tutorial.version
.levels.find((l: G.Level) => l.id === position.levelId)
.stages.find((s: G.Stage) => s.id === position.stageId)
.steps.find((s: G.Step) => s.id === position.stepId)
.solution

await gitClear()
await gitLoadCommits(solution, dispatch)
} catch (error) {
throw new Error(error)
}

await gitClear()
await gitLoadCommits(solution, dispatch)
}
100 changes: 49 additions & 51 deletions src/editor/index.ts
Original file line number Diff line number Diff line change
@@ -1,69 +1,67 @@
import * as vscode from 'vscode'
import * as CR from 'typings'
import { createCommands } from './commands'
import {createCommands} from './commands'
import * as storage from '../services/storage'
import * as git from '../services/git'
import * as position from '../services/position'

interface Props {
machine: CR.StateMachine
setWorkspaceRoot(rootPath: string): void
machine: CR.StateMachine
setWorkspaceRoot(rootPath: string): void
}

class Editor {
// extension context set on activation
// @ts-ignore
private context: vscode.ExtensionContext
private machine: CR.StateMachine
// extension context set on activation
// @ts-ignore
private vscodeExt: vscode.ExtensionContext
private machine: CR.StateMachine

constructor({ machine, setWorkspaceRoot }: Props) {
this.machine = machine
constructor({machine, setWorkspaceRoot}: Props) {
this.machine = machine

// set workspace root for node executions
const { workspace } = vscode
const { rootPath } = workspace
if (!rootPath) {
throw new Error('Requires a workspace. Please open a folder')
}
setWorkspaceRoot(rootPath)
}
public activate = (context: vscode.ExtensionContext): void => {
console.log('ACTIVATE!')
this.context = context
// commands
this.activateCommands()
// set workspace root for node executions
const {workspace} = vscode
const {rootPath} = workspace
if (!rootPath) {
throw new Error('Requires a workspace. Please open a folder')
}
setWorkspaceRoot(rootPath)
}
public activate = (vscodeExt: vscode.ExtensionContext): void => {
console.log('ACTIVATE!')
this.vscodeExt = vscodeExt
// commands
this.activateCommands()

// setup tasks or views here
}
public deactivate = (): void => {
console.log('DEACTIVATE!')
// cleanup subscriptions/tasks
for (const disposable of this.context.subscriptions) {
disposable.dispose()
}
// shut down state machine
console.log('deactivate machine')
this.machine.deactivate()
}
// setup tasks or views here
}
public deactivate = (): void => {
console.log('DEACTIVATE!')
// cleanup subscriptions/tasks
for (const disposable of this.vscodeExt.subscriptions) {
disposable.dispose()
}
// shut down state machine
console.log('deactivate machine')
this.machine.deactivate()
}

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

private activateCommands = (): void => {
const commands = createCommands({
context: this.context,
machine: this.machine,
storage,
git,
position,
})
for (const cmd in commands) {
const command: vscode.Disposable = vscode.commands.registerCommand(cmd, commands[cmd])
this.context.subscriptions.push(command)
}
}
private activateCommands = (): void => {
const commands = createCommands({
vscodeExt: this.vscodeExt,
machine: this.machine,
git,
})
for (const cmd in commands) {
const command: vscode.Disposable = vscode.commands.registerCommand(cmd, commands[cmd])
this.vscodeExt.subscriptions.push(command)
}
}
}

export default Editor
4 changes: 2 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import * as vscode from 'vscode'
import {setWorkspaceRoot} from './services/node'
import Tutorial from './services/tutorial'
import Tutorial, {TutorialModel} from './services/tutorial'
import StateMachine from './state'
import Editor from './editor'

export const tutorial = new Tutorial()
export const tutorial: TutorialModel = new Tutorial()
// state machine that governs application logic
export const machine = new StateMachine({dispatch: vscode.commands.executeCommand, tutorial})

Expand Down
83 changes: 42 additions & 41 deletions src/services/storage.ts
Original file line number Diff line number Diff line change
@@ -1,77 +1,78 @@
import * as CR from 'typings'
import * as G from 'typings/graphql'
import * as storage from '../editor/storage'

// TUTORIAL
const STORE_TUTORIAL = 'coderoad:tutorial'

export async function getTutorial(): Promise<CR.Tutorial | undefined> {
return storage.get<CR.Tutorial>(STORE_TUTORIAL)
export async function getTutorial(): Promise<G.Tutorial | undefined> {
return storage.get<G.Tutorial>(STORE_TUTORIAL)
}

export async function setTutorial(tutorial: CR.Tutorial): Promise<void> {
await storage.update<CR.Tutorial>(STORE_TUTORIAL, tutorial)
export async function setTutorial(tutorial: G.Tutorial): Promise<void> {
await storage.update<G.Tutorial>(STORE_TUTORIAL, tutorial)
}

// POSITION
const STORE_POSITION = 'coderoad:position'

const defaultPosition = { levelId: '', stageId: '', stepId: '' }
const defaultPosition = {levelId: '', stageId: '', stepId: ''}

export async function getPosition(): Promise<CR.Position> {
const position: CR.Position | undefined = storage.get<CR.Position>(STORE_POSITION)
return position || defaultPosition
const position: CR.Position | undefined = storage.get<CR.Position>(STORE_POSITION)
return position || defaultPosition
}

export async function setPosition(position: CR.Position): Promise<void> {
await storage.update<CR.Position>(STORE_POSITION, position)
await storage.update<CR.Position>(STORE_POSITION, position)
}

// PROGRESS
const STORE_PROGRESS = 'coderoad:progress'

const defaultProgress = { levels: {}, stages: {}, steps: {}, hints: {}, complete: false }
const defaultProgress = {levels: {}, stages: {}, steps: {}, hints: {}, complete: false}

export async function getProgress(): Promise<CR.Progress> {
const progress: CR.Progress | undefined = await storage.get<CR.Progress>(STORE_PROGRESS)
return progress || defaultProgress
const progress: CR.Progress | undefined = await storage.get<CR.Progress>(STORE_PROGRESS)
return progress || defaultProgress
}

export async function resetProgress(): Promise<void> {
await storage.update<CR.Progress>(STORE_PROGRESS, defaultProgress)
await storage.update<CR.Progress>(STORE_PROGRESS, defaultProgress)
}

interface ProgressUpdate {
levels?: {
[levelId: string]: boolean
}
stages?: {
[stageid: string]: boolean
}
steps?: {
[stepId: string]: boolean
}
levels?: {
[levelId: string]: boolean
}
stages?: {
[stageid: string]: boolean
}
steps?: {
[stepId: string]: boolean
}
}

export async function setProgress(record: ProgressUpdate): Promise<void> {
const progress = await getProgress()
if (record.levels) {
progress.levels = {
...progress.levels,
...record.levels,
}
}
if (record.stages) {
progress.stages = {
...progress.stages,
...record.stages,
}
}
if (record.steps) {
progress.steps = {
...progress.steps,
...record.steps,
}
}
const progress = await getProgress()
if (record.levels) {
progress.levels = {
...progress.levels,
...record.levels,
}
}
if (record.stages) {
progress.stages = {
...progress.stages,
...record.stages,
}
}
if (record.steps) {
progress.steps = {
...progress.steps,
...record.steps,
}
}

await storage.update<CR.Progress>(STORE_PROGRESS, progress)
await storage.update<CR.Progress>(STORE_PROGRESS, progress)
}
Loading