2018-02-07 19:06:07 +01:00
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import React from 'react';
|
2017-02-22 20:29:35 +01:00
|
|
|
import { bindActionCreators } from 'redux';
|
|
|
|
import { connect } from 'react-redux';
|
|
|
|
import { withRouter } from 'react-router';
|
2020-07-06 11:36:45 +02:00
|
|
|
import { withTranslation } from 'react-i18next';
|
2017-06-06 04:37:41 +02:00
|
|
|
import { Helmet } from 'react-helmet';
|
2017-02-22 20:29:35 +01:00
|
|
|
import SplitPane from 'react-split-pane';
|
2016-06-24 00:29:55 +02:00
|
|
|
import Editor from '../components/Editor';
|
2016-07-06 21:09:05 +02:00
|
|
|
import Sidebar from '../components/Sidebar';
|
2016-06-24 00:29:55 +02:00
|
|
|
import PreviewFrame from '../components/PreviewFrame';
|
|
|
|
import Toolbar from '../components/Toolbar';
|
2020-07-21 23:18:19 +02:00
|
|
|
import Preferences from '../components/Preferences/index';
|
2016-07-13 22:13:28 +02:00
|
|
|
import NewFileModal from '../components/NewFileModal';
|
2016-08-30 05:23:10 +02:00
|
|
|
import NewFolderModal from '../components/NewFolderModal';
|
2019-08-12 17:21:42 +02:00
|
|
|
import UploadFileModal from '../components/UploadFileModal';
|
2016-09-07 04:37:29 +02:00
|
|
|
import ShareModal from '../components/ShareModal';
|
2016-09-07 23:47:22 +02:00
|
|
|
import KeyboardShortcutModal from '../components/KeyboardShortcutModal';
|
2017-01-24 21:29:25 +01:00
|
|
|
import ErrorModal from '../components/ErrorModal';
|
2016-06-24 00:29:55 +02:00
|
|
|
import Nav from '../../../components/Nav';
|
2016-07-18 01:06:43 +02:00
|
|
|
import Console from '../components/Console';
|
2016-09-08 01:00:52 +02:00
|
|
|
import Toast from '../components/Toast';
|
2016-06-24 00:29:55 +02:00
|
|
|
import * as FileActions from '../actions/files';
|
|
|
|
import * as IDEActions from '../actions/ide';
|
|
|
|
import * as ProjectActions from '../actions/project';
|
2016-08-11 19:29:30 +02:00
|
|
|
import * as EditorAccessibilityActions from '../actions/editorAccessibility';
|
2016-08-05 03:43:13 +02:00
|
|
|
import * as PreferencesActions from '../actions/preferences';
|
2016-08-28 02:46:20 +02:00
|
|
|
import * as UserActions from '../../User/actions';
|
2016-09-08 01:00:52 +02:00
|
|
|
import * as ToastActions from '../actions/toast';
|
2017-01-10 00:09:23 +01:00
|
|
|
import * as ConsoleActions from '../actions/console';
|
2016-11-16 19:12:36 +01:00
|
|
|
import { getHTMLFile } from '../reducers/files';
|
2016-08-15 23:06:12 +02:00
|
|
|
import Overlay from '../../App/components/Overlay';
|
2016-08-22 18:35:59 +02:00
|
|
|
import About from '../components/About';
|
2019-11-25 21:19:22 +01:00
|
|
|
import AddToCollectionList from '../components/AddToCollectionList';
|
2018-02-09 22:32:06 +01:00
|
|
|
import Feedback from '../components/Feedback';
|
2019-11-10 21:39:22 +01:00
|
|
|
import { CollectionSearchbar } from '../components/Searchbar';
|
2016-06-24 00:29:55 +02:00
|
|
|
|
2020-08-17 11:23:58 +02:00
|
|
|
|
2020-06-29 22:27:31 +02:00
|
|
|
function getTitle(props) {
|
2020-06-20 16:31:15 +02:00
|
|
|
const { id } = props.project;
|
|
|
|
return id ? `p5.js Web Editor | ${props.project.name}` : 'p5.js Web Editor';
|
2020-06-29 22:27:31 +02:00
|
|
|
}
|
2020-06-20 16:31:15 +02:00
|
|
|
|
2020-06-29 22:27:31 +02:00
|
|
|
function isUserOwner(props) {
|
|
|
|
return props.project.owner && props.project.owner.id === props.user.id;
|
|
|
|
}
|
2020-06-20 16:31:15 +02:00
|
|
|
|
2020-07-24 23:11:10 +02:00
|
|
|
function warnIfUnsavedChanges(props) {
|
|
|
|
// eslint-disable-line
|
2020-07-02 14:28:45 +02:00
|
|
|
const { route } = props.route;
|
2020-07-24 23:11:10 +02:00
|
|
|
if (
|
|
|
|
route &&
|
|
|
|
route.action === 'PUSH' &&
|
|
|
|
(route.pathname === '/login' || route.pathname === '/signup')
|
|
|
|
) {
|
2020-07-02 14:28:45 +02:00
|
|
|
// don't warn
|
|
|
|
props.persistState();
|
|
|
|
window.onbeforeunload = null;
|
2020-07-24 23:11:10 +02:00
|
|
|
} else if (
|
|
|
|
route &&
|
|
|
|
(props.location.pathname === '/login' ||
|
|
|
|
props.location.pathname === '/signup')
|
|
|
|
) {
|
2020-07-02 14:28:45 +02:00
|
|
|
// don't warn
|
|
|
|
props.persistState();
|
|
|
|
window.onbeforeunload = null;
|
|
|
|
} else if (props.ide.unsavedChanges) {
|
2020-07-31 15:20:42 +02:00
|
|
|
if (!window.confirm(props.t('Nav.WarningUnsavedChanges'))) {
|
2020-07-02 14:28:45 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
props.setUnsavedChanges(false);
|
|
|
|
return true;
|
|
|
|
}
|
2020-08-03 19:35:28 +02:00
|
|
|
return true;
|
2020-07-02 14:28:45 +02:00
|
|
|
}
|
|
|
|
|
2016-06-24 00:29:55 +02:00
|
|
|
class IDEView extends React.Component {
|
2016-08-11 22:50:31 +02:00
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
2016-09-08 03:48:45 +02:00
|
|
|
this.handleGlobalKeydown = this.handleGlobalKeydown.bind(this);
|
2019-03-05 22:25:34 +01:00
|
|
|
|
|
|
|
this.state = {
|
|
|
|
consoleSize: props.ide.consoleIsExpanded ? 150 : 29,
|
2020-07-24 23:11:10 +02:00
|
|
|
sidebarSize: props.ide.sidebarIsExpanded ? 160 : 20,
|
2019-03-05 22:25:34 +01:00
|
|
|
};
|
2016-08-11 22:50:31 +02:00
|
|
|
}
|
|
|
|
|
2016-06-24 00:29:55 +02:00
|
|
|
componentDidMount() {
|
2017-04-20 20:05:15 +02:00
|
|
|
// If page doesn't reload after Sign In then we need
|
|
|
|
// to force cleared state to be cleared
|
|
|
|
this.props.clearPersistedState();
|
|
|
|
|
2016-08-28 03:52:00 +02:00
|
|
|
this.props.stopSketch();
|
2016-06-24 00:29:55 +02:00
|
|
|
if (this.props.params.project_id) {
|
2020-05-26 23:35:13 +02:00
|
|
|
const { project_id: id, username } = this.props.params;
|
2016-11-11 00:49:42 +01:00
|
|
|
if (id !== this.props.project.id) {
|
2020-05-26 23:35:13 +02:00
|
|
|
this.props.getProject(id, username);
|
2016-11-11 00:49:42 +01:00
|
|
|
}
|
2016-06-24 00:29:55 +02:00
|
|
|
}
|
2016-08-11 22:50:31 +02:00
|
|
|
|
2016-09-08 03:48:45 +02:00
|
|
|
this.isMac = navigator.userAgent.toLowerCase().indexOf('mac') !== -1;
|
|
|
|
document.addEventListener('keydown', this.handleGlobalKeydown, false);
|
2016-09-21 00:27:10 +02:00
|
|
|
|
2020-07-24 23:11:10 +02:00
|
|
|
this.props.router.setRouteLeaveHook(
|
|
|
|
this.props.route,
|
|
|
|
this.handleUnsavedChanges
|
|
|
|
);
|
2016-09-21 00:27:10 +02:00
|
|
|
|
2020-07-09 18:50:46 +02:00
|
|
|
window.onbeforeunload = this.handleUnsavedChanges;
|
2016-09-22 00:52:44 +02:00
|
|
|
|
2017-01-24 19:04:51 +01:00
|
|
|
this.autosaveInterval = null;
|
2016-08-11 22:50:31 +02:00
|
|
|
}
|
|
|
|
|
2016-11-10 22:13:00 +01:00
|
|
|
componentWillReceiveProps(nextProps) {
|
|
|
|
if (nextProps.location !== this.props.location) {
|
|
|
|
this.props.setPreviousPath(this.props.location.pathname);
|
|
|
|
}
|
|
|
|
|
2016-08-11 22:50:31 +02:00
|
|
|
if (this.props.ide.consoleIsExpanded !== nextProps.ide.consoleIsExpanded) {
|
2020-07-24 23:11:10 +02:00
|
|
|
this.setState({
|
|
|
|
consoleSize: nextProps.ide.consoleIsExpanded ? 150 : 29,
|
|
|
|
});
|
2016-08-11 22:50:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (this.props.ide.sidebarIsExpanded !== nextProps.ide.sidebarIsExpanded) {
|
2020-07-24 23:11:10 +02:00
|
|
|
this.setState({
|
|
|
|
sidebarSize: nextProps.ide.sidebarIsExpanded ? 160 : 20,
|
|
|
|
});
|
2016-08-11 22:50:31 +02:00
|
|
|
}
|
2019-03-05 22:25:34 +01:00
|
|
|
}
|
2016-08-15 23:06:12 +02:00
|
|
|
|
2019-03-05 22:25:34 +01:00
|
|
|
componentWillUpdate(nextProps) {
|
2016-08-15 23:06:12 +02:00
|
|
|
if (nextProps.params.project_id && !this.props.params.project_id) {
|
2016-11-11 00:49:42 +01:00
|
|
|
if (nextProps.params.project_id !== nextProps.project.id) {
|
|
|
|
this.props.getProject(nextProps.params.project_id);
|
|
|
|
}
|
|
|
|
}
|
2016-06-24 00:29:55 +02:00
|
|
|
}
|
|
|
|
|
2016-08-04 03:47:24 +02:00
|
|
|
componentDidUpdate(prevProps) {
|
2020-06-20 16:31:15 +02:00
|
|
|
if (isUserOwner(this.props) && this.props.project.id) {
|
2020-07-24 23:11:10 +02:00
|
|
|
if (
|
|
|
|
this.props.preferences.autosave &&
|
|
|
|
this.props.ide.unsavedChanges &&
|
|
|
|
!this.props.ide.justOpenedProject
|
|
|
|
) {
|
2017-06-06 04:33:32 +02:00
|
|
|
if (
|
|
|
|
this.props.selectedFile.name === prevProps.selectedFile.name &&
|
2020-07-24 23:11:10 +02:00
|
|
|
this.props.selectedFile.content !== prevProps.selectedFile.content
|
|
|
|
) {
|
2017-05-14 02:47:41 +02:00
|
|
|
if (this.autosaveInterval) {
|
|
|
|
clearTimeout(this.autosaveInterval);
|
|
|
|
}
|
|
|
|
console.log('will save project in 20 seconds');
|
|
|
|
this.autosaveInterval = setTimeout(this.props.autosaveProject, 20000);
|
|
|
|
}
|
2017-01-24 19:04:51 +01:00
|
|
|
} else if (this.autosaveInterval && !this.props.preferences.autosave) {
|
2017-01-18 22:43:41 +01:00
|
|
|
clearTimeout(this.autosaveInterval);
|
2016-08-10 00:45:59 +02:00
|
|
|
this.autosaveInterval = null;
|
|
|
|
}
|
2017-01-18 22:43:41 +01:00
|
|
|
} else if (this.autosaveInterval) {
|
|
|
|
clearTimeout(this.autosaveInterval);
|
2016-08-12 18:45:26 +02:00
|
|
|
this.autosaveInterval = null;
|
|
|
|
}
|
2016-09-21 00:27:10 +02:00
|
|
|
|
|
|
|
if (this.props.route.path !== prevProps.route.path) {
|
2020-07-24 23:11:10 +02:00
|
|
|
this.props.router.setRouteLeaveHook(this.props.route, () =>
|
|
|
|
warnIfUnsavedChanges(this.props));
|
2016-09-21 00:27:10 +02:00
|
|
|
}
|
2016-06-24 00:29:55 +02:00
|
|
|
}
|
2016-08-04 01:03:01 +02:00
|
|
|
componentWillUnmount() {
|
2018-03-02 18:26:20 +01:00
|
|
|
document.removeEventListener('keydown', this.handleGlobalKeydown, false);
|
2017-01-18 22:43:41 +01:00
|
|
|
clearTimeout(this.autosaveInterval);
|
2016-08-04 01:03:01 +02:00
|
|
|
this.autosaveInterval = null;
|
|
|
|
}
|
2016-09-08 03:48:45 +02:00
|
|
|
handleGlobalKeydown(e) {
|
2016-10-20 00:35:59 +02:00
|
|
|
// 83 === s
|
2020-07-24 23:11:10 +02:00
|
|
|
if (
|
|
|
|
e.keyCode === 83 &&
|
|
|
|
((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac))
|
|
|
|
) {
|
2016-09-08 03:48:45 +02:00
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
2020-07-24 23:11:10 +02:00
|
|
|
if (
|
|
|
|
isUserOwner(this.props) ||
|
|
|
|
(this.props.user.authenticated && !this.props.project.owner)
|
|
|
|
) {
|
2019-02-25 22:45:20 +01:00
|
|
|
this.props.saveProject(this.cmController.getContent());
|
2018-02-23 17:55:14 +01:00
|
|
|
} else if (this.props.user.authenticated) {
|
|
|
|
this.props.cloneProject();
|
|
|
|
} else {
|
|
|
|
this.props.showErrorModal('forceAuthentication');
|
2016-11-03 03:19:05 +01:00
|
|
|
}
|
2016-10-20 00:35:59 +02:00
|
|
|
// 13 === enter
|
2020-07-24 23:11:10 +02:00
|
|
|
} else if (
|
|
|
|
e.keyCode === 13 &&
|
|
|
|
e.shiftKey &&
|
|
|
|
((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac))
|
|
|
|
) {
|
2016-09-14 06:10:01 +02:00
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
this.props.stopSketch();
|
2020-07-24 23:11:10 +02:00
|
|
|
} else if (
|
|
|
|
e.keyCode === 13 &&
|
|
|
|
((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac))
|
|
|
|
) {
|
2016-09-14 06:10:01 +02:00
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
2017-09-14 23:57:09 +02:00
|
|
|
this.props.startSketch();
|
2017-09-15 18:10:25 +02:00
|
|
|
// 50 === 2
|
2020-07-24 23:11:10 +02:00
|
|
|
} else if (
|
|
|
|
e.keyCode === 50 &&
|
|
|
|
((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac)) &&
|
|
|
|
e.shiftKey
|
|
|
|
) {
|
2016-11-08 18:39:46 +01:00
|
|
|
e.preventDefault();
|
2017-09-15 18:10:25 +02:00
|
|
|
this.props.setAllAccessibleOutput(false);
|
|
|
|
// 49 === 1
|
2020-07-24 23:11:10 +02:00
|
|
|
} else if (
|
|
|
|
e.keyCode === 49 &&
|
|
|
|
((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac)) &&
|
|
|
|
e.shiftKey
|
|
|
|
) {
|
2016-11-08 18:39:46 +01:00
|
|
|
e.preventDefault();
|
2017-09-15 18:10:25 +02:00
|
|
|
this.props.setAllAccessibleOutput(true);
|
2020-07-24 23:11:10 +02:00
|
|
|
} else if (
|
|
|
|
e.keyCode === 66 &&
|
|
|
|
((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac))
|
|
|
|
) {
|
2020-02-23 19:48:16 +01:00
|
|
|
e.preventDefault();
|
|
|
|
if (!this.props.ide.sidebarIsExpanded) {
|
|
|
|
this.props.expandSidebar();
|
|
|
|
} else {
|
|
|
|
this.props.collapseSidebar();
|
|
|
|
}
|
2020-04-05 10:16:07 +02:00
|
|
|
} else if (e.keyCode === 192 && e.ctrlKey) {
|
2020-02-24 20:43:57 +01:00
|
|
|
e.preventDefault();
|
|
|
|
if (this.props.ide.consoleIsExpanded) {
|
|
|
|
this.props.collapseConsole();
|
|
|
|
} else {
|
|
|
|
this.props.expandConsole();
|
|
|
|
}
|
2020-04-23 20:59:33 +02:00
|
|
|
} else if (e.keyCode === 27) {
|
|
|
|
if (this.props.ide.newFolderModalVisible) {
|
|
|
|
this.props.closeNewFolderModal();
|
|
|
|
} else if (this.props.ide.uploadFileModalVisible) {
|
|
|
|
this.props.closeUploadFileModal();
|
|
|
|
} else if (this.props.ide.modalIsVisible) {
|
|
|
|
this.props.closeNewFileModal();
|
|
|
|
}
|
2016-09-08 03:48:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-09 18:50:46 +02:00
|
|
|
handleUnsavedChanges = () => warnIfUnsavedChanges(this.props);
|
2020-07-06 21:01:56 +02:00
|
|
|
|
2016-06-24 00:29:55 +02:00
|
|
|
render() {
|
|
|
|
return (
|
|
|
|
<div className="ide">
|
2017-06-06 04:37:41 +02:00
|
|
|
<Helmet>
|
2020-06-20 16:31:15 +02:00
|
|
|
<title>{getTitle(this.props)}</title>
|
2017-06-06 04:37:41 +02:00
|
|
|
</Helmet>
|
2016-09-08 01:00:52 +02:00
|
|
|
{this.props.toast.isVisible && <Toast />}
|
2016-06-24 00:29:55 +02:00
|
|
|
<Nav
|
2020-07-09 18:50:46 +02:00
|
|
|
warnIfUnsavedChanges={this.handleUnsavedChanges}
|
2017-09-01 18:40:15 +02:00
|
|
|
cmController={this.cmController}
|
2016-06-24 00:29:55 +02:00
|
|
|
/>
|
2020-06-28 15:05:33 +02:00
|
|
|
<Toolbar key={this.props.project.id} />
|
2020-07-24 23:11:10 +02:00
|
|
|
{this.props.ide.preferencesIsVisible && (
|
2017-11-14 01:09:08 +01:00
|
|
|
<Overlay
|
2020-07-31 15:20:42 +02:00
|
|
|
title={this.props.t('Preferences.Settings')}
|
|
|
|
ariaLabel={this.props.t('Preferences.Settings')}
|
2017-11-14 01:09:08 +01:00
|
|
|
closeOverlay={this.props.closePreferences}
|
|
|
|
>
|
|
|
|
<Preferences
|
|
|
|
fontSize={this.props.preferences.fontSize}
|
|
|
|
setFontSize={this.props.setFontSize}
|
|
|
|
autosave={this.props.preferences.autosave}
|
2019-03-26 20:37:44 +01:00
|
|
|
linewrap={this.props.preferences.linewrap}
|
2019-08-30 18:36:34 +02:00
|
|
|
lineNumbers={this.props.preferences.lineNumbers}
|
|
|
|
setLineNumbers={this.props.setLineNumbers}
|
2017-11-14 01:09:08 +01:00
|
|
|
setAutosave={this.props.setAutosave}
|
2019-03-26 20:37:44 +01:00
|
|
|
setLinewrap={this.props.setLinewrap}
|
2017-11-14 01:09:08 +01:00
|
|
|
lintWarning={this.props.preferences.lintWarning}
|
|
|
|
setLintWarning={this.props.setLintWarning}
|
|
|
|
textOutput={this.props.preferences.textOutput}
|
|
|
|
gridOutput={this.props.preferences.gridOutput}
|
|
|
|
soundOutput={this.props.preferences.soundOutput}
|
|
|
|
setTextOutput={this.props.setTextOutput}
|
|
|
|
setGridOutput={this.props.setGridOutput}
|
|
|
|
setSoundOutput={this.props.setSoundOutput}
|
|
|
|
theme={this.props.preferences.theme}
|
|
|
|
setTheme={this.props.setTheme}
|
|
|
|
/>
|
|
|
|
</Overlay>
|
2020-07-24 23:11:10 +02:00
|
|
|
)}
|
2020-05-19 21:34:00 +02:00
|
|
|
<main className="editor-preview-container">
|
2016-08-11 22:50:31 +02:00
|
|
|
<SplitPane
|
|
|
|
split="vertical"
|
2019-03-05 22:25:34 +01:00
|
|
|
size={this.state.sidebarSize}
|
|
|
|
onChange={size => this.setState({ sidebarSize: size })}
|
2016-08-11 22:50:31 +02:00
|
|
|
onDragFinished={this._handleSidebarPaneOnDragFinished}
|
|
|
|
allowResize={this.props.ide.sidebarIsExpanded}
|
2020-07-31 00:59:12 +02:00
|
|
|
minSize={125}
|
2016-08-11 22:50:31 +02:00
|
|
|
>
|
2016-08-11 21:41:13 +02:00
|
|
|
<Sidebar
|
2016-07-21 06:05:47 +02:00
|
|
|
files={this.props.files}
|
2016-08-11 21:41:13 +02:00
|
|
|
setSelectedFile={this.props.setSelectedFile}
|
|
|
|
newFile={this.props.newFile}
|
|
|
|
isExpanded={this.props.ide.sidebarIsExpanded}
|
|
|
|
deleteFile={this.props.deleteFile}
|
|
|
|
updateFileName={this.props.updateFileName}
|
2016-08-30 05:23:10 +02:00
|
|
|
projectOptionsVisible={this.props.ide.projectOptionsVisible}
|
|
|
|
openProjectOptions={this.props.openProjectOptions}
|
|
|
|
closeProjectOptions={this.props.closeProjectOptions}
|
|
|
|
newFolder={this.props.newFolder}
|
2017-04-12 02:06:04 +02:00
|
|
|
user={this.props.user}
|
|
|
|
owner={this.props.project.owner}
|
2019-09-11 00:42:23 +02:00
|
|
|
openUploadFileModal={this.props.openUploadFileModal}
|
|
|
|
closeUploadFileModal={this.props.closeUploadFileModal}
|
2016-07-21 06:05:47 +02:00
|
|
|
/>
|
2016-08-11 21:41:13 +02:00
|
|
|
<SplitPane
|
|
|
|
split="vertical"
|
2018-05-05 02:22:39 +02:00
|
|
|
defaultSize="50%"
|
2020-07-24 23:11:10 +02:00
|
|
|
onChange={() => {
|
|
|
|
this.overlay.style.display = 'block';
|
|
|
|
}}
|
|
|
|
onDragFinished={() => {
|
|
|
|
this.overlay.style.display = 'none';
|
|
|
|
}}
|
2019-03-20 19:28:15 +01:00
|
|
|
resizerStyle={{
|
2020-07-24 23:11:10 +02:00
|
|
|
borderLeftWidth: '2px',
|
|
|
|
borderRightWidth: '2px',
|
|
|
|
width: '2px',
|
|
|
|
margin: '0px 0px',
|
2019-03-20 19:28:15 +01:00
|
|
|
}}
|
2016-08-11 21:41:13 +02:00
|
|
|
>
|
2016-08-11 22:50:31 +02:00
|
|
|
<SplitPane
|
|
|
|
split="horizontal"
|
|
|
|
primary="second"
|
2019-03-05 22:25:34 +01:00
|
|
|
size={this.state.consoleSize}
|
2016-08-11 22:50:31 +02:00
|
|
|
minSize={29}
|
2019-03-05 22:25:34 +01:00
|
|
|
onChange={size => this.setState({ consoleSize: size })}
|
2016-08-11 22:50:31 +02:00
|
|
|
allowResize={this.props.ide.consoleIsExpanded}
|
2016-12-19 23:07:04 +01:00
|
|
|
className="editor-preview-subpanel"
|
2016-08-11 22:50:31 +02:00
|
|
|
>
|
2020-08-12 16:41:53 +02:00
|
|
|
<Editor provideController={(ctl) => { this.cmController = ctl; }} />
|
2020-07-16 23:20:52 +02:00
|
|
|
<Console />
|
2016-08-11 21:41:13 +02:00
|
|
|
</SplitPane>
|
2020-05-19 21:34:00 +02:00
|
|
|
<section className="preview-frame-holder">
|
2017-03-02 21:01:33 +01:00
|
|
|
<header className="preview-frame__header">
|
2020-07-31 15:20:42 +02:00
|
|
|
<h2 className="preview-frame__title">{this.props.t('Toolbar.Preview')}</h2>
|
2017-03-02 21:01:33 +01:00
|
|
|
</header>
|
2018-09-08 20:50:39 +02:00
|
|
|
<div className="preview-frame__content">
|
2020-07-24 23:11:10 +02:00
|
|
|
<div
|
|
|
|
className="preview-frame-overlay"
|
|
|
|
ref={(element) => {
|
|
|
|
this.overlay = element;
|
|
|
|
}}
|
|
|
|
>
|
2018-09-08 20:50:39 +02:00
|
|
|
</div>
|
|
|
|
<div>
|
2020-07-24 23:11:10 +02:00
|
|
|
{((this.props.preferences.textOutput ||
|
|
|
|
this.props.preferences.gridOutput ||
|
|
|
|
this.props.preferences.soundOutput) &&
|
|
|
|
this.props.ide.isPlaying) ||
|
|
|
|
this.props.ide.isAccessibleOutputPlaying}
|
2018-09-08 20:50:39 +02:00
|
|
|
</div>
|
|
|
|
<PreviewFrame
|
|
|
|
htmlFile={this.props.htmlFile}
|
|
|
|
files={this.props.files}
|
|
|
|
content={this.props.selectedFile.content}
|
|
|
|
isPlaying={this.props.ide.isPlaying}
|
2020-07-24 23:11:10 +02:00
|
|
|
isAccessibleOutputPlaying={
|
|
|
|
this.props.ide.isAccessibleOutputPlaying
|
|
|
|
}
|
2018-09-08 20:50:39 +02:00
|
|
|
textOutput={this.props.preferences.textOutput}
|
|
|
|
gridOutput={this.props.preferences.gridOutput}
|
|
|
|
soundOutput={this.props.preferences.soundOutput}
|
|
|
|
setTextOutput={this.props.setTextOutput}
|
|
|
|
setGridOutput={this.props.setGridOutput}
|
|
|
|
setSoundOutput={this.props.setSoundOutput}
|
|
|
|
dispatchConsoleEvent={this.props.dispatchConsoleEvent}
|
|
|
|
autorefresh={this.props.preferences.autorefresh}
|
|
|
|
previewIsRefreshing={this.props.ide.previewIsRefreshing}
|
|
|
|
endSketchRefresh={this.props.endSketchRefresh}
|
|
|
|
stopSketch={this.props.stopSketch}
|
|
|
|
setBlobUrl={this.props.setBlobUrl}
|
|
|
|
expandConsole={this.props.expandConsole}
|
2019-04-30 23:44:41 +02:00
|
|
|
clearConsole={this.props.clearConsole}
|
2019-05-02 21:30:56 +02:00
|
|
|
cmController={this.cmController}
|
2020-08-17 11:23:58 +02:00
|
|
|
language={this.props.preferences.language}
|
2018-09-08 20:50:39 +02:00
|
|
|
/>
|
2016-08-11 21:41:13 +02:00
|
|
|
</div>
|
2020-05-19 21:34:00 +02:00
|
|
|
</section>
|
2016-08-11 21:41:13 +02:00
|
|
|
</SplitPane>
|
|
|
|
</SplitPane>
|
2020-05-19 21:34:00 +02:00
|
|
|
</main>
|
2020-07-24 23:11:10 +02:00
|
|
|
{this.props.ide.modalIsVisible && <NewFileModal />}
|
|
|
|
{this.props.ide.newFolderModalVisible && (
|
2017-11-13 20:44:23 +01:00
|
|
|
<NewFolderModal
|
|
|
|
closeModal={this.props.closeNewFolderModal}
|
|
|
|
createFolder={this.props.createFolder}
|
|
|
|
/>
|
2020-07-24 23:11:10 +02:00
|
|
|
)}
|
|
|
|
{this.props.ide.uploadFileModalVisible && (
|
|
|
|
<UploadFileModal closeModal={this.props.closeUploadFileModal} />
|
|
|
|
)}
|
|
|
|
{this.props.location.pathname === '/about' && (
|
2017-11-13 20:44:23 +01:00
|
|
|
<Overlay
|
2020-07-31 15:20:42 +02:00
|
|
|
title={this.props.t('About.Title')}
|
2017-11-13 20:44:23 +01:00
|
|
|
previousPath={this.props.ide.previousPath}
|
2020-07-31 15:20:42 +02:00
|
|
|
ariaLabel={this.props.t('About.Title')}
|
2017-11-13 20:44:23 +01:00
|
|
|
>
|
|
|
|
<About previousPath={this.props.ide.previousPath} />
|
|
|
|
</Overlay>
|
2020-07-24 23:11:10 +02:00
|
|
|
)}
|
|
|
|
{this.props.location.pathname === '/feedback' && (
|
2018-02-09 22:32:06 +01:00
|
|
|
<Overlay
|
2020-07-31 15:20:42 +02:00
|
|
|
title={this.props.t('IDEView.SubmitFeedback')}
|
2018-02-09 22:32:06 +01:00
|
|
|
previousPath={this.props.ide.previousPath}
|
2020-08-22 12:57:39 +02:00
|
|
|
ariaLabel={this.props.t('IDEView.SubmitFeedbackARIA')}
|
2018-02-09 22:32:06 +01:00
|
|
|
>
|
|
|
|
<Feedback previousPath={this.props.ide.previousPath} />
|
|
|
|
</Overlay>
|
2020-07-24 23:11:10 +02:00
|
|
|
)}
|
|
|
|
{this.props.location.pathname.match(/add-to-collection$/) && (
|
2019-09-09 18:52:54 +02:00
|
|
|
<Overlay
|
2020-08-22 12:57:39 +02:00
|
|
|
ariaLabel={this.props.t('IDEView.AddCollectionARIA')}
|
|
|
|
title={this.props.t('IDEView.AddCollectionTitle')}
|
2019-09-09 18:52:54 +02:00
|
|
|
previousPath={this.props.ide.previousPath}
|
2019-11-10 21:39:22 +01:00
|
|
|
actions={<CollectionSearchbar />}
|
2019-12-11 15:12:00 +01:00
|
|
|
isFixedHeight
|
2019-09-09 18:52:54 +02:00
|
|
|
>
|
2019-11-25 21:19:22 +01:00
|
|
|
<AddToCollectionList
|
2019-09-09 18:52:54 +02:00
|
|
|
projectId={this.props.params.project_id}
|
|
|
|
username={this.props.params.username}
|
|
|
|
user={this.props.user}
|
|
|
|
/>
|
|
|
|
</Overlay>
|
2020-07-24 23:11:10 +02:00
|
|
|
)}
|
|
|
|
{this.props.ide.shareModalVisible && (
|
2017-11-13 20:44:23 +01:00
|
|
|
<Overlay
|
2020-08-22 12:57:39 +02:00
|
|
|
title={this.props.t('IDEView.ShareTitle')}
|
|
|
|
ariaLabel={this.props.t('IDEView.ShareARIA')}
|
2017-11-13 20:44:23 +01:00
|
|
|
closeOverlay={this.props.closeShareModal}
|
|
|
|
>
|
|
|
|
<ShareModal
|
2019-06-19 22:21:25 +02:00
|
|
|
projectId={this.props.ide.shareModalProjectId}
|
|
|
|
projectName={this.props.ide.shareModalProjectName}
|
|
|
|
ownerUsername={this.props.ide.shareModalProjectUsername}
|
2017-11-13 20:44:23 +01:00
|
|
|
/>
|
|
|
|
</Overlay>
|
2020-07-24 23:11:10 +02:00
|
|
|
)}
|
|
|
|
{this.props.ide.keyboardShortcutVisible && (
|
2017-11-13 20:44:23 +01:00
|
|
|
<Overlay
|
2020-07-31 15:20:42 +02:00
|
|
|
title={this.props.t('KeyboardShortcuts.Title')}
|
|
|
|
ariaLabel={this.props.t('KeyboardShortcuts.Title')}
|
2017-11-13 20:44:23 +01:00
|
|
|
closeOverlay={this.props.closeKeyboardShortcutModal}
|
|
|
|
>
|
|
|
|
<KeyboardShortcutModal />
|
|
|
|
</Overlay>
|
2020-07-24 23:11:10 +02:00
|
|
|
)}
|
|
|
|
{this.props.ide.errorType && (
|
2017-11-13 20:44:23 +01:00
|
|
|
<Overlay
|
2020-08-22 12:57:39 +02:00
|
|
|
title={this.props.t('Common.Error')}
|
|
|
|
ariaLabel={this.props.t('Common.ErrorARIA')}
|
2018-03-02 18:26:20 +01:00
|
|
|
closeOverlay={this.props.hideErrorModal}
|
2017-11-13 20:44:23 +01:00
|
|
|
>
|
|
|
|
<ErrorModal
|
|
|
|
type={this.props.ide.errorType}
|
2018-02-23 17:55:14 +01:00
|
|
|
closeModal={this.props.hideErrorModal}
|
2017-11-13 20:44:23 +01:00
|
|
|
/>
|
|
|
|
</Overlay>
|
2020-07-24 23:11:10 +02:00
|
|
|
)}
|
2016-06-24 00:29:55 +02:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-27 21:34:58 +02:00
|
|
|
IDEView.propTypes = {
|
|
|
|
params: PropTypes.shape({
|
2016-08-17 21:53:25 +02:00
|
|
|
project_id: PropTypes.string,
|
2016-10-18 22:07:25 +02:00
|
|
|
username: PropTypes.string,
|
|
|
|
reset_password_token: PropTypes.string,
|
2017-02-22 20:29:35 +01:00
|
|
|
}).isRequired,
|
2016-08-15 23:06:12 +02:00
|
|
|
location: PropTypes.shape({
|
2020-07-24 23:11:10 +02:00
|
|
|
pathname: PropTypes.string,
|
2017-02-22 20:29:35 +01:00
|
|
|
}).isRequired,
|
2016-06-27 21:34:58 +02:00
|
|
|
getProject: PropTypes.func.isRequired,
|
2016-07-21 04:18:20 +02:00
|
|
|
user: PropTypes.shape({
|
2016-08-10 00:45:59 +02:00
|
|
|
authenticated: PropTypes.bool.isRequired,
|
2016-11-04 23:54:14 +01:00
|
|
|
id: PropTypes.string,
|
2020-07-24 23:11:10 +02:00
|
|
|
username: PropTypes.string,
|
2016-07-21 04:18:20 +02:00
|
|
|
}).isRequired,
|
2016-06-27 21:34:58 +02:00
|
|
|
saveProject: PropTypes.func.isRequired,
|
|
|
|
ide: PropTypes.shape({
|
2020-08-12 16:41:53 +02:00
|
|
|
errorType: PropTypes.string,
|
|
|
|
keyboardShortcutVisible: PropTypes.bool.isRequired,
|
2016-09-07 22:33:01 +02:00
|
|
|
shareModalVisible: PropTypes.bool.isRequired,
|
2019-06-19 22:21:25 +02:00
|
|
|
shareModalProjectId: PropTypes.string.isRequired,
|
|
|
|
shareModalProjectName: PropTypes.string.isRequired,
|
|
|
|
shareModalProjectUsername: PropTypes.string.isRequired,
|
2016-11-29 21:51:16 +01:00
|
|
|
previousPath: PropTypes.string.isRequired,
|
2020-08-12 16:41:53 +02:00
|
|
|
previewIsRefreshing: PropTypes.bool.isRequired,
|
|
|
|
isPlaying: PropTypes.bool.isRequired,
|
|
|
|
isAccessibleOutputPlaying: PropTypes.bool.isRequired,
|
|
|
|
projectOptionsVisible: PropTypes.bool.isRequired,
|
|
|
|
preferencesIsVisible: PropTypes.bool.isRequired,
|
|
|
|
modalIsVisible: PropTypes.bool.isRequired,
|
2020-07-24 23:11:10 +02:00
|
|
|
uploadFileModalVisible: PropTypes.bool.isRequired,
|
2020-08-12 16:41:53 +02:00
|
|
|
newFolderModalVisible: PropTypes.bool.isRequired,
|
|
|
|
justOpenedProject: PropTypes.bool.isRequired,
|
|
|
|
sidebarIsExpanded: PropTypes.bool.isRequired,
|
|
|
|
consoleIsExpanded: PropTypes.bool.isRequired,
|
|
|
|
unsavedChanges: PropTypes.bool.isRequired,
|
2016-06-27 21:34:58 +02:00
|
|
|
}).isRequired,
|
|
|
|
stopSketch: PropTypes.func.isRequired,
|
|
|
|
project: PropTypes.shape({
|
2016-08-04 03:47:24 +02:00
|
|
|
id: PropTypes.string,
|
2016-07-15 17:54:47 +02:00
|
|
|
name: PropTypes.string.isRequired,
|
|
|
|
owner: PropTypes.shape({
|
2016-08-10 00:45:59 +02:00
|
|
|
username: PropTypes.string,
|
2020-07-24 23:11:10 +02:00
|
|
|
id: PropTypes.string,
|
2017-03-02 20:38:29 +01:00
|
|
|
}),
|
2020-07-24 23:11:10 +02:00
|
|
|
updatedAt: PropTypes.string,
|
2016-06-27 21:34:58 +02:00
|
|
|
}).isRequired,
|
2016-08-11 19:29:30 +02:00
|
|
|
editorAccessibility: PropTypes.shape({
|
2020-07-24 23:11:10 +02:00
|
|
|
lintMessages: PropTypes.array.isRequired, // eslint-disable-line
|
2016-08-10 17:13:17 +02:00
|
|
|
}).isRequired,
|
2016-06-27 21:34:58 +02:00
|
|
|
preferences: PropTypes.shape({
|
2016-08-11 20:09:59 +02:00
|
|
|
autosave: PropTypes.bool.isRequired,
|
2020-08-12 16:41:53 +02:00
|
|
|
fontSize: PropTypes.number.isRequired,
|
2019-03-26 20:37:44 +01:00
|
|
|
linewrap: PropTypes.bool.isRequired,
|
2019-08-30 18:36:34 +02:00
|
|
|
lineNumbers: PropTypes.bool.isRequired,
|
2016-08-12 21:50:33 +02:00
|
|
|
lintWarning: PropTypes.bool.isRequired,
|
2017-05-31 21:23:30 +02:00
|
|
|
textOutput: PropTypes.bool.isRequired,
|
|
|
|
gridOutput: PropTypes.bool.isRequired,
|
|
|
|
soundOutput: PropTypes.bool.isRequired,
|
2016-09-28 20:12:01 +02:00
|
|
|
theme: PropTypes.string.isRequired,
|
2020-07-24 23:11:10 +02:00
|
|
|
autorefresh: PropTypes.bool.isRequired,
|
2020-08-17 11:23:58 +02:00
|
|
|
language: PropTypes.string.isRequired
|
2016-06-27 21:34:58 +02:00
|
|
|
}).isRequired,
|
|
|
|
closePreferences: PropTypes.func.isRequired,
|
2016-08-05 03:43:13 +02:00
|
|
|
setFontSize: PropTypes.func.isRequired,
|
2016-08-09 22:15:28 +02:00
|
|
|
setAutosave: PropTypes.func.isRequired,
|
2019-08-30 18:36:34 +02:00
|
|
|
setLineNumbers: PropTypes.func.isRequired,
|
2019-03-26 20:37:44 +01:00
|
|
|
setLinewrap: PropTypes.func.isRequired,
|
2016-08-11 20:09:59 +02:00
|
|
|
setLintWarning: PropTypes.func.isRequired,
|
2016-08-12 21:50:33 +02:00
|
|
|
setTextOutput: PropTypes.func.isRequired,
|
2017-05-31 21:23:30 +02:00
|
|
|
setGridOutput: PropTypes.func.isRequired,
|
|
|
|
setSoundOutput: PropTypes.func.isRequired,
|
2017-09-15 18:10:25 +02:00
|
|
|
setAllAccessibleOutput: PropTypes.func.isRequired,
|
2017-02-22 20:29:35 +01:00
|
|
|
files: PropTypes.arrayOf(PropTypes.shape({
|
|
|
|
id: PropTypes.string.isRequired,
|
|
|
|
name: PropTypes.string.isRequired,
|
2020-07-24 23:11:10 +02:00
|
|
|
content: PropTypes.string.isRequired,
|
2017-02-22 20:29:35 +01:00
|
|
|
})).isRequired,
|
2016-07-08 20:57:22 +02:00
|
|
|
selectedFile: PropTypes.shape({
|
2016-07-08 21:58:49 +02:00
|
|
|
id: PropTypes.string.isRequired,
|
2017-05-14 02:47:41 +02:00
|
|
|
content: PropTypes.string.isRequired,
|
2020-07-24 23:11:10 +02:00
|
|
|
name: PropTypes.string.isRequired,
|
2017-02-22 20:29:35 +01:00
|
|
|
}).isRequired,
|
2016-07-11 21:22:29 +02:00
|
|
|
setSelectedFile: PropTypes.func.isRequired,
|
2017-02-22 20:29:35 +01:00
|
|
|
htmlFile: PropTypes.shape({
|
|
|
|
id: PropTypes.string.isRequired,
|
|
|
|
name: PropTypes.string.isRequired,
|
2020-07-24 23:11:10 +02:00
|
|
|
content: PropTypes.string.isRequired,
|
2017-02-22 20:29:35 +01:00
|
|
|
}).isRequired,
|
2016-07-18 01:15:13 +02:00
|
|
|
dispatchConsoleEvent: PropTypes.func.isRequired,
|
2016-07-13 22:13:28 +02:00
|
|
|
newFile: PropTypes.func.isRequired,
|
2016-07-14 18:47:54 +02:00
|
|
|
expandSidebar: PropTypes.func.isRequired,
|
2016-07-15 19:11:50 +02:00
|
|
|
collapseSidebar: PropTypes.func.isRequired,
|
2016-07-21 06:33:41 +02:00
|
|
|
cloneProject: PropTypes.func.isRequired,
|
|
|
|
expandConsole: PropTypes.func.isRequired,
|
|
|
|
collapseConsole: PropTypes.func.isRequired,
|
2016-08-03 23:10:03 +02:00
|
|
|
deleteFile: PropTypes.func.isRequired,
|
2016-08-15 18:42:13 +02:00
|
|
|
updateFileName: PropTypes.func.isRequired,
|
2016-08-30 05:23:10 +02:00
|
|
|
openProjectOptions: PropTypes.func.isRequired,
|
|
|
|
closeProjectOptions: PropTypes.func.isRequired,
|
|
|
|
newFolder: PropTypes.func.isRequired,
|
|
|
|
closeNewFolderModal: PropTypes.func.isRequired,
|
2020-04-23 20:59:33 +02:00
|
|
|
closeNewFileModal: PropTypes.func.isRequired,
|
2016-08-30 20:39:37 +02:00
|
|
|
createFolder: PropTypes.func.isRequired,
|
2016-09-07 22:33:01 +02:00
|
|
|
closeShareModal: PropTypes.func.isRequired,
|
2016-09-08 01:00:52 +02:00
|
|
|
closeKeyboardShortcutModal: PropTypes.func.isRequired,
|
|
|
|
toast: PropTypes.shape({
|
2020-07-24 23:11:10 +02:00
|
|
|
isVisible: PropTypes.bool.isRequired,
|
2016-09-08 01:00:52 +02:00
|
|
|
}).isRequired,
|
2016-09-21 00:27:10 +02:00
|
|
|
autosaveProject: PropTypes.func.isRequired,
|
|
|
|
router: PropTypes.shape({
|
2020-07-24 23:11:10 +02:00
|
|
|
setRouteLeaveHook: PropTypes.func,
|
2016-09-21 00:27:10 +02:00
|
|
|
}).isRequired,
|
2017-02-22 20:29:35 +01:00
|
|
|
route: PropTypes.oneOfType([PropTypes.object, PropTypes.element]).isRequired,
|
2016-09-13 20:15:46 +02:00
|
|
|
setTheme: PropTypes.func.isRequired,
|
2016-09-29 00:05:14 +02:00
|
|
|
endSketchRefresh: PropTypes.func.isRequired,
|
2016-11-10 22:13:00 +01:00
|
|
|
setBlobUrl: PropTypes.func.isRequired,
|
2016-11-11 00:49:42 +01:00
|
|
|
setPreviousPath: PropTypes.func.isRequired,
|
2017-01-13 23:17:31 +01:00
|
|
|
clearConsole: PropTypes.func.isRequired,
|
2017-01-24 21:29:25 +01:00
|
|
|
showErrorModal: PropTypes.func.isRequired,
|
2017-04-20 20:05:15 +02:00
|
|
|
hideErrorModal: PropTypes.func.isRequired,
|
|
|
|
clearPersistedState: PropTypes.func.isRequired,
|
2017-09-14 23:57:09 +02:00
|
|
|
startSketch: PropTypes.func.isRequired,
|
2019-09-11 00:42:23 +02:00
|
|
|
openUploadFileModal: PropTypes.func.isRequired,
|
2020-07-06 11:36:45 +02:00
|
|
|
closeUploadFileModal: PropTypes.func.isRequired,
|
2020-07-24 23:11:10 +02:00
|
|
|
t: PropTypes.func.isRequired,
|
2016-06-27 21:34:58 +02:00
|
|
|
};
|
|
|
|
|
2016-06-24 00:29:55 +02:00
|
|
|
function mapStateToProps(state) {
|
|
|
|
return {
|
2016-08-24 19:09:48 +02:00
|
|
|
files: state.files,
|
2020-07-24 23:11:10 +02:00
|
|
|
selectedFile:
|
|
|
|
state.files.find(file => file.isSelectedFile) ||
|
2019-05-06 20:50:28 +02:00
|
|
|
state.files.find(file => file.name === 'sketch.js') ||
|
|
|
|
state.files.find(file => file.name !== 'root'),
|
2016-07-11 21:22:29 +02:00
|
|
|
htmlFile: getHTMLFile(state.files),
|
2016-08-24 19:09:48 +02:00
|
|
|
ide: state.ide,
|
2016-06-24 00:29:55 +02:00
|
|
|
preferences: state.preferences,
|
2016-08-11 19:29:30 +02:00
|
|
|
editorAccessibility: state.editorAccessibility,
|
2016-06-24 00:29:55 +02:00
|
|
|
user: state.user,
|
2016-09-08 01:00:52 +02:00
|
|
|
project: state.project,
|
2017-01-10 00:09:23 +01:00
|
|
|
toast: state.toast,
|
2020-07-24 23:11:10 +02:00
|
|
|
console: state.console,
|
2016-06-24 00:29:55 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function mapDispatchToProps(dispatch) {
|
2018-05-05 02:22:39 +02:00
|
|
|
return bindActionCreators(
|
|
|
|
Object.assign(
|
|
|
|
{},
|
|
|
|
EditorAccessibilityActions,
|
|
|
|
FileActions,
|
|
|
|
ProjectActions,
|
|
|
|
IDEActions,
|
|
|
|
PreferencesActions,
|
|
|
|
UserActions,
|
|
|
|
ToastActions,
|
|
|
|
ConsoleActions
|
|
|
|
),
|
|
|
|
dispatch
|
|
|
|
);
|
2016-06-24 00:29:55 +02:00
|
|
|
}
|
|
|
|
|
2020-07-31 15:20:42 +02:00
|
|
|
|
|
|
|
export default withTranslation()(withRouter(connect(mapStateToProps, mapDispatchToProps)(IDEView)));
|