From e5f140f6699a84dc5f6022afeb9a0ccd49d45a06 Mon Sep 17 00:00:00 2001 From: shmck Date: Mon, 20 Apr 2020 20:18:00 -0700 Subject: [PATCH 1/6] cleanup test tracking Signed-off-by: shmck --- src/actions/setupActions.ts | 3 ++ src/actions/utils/loadWatchers.ts | 8 ++-- src/channel/index.ts | 14 ++++--- src/editor/commands.ts | 42 +++++++++++-------- src/services/logger/index.ts | 2 +- src/services/testRunner/index.ts | 32 +++++++------- src/webview/render.ts | 1 + web-app/src/services/state/actions/context.ts | 2 +- web-app/src/services/state/actions/editor.ts | 36 ++++++++++------ 9 files changed, 83 insertions(+), 57 deletions(-) diff --git a/src/actions/setupActions.ts b/src/actions/setupActions.ts index 6a349355..b5bf9e55 100644 --- a/src/actions/setupActions.ts +++ b/src/actions/setupActions.ts @@ -14,6 +14,9 @@ interface SetupActions { } export const setupActions = async ({ actions, send, path }: SetupActions): Promise => { + if (!actions) { + return + } const { commands, commits, files, watchers } = actions // validate commit is new diff --git a/src/actions/utils/loadWatchers.ts b/src/actions/utils/loadWatchers.ts index 77a15f6a..1463c055 100644 --- a/src/actions/utils/loadWatchers.ts +++ b/src/actions/utils/loadWatchers.ts @@ -37,9 +37,11 @@ const loadWatchers = (watchers: string[]) => { fsWatcher.on('change', (path, event) => { const now = +new Date() if (!lastFire || lastFire - now > 1000) { - vscode.commands.executeCommand(COMMANDS.RUN_TEST, null, () => { - // cleanup watcher on success - disposeWatcher(watcher) + vscode.commands.executeCommand(COMMANDS.RUN_TEST, { + onSuccess: () => { + // cleanup watcher on success + disposeWatcher(watcher) + }, }) } }) diff --git a/src/channel/index.ts b/src/channel/index.ts index 7a82c34e..458cc226 100644 --- a/src/channel/index.ts +++ b/src/channel/index.ts @@ -224,7 +224,7 @@ class Channel implements Channel { alreadyConfigured: true, }) // update the current stepId on startup - vscode.commands.executeCommand(COMMANDS.SET_CURRENT_STEP, action.payload) + vscode.commands.executeCommand(COMMANDS.SET_CURRENT_POSITION, action.payload.position) } catch (e) { const error = { type: 'UnknownError', @@ -286,14 +286,15 @@ class Channel implements Channel { return // load step actions (git commits, commands, open files) case 'SETUP_ACTIONS': - await vscode.commands.executeCommand(COMMANDS.SET_CURRENT_STEP, action.payload) - setupActions({ actions: action.payload, send: this.send }) + await vscode.commands.executeCommand(COMMANDS.SET_CURRENT_POSITION, action.payload.position) + setupActions({ actions: action.payload.actions, send: this.send }) return // load solution step actions (git commits, commands, open files) case 'SOLUTION_ACTIONS': - await solutionActions({ actions: action.payload, send: this.send }) + await vscode.commands.executeCommand(COMMANDS.SET_CURRENT_POSITION, action.payload.position) + await solutionActions({ actions: action.payload.actions, send: this.send }) // run test following solution to update position - vscode.commands.executeCommand(COMMANDS.RUN_TEST, action.payload) + vscode.commands.executeCommand(COMMANDS.RUN_TEST) return default: @@ -328,12 +329,13 @@ class Channel implements Channel { switch (actionType) { case 'TEST_PASS': + console.log('TEST_PASS', action) const tutorial = this.context.tutorial.get() if (!tutorial) { throw new Error('Error with current tutorial. Tutorial may be missing an id.') } // update local storage stepProgress - const progress = this.context.progress.setStepComplete(tutorial, action.payload.stepId) + const progress = this.context.progress.setStepComplete(tutorial, action.payload.position.stepId) this.context.position.setPositionFromProgress(tutorial, progress) saveCommit() } diff --git a/src/editor/commands.ts b/src/editor/commands.ts index 57ed0e0c..19b96585 100644 --- a/src/editor/commands.ts +++ b/src/editor/commands.ts @@ -1,15 +1,17 @@ +import * as T from 'typings' import * as TT from 'typings/tutorial' import * as vscode from 'vscode' -import createTestRunner, { Payload } from '../services/testRunner' +import createTestRunner from '../services/testRunner' import { setupActions } from '../actions/setupActions' import createWebView from '../webview' +import logger from '../services/logger' export const COMMANDS = { START: 'coderoad.start', OPEN_WEBVIEW: 'coderoad.open_webview', CONFIG_TEST_RUNNER: 'coderoad.config_test_runner', RUN_TEST: 'coderoad.run_test', - SET_CURRENT_STEP: 'coderoad.set_current_step', + SET_CURRENT_POSITION: 'coderoad.set_current_position', } interface CreateCommandProps { @@ -20,7 +22,7 @@ interface CreateCommandProps { export const createCommands = ({ extensionPath, workspaceState }: CreateCommandProps) => { // React panel webview let webview: any - let currentStepId: string | null = '' + let currentPosition: T.Position let testRunner: any return { @@ -55,32 +57,38 @@ export const createCommands = ({ extensionPath, workspaceState }: CreateCommandP await setupActions({ actions: config.actions, send: webview.send, path: config.path }) } testRunner = createTestRunner(config, { - onSuccess: (payload: Payload) => { + onSuccess: (position: T.Position) => { + logger('test pass position', position) // send test pass message back to client - webview.send({ type: 'TEST_PASS', payload }) + webview.send({ type: 'TEST_PASS', payload: { position } }) }, - onFail: (payload: Payload, message: string) => { + onFail: (position: T.Position, message: string) => { // send test fail message back to client with failure message - webview.send({ type: 'TEST_FAIL', payload: { ...payload, message } }) + webview.send({ type: 'TEST_FAIL', payload: { position, message } }) }, - onError: (payload: Payload) => { - // send test error message back to client - webview.send({ type: 'TEST_ERROR', payload }) + onError: (position: T.Position) => { + // TODO: send test error message back to client + const message = 'Error with test runner' + webview.send({ type: 'TEST_ERROR', payload: { position, message } }) }, - onRun: (payload: Payload) => { + onRun: (position: T.Position) => { // send test run message back to client - webview.send({ type: 'TEST_RUNNING', payload }) + webview.send({ type: 'TEST_RUNNING', payload: { position } }) }, }) }, - [COMMANDS.SET_CURRENT_STEP]: ({ stepId }: Payload) => { + [COMMANDS.SET_CURRENT_POSITION]: (position: T.Position) => { // set from last setup stepAction - currentStepId = stepId + currentPosition = position }, - [COMMANDS.RUN_TEST]: (current: Payload | undefined, onSuccess: () => void) => { + [COMMANDS.RUN_TEST]: (callback?: { onSuccess: () => void }) => { + logger('run test current', currentPosition) // use stepId from client, or last set stepId - const payload: Payload = { stepId: current && current.stepId?.length ? current.stepId : currentStepId } - testRunner(payload, onSuccess) + // const position: T.Position = { + // ...current, + // stepId: current && current.position.stepId?.length ? current.position.stepId : currentPosition.stepId, + // } + testRunner(currentPosition, callback?.onSuccess) }, } } diff --git a/src/services/logger/index.ts b/src/services/logger/index.ts index ea79bcdf..8d193311 100644 --- a/src/services/logger/index.ts +++ b/src/services/logger/index.ts @@ -1,6 +1,6 @@ import { LOG } from '../../environment' -export type Log = string | object | null +export type Log = string | object | null | undefined const logger = (...messages: Log[]): void => { if (!LOG) { diff --git a/src/services/testRunner/index.ts b/src/services/testRunner/index.ts index c9c12cf2..182abc5e 100644 --- a/src/services/testRunner/index.ts +++ b/src/services/testRunner/index.ts @@ -1,4 +1,5 @@ -import { TutorialTestRunnerConfig } from 'typings/tutorial' +import * as T from 'typings' +import * as TT from 'typings/tutorial' import { exec } from '../node' import logger from '../logger' import parser from './parser' @@ -7,22 +8,19 @@ import onError from '../sentry/onError' import { clearOutput, displayOutput } from './output' import { formatFailOutput } from './formatOutput' -export interface Payload { - stepId: string | null -} - interface Callbacks { - onSuccess(payload: Payload): void - onFail(payload: Payload, message: string): void - onRun(payload: Payload): void - onError(payload: Payload): void + onSuccess(position: T.Position): void + onFail(position: T.Position, message: string): void + onRun(position: T.Position): void + onError(position: T.Position): void } const failChannelName = 'CodeRoad (Tests)' const logChannelName = 'CodeRoad (Logs)' -const createTestRunner = (config: TutorialTestRunnerConfig, callbacks: Callbacks) => { - return async (payload: Payload, onSuccess?: () => void): Promise => { +const createTestRunner = (config: TT.TutorialTestRunnerConfig, callbacks: Callbacks) => { + return async (position: T.Position, onSuccess?: () => void): Promise => { + logger('createTestRunner', position) const startTime = throttle() // throttle time early if (!startTime) { @@ -32,7 +30,7 @@ const createTestRunner = (config: TutorialTestRunnerConfig, callbacks: Callbacks logger('------------------- RUN TEST -------------------') // flag as running - callbacks.onRun(payload) + callbacks.onRun(position) let result: { stdout: string | undefined; stderr: string | undefined } try { @@ -59,12 +57,12 @@ const createTestRunner = (config: TutorialTestRunnerConfig, callbacks: Callbacks // FAIL also trigger stderr if (stdout && stdout.length && !tap.ok) { const firstFailMessage = tap.failed[0].message - callbacks.onFail(payload, firstFailMessage) + callbacks.onFail(position, firstFailMessage) const output = formatFailOutput(tap) displayOutput({ channel: failChannelName, text: output, show: true }) return } else { - callbacks.onError(payload) + callbacks.onError(position) // open terminal with error string displayOutput({ channel: failChannelName, text: stderr, show: true }) return @@ -74,14 +72,14 @@ const createTestRunner = (config: TutorialTestRunnerConfig, callbacks: Callbacks // PASS if (tap.ok) { clearOutput(failChannelName) - callbacks.onSuccess(payload) + callbacks.onSuccess(position) if (onSuccess) { onSuccess() } } else { // should never get here - onError(new Error(`Error with running test ${JSON.stringify(payload)}`)) - callbacks.onError(payload) + onError(new Error(`Error with running test ${JSON.stringify(position)}`)) + callbacks.onError(position) } } } diff --git a/src/webview/render.ts b/src/webview/render.ts index 59379261..1bf5bf6a 100644 --- a/src/webview/render.ts +++ b/src/webview/render.ts @@ -31,6 +31,7 @@ async function render(panel: vscode.WebviewPanel, rootPath: string) { const createUri = (_filePath: string): any => { const filePath = (_filePath.startsWith('vscode') ? _filePath.substr(16) : _filePath).replace('///', '\\') + // @ts-ignore return panel.webview.asWebviewUri(vscode.Uri.file(path.join(rootPath, filePath))) } diff --git a/web-app/src/services/state/actions/context.ts b/web-app/src/services/state/actions/context.ts index 8dd3f083..e179effe 100644 --- a/web-app/src/services/state/actions/context.ts +++ b/web-app/src/services/state/actions/context.ts @@ -109,7 +109,7 @@ const contextActions: ActionFunctionMap = { // update progress by tracking completed const currentProgress: T.Progress = context.progress - const { stepId } = event.payload + const { stepId } = event.payload.position currentProgress.steps[stepId] = true diff --git a/web-app/src/services/state/actions/editor.ts b/web-app/src/services/state/actions/editor.ts index e8352bd5..5770abf2 100644 --- a/web-app/src/services/state/actions/editor.ts +++ b/web-app/src/services/state/actions/editor.ts @@ -22,19 +22,24 @@ export default (editorSend: any) => ({ type: 'EDITOR_TUTORIAL_CONTINUE_CONFIG', payload: { // pass position because current stepId or first stepId will be empty - stepId: context.position.stepId, + position: context.position, }, }) }, loadLevel(context: CR.MachineContext): void { const level: TT.Level = selectors.currentLevel(context) - if (level.setup) { - // load step actions - editorSend({ - type: 'SETUP_ACTIONS', - payload: level.setup, - }) - } + const step: TT.Step | null = selectors.currentStep(context) + // load step actions + editorSend({ + type: 'SETUP_ACTIONS', + payload: { + position: { + stepId: step?.id || null, + levelId: level.id, + }, + actions: level.setup, + }, + }) }, loadStep(context: CR.MachineContext): void { const step: TT.Step | null = selectors.currentStep(context) @@ -43,8 +48,12 @@ export default (editorSend: any) => ({ editorSend({ type: 'SETUP_ACTIONS', payload: { - stepId: step.id, - ...step.setup, + // set position here + position: { + stepId: step.id, + levelId: context.position.levelId, + }, + actions: step.setup, }, }) } @@ -56,8 +65,11 @@ export default (editorSend: any) => ({ editorSend({ type: 'SOLUTION_ACTIONS', payload: { - stepId: step.id, - ...step.solution, + position: { + stepId: step.id, + levelId: context.position.levelId, + }, + actions: step.solution, }, }) } From 1d9cf714aaf317a4d71eb77b8b1fa42a6dcbf15f Mon Sep 17 00:00:00 2001 From: shmck Date: Sun, 19 Apr 2020 18:45:32 -0700 Subject: [PATCH 2/6] setup menu dropdown of learn levels Signed-off-by: shmck --- .../containers/Tutorial/components/Level.tsx | 23 ++++++++++++-- web-app/src/containers/Tutorial/index.tsx | 30 +++++++++++++++++++ web-app/stories/Level.stories.tsx | 20 +++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/web-app/src/containers/Tutorial/components/Level.tsx b/web-app/src/containers/Tutorial/components/Level.tsx index bf782377..c89c8ef5 100644 --- a/web-app/src/containers/Tutorial/components/Level.tsx +++ b/web-app/src/containers/Tutorial/components/Level.tsx @@ -2,6 +2,8 @@ import * as React from 'react' import * as T from 'typings' import * as TT from 'typings/tutorial' import { css, jsx } from '@emotion/core' +import { Dropdown } from '@alifd/next' +import Icon from '../../../components/Icon' import Button from '../../../components/Button' import Markdown from '../../../components/Markdown' import ProcessMessages from '../../../components/ProcessMessages' @@ -22,12 +24,19 @@ const styles = { paddingBottom: '5rem', }, header: { + display: 'flex' as 'flex', + alignItems: 'center', + justifyContent: 'space-between', height: '2rem', backgroundColor: '#EBEBEB', fontSize: '1rem', lineHeight: '1rem', padding: '10px 1rem', }, + learn: { + textDecoration: 'none', + color: 'inherit', + }, text: { padding: '0rem 1rem', paddingBottom: '1rem', @@ -77,6 +86,7 @@ const styles = { } interface Props { + menu: any level: TT.Level & { status: T.ProgressStatus; index: number; steps: Array } processes: T.ProcessEvent[] testStatus: T.TestStatus | null @@ -84,7 +94,7 @@ interface Props { onLoadSolution(): void } -const Level = ({ level, onContinue, onLoadSolution, processes, testStatus }: Props) => { +const Level = ({ menu, level, onContinue, onLoadSolution, processes, testStatus }: Props) => { // @ts-ignore let currentStep = level.steps.findIndex((s) => s.status === 'ACTIVE') if (currentStep === -1) { @@ -103,7 +113,16 @@ const Level = ({ level, onContinue, onLoadSolution, processes, testStatus }: Pro
- Learn + + Learn {' '} + + } + triggerType="click" + > + {menu} +

{level.title}

diff --git a/web-app/src/containers/Tutorial/index.tsx b/web-app/src/containers/Tutorial/index.tsx index 0d49e7ad..3db2bb06 100644 --- a/web-app/src/containers/Tutorial/index.tsx +++ b/web-app/src/containers/Tutorial/index.tsx @@ -1,8 +1,11 @@ import * as React from 'react' import * as T from 'typings' import * as TT from 'typings/tutorial' +import { Menu } from '@alifd/next' import * as selectors from '../../services/selectors' +import Icon from '../../components/Icon' import Level from './components/Level' +import logger from 'services/logger' interface PageProps { context: T.MachineContext @@ -48,8 +51,35 @@ const TutorialPage = (props: PageProps) => { }), } + const menu = ( + + {tutorial.levels.map((level: TT.Level) => { + const isCurrent = level.id === position.levelId + logger('progress', progress) + const isComplete = progress.levels[level.id] + let icon + let disabled = false + + if (isComplete) { + icon = + } else if (isCurrent) { + icon = + } else { + disabled = true + icon = + } + return ( + + {icon}   {level.title} + + ) + })} + + ) + return ( } +const menu = ( + + {[{ id: '1', title: 'First' }].map((level: TT.Level) => { + const icon = + return ( + + {icon}   {level.title} + + ) + })} + +) + storiesOf('Level', module) .addDecorator(SideBarDecorator) .addDecorator(withKnobs) @@ -72,6 +87,7 @@ storiesOf('Level', module) } return ( Date: Sun, 19 Apr 2020 19:01:15 -0700 Subject: [PATCH 3/6] setup menu dropdown for level Signed-off-by: shmck --- .../containers/Tutorial/components/Level.tsx | 41 +++++++++++++------ web-app/src/containers/Tutorial/index.tsx | 36 ++++++++-------- web-app/stories/Level.stories.tsx | 37 +++++++++++++---- 3 files changed, 74 insertions(+), 40 deletions(-) diff --git a/web-app/src/containers/Tutorial/components/Level.tsx b/web-app/src/containers/Tutorial/components/Level.tsx index c89c8ef5..38f7a8bd 100644 --- a/web-app/src/containers/Tutorial/components/Level.tsx +++ b/web-app/src/containers/Tutorial/components/Level.tsx @@ -87,18 +87,33 @@ const styles = { interface Props { menu: any - level: TT.Level & { status: T.ProgressStatus; index: number; steps: Array } + steps: Array + title: string + index: number + content: string + status: 'COMPLETE' | 'ACTIVE' | 'INCOMPLETE' processes: T.ProcessEvent[] testStatus: T.TestStatus | null onContinue(): void onLoadSolution(): void } -const Level = ({ menu, level, onContinue, onLoadSolution, processes, testStatus }: Props) => { +const Level = ({ + menu, + steps, + title, + content, + index, + status, + onContinue, + onLoadSolution, + processes, + testStatus, +}: Props) => { // @ts-ignore - let currentStep = level.steps.findIndex((s) => s.status === 'ACTIVE') + let currentStep = steps.findIndex((s) => s.status === 'ACTIVE') if (currentStep === -1) { - currentStep = level.steps.length + currentStep = steps.length } const pageBottomRef = React.useRef(null) @@ -116,7 +131,7 @@ const Level = ({ menu, level, onContinue, onLoadSolution, processes, testStatus - Learn {' '} + Learn } triggerType="click" @@ -125,15 +140,15 @@ const Level = ({ menu, level, onContinue, onLoadSolution, processes, testStatus
-

{level.title}

- {level.content || ''} +

{title}

+ {content || ''}
- {level.steps.length ? ( + {steps.length ? (
Tasks
- {level.steps.map((step: (TT.Step & { status: T.ProgressStatus }) | null, index: number) => { + {steps.map((step: (TT.Step & { status: T.ProgressStatus }) | null, index: number) => { if (!step) { return null } @@ -165,17 +180,17 @@ const Level = ({ menu, level, onContinue, onLoadSolution, processes, testStatus
- {typeof level.index === 'number' ? `${level.index + 1}. ` : ''} - {level.title} + {typeof index === 'number' ? `${index + 1}. ` : ''} + {title} - {level.status === 'COMPLETE' || !level.steps.length ? ( + {status === 'COMPLETE' || !steps.length ? ( ) : ( - {currentStep} of {level.steps.length} tasks + {currentStep} of {steps.length} tasks )} diff --git a/web-app/src/containers/Tutorial/index.tsx b/web-app/src/containers/Tutorial/index.tsx index 3db2bb06..87ea83f1 100644 --- a/web-app/src/containers/Tutorial/index.tsx +++ b/web-app/src/containers/Tutorial/index.tsx @@ -17,6 +17,7 @@ const TutorialPage = (props: PageProps) => { const tutorial = selectors.currentTutorial(props.context) const levelData: TT.Level = selectors.currentLevel(props.context) + const [content, setContent] = React.useState(levelData.content) const onContinue = (): void => { props.send({ @@ -31,25 +32,16 @@ const TutorialPage = (props: PageProps) => { props.send({ type: 'STEP_SOLUTION_LOAD' }) } - const level: TT.Level & { - status: T.ProgressStatus - index: number - steps: Array - } = { - ...levelData, - index: tutorial.levels.findIndex((l: TT.Level) => l.id === position.levelId), - status: progress.levels[position.levelId] ? 'COMPLETE' : 'ACTIVE', - steps: levelData.steps.map((step: TT.Step) => { - // label step status for step component - let status: T.ProgressStatus = 'INCOMPLETE' - if (progress.steps[step.id]) { - status = 'COMPLETE' - } else if (step.id === position.stepId) { - status = 'ACTIVE' - } - return { ...step, status } - }), - } + const steps = levelData.steps.map((step: TT.Step) => { + // label step status for step component + let status: T.ProgressStatus = 'INCOMPLETE' + if (progress.steps[step.id]) { + status = 'COMPLETE' + } else if (step.id === position.stepId) { + status = 'ACTIVE' + } + return { ...step, status } + }) const menu = ( @@ -79,8 +71,12 @@ const TutorialPage = (props: PageProps) => { return ( l.id === position.levelId)} + steps={steps} + status={progress.levels[position.levelId] ? 'COMPLETE' : 'ACTIVE'} onContinue={onContinue} onLoadSolution={onLoadSolution} processes={processes} diff --git a/web-app/stories/Level.stories.tsx b/web-app/stories/Level.stories.tsx index cc4925a4..999a79f4 100644 --- a/web-app/stories/Level.stories.tsx +++ b/web-app/stories/Level.stories.tsx @@ -39,7 +39,7 @@ storiesOf('Level', module) description: 'A summary of the level', content: 'Some content here in markdown', setup: null, - status: 'ACTIVE', + status: 'ACTIVE' as 'ACTIVE', steps: [ { id: 'L1:S1', @@ -88,7 +88,11 @@ storiesOf('Level', module) return ( Date: Sun, 19 Apr 2020 19:07:51 -0700 Subject: [PATCH 4/6] toggle content with dropdown Signed-off-by: shmck --- web-app/src/containers/Tutorial/index.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/web-app/src/containers/Tutorial/index.tsx b/web-app/src/containers/Tutorial/index.tsx index 87ea83f1..1726bd66 100644 --- a/web-app/src/containers/Tutorial/index.tsx +++ b/web-app/src/containers/Tutorial/index.tsx @@ -17,6 +17,8 @@ const TutorialPage = (props: PageProps) => { const tutorial = selectors.currentTutorial(props.context) const levelData: TT.Level = selectors.currentLevel(props.context) + + const [title, setTitle] = React.useState(levelData.title) const [content, setContent] = React.useState(levelData.content) const onContinue = (): void => { @@ -43,6 +45,14 @@ const TutorialPage = (props: PageProps) => { return { ...step, status } }) + const setMenuContent = (levelId: string) => { + const selectedLevel: TT.Level | undefined = tutorial.levels.find((l: TT.Level) => l.id === levelId) + if (selectedLevel) { + setTitle(selectedLevel.title) + setContent(selectedLevel.content) + } + } + const menu = ( {tutorial.levels.map((level: TT.Level) => { @@ -61,7 +71,7 @@ const TutorialPage = (props: PageProps) => { icon = } return ( - + setMenuContent(level.id)}> {icon}   {level.title} ) @@ -71,7 +81,7 @@ const TutorialPage = (props: PageProps) => { return ( l.id === position.levelId)} From 9e2d05ae690aefc343e09a50d6348cd93468ab4d Mon Sep 17 00:00:00 2001 From: shmck Date: Sun, 26 Apr 2020 15:05:41 -0700 Subject: [PATCH 5/6] fix level continue issue Signed-off-by: shmck --- src/channel/index.ts | 5 +++++ src/channel/state/Progress.ts | 4 ++++ web-app/src/containers/Tutorial/index.tsx | 2 +- web-app/src/environment.ts | 3 +-- web-app/src/services/state/actions/editor.ts | 8 ++++++++ web-app/src/services/state/machine.ts | 7 +++---- 6 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/channel/index.ts b/src/channel/index.ts index 458cc226..5d2a7dec 100644 --- a/src/channel/index.ts +++ b/src/channel/index.ts @@ -297,6 +297,11 @@ class Channel implements Channel { vscode.commands.executeCommand(COMMANDS.RUN_TEST) return + case 'EDITOR_SYNC_PROGRESS': + // update progress when a level is deemed complete in the client + await this.context.progress.syncProgress(action.payload.progress) + return + default: logger(`No match for action type: ${actionType}`) return diff --git a/src/channel/state/Progress.ts b/src/channel/state/Progress.ts index 839614cb..4b143d8d 100644 --- a/src/channel/state/Progress.ts +++ b/src/channel/state/Progress.ts @@ -39,6 +39,10 @@ class Progress { public reset = () => { this.set(defaultValue) } + public syncProgress = (progress: T.Progress): T.Progress => { + const next = { ...this.value, ...progress } + return this.set(next) + } public setStepComplete = (tutorial: TT.Tutorial, stepId: string): T.Progress => { const next = this.value // mark step complete diff --git a/web-app/src/containers/Tutorial/index.tsx b/web-app/src/containers/Tutorial/index.tsx index 1726bd66..1c1f2f34 100644 --- a/web-app/src/containers/Tutorial/index.tsx +++ b/web-app/src/containers/Tutorial/index.tsx @@ -5,7 +5,7 @@ import { Menu } from '@alifd/next' import * as selectors from '../../services/selectors' import Icon from '../../components/Icon' import Level from './components/Level' -import logger from 'services/logger' +import logger from '../../services/logger' interface PageProps { context: T.MachineContext diff --git a/web-app/src/environment.ts b/web-app/src/environment.ts index e5e18633..d997cb9c 100644 --- a/web-app/src/environment.ts +++ b/web-app/src/environment.ts @@ -9,7 +9,6 @@ for (const required of requiredKeys) { export const DEBUG: boolean = (process.env.REACT_APP_DEBUG || '').toLowerCase() === 'true' export const VERSION: string = process.env.VERSION || 'unknown' export const NODE_ENV: string = process.env.NODE_ENV || 'development' -export const LOG: boolean = - (process.env.REACT_APP_LOG || '').toLowerCase() === 'true' && process.env.NODE_ENV !== 'production' +export const LOG: boolean = (process.env.REACT_APP_LOG || '').toLowerCase() === 'true' export const TUTORIAL_LIST_URL: string = process.env.REACT_APP_TUTORIAL_LIST_URL || '' export const SENTRY_DSN: string | null = process.env.REACT_APP_SENTRY_DSN || null diff --git a/web-app/src/services/state/actions/editor.ts b/web-app/src/services/state/actions/editor.ts index 5770abf2..e3121018 100644 --- a/web-app/src/services/state/actions/editor.ts +++ b/web-app/src/services/state/actions/editor.ts @@ -74,6 +74,14 @@ export default (editorSend: any) => ({ }) } }, + syncLevelProgress(context: CR.MachineContext): void { + editorSend({ + type: 'EDITOR_SYNC_PROGRESS', + payload: { + progress: context.progress, + }, + }) + }, clearStorage(): void { editorSend({ type: 'TUTORIAL_CLEAR' }) }, diff --git a/web-app/src/services/state/machine.ts b/web-app/src/services/state/machine.ts index c928c8e4..4441a968 100644 --- a/web-app/src/services/state/machine.ts +++ b/web-app/src/services/state/machine.ts @@ -207,13 +207,12 @@ export const createMachine = (options: any) => { target: 'Normal', actions: ['loadStep'], }, - LEVEL_COMPLETE: { - target: 'LevelComplete', - actions: ['updateLevelProgress'], - }, + LEVEL_COMPLETE: 'LevelComplete', }, }, LevelComplete: { + onEntry: ['updateLevelProgress'], + onExit: ['syncLevelProgress'], on: { LEVEL_NEXT: { target: '#tutorial-load-next', From 965064ef0f0e290f88f2ce58e05c99f29489f2cc Mon Sep 17 00:00:00 2001 From: shmck Date: Sun, 26 Apr 2020 15:10:26 -0700 Subject: [PATCH 6/6] update learn dropdown icons Signed-off-by: shmck --- web-app/src/containers/Tutorial/index.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/web-app/src/containers/Tutorial/index.tsx b/web-app/src/containers/Tutorial/index.tsx index 1c1f2f34..c7e21201 100644 --- a/web-app/src/containers/Tutorial/index.tsx +++ b/web-app/src/containers/Tutorial/index.tsx @@ -63,12 +63,15 @@ const TutorialPage = (props: PageProps) => { let disabled = false if (isComplete) { - icon = + // completed icon + icon = } else if (isCurrent) { - icon = + // current icon` + icon = } else { + // upcoming disabled = true - icon = + icon = } return ( setMenuContent(level.id)}>