diff --git a/src/channel/index.ts b/src/channel/index.ts index b19bc650..34f5d3da 100644 --- a/src/channel/index.ts +++ b/src/channel/index.ts @@ -8,6 +8,7 @@ import tutorialConfig from '../actions/tutorialConfig' import { COMMANDS } from '../editor/commands' import logger from '../services/logger' import Context from './context' +import { openWorkspace, checkWorkspaceEmpty } from '../services/workspace' interface Channel { receive(action: T.Action): Promise @@ -108,6 +109,18 @@ class Channel implements Channel { // update the current stepId on startup vscode.commands.executeCommand(COMMANDS.SET_CURRENT_STEP, action.payload) return + case 'EDITOR_CHECK_WORKSPACE': + const isEmptyWorkspace = await checkWorkspaceEmpty(this.workspaceRoot.uri.path) + if (isEmptyWorkspace) { + this.send({ type: 'IS_EMPTY_WORKSPACE' }) + } else { + this.send({ type: 'NOT_EMPTY_WORKSPACE' }) + } + return + case 'EDITOR_REQUEST_WORKSPACE': + console.log('request workspace') + openWorkspace() + return // load step actions (git commits, commands, open files) case 'SETUP_ACTIONS': await vscode.commands.executeCommand(COMMANDS.SET_CURRENT_STEP, action.payload) diff --git a/src/services/testRunner/index.ts b/src/services/testRunner/index.ts index 0cbd6cde..4dba1966 100644 --- a/src/services/testRunner/index.ts +++ b/src/services/testRunner/index.ts @@ -1,5 +1,5 @@ -import node from '../../services/node' -import logger from '../../services/logger' +import node from '../node' +import logger from '../logger' import parser from './parser' import { debounce, throttle } from './throttle' import onError from '../sentry/onError' diff --git a/src/services/workspace/index.ts b/src/services/workspace/index.ts new file mode 100644 index 00000000..0a6f9e9e --- /dev/null +++ b/src/services/workspace/index.ts @@ -0,0 +1,17 @@ +import * as vscode from 'vscode' +import * as fs from 'fs' + +export const openWorkspace = () => { + const openInNewWindow = false + vscode.commands.executeCommand('vscode.openFolder', undefined, openInNewWindow) +} + +export const checkWorkspaceEmpty = async (dirname: string) => { + let files + try { + files = await fs.promises.readdir(dirname) + } catch (error) { + throw new Error('Failed to check workspace') + } + return files.length === 0 +} diff --git a/typings/index.d.ts b/typings/index.d.ts index a6a49333..94e3e9af 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -73,6 +73,8 @@ export interface MachineStateSchema { Error: {} LoadStoredTutorial: {} Start: {} + CheckEmptyWorkspace: {} + NonEmptyWorkspace: {} SelectTutorial: {} LoadTutorialSummary: {} Summary: {} diff --git a/web-app/src/Routes.tsx b/web-app/src/Routes.tsx index e06eed95..90db8f2a 100644 --- a/web-app/src/Routes.tsx +++ b/web-app/src/Routes.tsx @@ -7,6 +7,7 @@ import SelectTutorialPage from './containers/SelectTutorial' import OverviewPage from './containers/Overview' import CompletedPage from './containers/Tutorial/CompletedPage' import LevelSummaryPage from './containers/Tutorial/LevelPage' +import SelectEmptyWorkspace from './containers/Check/SelectWorkspace' const Routes = () => { const { context, send, Router, Route } = useRouter() @@ -14,7 +15,7 @@ const Routes = () => { {/* Setup */} - + @@ -23,6 +24,9 @@ const Routes = () => { + + + diff --git a/web-app/src/containers/Check/SelectWorkspace.tsx b/web-app/src/containers/Check/SelectWorkspace.tsx new file mode 100644 index 00000000..613b1659 --- /dev/null +++ b/web-app/src/containers/Check/SelectWorkspace.tsx @@ -0,0 +1,31 @@ +import * as React from 'react' +import * as T from 'typings' +import { css, jsx } from '@emotion/core' +import Button from '../../components/Button' + +const styles = { + container: { + padding: '1rem', + }, +} + +type Props = { + send: (action: T.Action) => void +} + +const SelectWorkspace = (props: Props) => { + const onOpenWorkspace = () => props.send({ type: 'REQUEST_WORKSPACE' }) + return ( +
+

Select An Empty VSCode Workspace

+

Start a project in an empty folder.

+

Once selected, the extension will close and need to be re-started.

+
+ +
+ ) +} + +export default SelectWorkspace diff --git a/web-app/src/services/state/actions/editor.ts b/web-app/src/services/state/actions/editor.ts index 777b7e31..3a1abd25 100644 --- a/web-app/src/services/state/actions/editor.ts +++ b/web-app/src/services/state/actions/editor.ts @@ -70,4 +70,14 @@ export default (editorSend: any) => ({ clearStorage(): void { editorSend({ type: 'TUTORIAL_CLEAR' }) }, + checkEmptyWorkspace() { + editorSend({ + type: 'EDITOR_CHECK_WORKSPACE', + }) + }, + requestWorkspaceSelect() { + editorSend({ + type: 'EDITOR_REQUEST_WORKSPACE', + }) + }, }) diff --git a/web-app/src/services/state/machine.ts b/web-app/src/services/state/machine.ts index fe275717..22ec14e4 100644 --- a/web-app/src/services/state/machine.ts +++ b/web-app/src/services/state/machine.ts @@ -68,13 +68,29 @@ export const createMachine = (options: any) => { }, Start: { on: { - NEW_TUTORIAL: 'SelectTutorial', + NEW_TUTORIAL: 'CheckEmptyWorkspace', CONTINUE_TUTORIAL: { target: '#tutorial-level', actions: ['continueConfig'], }, }, }, + CheckEmptyWorkspace: { + onEntry: ['checkEmptyWorkspace'], + on: { + IS_EMPTY_WORKSPACE: 'SelectTutorial', + NOT_EMPTY_WORKSPACE: 'NonEmptyWorkspace', + }, + }, + NonEmptyWorkspace: { + on: { + REQUEST_WORKSPACE: { + target: 'NonEmptyWorkspace', + actions: 'requestWorkspaceSelect', + }, + WORKSPACE_LOADED: 'CheckEmptyWorkspace', + }, + }, SelectTutorial: { onEntry: ['clearStorage'], id: 'select-new-tutorial', diff --git a/web-app/stories/Check.stories.tsx b/web-app/stories/Check.stories.tsx new file mode 100644 index 00000000..f1b72220 --- /dev/null +++ b/web-app/stories/Check.stories.tsx @@ -0,0 +1,21 @@ +import { storiesOf } from '@storybook/react' +import { action } from '@storybook/addon-actions' +import React from 'react' +import { css, jsx } from '@emotion/core' +import SelectWorkspace from '../src/containers/Check/SelectWorkspace' +import SideBarDecorator from './utils/SideBarDecorator' + +const styles = { + container: { + display: 'flex' as 'flex', + flexDirection: 'column' as 'column', + }, +} + +storiesOf('Check', module) + .addDecorator(SideBarDecorator) + .add('Select Workspace', () => ( +
+ +
+ ))