Skip to content
This repository has been archived by the owner on Jun 17, 2021. It is now read-only.

Commit

Permalink
Add Project Path field to new project flow (#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
kerm1it authored and joshwcomeau committed Sep 6, 2018
1 parent 802ee93 commit 79d88c6
Show file tree
Hide file tree
Showing 12 changed files with 225 additions and 34 deletions.
6 changes: 6 additions & 0 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const CREATE_NEW_PROJECT_START = 'CREATE_NEW_PROJECT_START';
export const CREATE_NEW_PROJECT_CANCEL = 'CREATE_NEW_PROJECT_CANCEL';
export const CREATE_NEW_PROJECT_FINISH = 'CREATE_NEW_PROJECT_FINISH';
export const ADD_PROJECT = 'ADD_PROJECT';
export const CHANGE_PROJECT_HOME_PATH = 'CHANGE_PROJECT_HOME_PATH';
export const HIDE_MODAL = 'HIDE_MODAL';
export const DISMISS_SIDEBAR_INTRO = 'DISMISS_SIDEBAR_INTRO';
export const SELECT_PROJECT = 'SELECT_PROJECT';
Expand Down Expand Up @@ -123,6 +124,11 @@ export const createNewProjectFinish = () => ({
type: CREATE_NEW_PROJECT_FINISH,
});

export const changeProjectHomePath = (homePath: string) => ({
type: CHANGE_PROJECT_HOME_PATH,
homePath,
});

export const dismissSidebarIntro = () => ({
type: DISMISS_SIDEBAR_INTRO,
});
Expand Down
2 changes: 2 additions & 0 deletions src/components/CreateNewProjectWizard/BuildPane.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const BUILD_STEP_KEYS: Array<BuildStep> = Object.keys(BUILD_STEPS);

type Props = {
project: SubmittedProject,
projectHomePath: string,
handleCompleteBuild: (project: Project) => void,
};

Expand All @@ -55,6 +56,7 @@ class BuildPane extends PureComponent<Props, State> {
componentDidMount() {
createProject(
this.props.project,
this.props.projectHomePath,
this.handleStatusUpdate,
this.handleError,
this.handleComplete
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import slug from 'slug';

import * as actions from '../../actions';
import { getById } from '../../reducers/projects.reducer';
import { getProjectHomePath } from '../../reducers/paths.reducer';
import { getOnboardingCompleted } from '../../reducers/onboarding-status.reducer';

import TwoPaneModal from '../TwoPaneModal';
Expand All @@ -23,6 +24,7 @@ const FORM_STEPS: Array<Field> = ['projectName', 'projectType', 'projectIcon'];

type Props = {
projects: { [projectId: string]: ProjectInternal },
projectHomePath: string,
isVisible: boolean,
isOnboardingCompleted: boolean,
addProject: (project: Project, isOnboardingCompleted: boolean) => void,
Expand Down Expand Up @@ -121,7 +123,7 @@ class CreateNewProjectWizard extends PureComponent<Props, State> {
};

render() {
const { isVisible, createNewProjectCancel } = this.props;
const { isVisible, createNewProjectCancel, projectHomePath } = this.props;
const {
projectName,
projectType,
Expand Down Expand Up @@ -174,6 +176,7 @@ class CreateNewProjectWizard extends PureComponent<Props, State> {
// like it.
// $FlowFixMe
project={project}
projectHomePath={projectHomePath}
handleCompleteBuild={this.finishBuilding}
/>
)
Expand All @@ -187,6 +190,7 @@ class CreateNewProjectWizard extends PureComponent<Props, State> {

const mapStateToProps = state => ({
projects: getById(state),
projectHomePath: getProjectHomePath(state.paths),
isVisible: state.modal === 'new-project-wizard',
isOnboardingCompleted: getOnboardingCompleted(state),
});
Expand Down
2 changes: 2 additions & 0 deletions src/components/CreateNewProjectWizard/MainPane.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Spacer from '../Spacer';
import FadeIn from '../FadeIn';

import ProjectName from './ProjectName';
import ProjectPath from './ProjectPath';
import SubmitButton from './SubmitButton';

import type { Field, Status } from './types';
Expand Down Expand Up @@ -77,6 +78,7 @@ class MainPane extends PureComponent<Props> {
handleSubmit={handleSubmit}
isProjectNameTaken={isProjectNameTaken}
/>
<ProjectPath />

{currentStepIndex > 0 && (
<FadeIn>
Expand Down
75 changes: 75 additions & 0 deletions src/components/CreateNewProjectWizard/ProjectPath.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// @flow
import React, { PureComponent } from 'react';
import styled from 'styled-components';
import { connect } from 'react-redux';
import { changeProjectHomePath } from '../../actions';
import { getProjectHomePath } from '../../reducers/paths.reducer';
import TextButton from '../TextButton';
import { Tooltip } from 'react-tippy';
import { COLORS } from '../../constants';
const { dialog } = window.require('electron').remote;

type Props = {
defaultProjectHome: string,
changeProjectHomePath: (path: string) => void,
};

class ProjectPath extends PureComponent<Props> {
updatePath = () => {
dialog.showOpenDialog(
{
message: 'Select the directory of Project',
properties: ['openDirectory'],
},
(paths: ?[string]) => {
// The user might cancel out without selecting a directory.
// In that case, do nothing.
if (!paths) {
return;
}

// Only a single path should be selected
const [path] = paths;
this.props.changeProjectHomePath(path);
}
);
};

render() {
const { defaultProjectHome } = this.props;
return (
<MainText>
created in
<Tooltip title={defaultProjectHome} position="bottom">
<TextButton onClick={() => this.updatePath()}>
{defaultProjectHome.length > 30
? `${defaultProjectHome.slice(0, 30)}...`
: defaultProjectHome}
</TextButton>
</Tooltip>
</MainText>
);
}
}

const MainText = styled.div`
text-align: left;
margin: -25px 0 30px 5px;
font-size: 18px;
color: ${COLORS.gray['500']};
`;

const mapStateToProps = state => {
return {
defaultProjectHome: getProjectHomePath(state.paths),
};
};

const mapDispatchToProps = {
changeProjectHomePath,
};

export default connect(
mapStateToProps,
mapDispatchToProps
)(ProjectPath);
1 change: 1 addition & 0 deletions src/components/TextButton/TextButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import styled from 'styled-components';
const TextButton = styled.button`
background: transparent;
border: none;
outline: none;
text-decoration: underline;
padding: 0;
margin: 0;
Expand Down
51 changes: 32 additions & 19 deletions src/reducers/paths.reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,47 @@ import {
IMPORT_EXISTING_PROJECT_FINISH,
FINISH_DELETING_PROJECT,
RESET_ALL_STATE,
CHANGE_PROJECT_HOME_PATH,
} from '../actions';
import { windowsHomeDir, isWin } from '../services/platform.service';

import type { Action } from 'redux';

type State = {
[projectId: string]: string,
homePath: string,
byId: {
[projectId: string]: string,
},
};

const initialState = {};
const homedir = isWin ? windowsHomeDir : os.homedir();
// Noticing some weird quirks when I try to use a dev project on the compiled
// "production" app, so separating their home paths should help.

const initialState = {
homePath:
process.env.NODE_ENV === 'development'
? path.join(homedir, 'guppy-projects-dev')
: path.join(homedir, 'guppy-projects'),
byId: {},
};

export default (state: State = initialState, action: Action) => {
switch (action.type) {
case ADD_PROJECT:
case IMPORT_EXISTING_PROJECT_FINISH: {
const { projectPath, project } = action;
return produce(state, draftState => {
draftState.byId[project.guppy.id] =
projectPath || formatProjectPath(state.homePath, project.guppy.id);
});
}

return {
...state,
[project.guppy.id]: projectPath || getDefaultPath(project.guppy.id),
};
case CHANGE_PROJECT_HOME_PATH: {
const { homePath } = action;
return produce(state, draftState => {
draftState.homePath = homePath;
});
}

case FINISH_DELETING_PROJECT: {
Expand All @@ -61,21 +81,14 @@ export default (state: State = initialState, action: Action) => {
//
//
// Helpers
const homedir = isWin ? windowsHomeDir : os.homedir();
// Noticing some weird quirks when I try to use a dev project on the compiled
// "production" app, so separating their home paths should help.
export const defaultParentPath =
process.env.NODE_ENV === 'development'
? path.join(homedir, '/guppy-projects-dev')
: path.join(homedir, '/guppy-projects');

export const getDefaultPath = (projectId: string) =>
`${defaultParentPath}/${projectId}`;

const formatProjectPath = (homePath, projectId) =>
path.join(homePath, projectId);
//
//
//
// Selectors
export const getPathsArray = (state: any) => Object.values(state.paths);
export const getProjectHomePath = (state: State = initialState) =>
state.homePath;
export const getPathsArray = (state: any) => Object.values(state.paths.byId);
export const getPathForProjectId = (state: any, projectId: string) =>
state.paths[projectId] || getDefaultPath(projectId);
state.paths.byId[projectId];
10 changes: 4 additions & 6 deletions src/services/create-project.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import * as fs from 'fs';
import * as path from 'path';

import { COLORS } from '../constants';
import { defaultParentPath } from '../reducers/paths.reducer';

import { formatCommandForPlatform } from './platform.service';

Expand Down Expand Up @@ -43,6 +42,7 @@ type ProjectInfo = {
*/
export default (
{ projectName, projectType, projectIcon }: ProjectInfo,
projectHomePath: string,
onStatusUpdate: (update: string) => void,
onError: (err: string) => void,
onComplete: (packageJson: any) => void
Expand All @@ -52,12 +52,10 @@ export default (
return;
}

const parentPath = defaultParentPath;

// Create the projects directory, if this is the first time creating a
// project.
if (!fs.existsSync(parentPath)) {
fs.mkdirSync(parentPath);
if (!fs.existsSync(projectHomePath)) {
fs.mkdirSync(projectHomePath);
}

onStatusUpdate('Created parent directory');
Expand All @@ -66,7 +64,7 @@ export default (

// For Windows Support
// To support cross platform with slashes and escapes
const projectPath = path.join(parentPath, id);
const projectPath = path.join(projectHomePath, id);

const [instruction, ...args] = getBuildInstructions(projectType, projectPath);

Expand Down
4 changes: 0 additions & 4 deletions src/services/create-project.service.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ jest.mock('os', () => ({
platform: () => process.platform,
}));

jest.mock('../reducers/paths.reducer.js', () => ({
defaultParentPath: 'test',
}));

jest.mock('../services/platform.service', () => ({
formatCommandForPlatform: cmd => cmd,
}));
Expand Down
4 changes: 3 additions & 1 deletion src/services/read-from-disk.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ export const loadGuppyProjects = (projectPaths: Array<string>) =>
// because it was deleted.
// TODO: Maybe a warning prompt should be raised if this is the case,
// so that users don't wonder where the project went?
const validProjects = results.filter(project => !!project);
const validProjects = results.filter(
project => !!project && project.guppy
);

// The results will be an array of package.jsons.
// I want a database-style map.
Expand Down
36 changes: 34 additions & 2 deletions src/store/migrations.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@
* For more information, see:
* https://github.com/mathieudutour/redux-storage-decorator-migrate
*/

import * as path from 'path';
import * as os from 'os';
import { windowsHomeDir, isWin } from '../services/platform.service';
import migrate from 'redux-storage-decorator-migrate';

// Update this constant whenever the Redux reducers change in a
// not-backwards-incompatible way:
const STATE_VERSION = 1;
const STATE_VERSION = 2;

const homedir = isWin ? windowsHomeDir : os.homedir();

export function migrateToReduxStorage(state: any) {
// 1. UPDATE TO REDUX STORAGE.
Expand All @@ -41,10 +45,38 @@ export function migrateToReduxStorage(state: any) {
return parsedState;
}

export function migrateToSupportProjectHomePath(state: any) {
// 2. UPDATE TO SUPPORT PROJECT_HOME_PATH FEATURE
//
// - add homePath, byId field to paths state
// - migrate values of old paths state to byId field
if (!state) {
return state;
}
const {
paths: { byId: pathsById, homePath },
} = state;

if (pathsById && homePath) {
return state;
}

state.paths = {
homePath:
process.env.NODE_ENV === 'development'
? path.join(homedir, 'guppy-projects-dev')
: path.join(homedir, 'guppy-projects'),
byId: state.paths || {},
};

return state;
}

export default function handleMigrations(engine: any) {
engine = migrate(engine, STATE_VERSION);

engine.addMigration(1, migrateToReduxStorage);
engine.addMigration(2, migrateToSupportProjectHomePath);

return engine;
}
Loading

0 comments on commit 79d88c6

Please sign in to comment.