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

[WIP] Export to Codesandbox #324

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
]
},
"dependencies": {
"codesandbox": "1.3.8",
"create-react-app": "1.5.2",
"electron-debug": "2.0.0",
"electron-devtools-installer": "2.2.4",
Expand Down
37 changes: 37 additions & 0 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,19 @@ export const SHOW_PROJECT_SETTINGS = 'SHOW_PROJECT_SETTINGS';
export const SAVE_PROJECT_SETTINGS_START = 'SAVE_PROJECT_SETTINGS_START';
export const SAVE_PROJECT_SETTINGS_ERROR = 'SAVE_PROJECT_SETTINGS_ERROR';
export const SAVE_PROJECT_SETTINGS_FINISH = 'SAVE_PROJECT_SETTINGS_FINISH';
export const SET_CODESANDBOX_URL = 'SET_CODESANDBOX_URL';

// app settings
export const SHOW_APP_SETTINGS = 'SHOW_APP_SETTINGS';
export const SAVE_APP_SETTINGS_START = 'SAVE_APP_SETTINGS_START';
export const CHANGE_DEFAULT_PROJECT_PATH = 'CHANGE_DEFAULT_PROJECT_PATH';
export const UPDATE_CODESANDBOX_TOKEN = 'UPDATE_CODESANDBOX_TOKEN';

// export to codesandbox
export const EXPORT_TO_CODESANDBOX_START = 'EXPORT_TO_CODESANDBOX_START';
export const EXPORT_TO_CODESANDBOX_FINISH = 'EXPORT_TO_CODESANDBOX_FINISH';
export const CODESANDBOX_LOGOUT = 'CODESANDBOX_LOGOUT';

//
//
// Action Creators
Expand Down Expand Up @@ -379,6 +387,20 @@ export const changeDefaultProjectPath = (defaultProjectPath: string) => ({
defaultProjectPath,
});

export const updateCodesandboxToken = (codesandboxToken: string) => ({
type: UPDATE_CODESANDBOX_TOKEN,
codesandboxToken,
});

export const setCodesandboxUrl = (
projectId: string,
codesandboxUrl: string
) => ({
type: SET_CODESANDBOX_URL,
projectId,
codesandboxUrl,
});

// project settings related actions
export const saveProjectSettingsStart = (
name: string,
Expand Down Expand Up @@ -418,3 +440,18 @@ export const showResetStatePrompt = () => ({
});

export const resetAllState = () => ({ type: RESET_ALL_STATE });

export const exportToCodesandboxStart = (projectId: string) => ({
type: EXPORT_TO_CODESANDBOX_START,
projectId,
});

export const exportToCodesandboxFinish = (projectId: string) => ({
type: EXPORT_TO_CODESANDBOX_FINISH,
projectId,
});

export const logoutCodesandbox = (projectPath: string) => ({
type: CODESANDBOX_LOGOUT,
projectPath,
});
46 changes: 46 additions & 0 deletions src/assets/images/codesandbox-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/components/Button/ButtonBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const ButtonBaseStyles = styled.button`
align-items: center;
background: ${props => props.background};
color: ${props => props.textColor};
cursor: pointer;
cursor: ${props => (props.disabled ? 'not-allowed' : 'pointer')};
outline: none;
white-space: nowrap;

Expand Down
8 changes: 8 additions & 0 deletions src/components/DisabledText/DisabledText.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// @flow
import styled from 'styled-components';
import { COLORS } from '../../constants';

export default styled.div`
padding-top: 16px;
color: ${COLORS.gray[500]};
`;
2 changes: 2 additions & 0 deletions src/components/DisabledText/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// @flow
export { default } from './DisabledText';
183 changes: 183 additions & 0 deletions src/components/ExportToCodesandbox/ExportToCodesandbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// @flow
import React, { Fragment, PureComponent } from 'react';
import styled from 'styled-components';
import { connect } from 'react-redux';

import * as actions from '../../actions';
import { COLORS } from '../../constants';

import { getSelectedProject } from '../../reducers/projects.reducer';
import { getCodesandboxToken } from '../../reducers/app-settings.reducer';
import { getExportingActiveStatus } from '../../reducers/project-status.reducer';

import ExternalLink from '../ExternalLink';
import FormField from '../FormField';
import TokenInput from '../TokenInput';
import StrokeButton from '../Button/StrokeButton';
import DisabledText from '../DisabledText';
import Spacer from '../Spacer';
import Spinner from '../Spinner';

import type { Project } from '../../types';
import type { Dispatch } from '../../actions/types';

type Props = {
exportingActive: boolean,
codesandboxToken: string,
isFocused: boolean,
project: Project,
exportToCodesandbox: Dispatch<typeof actions.exportToCodesandboxStart>,
updateToken: Dispatch<typeof actions.updateCodesandboxToken>,
setCodesandboxUrl: Dispatch<typeof actions.setCodesandboxUrl>,
logout: Dispatch<typeof actions.logoutCodesandbox>,
onFocus: () => void,
onBlur: () => void,
};

class ExportToCodesandbox extends PureComponent<Props> {
logout = (evt: any) => {
evt.preventDefault();
const { logout, project } = this.props;

logout(project.path);
};
handleExport = (evt: any) => {
const { project, exportToCodesandbox } = this.props;
evt.preventDefault();

exportToCodesandbox(project.id);
};
// todo: Change flow:
// 1. Token always available before running the code - so no need to opne codesandbox page
// 2. After export click display External link to created sandbox & store link in project
render() {
const {
codesandboxToken,
project,
isFocused,
onFocus,
onBlur,
exportingActive,
} = this.props;
const { codesandboxUrl } = project;

return (
<ExportToCodesandboxWrapper>
<FormField size="small" label="Export to Codesandbox.io" spacing={5}>
<InfoText>
Go to the Codesandbox website by clicking the link and copy the
token. Then paste the token in the token field.{' '}
</InfoText>
<ExternalLink href="https://codesandbox.io/cli/login">
Get new token
</ExternalLink>
</FormField>
<Wrapper>
<FormField label="Codesandbox token" spacing={0}>
<TokenRow>
<TokenInput
token={codesandboxToken}
onChange={this.props.updateToken}
onFocus={onFocus}
onBlur={onBlur}
focused={isFocused}
disabled={codesandboxToken !== ''}
/>
{codesandboxToken &&
!isFocused && (
<Fragment>
<Spacer size={10} />
<StrokeButton size="small" onClick={this.logout}>
Logout
</StrokeButton>
</Fragment>
)}
</TokenRow>
</FormField>
<Action>
<StrokeButton
onClick={this.handleExport}
size="small"
strokeColors={[COLORS.green[700], COLORS.lightGreen[500]]}
disabled={!codesandboxToken || exportingActive}
>
<ButtonCaption>
{exportingActive && (
<Fragment>
<Spinner size={24} />
<Spacer size={6} />
</Fragment>
)}
Export to Codesandbox
</ButtonCaption>
</StrokeButton>
{!codesandboxToken && (
<DisabledText>
Export disabled because token is missing.
</DisabledText>
)}

{codesandboxUrl && (
<Fragment>
<Spacer size={5} />
<InfoText>
Codesandbox created <strong>{codesandboxUrl}</strong>
<ExternalLink href={codesandboxUrl}>
Open sandbox
</ExternalLink>
</InfoText>
</Fragment>
)}
</Action>
</Wrapper>
</ExportToCodesandboxWrapper>
);
}
}

const Wrapper = styled.div`
display: grid;
grid-template-columns: 1fr 1fr;
`;

const ButtonCaption = styled.div`
display: flex;
justify-content: flex-end;
align-items: center;
`;
const ExportToCodesandboxWrapper = styled.div`
margin-top: 16px;
`;

const Action = styled.div`
margin: auto 0;
padding-top: 5px;
`;

const InfoText = styled.div``;

const TokenRow = styled.div`
display: flex;
align-items: center;
`;

const mapStateToProps = state => {
const project = getSelectedProject(state);

return {
project,
exportingActive: getExportingActiveStatus(state, project && project.id),
codesandboxToken: getCodesandboxToken(state),
};
};

const mapDispatchToProps = {
exportToCodesandbox: actions.exportToCodesandboxStart,
logout: actions.logoutCodesandbox,
updateToken: actions.updateCodesandboxToken,
};

export default connect(
mapStateToProps,
mapDispatchToProps
)(ExportToCodesandbox);
2 changes: 2 additions & 0 deletions src/components/ExportToCodesandbox/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// @flow
export { default } from './ExportToCodesandbox';
20 changes: 16 additions & 4 deletions src/components/FormField/FormField.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,28 @@ type Props = {
isFocused?: boolean,
hasError?: boolean,
children: React$Node,
spacing: number,
};

class FormField extends PureComponent<Props> {
static defaultProps = {
spacing: 30,
};

render() {
const { label, useLabelTag, isFocused, hasError, children } = this.props;
const {
label,
useLabelTag,
isFocused,
hasError,
children,
spacing,
} = this.props;

const Wrapper = useLabelTag ? WrapperLabel : WrapperDiv;

return (
<Wrapper>
<Wrapper spacing={spacing}>
<LabelText isFocused={isFocused} hasError={hasError}>
{label}
</LabelText>
Expand All @@ -43,11 +55,11 @@ const getTextColor = (props: Props) => {

const WrapperLabel = styled.label`
display: block;
margin-bottom: 30px;
margin-bottom: ${props => props.spacing}px;
`;

const WrapperDiv = styled.div`
margin-bottom: 30px;
margin-bottom: ${props => props.spacing}px;
`;

const LabelText = styled(Label)`
Expand Down
1 change: 0 additions & 1 deletion src/components/Modal/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ class Modal extends PureComponent<Props, State> {
{({ interpolatedTranslateY, opacity }) => (
<Wrapper opacity={opacity} clickable={!inTransit}>
<Backdrop onClick={onDismiss} />

<PaneWrapper
width={width}
height={height}
Expand Down
Loading