Skip to content

Fix/state machine #97

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 8 commits into from
Feb 1, 2020
Prev Previous commit
Next Next commit
add SetupNewTutorial state
  • Loading branch information
ShMcK committed Jan 31, 2020
commit 6869a22663805b8a4766f6104231e0b4d5ac6e3c
18 changes: 3 additions & 15 deletions typings/graphql.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export type MutationUpdateTutorialProgressArgs = {
}

export type MutationCreateTutorialArgs = {
input: CreateTutorialInput
input: Scalars['String']
}

export type MutationCreateTutorialVersionArgs = {
Expand Down Expand Up @@ -347,14 +347,14 @@ export type ResolversTypes = {
TutorialProgressType: TutorialProgressType
TutorialProgressStatus: TutorialProgressStatus
Boolean: ResolverTypeWrapper<Scalars['Boolean']>
createTutorialInput: CreateTutorialInput
createTutorialVersionInput: CreateTutorialVersionInput
updateTutorialVersionInput: UpdateTutorialVersionInput
JSONObject: ResolverTypeWrapper<Scalars['JSONObject']>
Sha1: ResolverTypeWrapper<Scalars['Sha1']>
Role: Role
FileFormat: FileFormat
tutorialRepoInput: TutorialRepoInput
createTutorialInput: CreateTutorialInput
GithubUser: ResolverTypeWrapper<GithubUser>
}

Expand All @@ -380,14 +380,14 @@ export type ResolversParentTypes = {
TutorialProgressType: TutorialProgressType
TutorialProgressStatus: TutorialProgressStatus
Boolean: Scalars['Boolean']
createTutorialInput: CreateTutorialInput
createTutorialVersionInput: CreateTutorialVersionInput
updateTutorialVersionInput: UpdateTutorialVersionInput
JSONObject: Scalars['JSONObject']
Sha1: Scalars['Sha1']
Role: Role
FileFormat: FileFormat
tutorialRepoInput: TutorialRepoInput
createTutorialInput: CreateTutorialInput
GithubUser: GithubUser
}

Expand Down Expand Up @@ -601,15 +601,3 @@ export type DirectiveResolvers<ContextType = any> = {
* Use "DirectiveResolvers" root object instead. If you wish to get "IDirectiveResolvers", add "typesPrefix: I" to your config.
*/
export type IDirectiveResolvers<ContextType = any> = DirectiveResolvers<ContextType>

export interface IntrospectionResultData {
__schema: {
types: {
kind: string
name: string
possibleTypes: {
name: string
}[]
}[]
}
}
4 changes: 3 additions & 1 deletion typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@ export interface MachineStateSchema {
Error: {}
NewOrContinue: {}
SelectTutorial: {}
LoadTutorial: {}
LoadTutorialSummary: {}
Summary: {}
LoadTutorialData: {}
SetupNewTutorial: {}
ContinueTutorial: {}
}
}
Expand Down
8 changes: 5 additions & 3 deletions web-app/src/Routes.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as React from 'react'
import * as CR from 'typings'
import useRouter from './components/Router'
import Workspace from './components/Workspace'
import ContinuePage from './containers/Continue'
Expand All @@ -21,8 +20,8 @@ const Routes = () => {
<Route path="Start.ContinueTutorial">
<ContinuePage send={send} context={context} />
</Route>
<Route path="Start.Initialize">
<LoadingPage text="Initializing..." context={context} />
<Route path={['Start.LoadTutorialSummary', 'Start.LoadTutorialData']}>
<LoadingPage text="Loading Tutorial..." context={context} />
</Route>
<Route path={'Start.Error'}>
<LoadingPage text="Error" context={context} />
Expand All @@ -33,6 +32,9 @@ const Routes = () => {
<Route path="Start.Summary">
<OverviewPage send={send} context={context} />
</Route>
<Route path="SetupNewTutorial">
<LoadingPage text="Configuring tutorial..." context={context} />
</Route>
{/* Tutorial */}
<Route path="Tutorial.LoadNext">
<LoadingPage text="Loading Level..." context={context} />
Expand Down
14 changes: 9 additions & 5 deletions web-app/src/components/Router/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import * as React from 'react'
import * as T from 'typings'
import { createMachine } from '../../services/state/machine'
import { useMachine } from '../../services/xstate-react'
import Route from './Route'
import onError from '../../services/sentry/onError'

interface Props {
children: any
interface Output {
context: T.MachineContext
send: (action: any) => void
Router: any
Route: any
}

declare let acquireVsCodeApi: any

const editor = acquireVsCodeApi()

// router finds first state match of <Route path='' />
const useRouter = () => {
const [state, send] = useMachine(createMachine({ editorSend: editor.postMessage }))
const useRouter = (): Output => {
const [state, send] = useMachine<T.MachineContext, any>(createMachine({ editorSend: editor.postMessage }))

// event bus listener
React.useEffect(() => {
Expand All @@ -35,7 +39,7 @@ const useRouter = () => {
}
}, [])

const Router = ({ children }: Props): React.ReactElement<any> | null => {
const Router = ({ children }: any) => {
const childArray = React.Children.toArray(children)
for (const child of childArray) {
// match path
Expand Down
44 changes: 44 additions & 0 deletions web-app/src/services/apollo/queries/summary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { gql } from 'apollo-boost'

// TODO: add version to query

export default gql`
query getTutorial($tutorialId: ID!) {
tutorial(id: $tutorialId) {
id
createdBy {
id
name
email
}
summary {
title
description
}
version {
id
createdAt
createdBy {
id
name
}
updatedAt
updatedBy {
id
name
}
publishedAt
publishedBy {
name
}
data {
levels {
id
title
summary
}
}
}
}
}
`
10 changes: 2 additions & 8 deletions web-app/src/services/state/actions/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,10 @@ const contextActions: ActionFunctionMap<CR.MachineContext, CR.MachineEvent> = {
},
}),
// @ts-ignore
initTutorial: assign({
// loads complete tutorial
tutorial: (context: CR.MachineContext, event: CR.MachineEvent): any => {
return event.payload.tutorial
},
}),
// @ts-ignore
initPosition: assign({
position: (context: CR.MachineContext, event: CR.MachineEvent): CR.Position => {
const position: CR.Position = selectors.initialPosition(event.payload)
console.log('init position')
const position: CR.Position = selectors.initialPosition(context)
return position
},
}),
Expand Down
9 changes: 9 additions & 0 deletions web-app/src/services/state/actions/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ export default (editorSend: any) => ({
type: 'EDITOR_TUTORIAL_LOAD',
})
},
configureNewTutorial(context: CR.MachineContext) {
editorSend({
type: 'EDITOR_TUTORIAL_CONFIG',
payload: {
// pass position because current stepId or first stepId will be empty
tutorial: context.tutorial,
},
})
},
continueConfig(context: CR.MachineContext) {
editorSend({
type: 'EDITOR_TUTORIAL_CONTINUE_CONFIG',
Expand Down
38 changes: 30 additions & 8 deletions web-app/src/services/state/machine.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as CR from 'typings'
import { assign, Machine, MachineOptions, actions } from 'xstate'
import { assign, Machine, MachineOptions } from 'xstate'
import editorActions from './actions/editor'
import commandActions from './actions/command'
import contextActions from './actions/context'
Expand Down Expand Up @@ -72,18 +72,18 @@ export const createMachine = (options: any) => {
},
SelectTutorial: {
onEntry: ['clearStorage'],
id: 'start-new-tutorial',
id: 'select-new-tutorial',
on: {
SELECT_TUTORIAL: {
target: 'LoadTutorial',
target: 'LoadTutorialSummary',
actions: ['newTutorial'],
},
},
},
// TODO move Initialize into New Tutorial setup
LoadTutorial: {
LoadTutorialSummary: {
invoke: {
src: services.loadTutorial,
src: services.loadTutorialSummary,
onDone: {
target: 'Summary',
actions: assign({
Expand All @@ -102,9 +102,31 @@ export const createMachine = (options: any) => {
on: {
BACK: 'SelectTutorial',
TUTORIAL_START: {
target: '#tutorial',
actions: ['initPosition', 'initTutorial'],
target: 'LoadTutorialData',
},
},
},
LoadTutorialData: {
invoke: {
src: services.loadTutorialData,
onDone: {
target: 'SetupNewTutorial',
actions: assign({
tutorial: (context, event) => event.data,
}),
},
onError: {
target: 'Error',
actions: assign({
error: (context, event) => event.data,
}),
},
},
},
SetupNewTutorial: {
onEntry: ['configureNewTutorial', 'initPosition'],
after: {
0: '#tutorial',
},
},
ContinueTutorial: {
Expand Down Expand Up @@ -224,7 +246,7 @@ export const createMachine = (options: any) => {
onEntry: ['userTutorialComplete'],
on: {
SELECT_TUTORIAL: {
target: '#start-new-tutorial',
target: '#select-new-tutorial',
actions: ['reset'],
},
},
Expand Down
3 changes: 2 additions & 1 deletion web-app/src/services/state/services/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { authenticate } from './authenticate'
export { loadTutorial } from './loadTutorial'
export { loadTutorialData } from './loadTutorialData'
export { loadTutorialSummary } from './loadTutorialSummary'
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface TutorialDataVariables {
// version: string
}

export async function loadTutorial(context: CR.MachineContext): Promise<any> {
export async function loadTutorialData(context: CR.MachineContext): Promise<any> {
// setup test runner and git
if (!context.tutorial) {
const error = new Error('Tutorial not available to load')
Expand Down
43 changes: 43 additions & 0 deletions web-app/src/services/state/services/loadTutorialSummary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as CR from 'typings'
import * as G from 'typings/graphql'
import client from '../../apollo'
import summaryQuery from '../../apollo/queries/summary'
import onError from '../../../services/sentry/onError'

interface TutorialData {
tutorial: G.Tutorial
}

interface TutorialDataVariables {
tutorialId: string
// version: string
}

export async function loadTutorialSummary(context: CR.MachineContext): Promise<any> {
// setup test runner and git
if (!context.tutorial) {
const error = new Error('Tutorial not available to load')
onError(error)
throw error
}

try {
const result = await client.query<TutorialData, TutorialDataVariables>({
query: summaryQuery,
variables: {
tutorialId: context.tutorial.id,
// version: context.tutorial.version.version, // TODO: reimplement version
},
})
if (!result || !result.data || !result.data.tutorial) {
const message = 'No tutorial returned from tutorial config query'
onError(new Error(message))
return Promise.reject(message)
}
return Promise.resolve(result.data.tutorial)
} catch (error) {
const message: CR.ErrorMessage = { title: 'Failed to load tutorial config', description: error.message }
onError(error)
return Promise.reject(message)
}
}