🚨 ignore proptype errors
This commit is contained in:
parent
7dbcde452b
commit
de0e32f6a3
14 changed files with 454 additions and 259 deletions
|
@ -5,13 +5,20 @@ import EditIcon from '../../../images/pencil.svg';
|
||||||
|
|
||||||
// TODO I think this needs a description prop so that it's accessible
|
// TODO I think this needs a description prop so that it's accessible
|
||||||
function EditableInput({
|
function EditableInput({
|
||||||
validate, value, emptyPlaceholder, InputComponent, inputProps, onChange
|
validate,
|
||||||
|
value,
|
||||||
|
emptyPlaceholder,
|
||||||
|
InputComponent,
|
||||||
|
inputProps,
|
||||||
|
onChange,
|
||||||
}) {
|
}) {
|
||||||
const [isEditing, setIsEditing] = React.useState(false);
|
const [isEditing, setIsEditing] = React.useState(false);
|
||||||
const [currentValue, setCurrentValue] = React.useState(value || '');
|
const [currentValue, setCurrentValue] = React.useState(value || '');
|
||||||
const displayValue = currentValue || emptyPlaceholder;
|
const displayValue = currentValue || emptyPlaceholder;
|
||||||
const hasValue = currentValue !== '';
|
const hasValue = currentValue !== '';
|
||||||
const classes = `editable-input editable-input--${isEditing ? 'is-editing' : 'is-not-editing'} editable-input--${hasValue ? 'has-value' : 'has-placeholder'}`;
|
const classes = `editable-input editable-input--${
|
||||||
|
isEditing ? 'is-editing' : 'is-not-editing'
|
||||||
|
} editable-input--${hasValue ? 'has-value' : 'has-placeholder'}`;
|
||||||
const inputRef = React.createRef();
|
const inputRef = React.createRef();
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
@ -54,7 +61,11 @@ function EditableInput({
|
||||||
aria-label={`Edit ${displayValue} value`}
|
aria-label={`Edit ${displayValue} value`}
|
||||||
>
|
>
|
||||||
<span>{displayValue}</span>
|
<span>{displayValue}</span>
|
||||||
<EditIcon className="editable-input__icon" focusable="false" aria-hidden="true" />
|
<EditIcon
|
||||||
|
className="editable-input__icon"
|
||||||
|
focusable="false"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<InputComponent
|
<InputComponent
|
||||||
|
@ -68,7 +79,7 @@ function EditableInput({
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
value={currentValue}
|
value={currentValue}
|
||||||
/>
|
/>
|
||||||
</span >
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +95,7 @@ EditableInput.propTypes = {
|
||||||
emptyPlaceholder: PropTypes.string,
|
emptyPlaceholder: PropTypes.string,
|
||||||
InputComponent: PropTypes.elementType,
|
InputComponent: PropTypes.elementType,
|
||||||
// eslint-disable-next-line react/forbid-prop-types
|
// eslint-disable-next-line react/forbid-prop-types
|
||||||
inputProps: PropTypes.object,
|
inputProps: PropTypes.object, // eslint-disable-line
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
validate: PropTypes.func,
|
validate: PropTypes.func,
|
||||||
value: PropTypes.string,
|
value: PropTypes.string,
|
||||||
|
|
|
@ -15,7 +15,10 @@ class NewFileForm extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { fields: { name }, handleSubmit } = this.props;
|
const {
|
||||||
|
fields: { name },
|
||||||
|
handleSubmit,
|
||||||
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
className="new-file-form"
|
className="new-file-form"
|
||||||
|
@ -25,7 +28,9 @@ class NewFileForm extends React.Component {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="new-file-form__input-wrapper">
|
<div className="new-file-form__input-wrapper">
|
||||||
<label className="new-file-form__name-label" htmlFor="name">Name:</label>
|
<label className="new-file-form__name-label" htmlFor="name">
|
||||||
|
Name:
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
className="new-file-form__name-input"
|
className="new-file-form__name-input"
|
||||||
id="name"
|
id="name"
|
||||||
|
@ -33,14 +38,15 @@ class NewFileForm extends React.Component {
|
||||||
placeholder="Name"
|
placeholder="Name"
|
||||||
maxLength="128"
|
maxLength="128"
|
||||||
{...domOnlyProps(name)}
|
{...domOnlyProps(name)}
|
||||||
ref={(element) => { this.fileName = element; }}
|
ref={(element) => {
|
||||||
|
this.fileName = element;
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button type="submit">Add File</Button>
|
||||||
type="submit"
|
|
||||||
>Add File
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
{name.touched && name.error && <span className="form-error">{name.error}</span>}
|
{name.touched && name.error && (
|
||||||
|
<span className="form-error">{name.error}</span>
|
||||||
|
)}
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -48,11 +54,11 @@ class NewFileForm extends React.Component {
|
||||||
|
|
||||||
NewFileForm.propTypes = {
|
NewFileForm.propTypes = {
|
||||||
fields: PropTypes.shape({
|
fields: PropTypes.shape({
|
||||||
name: PropTypes.object.isRequired
|
name: PropTypes.object.isRequired, // eslint-disable-line
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
handleSubmit: PropTypes.func.isRequired,
|
handleSubmit: PropTypes.func.isRequired,
|
||||||
createFile: PropTypes.func.isRequired,
|
createFile: PropTypes.func.isRequired,
|
||||||
focusOnModal: PropTypes.func.isRequired
|
focusOnModal: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NewFileForm;
|
export default NewFileForm;
|
||||||
|
|
|
@ -16,7 +16,8 @@ class NewFolderForm extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
fields: { name }, handleSubmit
|
fields: { name },
|
||||||
|
handleSubmit,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
|
@ -26,22 +27,25 @@ class NewFolderForm extends React.Component {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="new-folder-form__input-wrapper">
|
<div className="new-folder-form__input-wrapper">
|
||||||
<label className="new-folder-form__name-label" htmlFor="name">Name:</label>
|
<label className="new-folder-form__name-label" htmlFor="name">
|
||||||
|
Name:
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
className="new-folder-form__name-input"
|
className="new-folder-form__name-input"
|
||||||
id="name"
|
id="name"
|
||||||
type="text"
|
type="text"
|
||||||
maxLength="128"
|
maxLength="128"
|
||||||
placeholder="Name"
|
placeholder="Name"
|
||||||
ref={(element) => { this.fileName = element; }}
|
ref={(element) => {
|
||||||
|
this.fileName = element;
|
||||||
|
}}
|
||||||
{...domOnlyProps(name)}
|
{...domOnlyProps(name)}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button type="submit">Add Folder</Button>
|
||||||
type="submit"
|
|
||||||
>Add Folder
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
{name.touched && name.error && <span className="form-error">{name.error}</span>}
|
{name.touched && name.error && (
|
||||||
|
<span className="form-error">{name.error}</span>
|
||||||
|
)}
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -49,16 +53,16 @@ class NewFolderForm extends React.Component {
|
||||||
|
|
||||||
NewFolderForm.propTypes = {
|
NewFolderForm.propTypes = {
|
||||||
fields: PropTypes.shape({
|
fields: PropTypes.shape({
|
||||||
name: PropTypes.object.isRequired
|
name: PropTypes.object.isRequired, // eslint-disable-line
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
handleSubmit: PropTypes.func.isRequired,
|
handleSubmit: PropTypes.func.isRequired,
|
||||||
createFolder: PropTypes.func.isRequired,
|
createFolder: PropTypes.func.isRequired,
|
||||||
closeModal: PropTypes.func.isRequired,
|
closeModal: PropTypes.func.isRequired,
|
||||||
submitting: PropTypes.bool,
|
submitting: PropTypes.bool,
|
||||||
pristine: PropTypes.bool
|
pristine: PropTypes.bool,
|
||||||
};
|
};
|
||||||
NewFolderForm.defaultProps = {
|
NewFolderForm.defaultProps = {
|
||||||
submitting: false,
|
submitting: false,
|
||||||
pristine: true
|
pristine: true,
|
||||||
};
|
};
|
||||||
export default NewFolderForm;
|
export default NewFolderForm;
|
||||||
|
|
|
@ -44,13 +44,22 @@ function isUserOwner(props) {
|
||||||
return props.project.owner && props.project.owner.id === props.user.id;
|
return props.project.owner && props.project.owner.id === props.user.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
function warnIfUnsavedChanges(props) { // eslint-disable-line
|
function warnIfUnsavedChanges(props) {
|
||||||
|
// eslint-disable-line
|
||||||
const { route } = props.route;
|
const { route } = props.route;
|
||||||
if (route && (route.action === 'PUSH' && (route.pathname === '/login' || route.pathname === '/signup'))) {
|
if (
|
||||||
|
route &&
|
||||||
|
route.action === 'PUSH' &&
|
||||||
|
(route.pathname === '/login' || route.pathname === '/signup')
|
||||||
|
) {
|
||||||
// don't warn
|
// don't warn
|
||||||
props.persistState();
|
props.persistState();
|
||||||
window.onbeforeunload = null;
|
window.onbeforeunload = null;
|
||||||
} else if (route && (props.location.pathname === '/login' || props.location.pathname === '/signup')) {
|
} else if (
|
||||||
|
route &&
|
||||||
|
(props.location.pathname === '/login' ||
|
||||||
|
props.location.pathname === '/signup')
|
||||||
|
) {
|
||||||
// don't warn
|
// don't warn
|
||||||
props.persistState();
|
props.persistState();
|
||||||
window.onbeforeunload = null;
|
window.onbeforeunload = null;
|
||||||
|
@ -61,6 +70,7 @@ function warnIfUnsavedChanges(props) { // eslint-disable-line
|
||||||
props.setUnsavedChanges(false);
|
props.setUnsavedChanges(false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
class IDEView extends React.Component {
|
class IDEView extends React.Component {
|
||||||
|
@ -70,7 +80,7 @@ class IDEView extends React.Component {
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
consoleSize: props.ide.consoleIsExpanded ? 150 : 29,
|
consoleSize: props.ide.consoleIsExpanded ? 150 : 29,
|
||||||
sidebarSize: props.ide.sidebarIsExpanded ? 160 : 20
|
sidebarSize: props.ide.sidebarIsExpanded ? 160 : 20,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +100,10 @@ class IDEView extends React.Component {
|
||||||
this.isMac = navigator.userAgent.toLowerCase().indexOf('mac') !== -1;
|
this.isMac = navigator.userAgent.toLowerCase().indexOf('mac') !== -1;
|
||||||
document.addEventListener('keydown', this.handleGlobalKeydown, false);
|
document.addEventListener('keydown', this.handleGlobalKeydown, false);
|
||||||
|
|
||||||
this.props.router.setRouteLeaveHook(this.props.route, this.handleUnsavedChanges);
|
this.props.router.setRouteLeaveHook(
|
||||||
|
this.props.route,
|
||||||
|
this.handleUnsavedChanges
|
||||||
|
);
|
||||||
|
|
||||||
window.onbeforeunload = this.handleUnsavedChanges;
|
window.onbeforeunload = this.handleUnsavedChanges;
|
||||||
|
|
||||||
|
@ -103,11 +116,15 @@ class IDEView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.ide.consoleIsExpanded !== nextProps.ide.consoleIsExpanded) {
|
if (this.props.ide.consoleIsExpanded !== nextProps.ide.consoleIsExpanded) {
|
||||||
this.setState({ consoleSize: nextProps.ide.consoleIsExpanded ? 150 : 29 });
|
this.setState({
|
||||||
|
consoleSize: nextProps.ide.consoleIsExpanded ? 150 : 29,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.ide.sidebarIsExpanded !== nextProps.ide.sidebarIsExpanded) {
|
if (this.props.ide.sidebarIsExpanded !== nextProps.ide.sidebarIsExpanded) {
|
||||||
this.setState({ sidebarSize: nextProps.ide.sidebarIsExpanded ? 160 : 20 });
|
this.setState({
|
||||||
|
sidebarSize: nextProps.ide.sidebarIsExpanded ? 160 : 20,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,10 +138,15 @@ class IDEView extends React.Component {
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
if (isUserOwner(this.props) && this.props.project.id) {
|
if (isUserOwner(this.props) && this.props.project.id) {
|
||||||
if (this.props.preferences.autosave && this.props.ide.unsavedChanges && !this.props.ide.justOpenedProject) {
|
if (
|
||||||
|
this.props.preferences.autosave &&
|
||||||
|
this.props.ide.unsavedChanges &&
|
||||||
|
!this.props.ide.justOpenedProject
|
||||||
|
) {
|
||||||
if (
|
if (
|
||||||
this.props.selectedFile.name === prevProps.selectedFile.name &&
|
this.props.selectedFile.name === prevProps.selectedFile.name &&
|
||||||
this.props.selectedFile.content !== prevProps.selectedFile.content) {
|
this.props.selectedFile.content !== prevProps.selectedFile.content
|
||||||
|
) {
|
||||||
if (this.autosaveInterval) {
|
if (this.autosaveInterval) {
|
||||||
clearTimeout(this.autosaveInterval);
|
clearTimeout(this.autosaveInterval);
|
||||||
}
|
}
|
||||||
|
@ -141,7 +163,8 @@ class IDEView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.route.path !== prevProps.route.path) {
|
if (this.props.route.path !== prevProps.route.path) {
|
||||||
this.props.router.setRouteLeaveHook(this.props.route, () => warnIfUnsavedChanges(this.props));
|
this.props.router.setRouteLeaveHook(this.props.route, () =>
|
||||||
|
warnIfUnsavedChanges(this.props));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,10 +176,16 @@ class IDEView extends React.Component {
|
||||||
|
|
||||||
handleGlobalKeydown(e) {
|
handleGlobalKeydown(e) {
|
||||||
// 83 === s
|
// 83 === s
|
||||||
if (e.keyCode === 83 && ((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac))) {
|
if (
|
||||||
|
e.keyCode === 83 &&
|
||||||
|
((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac))
|
||||||
|
) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (isUserOwner(this.props) || (this.props.user.authenticated && !this.props.project.owner)) {
|
if (
|
||||||
|
isUserOwner(this.props) ||
|
||||||
|
(this.props.user.authenticated && !this.props.project.owner)
|
||||||
|
) {
|
||||||
this.props.saveProject(this.cmController.getContent());
|
this.props.saveProject(this.cmController.getContent());
|
||||||
} else if (this.props.user.authenticated) {
|
} else if (this.props.user.authenticated) {
|
||||||
this.props.cloneProject();
|
this.props.cloneProject();
|
||||||
|
@ -164,23 +193,41 @@ class IDEView extends React.Component {
|
||||||
this.props.showErrorModal('forceAuthentication');
|
this.props.showErrorModal('forceAuthentication');
|
||||||
}
|
}
|
||||||
// 13 === enter
|
// 13 === enter
|
||||||
} else if (e.keyCode === 13 && e.shiftKey && ((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac))) {
|
} else if (
|
||||||
|
e.keyCode === 13 &&
|
||||||
|
e.shiftKey &&
|
||||||
|
((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac))
|
||||||
|
) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.props.stopSketch();
|
this.props.stopSketch();
|
||||||
} else if (e.keyCode === 13 && ((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac))) {
|
} else if (
|
||||||
|
e.keyCode === 13 &&
|
||||||
|
((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac))
|
||||||
|
) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.props.startSketch();
|
this.props.startSketch();
|
||||||
// 50 === 2
|
// 50 === 2
|
||||||
} else if (e.keyCode === 50 && ((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac)) && e.shiftKey) {
|
} else if (
|
||||||
|
e.keyCode === 50 &&
|
||||||
|
((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac)) &&
|
||||||
|
e.shiftKey
|
||||||
|
) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.props.setAllAccessibleOutput(false);
|
this.props.setAllAccessibleOutput(false);
|
||||||
// 49 === 1
|
// 49 === 1
|
||||||
} else if (e.keyCode === 49 && ((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac)) && e.shiftKey) {
|
} else if (
|
||||||
|
e.keyCode === 49 &&
|
||||||
|
((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac)) &&
|
||||||
|
e.shiftKey
|
||||||
|
) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.props.setAllAccessibleOutput(true);
|
this.props.setAllAccessibleOutput(true);
|
||||||
} else if (e.keyCode === 66 && ((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac))) {
|
} else if (
|
||||||
|
e.keyCode === 66 &&
|
||||||
|
((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac))
|
||||||
|
) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!this.props.ide.sidebarIsExpanded) {
|
if (!this.props.ide.sidebarIsExpanded) {
|
||||||
this.props.expandSidebar();
|
this.props.expandSidebar();
|
||||||
|
@ -219,7 +266,7 @@ class IDEView extends React.Component {
|
||||||
cmController={this.cmController}
|
cmController={this.cmController}
|
||||||
/>
|
/>
|
||||||
<Toolbar key={this.props.project.id} />
|
<Toolbar key={this.props.project.id} />
|
||||||
{this.props.ide.preferencesIsVisible &&
|
{this.props.ide.preferencesIsVisible && (
|
||||||
<Overlay
|
<Overlay
|
||||||
title={this.props.t('Settings')}
|
title={this.props.t('Settings')}
|
||||||
ariaLabel="settings"
|
ariaLabel="settings"
|
||||||
|
@ -246,7 +293,7 @@ class IDEView extends React.Component {
|
||||||
setTheme={this.props.setTheme}
|
setTheme={this.props.setTheme}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
}
|
)}
|
||||||
<main className="editor-preview-container">
|
<main className="editor-preview-container">
|
||||||
<SplitPane
|
<SplitPane
|
||||||
split="vertical"
|
split="vertical"
|
||||||
|
@ -275,10 +322,17 @@ class IDEView extends React.Component {
|
||||||
<SplitPane
|
<SplitPane
|
||||||
split="vertical"
|
split="vertical"
|
||||||
defaultSize="50%"
|
defaultSize="50%"
|
||||||
onChange={() => { this.overlay.style.display = 'block'; }}
|
onChange={() => {
|
||||||
onDragFinished={() => { this.overlay.style.display = 'none'; }}
|
this.overlay.style.display = 'block';
|
||||||
|
}}
|
||||||
|
onDragFinished={() => {
|
||||||
|
this.overlay.style.display = 'none';
|
||||||
|
}}
|
||||||
resizerStyle={{
|
resizerStyle={{
|
||||||
borderLeftWidth: '2px', borderRightWidth: '2px', width: '2px', margin: '0px 0px'
|
borderLeftWidth: '2px',
|
||||||
|
borderRightWidth: '2px',
|
||||||
|
width: '2px',
|
||||||
|
margin: '0px 0px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SplitPane
|
<SplitPane
|
||||||
|
@ -304,7 +358,9 @@ class IDEView extends React.Component {
|
||||||
editorOptionsVisible={this.props.ide.editorOptionsVisible}
|
editorOptionsVisible={this.props.ide.editorOptionsVisible}
|
||||||
showEditorOptions={this.props.showEditorOptions}
|
showEditorOptions={this.props.showEditorOptions}
|
||||||
closeEditorOptions={this.props.closeEditorOptions}
|
closeEditorOptions={this.props.closeEditorOptions}
|
||||||
showKeyboardShortcutModal={this.props.showKeyboardShortcutModal}
|
showKeyboardShortcutModal={
|
||||||
|
this.props.showKeyboardShortcutModal
|
||||||
|
}
|
||||||
setUnsavedChanges={this.props.setUnsavedChanges}
|
setUnsavedChanges={this.props.setUnsavedChanges}
|
||||||
isPlaying={this.props.ide.isPlaying}
|
isPlaying={this.props.ide.isPlaying}
|
||||||
theme={this.props.preferences.theme}
|
theme={this.props.preferences.theme}
|
||||||
|
@ -321,37 +377,44 @@ class IDEView extends React.Component {
|
||||||
consoleEvents={this.props.console}
|
consoleEvents={this.props.console}
|
||||||
showRuntimeErrorWarning={this.props.showRuntimeErrorWarning}
|
showRuntimeErrorWarning={this.props.showRuntimeErrorWarning}
|
||||||
hideRuntimeErrorWarning={this.props.hideRuntimeErrorWarning}
|
hideRuntimeErrorWarning={this.props.hideRuntimeErrorWarning}
|
||||||
runtimeErrorWarningVisible={this.props.ide.runtimeErrorWarningVisible}
|
runtimeErrorWarningVisible={
|
||||||
provideController={(ctl) => { this.cmController = ctl; }}
|
this.props.ide.runtimeErrorWarningVisible
|
||||||
|
}
|
||||||
|
provideController={(ctl) => {
|
||||||
|
this.cmController = ctl;
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Console />
|
<Console />
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
<section className="preview-frame-holder">
|
<section className="preview-frame-holder">
|
||||||
<header className="preview-frame__header">
|
<header className="preview-frame__header">
|
||||||
<h2 className="preview-frame__title">{this.props.t('Preview')}</h2>
|
<h2 className="preview-frame__title">
|
||||||
|
{this.props.t('Preview')}
|
||||||
|
</h2>
|
||||||
</header>
|
</header>
|
||||||
<div className="preview-frame__content">
|
<div className="preview-frame__content">
|
||||||
<div className="preview-frame-overlay" ref={(element) => { this.overlay = element; }}>
|
<div
|
||||||
|
className="preview-frame-overlay"
|
||||||
|
ref={(element) => {
|
||||||
|
this.overlay = element;
|
||||||
|
}}
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{(
|
{((this.props.preferences.textOutput ||
|
||||||
(
|
this.props.preferences.gridOutput ||
|
||||||
(this.props.preferences.textOutput ||
|
this.props.preferences.soundOutput) &&
|
||||||
this.props.preferences.gridOutput ||
|
this.props.ide.isPlaying) ||
|
||||||
this.props.preferences.soundOutput
|
this.props.ide.isAccessibleOutputPlaying}
|
||||||
) &&
|
|
||||||
this.props.ide.isPlaying
|
|
||||||
) ||
|
|
||||||
this.props.ide.isAccessibleOutputPlaying
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
<PreviewFrame
|
<PreviewFrame
|
||||||
htmlFile={this.props.htmlFile}
|
htmlFile={this.props.htmlFile}
|
||||||
files={this.props.files}
|
files={this.props.files}
|
||||||
content={this.props.selectedFile.content}
|
content={this.props.selectedFile.content}
|
||||||
isPlaying={this.props.ide.isPlaying}
|
isPlaying={this.props.ide.isPlaying}
|
||||||
isAccessibleOutputPlaying={this.props.ide.isAccessibleOutputPlaying}
|
isAccessibleOutputPlaying={
|
||||||
|
this.props.ide.isAccessibleOutputPlaying
|
||||||
|
}
|
||||||
textOutput={this.props.preferences.textOutput}
|
textOutput={this.props.preferences.textOutput}
|
||||||
gridOutput={this.props.preferences.gridOutput}
|
gridOutput={this.props.preferences.gridOutput}
|
||||||
soundOutput={this.props.preferences.soundOutput}
|
soundOutput={this.props.preferences.soundOutput}
|
||||||
|
@ -373,21 +436,17 @@ class IDEView extends React.Component {
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</main>
|
</main>
|
||||||
{ this.props.ide.modalIsVisible &&
|
{this.props.ide.modalIsVisible && <NewFileModal />}
|
||||||
<NewFileModal />
|
{this.props.ide.newFolderModalVisible && (
|
||||||
}
|
|
||||||
{this.props.ide.newFolderModalVisible &&
|
|
||||||
<NewFolderModal
|
<NewFolderModal
|
||||||
closeModal={this.props.closeNewFolderModal}
|
closeModal={this.props.closeNewFolderModal}
|
||||||
createFolder={this.props.createFolder}
|
createFolder={this.props.createFolder}
|
||||||
/>
|
/>
|
||||||
}
|
)}
|
||||||
{this.props.ide.uploadFileModalVisible &&
|
{this.props.ide.uploadFileModalVisible && (
|
||||||
<UploadFileModal
|
<UploadFileModal closeModal={this.props.closeUploadFileModal} />
|
||||||
closeModal={this.props.closeUploadFileModal}
|
)}
|
||||||
/>
|
{this.props.location.pathname === '/about' && (
|
||||||
}
|
|
||||||
{ this.props.location.pathname === '/about' &&
|
|
||||||
<Overlay
|
<Overlay
|
||||||
title={this.props.t('About')}
|
title={this.props.t('About')}
|
||||||
previousPath={this.props.ide.previousPath}
|
previousPath={this.props.ide.previousPath}
|
||||||
|
@ -395,8 +454,8 @@ class IDEView extends React.Component {
|
||||||
>
|
>
|
||||||
<About previousPath={this.props.ide.previousPath} />
|
<About previousPath={this.props.ide.previousPath} />
|
||||||
</Overlay>
|
</Overlay>
|
||||||
}
|
)}
|
||||||
{this.props.location.pathname === '/feedback' &&
|
{this.props.location.pathname === '/feedback' && (
|
||||||
<Overlay
|
<Overlay
|
||||||
title="Submit Feedback"
|
title="Submit Feedback"
|
||||||
previousPath={this.props.ide.previousPath}
|
previousPath={this.props.ide.previousPath}
|
||||||
|
@ -404,8 +463,8 @@ class IDEView extends React.Component {
|
||||||
>
|
>
|
||||||
<Feedback previousPath={this.props.ide.previousPath} />
|
<Feedback previousPath={this.props.ide.previousPath} />
|
||||||
</Overlay>
|
</Overlay>
|
||||||
}
|
)}
|
||||||
{this.props.location.pathname.match(/add-to-collection$/) &&
|
{this.props.location.pathname.match(/add-to-collection$/) && (
|
||||||
<Overlay
|
<Overlay
|
||||||
ariaLabel="add to collection"
|
ariaLabel="add to collection"
|
||||||
title="Add to collection"
|
title="Add to collection"
|
||||||
|
@ -419,8 +478,8 @@ class IDEView extends React.Component {
|
||||||
user={this.props.user}
|
user={this.props.user}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
}
|
)}
|
||||||
{this.props.ide.shareModalVisible &&
|
{this.props.ide.shareModalVisible && (
|
||||||
<Overlay
|
<Overlay
|
||||||
title="Share"
|
title="Share"
|
||||||
ariaLabel="share"
|
ariaLabel="share"
|
||||||
|
@ -432,8 +491,8 @@ class IDEView extends React.Component {
|
||||||
ownerUsername={this.props.ide.shareModalProjectUsername}
|
ownerUsername={this.props.ide.shareModalProjectUsername}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
}
|
)}
|
||||||
{this.props.ide.keyboardShortcutVisible &&
|
{this.props.ide.keyboardShortcutVisible && (
|
||||||
<Overlay
|
<Overlay
|
||||||
title={this.props.t('KeyboardShortcuts')}
|
title={this.props.t('KeyboardShortcuts')}
|
||||||
ariaLabel="keyboard shortcuts"
|
ariaLabel="keyboard shortcuts"
|
||||||
|
@ -441,8 +500,8 @@ class IDEView extends React.Component {
|
||||||
>
|
>
|
||||||
<KeyboardShortcutModal />
|
<KeyboardShortcutModal />
|
||||||
</Overlay>
|
</Overlay>
|
||||||
}
|
)}
|
||||||
{this.props.ide.errorType &&
|
{this.props.ide.errorType && (
|
||||||
<Overlay
|
<Overlay
|
||||||
title="Error"
|
title="Error"
|
||||||
ariaLabel="error"
|
ariaLabel="error"
|
||||||
|
@ -453,7 +512,7 @@ class IDEView extends React.Component {
|
||||||
closeModal={this.props.hideErrorModal}
|
closeModal={this.props.hideErrorModal}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -466,19 +525,19 @@ IDEView.propTypes = {
|
||||||
reset_password_token: PropTypes.string,
|
reset_password_token: PropTypes.string,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
location: PropTypes.shape({
|
location: PropTypes.shape({
|
||||||
pathname: PropTypes.string
|
pathname: PropTypes.string,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
getProject: PropTypes.func.isRequired,
|
getProject: PropTypes.func.isRequired,
|
||||||
user: PropTypes.shape({
|
user: PropTypes.shape({
|
||||||
authenticated: PropTypes.bool.isRequired,
|
authenticated: PropTypes.bool.isRequired,
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
username: PropTypes.string
|
username: PropTypes.string,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
saveProject: PropTypes.func.isRequired,
|
saveProject: PropTypes.func.isRequired,
|
||||||
ide: PropTypes.shape({
|
ide: PropTypes.shape({
|
||||||
isPlaying: PropTypes.bool.isRequired,
|
isPlaying: PropTypes.bool.isRequired,
|
||||||
isAccessibleOutputPlaying: PropTypes.bool.isRequired,
|
isAccessibleOutputPlaying: PropTypes.bool.isRequired,
|
||||||
consoleEvent: PropTypes.array,
|
consoleEvent: PropTypes.array, // eslint-disable-line
|
||||||
modalIsVisible: PropTypes.bool.isRequired,
|
modalIsVisible: PropTypes.bool.isRequired,
|
||||||
sidebarIsExpanded: PropTypes.bool.isRequired,
|
sidebarIsExpanded: PropTypes.bool.isRequired,
|
||||||
consoleIsExpanded: PropTypes.bool.isRequired,
|
consoleIsExpanded: PropTypes.bool.isRequired,
|
||||||
|
@ -500,7 +559,7 @@ IDEView.propTypes = {
|
||||||
justOpenedProject: PropTypes.bool.isRequired,
|
justOpenedProject: PropTypes.bool.isRequired,
|
||||||
errorType: PropTypes.string,
|
errorType: PropTypes.string,
|
||||||
runtimeErrorWarningVisible: PropTypes.bool.isRequired,
|
runtimeErrorWarningVisible: PropTypes.bool.isRequired,
|
||||||
uploadFileModalVisible: PropTypes.bool.isRequired
|
uploadFileModalVisible: PropTypes.bool.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
stopSketch: PropTypes.func.isRequired,
|
stopSketch: PropTypes.func.isRequired,
|
||||||
project: PropTypes.shape({
|
project: PropTypes.shape({
|
||||||
|
@ -508,12 +567,12 @@ IDEView.propTypes = {
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
owner: PropTypes.shape({
|
owner: PropTypes.shape({
|
||||||
username: PropTypes.string,
|
username: PropTypes.string,
|
||||||
id: PropTypes.string
|
id: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
updatedAt: PropTypes.string
|
updatedAt: PropTypes.string,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
editorAccessibility: PropTypes.shape({
|
editorAccessibility: PropTypes.shape({
|
||||||
lintMessages: PropTypes.array.isRequired,
|
lintMessages: PropTypes.array.isRequired, // eslint-disable-line
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
updateLintMessage: PropTypes.func.isRequired,
|
updateLintMessage: PropTypes.func.isRequired,
|
||||||
clearLintMessage: PropTypes.func.isRequired,
|
clearLintMessage: PropTypes.func.isRequired,
|
||||||
|
@ -527,7 +586,7 @@ IDEView.propTypes = {
|
||||||
gridOutput: PropTypes.bool.isRequired,
|
gridOutput: PropTypes.bool.isRequired,
|
||||||
soundOutput: PropTypes.bool.isRequired,
|
soundOutput: PropTypes.bool.isRequired,
|
||||||
theme: PropTypes.string.isRequired,
|
theme: PropTypes.string.isRequired,
|
||||||
autorefresh: PropTypes.bool.isRequired
|
autorefresh: PropTypes.bool.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
closePreferences: PropTypes.func.isRequired,
|
closePreferences: PropTypes.func.isRequired,
|
||||||
setFontSize: PropTypes.func.isRequired,
|
setFontSize: PropTypes.func.isRequired,
|
||||||
|
@ -542,19 +601,19 @@ IDEView.propTypes = {
|
||||||
files: PropTypes.arrayOf(PropTypes.shape({
|
files: PropTypes.arrayOf(PropTypes.shape({
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
content: PropTypes.string.isRequired
|
content: PropTypes.string.isRequired,
|
||||||
})).isRequired,
|
})).isRequired,
|
||||||
updateFileContent: PropTypes.func.isRequired,
|
updateFileContent: PropTypes.func.isRequired,
|
||||||
selectedFile: PropTypes.shape({
|
selectedFile: PropTypes.shape({
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
content: PropTypes.string.isRequired,
|
content: PropTypes.string.isRequired,
|
||||||
name: PropTypes.string.isRequired
|
name: PropTypes.string.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
setSelectedFile: PropTypes.func.isRequired,
|
setSelectedFile: PropTypes.func.isRequired,
|
||||||
htmlFile: PropTypes.shape({
|
htmlFile: PropTypes.shape({
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
content: PropTypes.string.isRequired
|
content: PropTypes.string.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
dispatchConsoleEvent: PropTypes.func.isRequired,
|
dispatchConsoleEvent: PropTypes.func.isRequired,
|
||||||
newFile: PropTypes.func.isRequired,
|
newFile: PropTypes.func.isRequired,
|
||||||
|
@ -577,11 +636,11 @@ IDEView.propTypes = {
|
||||||
showKeyboardShortcutModal: PropTypes.func.isRequired,
|
showKeyboardShortcutModal: PropTypes.func.isRequired,
|
||||||
closeKeyboardShortcutModal: PropTypes.func.isRequired,
|
closeKeyboardShortcutModal: PropTypes.func.isRequired,
|
||||||
toast: PropTypes.shape({
|
toast: PropTypes.shape({
|
||||||
isVisible: PropTypes.bool.isRequired
|
isVisible: PropTypes.bool.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
autosaveProject: PropTypes.func.isRequired,
|
autosaveProject: PropTypes.func.isRequired,
|
||||||
router: PropTypes.shape({
|
router: PropTypes.shape({
|
||||||
setRouteLeaveHook: PropTypes.func
|
setRouteLeaveHook: PropTypes.func,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
route: PropTypes.oneOfType([PropTypes.object, PropTypes.element]).isRequired,
|
route: PropTypes.oneOfType([PropTypes.object, PropTypes.element]).isRequired,
|
||||||
setUnsavedChanges: PropTypes.func.isRequired,
|
setUnsavedChanges: PropTypes.func.isRequired,
|
||||||
|
@ -592,7 +651,7 @@ IDEView.propTypes = {
|
||||||
setPreviousPath: PropTypes.func.isRequired,
|
setPreviousPath: PropTypes.func.isRequired,
|
||||||
console: PropTypes.arrayOf(PropTypes.shape({
|
console: PropTypes.arrayOf(PropTypes.shape({
|
||||||
method: PropTypes.string.isRequired,
|
method: PropTypes.string.isRequired,
|
||||||
args: PropTypes.arrayOf(PropTypes.string)
|
args: PropTypes.arrayOf(PropTypes.string),
|
||||||
})).isRequired,
|
})).isRequired,
|
||||||
clearConsole: PropTypes.func.isRequired,
|
clearConsole: PropTypes.func.isRequired,
|
||||||
showErrorModal: PropTypes.func.isRequired,
|
showErrorModal: PropTypes.func.isRequired,
|
||||||
|
@ -603,13 +662,14 @@ IDEView.propTypes = {
|
||||||
startSketch: PropTypes.func.isRequired,
|
startSketch: PropTypes.func.isRequired,
|
||||||
openUploadFileModal: PropTypes.func.isRequired,
|
openUploadFileModal: PropTypes.func.isRequired,
|
||||||
closeUploadFileModal: PropTypes.func.isRequired,
|
closeUploadFileModal: PropTypes.func.isRequired,
|
||||||
t: PropTypes.func.isRequired
|
t: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
function mapStateToProps(state) {
|
||||||
return {
|
return {
|
||||||
files: state.files,
|
files: state.files,
|
||||||
selectedFile: state.files.find(file => file.isSelectedFile) ||
|
selectedFile:
|
||||||
|
state.files.find(file => file.isSelectedFile) ||
|
||||||
state.files.find(file => file.name === 'sketch.js') ||
|
state.files.find(file => file.name === 'sketch.js') ||
|
||||||
state.files.find(file => file.name !== 'root'),
|
state.files.find(file => file.name !== 'root'),
|
||||||
htmlFile: getHTMLFile(state.files),
|
htmlFile: getHTMLFile(state.files),
|
||||||
|
@ -619,7 +679,7 @@ function mapStateToProps(state) {
|
||||||
user: state.user,
|
user: state.user,
|
||||||
project: state.project,
|
project: state.project,
|
||||||
toast: state.toast,
|
toast: state.toast,
|
||||||
console: state.console
|
console: state.console,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,8 +30,8 @@ import Console from '../components/Console';
|
||||||
import { remSize } from '../../../theme';
|
import { remSize } from '../../../theme';
|
||||||
import ActionStrip from '../../../components/mobile/ActionStrip';
|
import ActionStrip from '../../../components/mobile/ActionStrip';
|
||||||
|
|
||||||
const isUserOwner = ({ project, user }) => (project.owner && project.owner.id === user.id);
|
const isUserOwner = ({ project, user }) =>
|
||||||
|
project.owner && project.owner.id === user.id;
|
||||||
|
|
||||||
const Expander = styled.div`
|
const Expander = styled.div`
|
||||||
height: ${props => (props.expanded ? remSize(160) : remSize(27))};
|
height: ${props => (props.expanded ? remSize(160) : remSize(27))};
|
||||||
|
@ -39,11 +39,28 @@ const Expander = styled.div`
|
||||||
|
|
||||||
const MobileIDEView = (props) => {
|
const MobileIDEView = (props) => {
|
||||||
const {
|
const {
|
||||||
preferences, ide, editorAccessibility, project, updateLintMessage, clearLintMessage,
|
preferences,
|
||||||
selectedFile, updateFileContent, files,
|
ide,
|
||||||
closeEditorOptions, showEditorOptions, showKeyboardShortcutModal, setUnsavedChanges,
|
editorAccessibility,
|
||||||
startRefreshSketch, stopSketch, expandSidebar, collapseSidebar, clearConsole, console,
|
project,
|
||||||
showRuntimeErrorWarning, hideRuntimeErrorWarning, startSketch
|
updateLintMessage,
|
||||||
|
clearLintMessage,
|
||||||
|
selectedFile,
|
||||||
|
updateFileContent,
|
||||||
|
files,
|
||||||
|
closeEditorOptions,
|
||||||
|
showEditorOptions,
|
||||||
|
showKeyboardShortcutModal,
|
||||||
|
setUnsavedChanges,
|
||||||
|
startRefreshSketch,
|
||||||
|
stopSketch,
|
||||||
|
expandSidebar,
|
||||||
|
collapseSidebar,
|
||||||
|
clearConsole,
|
||||||
|
console,
|
||||||
|
showRuntimeErrorWarning,
|
||||||
|
hideRuntimeErrorWarning,
|
||||||
|
startSketch,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [tmController, setTmController] = useState(null); // eslint-disable-line
|
const [tmController, setTmController] = useState(null); // eslint-disable-line
|
||||||
|
@ -55,7 +72,11 @@ const MobileIDEView = (props) => {
|
||||||
title={project.name}
|
title={project.name}
|
||||||
subtitle={selectedFile.name}
|
subtitle={selectedFile.name}
|
||||||
leftButton={
|
leftButton={
|
||||||
<IconButton to="/mobile" icon={ExitIcon} aria-label="Return to original editor" />
|
<IconButton
|
||||||
|
to="/mobile"
|
||||||
|
icon={ExitIcon}
|
||||||
|
aria-label="Return to original editor"
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
@ -64,7 +85,14 @@ const MobileIDEView = (props) => {
|
||||||
icon={PreferencesIcon}
|
icon={PreferencesIcon}
|
||||||
aria-label="Open preferences menu"
|
aria-label="Open preferences menu"
|
||||||
/>
|
/>
|
||||||
<IconButton to="/mobile/preview" onClick={() => { startSketch(); }} icon={PlayIcon} aria-label="Run sketch" />
|
<IconButton
|
||||||
|
to="/mobile/preview"
|
||||||
|
onClick={() => {
|
||||||
|
startSketch();
|
||||||
|
}}
|
||||||
|
icon={PlayIcon}
|
||||||
|
aria-label="Run sketch"
|
||||||
|
/>
|
||||||
</Header>
|
</Header>
|
||||||
|
|
||||||
<IDEWrapper>
|
<IDEWrapper>
|
||||||
|
@ -104,16 +132,18 @@ const MobileIDEView = (props) => {
|
||||||
/>
|
/>
|
||||||
</IDEWrapper>
|
</IDEWrapper>
|
||||||
<Footer>
|
<Footer>
|
||||||
{ide.consoleIsExpanded && <Expander expanded><Console /></Expander>}
|
{ide.consoleIsExpanded && (
|
||||||
|
<Expander expanded>
|
||||||
|
<Console />
|
||||||
|
</Expander>
|
||||||
|
)}
|
||||||
<ActionStrip />
|
<ActionStrip />
|
||||||
</Footer>
|
</Footer>
|
||||||
</Screen>
|
</Screen>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
MobileIDEView.propTypes = {
|
MobileIDEView.propTypes = {
|
||||||
|
|
||||||
preferences: PropTypes.shape({
|
preferences: PropTypes.shape({
|
||||||
fontSize: PropTypes.number.isRequired,
|
fontSize: PropTypes.number.isRequired,
|
||||||
autosave: PropTypes.bool.isRequired,
|
autosave: PropTypes.bool.isRequired,
|
||||||
|
@ -124,13 +154,13 @@ MobileIDEView.propTypes = {
|
||||||
gridOutput: PropTypes.bool.isRequired,
|
gridOutput: PropTypes.bool.isRequired,
|
||||||
soundOutput: PropTypes.bool.isRequired,
|
soundOutput: PropTypes.bool.isRequired,
|
||||||
theme: PropTypes.string.isRequired,
|
theme: PropTypes.string.isRequired,
|
||||||
autorefresh: PropTypes.bool.isRequired
|
autorefresh: PropTypes.bool.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
|
|
||||||
ide: PropTypes.shape({
|
ide: PropTypes.shape({
|
||||||
isPlaying: PropTypes.bool.isRequired,
|
isPlaying: PropTypes.bool.isRequired,
|
||||||
isAccessibleOutputPlaying: PropTypes.bool.isRequired,
|
isAccessibleOutputPlaying: PropTypes.bool.isRequired,
|
||||||
consoleEvent: PropTypes.array,
|
consoleEvent: PropTypes.array, // eslint-disable-line
|
||||||
modalIsVisible: PropTypes.bool.isRequired,
|
modalIsVisible: PropTypes.bool.isRequired,
|
||||||
sidebarIsExpanded: PropTypes.bool.isRequired,
|
sidebarIsExpanded: PropTypes.bool.isRequired,
|
||||||
consoleIsExpanded: PropTypes.bool.isRequired,
|
consoleIsExpanded: PropTypes.bool.isRequired,
|
||||||
|
@ -152,11 +182,11 @@ MobileIDEView.propTypes = {
|
||||||
justOpenedProject: PropTypes.bool.isRequired,
|
justOpenedProject: PropTypes.bool.isRequired,
|
||||||
errorType: PropTypes.string,
|
errorType: PropTypes.string,
|
||||||
runtimeErrorWarningVisible: PropTypes.bool.isRequired,
|
runtimeErrorWarningVisible: PropTypes.bool.isRequired,
|
||||||
uploadFileModalVisible: PropTypes.bool.isRequired
|
uploadFileModalVisible: PropTypes.bool.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
|
|
||||||
editorAccessibility: PropTypes.shape({
|
editorAccessibility: PropTypes.shape({
|
||||||
lintMessages: PropTypes.array.isRequired,
|
lintMessages: PropTypes.array.isRequired, // eslint-disable-line
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
|
|
||||||
project: PropTypes.shape({
|
project: PropTypes.shape({
|
||||||
|
@ -164,9 +194,9 @@ MobileIDEView.propTypes = {
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
owner: PropTypes.shape({
|
owner: PropTypes.shape({
|
||||||
username: PropTypes.string,
|
username: PropTypes.string,
|
||||||
id: PropTypes.string
|
id: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
updatedAt: PropTypes.string
|
updatedAt: PropTypes.string,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
|
|
||||||
startSketch: PropTypes.func.isRequired,
|
startSketch: PropTypes.func.isRequired,
|
||||||
|
@ -178,7 +208,7 @@ MobileIDEView.propTypes = {
|
||||||
selectedFile: PropTypes.shape({
|
selectedFile: PropTypes.shape({
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
content: PropTypes.string.isRequired,
|
content: PropTypes.string.isRequired,
|
||||||
name: PropTypes.string.isRequired
|
name: PropTypes.string.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
|
|
||||||
updateFileContent: PropTypes.func.isRequired,
|
updateFileContent: PropTypes.func.isRequired,
|
||||||
|
@ -186,7 +216,7 @@ MobileIDEView.propTypes = {
|
||||||
files: PropTypes.arrayOf(PropTypes.shape({
|
files: PropTypes.arrayOf(PropTypes.shape({
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
content: PropTypes.string.isRequired
|
content: PropTypes.string.isRequired,
|
||||||
})).isRequired,
|
})).isRequired,
|
||||||
|
|
||||||
closeEditorOptions: PropTypes.func.isRequired,
|
closeEditorOptions: PropTypes.func.isRequired,
|
||||||
|
@ -209,7 +239,7 @@ MobileIDEView.propTypes = {
|
||||||
|
|
||||||
console: PropTypes.arrayOf(PropTypes.shape({
|
console: PropTypes.arrayOf(PropTypes.shape({
|
||||||
method: PropTypes.string.isRequired,
|
method: PropTypes.string.isRequired,
|
||||||
args: PropTypes.arrayOf(PropTypes.string)
|
args: PropTypes.arrayOf(PropTypes.string),
|
||||||
})).isRequired,
|
})).isRequired,
|
||||||
|
|
||||||
showRuntimeErrorWarning: PropTypes.func.isRequired,
|
showRuntimeErrorWarning: PropTypes.func.isRequired,
|
||||||
|
@ -219,15 +249,15 @@ MobileIDEView.propTypes = {
|
||||||
user: PropTypes.shape({
|
user: PropTypes.shape({
|
||||||
authenticated: PropTypes.bool.isRequired,
|
authenticated: PropTypes.bool.isRequired,
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
username: PropTypes.string
|
username: PropTypes.string,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
function mapStateToProps(state) {
|
||||||
return {
|
return {
|
||||||
files: state.files,
|
files: state.files,
|
||||||
selectedFile: state.files.find(file => file.isSelectedFile) ||
|
selectedFile:
|
||||||
|
state.files.find(file => file.isSelectedFile) ||
|
||||||
state.files.find(file => file.name === 'sketch.js') ||
|
state.files.find(file => file.name === 'sketch.js') ||
|
||||||
state.files.find(file => file.name !== 'root'),
|
state.files.find(file => file.name !== 'root'),
|
||||||
htmlFile: getHTMLFile(state.files),
|
htmlFile: getHTMLFile(state.files),
|
||||||
|
@ -237,7 +267,7 @@ function mapStateToProps(state) {
|
||||||
user: state.user,
|
user: state.user,
|
||||||
project: state.project,
|
project: state.project,
|
||||||
toast: state.toast,
|
toast: state.toast,
|
||||||
console: state.console
|
console: state.console,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,5 +288,4 @@ function mapDispatchToProps(dispatch) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MobileIDEView));
|
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MobileIDEView));
|
||||||
|
|
16
client/modules/Mobile/MobileExamples.jsx
Normal file
16
client/modules/Mobile/MobileExamples.jsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { bindActionCreators } from 'redux';
|
||||||
|
import Screen from '../../components/mobile/MobileScreen';
|
||||||
|
import Header from '../../components/mobile/Header';
|
||||||
|
import IconButton from '../../components/mobile/IconButton';
|
||||||
|
import { ExitIcon } from '../../common/icons';
|
||||||
|
|
||||||
|
const MobileExamples = () => (
|
||||||
|
<Screen fullscreen>
|
||||||
|
<Header transparent title="My Stuff">
|
||||||
|
<IconButton to="/mobile" icon={ExitIcon} aria-label="Return to ide view" />
|
||||||
|
</Header>
|
||||||
|
</Screen>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default MobileExamples;
|
|
@ -8,8 +8,8 @@ import CopyableInput from '../../IDE/components/CopyableInput';
|
||||||
import APIKeyList from './APIKeyList';
|
import APIKeyList from './APIKeyList';
|
||||||
|
|
||||||
export const APIKeyPropType = PropTypes.shape({
|
export const APIKeyPropType = PropTypes.shape({
|
||||||
id: PropTypes.object.isRequired,
|
id: PropTypes.object.isRequired, // eslint-disable-line
|
||||||
token: PropTypes.object,
|
token: PropTypes.object, // eslint-disable-line
|
||||||
label: PropTypes.string.isRequired,
|
label: PropTypes.string.isRequired,
|
||||||
createdAt: PropTypes.string.isRequired,
|
createdAt: PropTypes.string.isRequired,
|
||||||
lastUsedAt: PropTypes.string,
|
lastUsedAt: PropTypes.string,
|
||||||
|
@ -30,7 +30,7 @@ class APIKeyForm extends React.Component {
|
||||||
const { keyLabel } = this.state;
|
const { keyLabel } = this.state;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
keyLabel: ''
|
keyLabel: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
this.props.createApiKey(keyLabel);
|
this.props.createApiKey(keyLabel);
|
||||||
|
@ -64,18 +64,25 @@ class APIKeyForm extends React.Component {
|
||||||
<div className="api-key-form">
|
<div className="api-key-form">
|
||||||
<p className="api-key-form__summary">
|
<p className="api-key-form__summary">
|
||||||
Personal Access Tokens act like your password to allow automated
|
Personal Access Tokens act like your password to allow automated
|
||||||
scripts to access the Editor API. Create a token for each script
|
scripts to access the Editor API. Create a token for each script that
|
||||||
that needs access.
|
needs access.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="api-key-form__section">
|
<div className="api-key-form__section">
|
||||||
<h3 className="api-key-form__title">Create new token</h3>
|
<h3 className="api-key-form__title">Create new token</h3>
|
||||||
<form className="form form--inline" onSubmit={this.addKey}>
|
<form className="form form--inline" onSubmit={this.addKey}>
|
||||||
<label htmlFor="keyLabel" className="form__label form__label--hidden ">What is this token for?</label>
|
<label
|
||||||
|
htmlFor="keyLabel"
|
||||||
|
className="form__label form__label--hidden "
|
||||||
|
>
|
||||||
|
What is this token for?
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
className="form__input"
|
className="form__input"
|
||||||
id="keyLabel"
|
id="keyLabel"
|
||||||
onChange={(event) => { this.setState({ keyLabel: event.target.value }); }}
|
onChange={(event) => {
|
||||||
|
this.setState({ keyLabel: event.target.value });
|
||||||
|
}}
|
||||||
placeholder="What is this token for? e.g. Example import script"
|
placeholder="What is this token for? e.g. Example import script"
|
||||||
type="text"
|
type="text"
|
||||||
value={this.state.keyLabel}
|
value={this.state.keyLabel}
|
||||||
|
@ -90,18 +97,21 @@ class APIKeyForm extends React.Component {
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{
|
{keyWithToken && (
|
||||||
keyWithToken && (
|
<div className="api-key-form__new-token">
|
||||||
<div className="api-key-form__new-token">
|
<h4 className="api-key-form__new-token__title">
|
||||||
<h4 className="api-key-form__new-token__title">Your new access token</h4>
|
Your new access token
|
||||||
<p className="api-key-form__new-token__info">
|
</h4>
|
||||||
Make sure to copy your new personal access token now.
|
<p className="api-key-form__new-token__info">
|
||||||
You won’t be able to see it again!
|
Make sure to copy your new personal access token now. You won’t
|
||||||
</p>
|
be able to see it again!
|
||||||
<CopyableInput label={keyWithToken.label} value={keyWithToken.token} />
|
</p>
|
||||||
</div>
|
<CopyableInput
|
||||||
)
|
label={keyWithToken.label}
|
||||||
}
|
value={keyWithToken.token}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="api-key-form__section">
|
<div className="api-key-form__section">
|
||||||
|
|
|
@ -13,7 +13,7 @@ function AccountForm(props) {
|
||||||
initiateVerification,
|
initiateVerification,
|
||||||
submitting,
|
submitting,
|
||||||
invalid,
|
invalid,
|
||||||
pristine
|
pristine,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const handleInitiateVerification = (evt) => {
|
const handleInitiateVerification = (evt) => {
|
||||||
|
@ -24,7 +24,9 @@ function AccountForm(props) {
|
||||||
return (
|
return (
|
||||||
<form className="form" onSubmit={handleSubmit(props.updateSettings)}>
|
<form className="form" onSubmit={handleSubmit(props.updateSettings)}>
|
||||||
<p className="form__field">
|
<p className="form__field">
|
||||||
<label htmlFor="email" className="form__label">Email</label>
|
<label htmlFor="email" className="form__label">
|
||||||
|
Email
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
className="form__input"
|
className="form__input"
|
||||||
aria-label="email"
|
aria-label="email"
|
||||||
|
@ -32,30 +34,29 @@ function AccountForm(props) {
|
||||||
id="email"
|
id="email"
|
||||||
{...domOnlyProps(email)}
|
{...domOnlyProps(email)}
|
||||||
/>
|
/>
|
||||||
{email.touched && email.error && <span className="form-error">{email.error}</span>}
|
{email.touched && email.error && (
|
||||||
|
<span className="form-error">{email.error}</span>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
{
|
{user.verified !== 'verified' && (
|
||||||
user.verified !== 'verified' &&
|
<p className="form__context">
|
||||||
(
|
<span className="form__status">Unconfirmed.</span>
|
||||||
<p className="form__context">
|
{user.emailVerificationInitiate === true ? (
|
||||||
<span className="form__status">Unconfirmed.</span>
|
<span className="form__status">
|
||||||
{
|
{' '}
|
||||||
user.emailVerificationInitiate === true ?
|
Confirmation sent, check your email.
|
||||||
(
|
</span>
|
||||||
<span className="form__status"> Confirmation sent, check your email.</span>
|
) : (
|
||||||
) :
|
<Button onClick={handleInitiateVerification}>
|
||||||
(
|
Resend confirmation email
|
||||||
<Button
|
</Button>
|
||||||
onClick={handleInitiateVerification}
|
)}
|
||||||
>Resend confirmation email
|
</p>
|
||||||
</Button>
|
)}
|
||||||
)
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<p className="form__field">
|
<p className="form__field">
|
||||||
<label htmlFor="username" className="form__label">User Name</label>
|
<label htmlFor="username" className="form__label">
|
||||||
|
User Name
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
className="form__input"
|
className="form__input"
|
||||||
aria-label="username"
|
aria-label="username"
|
||||||
|
@ -64,10 +65,14 @@ function AccountForm(props) {
|
||||||
defaultValue={username}
|
defaultValue={username}
|
||||||
{...domOnlyProps(username)}
|
{...domOnlyProps(username)}
|
||||||
/>
|
/>
|
||||||
{username.touched && username.error && <span className="form-error">{username.error}</span>}
|
{username.touched && username.error && (
|
||||||
|
<span className="form-error">{username.error}</span>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
<p className="form__field">
|
<p className="form__field">
|
||||||
<label htmlFor="current password" className="form__label">Current Password</label>
|
<label htmlFor="current password" className="form__label">
|
||||||
|
Current Password
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
className="form__input"
|
className="form__input"
|
||||||
aria-label="currentPassword"
|
aria-label="currentPassword"
|
||||||
|
@ -75,14 +80,14 @@ function AccountForm(props) {
|
||||||
id="currentPassword"
|
id="currentPassword"
|
||||||
{...domOnlyProps(currentPassword)}
|
{...domOnlyProps(currentPassword)}
|
||||||
/>
|
/>
|
||||||
{
|
{currentPassword.touched && currentPassword.error && (
|
||||||
currentPassword.touched &&
|
|
||||||
currentPassword.error &&
|
|
||||||
<span className="form-error">{currentPassword.error}</span>
|
<span className="form-error">{currentPassword.error}</span>
|
||||||
}
|
)}
|
||||||
</p>
|
</p>
|
||||||
<p className="form__field">
|
<p className="form__field">
|
||||||
<label htmlFor="new password" className="form__label">New Password</label>
|
<label htmlFor="new password" className="form__label">
|
||||||
|
New Password
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
className="form__input"
|
className="form__input"
|
||||||
aria-label="newPassword"
|
aria-label="newPassword"
|
||||||
|
@ -90,12 +95,12 @@ function AccountForm(props) {
|
||||||
id="newPassword"
|
id="newPassword"
|
||||||
{...domOnlyProps(newPassword)}
|
{...domOnlyProps(newPassword)}
|
||||||
/>
|
/>
|
||||||
{newPassword.touched && newPassword.error && <span className="form-error">{newPassword.error}</span>}
|
{newPassword.touched && newPassword.error && (
|
||||||
|
<span className="form-error">{newPassword.error}</span>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
<Button
|
<Button type="submit" disabled={submitting || invalid || pristine}>
|
||||||
type="submit"
|
Save All Settings
|
||||||
disabled={submitting || invalid || pristine}
|
|
||||||
>Save All Settings
|
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
@ -103,10 +108,10 @@ function AccountForm(props) {
|
||||||
|
|
||||||
AccountForm.propTypes = {
|
AccountForm.propTypes = {
|
||||||
fields: PropTypes.shape({
|
fields: PropTypes.shape({
|
||||||
username: PropTypes.object.isRequired,
|
username: PropTypes.object.isRequired, // eslint-disable-line
|
||||||
email: PropTypes.object.isRequired,
|
email: PropTypes.object.isRequired, // eslint-disable-line
|
||||||
currentPassword: PropTypes.object.isRequired,
|
currentPassword: PropTypes.object.isRequired, // eslint-disable-line
|
||||||
newPassword: PropTypes.object.isRequired,
|
newPassword: PropTypes.object.isRequired, // eslint-disable-line
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
user: PropTypes.shape({
|
user: PropTypes.shape({
|
||||||
verified: PropTypes.number.isRequired,
|
verified: PropTypes.number.isRequired,
|
||||||
|
@ -123,7 +128,7 @@ AccountForm.propTypes = {
|
||||||
AccountForm.defaultProps = {
|
AccountForm.defaultProps = {
|
||||||
submitting: false,
|
submitting: false,
|
||||||
pristine: true,
|
pristine: true,
|
||||||
invalid: false
|
invalid: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AccountForm;
|
export default AccountForm;
|
||||||
|
|
|
@ -7,12 +7,20 @@ import { domOnlyProps } from '../../../utils/reduxFormUtils';
|
||||||
|
|
||||||
function LoginForm(props) {
|
function LoginForm(props) {
|
||||||
const {
|
const {
|
||||||
fields: { email, password }, handleSubmit, submitting, pristine
|
fields: { email, password },
|
||||||
|
handleSubmit,
|
||||||
|
submitting,
|
||||||
|
pristine,
|
||||||
} = props;
|
} = props;
|
||||||
return (
|
return (
|
||||||
<form className="form" onSubmit={handleSubmit(props.validateAndLoginUser.bind(this, props.previousPath))}>
|
<form
|
||||||
|
className="form"
|
||||||
|
onSubmit={handleSubmit(props.validateAndLoginUser.bind(this, props.previousPath))}
|
||||||
|
>
|
||||||
<p className="form__field">
|
<p className="form__field">
|
||||||
<label htmlFor="email" className="form__label">Email or Username</label>
|
<label htmlFor="email" className="form__label">
|
||||||
|
Email or Username
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
className="form__input"
|
className="form__input"
|
||||||
aria-label="email or username"
|
aria-label="email or username"
|
||||||
|
@ -20,10 +28,14 @@ function LoginForm(props) {
|
||||||
id="email"
|
id="email"
|
||||||
{...domOnlyProps(email)}
|
{...domOnlyProps(email)}
|
||||||
/>
|
/>
|
||||||
{email.touched && email.error && <span className="form-error">{email.error}</span>}
|
{email.touched && email.error && (
|
||||||
|
<span className="form-error">{email.error}</span>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
<p className="form__field">
|
<p className="form__field">
|
||||||
<label htmlFor="password" className="form__label">Password</label>
|
<label htmlFor="password" className="form__label">
|
||||||
|
Password
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
className="form__input"
|
className="form__input"
|
||||||
aria-label="password"
|
aria-label="password"
|
||||||
|
@ -31,12 +43,12 @@ function LoginForm(props) {
|
||||||
id="password"
|
id="password"
|
||||||
{...domOnlyProps(password)}
|
{...domOnlyProps(password)}
|
||||||
/>
|
/>
|
||||||
{password.touched && password.error && <span className="form-error">{password.error}</span>}
|
{password.touched && password.error && (
|
||||||
|
<span className="form-error">{password.error}</span>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
<Button
|
<Button type="submit" disabled={submitting || pristine}>
|
||||||
type="submit"
|
Log In
|
||||||
disabled={submitting || pristine}
|
|
||||||
>Log In
|
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
@ -44,21 +56,21 @@ function LoginForm(props) {
|
||||||
|
|
||||||
LoginForm.propTypes = {
|
LoginForm.propTypes = {
|
||||||
fields: PropTypes.shape({
|
fields: PropTypes.shape({
|
||||||
email: PropTypes.object.isRequired,
|
email: PropTypes.object.isRequired, // eslint-disable-line
|
||||||
password: PropTypes.object.isRequired
|
password: PropTypes.object.isRequired, // eslint-disable-line
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
handleSubmit: PropTypes.func.isRequired,
|
handleSubmit: PropTypes.func.isRequired,
|
||||||
validateAndLoginUser: PropTypes.func.isRequired,
|
validateAndLoginUser: PropTypes.func.isRequired,
|
||||||
submitting: PropTypes.bool,
|
submitting: PropTypes.bool,
|
||||||
pristine: PropTypes.bool,
|
pristine: PropTypes.bool,
|
||||||
invalid: PropTypes.bool,
|
invalid: PropTypes.bool,
|
||||||
previousPath: PropTypes.string.isRequired
|
previousPath: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
LoginForm.defaultProps = {
|
LoginForm.defaultProps = {
|
||||||
submitting: false,
|
submitting: false,
|
||||||
pristine: true,
|
pristine: true,
|
||||||
invalid: false
|
invalid: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LoginForm;
|
export default LoginForm;
|
||||||
|
|
|
@ -6,12 +6,21 @@ import Button from '../../../common/Button';
|
||||||
|
|
||||||
function NewPasswordForm(props) {
|
function NewPasswordForm(props) {
|
||||||
const {
|
const {
|
||||||
fields: { password, confirmPassword }, handleSubmit, submitting, invalid, pristine
|
fields: { password, confirmPassword },
|
||||||
|
handleSubmit,
|
||||||
|
submitting,
|
||||||
|
invalid,
|
||||||
|
pristine,
|
||||||
} = props;
|
} = props;
|
||||||
return (
|
return (
|
||||||
<form className="form" onSubmit={handleSubmit(props.updatePassword.bind(this, props.params.reset_password_token))}>
|
<form
|
||||||
|
className="form"
|
||||||
|
onSubmit={handleSubmit(props.updatePassword.bind(this, props.params.reset_password_token))}
|
||||||
|
>
|
||||||
<p className="form__field">
|
<p className="form__field">
|
||||||
<label htmlFor="password" className="form__label">Password</label>
|
<label htmlFor="password" className="form__label">
|
||||||
|
Password
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
className="form__input"
|
className="form__input"
|
||||||
aria-label="password"
|
aria-label="password"
|
||||||
|
@ -19,10 +28,14 @@ function NewPasswordForm(props) {
|
||||||
id="Password"
|
id="Password"
|
||||||
{...domOnlyProps(password)}
|
{...domOnlyProps(password)}
|
||||||
/>
|
/>
|
||||||
{password.touched && password.error && <span className="form-error">{password.error}</span>}
|
{password.touched && password.error && (
|
||||||
|
<span className="form-error">{password.error}</span>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
<p className="form__field">
|
<p className="form__field">
|
||||||
<label htmlFor="confirm password" className="form__label">Confirm Password</label>
|
<label htmlFor="confirm password" className="form__label">
|
||||||
|
Confirm Password
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
className="form__input"
|
className="form__input"
|
||||||
type="password"
|
type="password"
|
||||||
|
@ -30,21 +43,21 @@ function NewPasswordForm(props) {
|
||||||
id="confirm password"
|
id="confirm password"
|
||||||
{...domOnlyProps(confirmPassword)}
|
{...domOnlyProps(confirmPassword)}
|
||||||
/>
|
/>
|
||||||
{
|
{confirmPassword.touched && confirmPassword.error && (
|
||||||
confirmPassword.touched &&
|
|
||||||
confirmPassword.error &&
|
|
||||||
<span className="form-error">{confirmPassword.error}</span>
|
<span className="form-error">{confirmPassword.error}</span>
|
||||||
}
|
)}
|
||||||
</p>
|
</p>
|
||||||
<Button type="submit" disabled={submitting || invalid || pristine}>Set New Password</Button>
|
<Button type="submit" disabled={submitting || invalid || pristine}>
|
||||||
|
Set New Password
|
||||||
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
NewPasswordForm.propTypes = {
|
NewPasswordForm.propTypes = {
|
||||||
fields: PropTypes.shape({
|
fields: PropTypes.shape({
|
||||||
password: PropTypes.object.isRequired,
|
password: PropTypes.object.isRequired, // eslint-disable-line
|
||||||
confirmPassword: PropTypes.object.isRequired
|
confirmPassword: PropTypes.object.isRequired, // eslint-disable-line
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
handleSubmit: PropTypes.func.isRequired,
|
handleSubmit: PropTypes.func.isRequired,
|
||||||
updatePassword: PropTypes.func.isRequired,
|
updatePassword: PropTypes.func.isRequired,
|
||||||
|
@ -59,7 +72,7 @@ NewPasswordForm.propTypes = {
|
||||||
NewPasswordForm.defaultProps = {
|
NewPasswordForm.defaultProps = {
|
||||||
invalid: false,
|
invalid: false,
|
||||||
pristine: true,
|
pristine: true,
|
||||||
submitting: false
|
submitting: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NewPasswordForm;
|
export default NewPasswordForm;
|
||||||
|
|
|
@ -6,12 +6,21 @@ import Button from '../../../common/Button';
|
||||||
|
|
||||||
function ResetPasswordForm(props) {
|
function ResetPasswordForm(props) {
|
||||||
const {
|
const {
|
||||||
fields: { email }, handleSubmit, submitting, invalid, pristine
|
fields: { email },
|
||||||
|
handleSubmit,
|
||||||
|
submitting,
|
||||||
|
invalid,
|
||||||
|
pristine,
|
||||||
} = props;
|
} = props;
|
||||||
return (
|
return (
|
||||||
<form className="form" onSubmit={handleSubmit(props.initiateResetPassword.bind(this))}>
|
<form
|
||||||
|
className="form"
|
||||||
|
onSubmit={handleSubmit(props.initiateResetPassword.bind(this))}
|
||||||
|
>
|
||||||
<p className="form__field">
|
<p className="form__field">
|
||||||
<label htmlFor="email" className="form__label">Email used for registration</label>
|
<label htmlFor="email" className="form__label">
|
||||||
|
Email used for registration
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
className="form__input"
|
className="form__input"
|
||||||
aria-label="email"
|
aria-label="email"
|
||||||
|
@ -19,12 +28,17 @@ function ResetPasswordForm(props) {
|
||||||
id="email"
|
id="email"
|
||||||
{...domOnlyProps(email)}
|
{...domOnlyProps(email)}
|
||||||
/>
|
/>
|
||||||
{email.touched && email.error && <span className="form-error">{email.error}</span>}
|
{email.touched && email.error && (
|
||||||
|
<span className="form-error">{email.error}</span>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={submitting || invalid || pristine || props.user.resetPasswordInitiate}
|
disabled={
|
||||||
>Send Password Reset Email
|
submitting || invalid || pristine || props.user.resetPasswordInitiate
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Send Password Reset Email
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
@ -32,7 +46,7 @@ function ResetPasswordForm(props) {
|
||||||
|
|
||||||
ResetPasswordForm.propTypes = {
|
ResetPasswordForm.propTypes = {
|
||||||
fields: PropTypes.shape({
|
fields: PropTypes.shape({
|
||||||
email: PropTypes.object.isRequired
|
email: PropTypes.object.isRequired, // eslint-disable-line
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
handleSubmit: PropTypes.func.isRequired,
|
handleSubmit: PropTypes.func.isRequired,
|
||||||
initiateResetPassword: PropTypes.func.isRequired,
|
initiateResetPassword: PropTypes.func.isRequired,
|
||||||
|
@ -40,14 +54,14 @@ ResetPasswordForm.propTypes = {
|
||||||
invalid: PropTypes.bool,
|
invalid: PropTypes.bool,
|
||||||
pristine: PropTypes.bool,
|
pristine: PropTypes.bool,
|
||||||
user: PropTypes.shape({
|
user: PropTypes.shape({
|
||||||
resetPasswordInitiate: PropTypes.bool
|
resetPasswordInitiate: PropTypes.bool,
|
||||||
}).isRequired
|
}).isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
ResetPasswordForm.defaultProps = {
|
ResetPasswordForm.defaultProps = {
|
||||||
submitting: false,
|
submitting: false,
|
||||||
pristine: true,
|
pristine: true,
|
||||||
invalid: false
|
invalid: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ResetPasswordForm;
|
export default ResetPasswordForm;
|
||||||
|
|
|
@ -8,12 +8,21 @@ function SignupForm(props) {
|
||||||
const {
|
const {
|
||||||
fields: {
|
fields: {
|
||||||
username, email, password, confirmPassword
|
username, email, password, confirmPassword
|
||||||
}, handleSubmit, submitting, invalid, pristine
|
},
|
||||||
|
handleSubmit,
|
||||||
|
submitting,
|
||||||
|
invalid,
|
||||||
|
pristine,
|
||||||
} = props;
|
} = props;
|
||||||
return (
|
return (
|
||||||
<form className="form" onSubmit={handleSubmit(props.signUpUser.bind(this, props.previousPath))}>
|
<form
|
||||||
|
className="form"
|
||||||
|
onSubmit={handleSubmit(props.signUpUser.bind(this, props.previousPath))}
|
||||||
|
>
|
||||||
<p className="form__field">
|
<p className="form__field">
|
||||||
<label htmlFor="username" className="form__label">User Name</label>
|
<label htmlFor="username" className="form__label">
|
||||||
|
User Name
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
className="form__input"
|
className="form__input"
|
||||||
aria-label="username"
|
aria-label="username"
|
||||||
|
@ -21,10 +30,14 @@ function SignupForm(props) {
|
||||||
id="username"
|
id="username"
|
||||||
{...domOnlyProps(username)}
|
{...domOnlyProps(username)}
|
||||||
/>
|
/>
|
||||||
{username.touched && username.error && <span className="form-error">{username.error}</span>}
|
{username.touched && username.error && (
|
||||||
|
<span className="form-error">{username.error}</span>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
<p className="form__field">
|
<p className="form__field">
|
||||||
<label htmlFor="email" className="form__label">Email</label>
|
<label htmlFor="email" className="form__label">
|
||||||
|
Email
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
className="form__input"
|
className="form__input"
|
||||||
aria-label="email"
|
aria-label="email"
|
||||||
|
@ -32,10 +45,14 @@ function SignupForm(props) {
|
||||||
id="email"
|
id="email"
|
||||||
{...domOnlyProps(email)}
|
{...domOnlyProps(email)}
|
||||||
/>
|
/>
|
||||||
{email.touched && email.error && <span className="form-error">{email.error}</span>}
|
{email.touched && email.error && (
|
||||||
|
<span className="form-error">{email.error}</span>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
<p className="form__field">
|
<p className="form__field">
|
||||||
<label htmlFor="password" className="form__label">Password</label>
|
<label htmlFor="password" className="form__label">
|
||||||
|
Password
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
className="form__input"
|
className="form__input"
|
||||||
aria-label="password"
|
aria-label="password"
|
||||||
|
@ -43,10 +60,14 @@ function SignupForm(props) {
|
||||||
id="password"
|
id="password"
|
||||||
{...domOnlyProps(password)}
|
{...domOnlyProps(password)}
|
||||||
/>
|
/>
|
||||||
{password.touched && password.error && <span className="form-error">{password.error}</span>}
|
{password.touched && password.error && (
|
||||||
|
<span className="form-error">{password.error}</span>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
<p className="form__field">
|
<p className="form__field">
|
||||||
<label htmlFor="confirm password" className="form__label">Confirm Password</label>
|
<label htmlFor="confirm password" className="form__label">
|
||||||
|
Confirm Password
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
className="form__input"
|
className="form__input"
|
||||||
type="password"
|
type="password"
|
||||||
|
@ -54,16 +75,12 @@ function SignupForm(props) {
|
||||||
id="confirm password"
|
id="confirm password"
|
||||||
{...domOnlyProps(confirmPassword)}
|
{...domOnlyProps(confirmPassword)}
|
||||||
/>
|
/>
|
||||||
{
|
{confirmPassword.touched && confirmPassword.error && (
|
||||||
confirmPassword.touched &&
|
|
||||||
confirmPassword.error &&
|
|
||||||
<span className="form-error">{confirmPassword.error}</span>
|
<span className="form-error">{confirmPassword.error}</span>
|
||||||
}
|
)}
|
||||||
</p>
|
</p>
|
||||||
<Button
|
<Button type="submit" disabled={submitting || invalid || pristine}>
|
||||||
type="submit"
|
Sign Up
|
||||||
disabled={submitting || invalid || pristine}
|
|
||||||
>Sign Up
|
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
@ -71,23 +88,23 @@ function SignupForm(props) {
|
||||||
|
|
||||||
SignupForm.propTypes = {
|
SignupForm.propTypes = {
|
||||||
fields: PropTypes.shape({
|
fields: PropTypes.shape({
|
||||||
username: PropTypes.object.isRequired,
|
username: PropTypes.object.isRequired, // eslint-disable-line
|
||||||
email: PropTypes.object.isRequired,
|
email: PropTypes.object.isRequired, // eslint-disable-line
|
||||||
password: PropTypes.object.isRequired,
|
password: PropTypes.object.isRequired, // eslint-disable-line
|
||||||
confirmPassword: PropTypes.object.isRequired
|
confirmPassword: PropTypes.object.isRequired, // eslint-disable-line
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
handleSubmit: PropTypes.func.isRequired,
|
handleSubmit: PropTypes.func.isRequired,
|
||||||
signUpUser: PropTypes.func.isRequired,
|
signUpUser: PropTypes.func.isRequired,
|
||||||
submitting: PropTypes.bool,
|
submitting: PropTypes.bool,
|
||||||
invalid: PropTypes.bool,
|
invalid: PropTypes.bool,
|
||||||
pristine: PropTypes.bool,
|
pristine: PropTypes.bool,
|
||||||
previousPath: PropTypes.string.isRequired
|
previousPath: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
SignupForm.defaultProps = {
|
SignupForm.defaultProps = {
|
||||||
submitting: false,
|
submitting: false,
|
||||||
pristine: true,
|
pristine: true,
|
||||||
invalid: false
|
invalid: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SignupForm;
|
export default SignupForm;
|
||||||
|
|
|
@ -18,6 +18,7 @@ import createRedirectWithUsername from './components/createRedirectWithUsername'
|
||||||
import { getUser } from './modules/User/actions';
|
import { getUser } from './modules/User/actions';
|
||||||
import { stopSketch } from './modules/IDE/actions/ide';
|
import { stopSketch } from './modules/IDE/actions/ide';
|
||||||
import { userIsAuthenticated, userIsNotAuthenticated, userIsAuthorized } from './utils/auth';
|
import { userIsAuthenticated, userIsNotAuthenticated, userIsAuthorized } from './utils/auth';
|
||||||
|
import MobileExamples from './modules/Mobile/MobileExamples';
|
||||||
|
|
||||||
const checkAuth = (store) => {
|
const checkAuth = (store) => {
|
||||||
store.dispatch(getUser());
|
store.dispatch(getUser());
|
||||||
|
@ -60,6 +61,7 @@ const routes = store => (
|
||||||
<Route path="/mobile" component={MobileIDEView} />
|
<Route path="/mobile" component={MobileIDEView} />
|
||||||
<Route path="/mobile/preview" component={MobileSketchView} />
|
<Route path="/mobile/preview" component={MobileSketchView} />
|
||||||
<Route path="/mobile/preferences" component={MobilePreferences} />
|
<Route path="/mobile/preferences" component={MobilePreferences} />
|
||||||
|
<Route path="/mobile/examples" component={MobileExamples} />
|
||||||
</Route>
|
</Route>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -115,17 +115,13 @@ router.get('/about', (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (process.env.MOBILE_ENABLED) {
|
if (process.env.MOBILE_ENABLED) {
|
||||||
router.get('/mobile', (req, res) => {
|
router.get('/mobile', (req, res) => res.send(renderIndex()));
|
||||||
res.send(renderIndex());
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/mobile/preview', (req, res) => {
|
router.get('/mobile/preview', (req, res) => res.send(renderIndex()));
|
||||||
res.send(renderIndex());
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/mobile/preferences', (req, res) => {
|
router.get('/mobile/preferences', (req, res) => res.send(renderIndex()));
|
||||||
res.send(renderIndex());
|
|
||||||
});
|
router.get('/mobile/examples', (req, res) => res.send(renderIndex()));
|
||||||
}
|
}
|
||||||
|
|
||||||
router.get('/:username/collections/create', (req, res) => {
|
router.get('/:username/collections/create', (req, res) => {
|
||||||
|
|
Loading…
Reference in a new issue