Skip to content
17 changes: 11 additions & 6 deletions src/components/courses/CourseSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,19 +95,24 @@ class CourseSelectModal extends Component {
render() {
const { classes } = this.props;
const courses = [].concat(this.props.courses);
const style = {
default: {
margin: 1,
marginTop: 8,
color: "#fff",
}
};
return (
<div>
{
!this.props.hideTooltip ?
<Tooltip title="Courses">
<Tooltip title="Courses" placement="bottom-start">
<IconButton
onClick={this.props.handleCoursesToggle}
id="select-course"
className="header-btn d-none d-md-block"
style={{
color: "#fff",
margin: 2,
}}>
size="small"
style={style.default}
className="header-btn d-none d-sm-block">
<Icon className="material-icons">school</Icon>
</IconButton >
</Tooltip>
Expand Down
231 changes: 226 additions & 5 deletions src/components/editor/Editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,52 @@ import customCompleter from "./customCompleter.js";
import KeyboardShortcut from "./KeyboardShortcut.js";
import { browserType } from "../../utils/browserType";
import FontSize from "./FontSize.js";
import Reference from "../reference/Reference.js";
import SceneConfigMenu from "../structural/header/SceneConfigMenu.js";
import { save } from "../../actions/projectActions.js";


import sockets from "socket.io-client";



import "../../css/App.css";

import {
createTheme,
ThemeProvider,
} from "@material-ui/core";

/**
* Editor is a React Component that create the Ace Editor in the DOM.
* Editor is a React Component that creates the Ace Editor in the DOM.
*/
class Editor extends Component {
constructor(props) {
super(props);
this.state = {
savedSettings: [],
logMenuOpen: false,
availProj: [],
sampleProj: [],
collectionOpen: false,
projectsOpen: false,
projectTab: "a",
snackOpen: false,
lastMsgTime: 0,
anchorEl: null,
navAwayModal: false,
needsNewId: false, // this explicitly tells us to make a new id
spinnerOpen: false,
coursesOpen: false,
tourOpen: false,
welcomeOpen: false,
updateCollection: false,
fetchCollection: false,
socket: sockets(),
googleUser: undefined
};
}

/**
* Called when the Edtior is unmounting (Being removed from the DOM)
*
Expand Down Expand Up @@ -43,6 +84,9 @@ class Editor extends Component {
console.error("Unable to attach custom completers");
}

if (this.props.refExName) {
this.props.referenceExampleActions.fetchReferenceExample(this.props.refExName);
}
// Warn the issue before refreshing the page
window.addEventListener("beforeunload", (event) => {
let text;
Expand Down Expand Up @@ -79,19 +123,170 @@ class Editor extends Component {
this.props.userActions.updateUserSettings(this.props.user.uid,this.props.settings);
this.setState({"previousSettings":this.props.settings});
}
/*if (this.state.savedSettings.length === 0 && this.props.scene.id !== 0) {
this.setState({ savedSettings: this.buildSettingsArr() });

window.addEventListener("beforeunload", (event) => {
let finalSettings = this.buildSettingsArr();

if (!this.settingsEqual(finalSettings)) {
event.preventDefault();
event.returnValue = "You may have unsaved changes!";
}
});
} */
}

/**
* Flatten the sceneSettings from props (object) into an array for comparason
* @returns {array} Array of the scene settings
*/
buildSettingsArr = () => {
const sceneSettings = this.props.scene.settings;

return [sceneSettings.floorColor,
sceneSettings.showCoordHelper, sceneSettings.showFloor,
sceneSettings.skyColor, sceneSettings.viewOnly];
};
/**
* Compare two arrays of setting and determine whether is the settings are equal or not
* @param {array} newSettings Settings to compare
* @returns {boolean} If settings are equal or not
*/
settingsEqual = (newSettings) => {
for (let i = 0; i < newSettings.length; ++i) {
if (newSettings[i] !== this.state.savedSettings[i]) {
return false;
}
}
return true;
}

/**
* handeRender gets the information from Ace Editor and calls the action: render()
*/
handleRender = () => {
try {
let editor = window.ace.edit("ace-editor");
this.props.actions.render(editor.getSession().getValue(), this.props.user ? this.props.user.uid : "anon");
} catch (error) {
this.props.actions.render(this.props.text, this.props.user ? this.props.user.uid : "anon");
}
}

/**
* When the user clicks save it will upload the information to Firebase
*/
handleSave = (newCollectionID = undefined) => {
let editor, text;
if (!this.props.viewOnly) {
//If in editor mode, gets text directly from editor
editor = window.ace.edit("ace-editor");
text = editor.getSession().getValue();
} else {
//Otherwise, gets text from state (should be up to date since it is refreshed on editor unmount)
text = this.props.text;
}

if (this.props.user && this.props.user.uid && text) {
this.setState({ spinnerOpen: true });
let scene = document.querySelector("a-scene");
// Access the scene and screen shot, with perspective view in a lossy jpeg format
let img = scene.components.screenshot.getCanvas("perspective").toDataURL("image/jpeg", 0.1);

let newScene = {
name: (this.props.scene.name ? this.props.scene.name : "Untitled Scene"),
desc: this.props.scene.desc,
code: text,
uid: this.props.user.uid,
settings: {
...this.props.scene.settings,
collectionID: newCollectionID || this.props.scene.settings.collectionID
},
updateTime: Date.now(),
createTime: (this.props.scene.createTime ? this.props.scene.createTime : Date.now())
};

save(this.props.user.uid, newScene, img, this.props.projectId).then((projectId) => {
if (!projectId) {
console.error("Could not save the scene");
}

this.props.actions.updateSavedText(text);
// If we have a new projectId reload page with it
if (projectId !== this.props.projectId) {
this.setState({ spinnerOpen: false });
window.location.assign(`${window.origin}/scene/${projectId}`);
this.props.projectActions.asyncUserProj(this.props.user.uid);
}
if (!this.state.viewOnly) {
this.props.actions.refresh(text, this.props.user ? this.props.user.uid : "anon");
}
this.setState({ spinnerOpen: false, saveOpen: false });
this.state.socket.emit("save");
return true;
});
} else if (!text) {
alert("There is no code to save for this scene. Try adding some in the editor!");
} else {
// TODO: Don't use alert
alert("We were unable to save your project. Are you currently logged in?");
}

if (!this.state.viewOnly) {
this.props.actions.refresh(text, this.props.user ? this.props.user.uid : "anon");
}
this.setState({ savedSettings: this.buildSettingsArr() });
}

/**
* toggles the save drawer
*/
handleSaveToggle = () => this.setState({ saveOpen: !this.state.saveOpen });

/**
* forces save drawer closed
*/
handleSaveClose = () => this.setState({ saveOpen: false });

/**
* forces save drawer closed
*/
handleSaveOpen = () => this.setState({ saveOpen: true });

/**
* Creates the editor in the DOM
*/
render() {
const keyTheme = createTheme({
palette: {
primary: {
main: "#ffeb3b",
},
secondary: {
main: "#9c27b0",
}
},
});
const refTheme = createTheme({
palette: {
primary: {
main: "#a31545",
contrastText: "#FFFFFF"
},
secondary: {
main: "#4caf50",
contrastText: "#FFFFFF"
},
},
});
return (
<div>
<div id="leftConsole">
<AceEditor
editorProps={{
$blockScrolling: Infinity,
}}
height="90vh"
height="86.5vh"
mode="javascript"
name="ace-editor"
// eslint-disable-next-line
Expand All @@ -105,8 +300,34 @@ class Editor extends Component {
enableLiveAutocompletion={true}
onLoad={this.onLoad}
/>
{ browserType() === "desktop" ? <div><KeyboardShortcut/>
<FontSize userActions={this.props.userActions} settings={this.props.settings}/></div> : null }
{ browserType() === "desktop" ? <div className="console-footer">
<ThemeProvider theme={keyTheme}>
<KeyboardShortcut/>
</ThemeProvider>
<ThemeProvider theme={keyTheme}>
<FontSize userActions={this.props.userActions} settings={this.props.settings}/>
</ThemeProvider>
<ThemeProvider theme={refTheme}>
<Reference
layoutType={this.props.layoutType}/>
</ThemeProvider>
<ThemeProvider>
<SceneConfigMenu
scene={this.props.scene}
sceneActions={this.props.sceneActions}
collectionActions={this.props.collectionActions}
user={this.props.user}
settings={this.props.settings}
userActions={this.props.userActions}
handleRender={this.handleRender}
handleSave={this.handleSave}
handleSaveClose={this.handleSaveClose}
layoutType={this.props.layoutType}
displayCollectionConfig={!this.props.collection}
/>
</ThemeProvider>

</div> : null }
</div>
);
}
Expand Down
47 changes: 32 additions & 15 deletions src/components/editor/FontSize.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,25 @@ import {
Select,
Button,
Tooltip,
createTheme,
ThemeProvider,
Icon
} from "@material-ui/core";

import "../../css/KeyboardShortcut.css";


const fontTheme = createTheme({
palette: {
primary: {
main: "#9c27b0",
},
secondary: {
main: "#9c27b0",
}
},
});

const options=[12, 14, 18, 24, 30, 36, 48];
class FontSize extends React.Component {

Expand All @@ -17,21 +32,23 @@ class FontSize extends React.Component {
render(){
return(
<div className="font">
<Tooltip title="Font Size">
<Button className="font-button"
variant="contained"
size="small"
color="primary">
<Icon className="material-icons">format_size</Icon>
<Select className="select"
labelId="input-label"
defaultValue = {12}
value = {this.props.settings.fontSize}
onChange = {this.handleFontSizeUpdate}>
{options.map(size=>(<option value={size}>{size}</option>))}
</Select>
</Button>
</Tooltip>
<ThemeProvider theme={fontTheme}>
<Tooltip title="Font Size">
<Button className="font-button"
variant="contained"
size="small"
color="primary">
<Icon className="material-icons">format_size</Icon>
<Select className="select"
labelId="input-label"
defaultValue = {12}
value = {this.props.settings.fontSize}
onChange = {this.handleFontSizeUpdate}>
{options.map(size=>(<option value={size}>{size}</option>))}
</Select>
</Button>
</Tooltip>
</ThemeProvider>
</div>
);
}
Expand Down
Loading