src/components/structural/WelcomeScreen.js
import React from "react";
import {
Button,
Icon,
IconButton,
Modal,
Hidden
} from "@material-ui/core";
import WelcomeScene from "./WelcomeScene.js";
import ProjectView from "./header/ProjectView.js";
import CourseSelect from "../courses/CourseSelect.js";
import { withStyles } from "@material-ui/core/styles";
import "../../css/WelcomeScreen.css";
const general = [
{
shortcut: ["Ctrl/⌘", "S"],
description: "Save scene"
},
{
shortcut: ["Ctrl/⌘", "Shift", "S"],
description: "Save scene as"
},
{
shortcut: ["Ctrl/⌘", "Enter"],
description: "Render scene"
},
];
const editor = [
{
shortcut: ["Ctrl/⌘", "/"],
description: "Comment current or selected line of code"
},
{
shortcut: ["Alt/Option", "Up"],
description: "Move current line of code up 1 line"
},
{
shortcut: ["Alt/Option", "Down"],
description: "Move current line of code down 1 line"
},
{
shortcut: ["Alt/⌘", "D"],
description: "Delete current line of code"
}
];
const scene = [
{
shortcut: ["W"],
description: "Move forwards"
},
{
shortcut: ["S"],
description: "Move backwards"
},
{
shortcut: ["A"],
description: "Move left"
},
{
shortcut: ["D"],
description: "Move right"
},
{
shortcut: ["Space"],
description: "Move up"
},
{
shortcut: ["Shift"],
description: "Move down"
},
];
/**
* @returns {object} Center the Welcome Screen
*/
function getOuterModalStyle() {
const top = 50;
const left = 50;
return {
top: `${top}%`,
left: `${left}%`,
transform: `translate(-${top}%, -${left}%)`,
maxWidth: "90%",
maxHeight: "90%"
};
}
/**
* CSS for modal
* @param {*} theme
*/
const modelStyles = theme => ({
outer: {
position: "absolute",
width: theme.spacing(150),
backgroundColor: theme.palette.background.paper,
boxShadow: theme.shadows[5],
padding: theme.spacing(4),
"overflow-y": "auto"
},
paper: {
position: "absolute",
width: theme.spacing(50),
backgroundColor: theme.palette.background.paper,
boxShadow: theme.shadows[5],
padding: theme.spacing(4),
},
info: {
position: "absolute",
width: theme.spacing(100),
backgroundColor: theme.palette.background.paper,
boxShadow: theme.shadows[5],
padding: theme.spacing(4),
},
button: {
margin: theme.spacing(1),
}
});
const exitBtnStyle = {
position: "absolute",
top: 0,
right: 0,
};
/**
* Welcome Component returns a modal of welcome screen that shows up when user first enter MYR
*/
class Welcome extends React.Component {
constructor(props) {
super(props);
this.state = {
projectsOpen: false,
projectsTab: "b",
coursesOpen: false
};
}
/**
* Helper function to convert the shortcuts to an equivalent DOM elements
*
* @param {array} data
*/
shortcutHelper = (data) => {
let shortcuts = [];
data.shortcut.forEach((key,i)=>{
shortcuts.push(<kbd>{key}</kbd>);
if(i < data.shortcut.length-1){
shortcuts.push(" + ");
}
});
return (<p>{shortcuts} {data.description}</p>);
};
/**
* Called when the Welcome Screen is mounted (component has been rendererd to the DOM)
*
* Header.js has a state to control whether to show welcome screen or not. By default it's false.
* So if user hasn't visisted the MYR, toggle the state to true.
*/
componentDidMount() {
if (!this.getCookie("hasVisited")) {
this.props.handleWelcomeToggle();
}
}
/**
* Get value of cookie
* @param {string} cookieName name of cookie
* @returns {string} value of cookie if it exist, return empty string otherwise
*/
getCookie = (cookieName) => {
let name = cookieName + "=";
let decodedCookie = decodeURIComponent(document.cookie);
let ca = decodedCookie.split(";");
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) === " ") {
c = c.substring(1);
}
if (c.indexOf(name) === 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
/**
* If the user is first visiting the MYR, set "hasVisisted" to true
* and set the expiration date to current date + 24 hrs
* So it will re-appear after 24 hrs
*/
setCookie = () => {
if (!this.getCookie("hasVisited")) {
let date = new Date();
date.setTime(date.getTime() + (1000 * 60 * 60 * 24));
let expiration = "; expires=" + date.toGMTString();
let cookie = "hasVisited=true" + expiration;
document.cookie = cookie;
}
}
/**
* Handler for when the welcome screen is close either by close button or clicking outside of modal
*/
handleClose = () => {
this.setCookie();
this.props.handleWelcomeToggle();
};
/**
* Handler for when user click "never again" button
* It sets a long expiration date so it will "never" expireds
*/
neverAgainCookie = () => {
document.cookie = "hasVisited=true; expires=Thu, 31 Dec 2099 12:00:00 UTC;";
this.handleClose();
}
/**
* @returns {*} Button with don't show again option
*/
neverAgain = () => {
return (
<Button
onClick={this.neverAgainCookie}
className="neverAgain-btn">
Don't show again
</Button >
);
}
/**
* Handler for when user click on example project button
*/
handleProjectToggle = () => {
this.setCookie();
this.setState({ projectsOpen: !this.state.projectsOpen });
this.setState({ projectTab: "b" });
};
/**
* Handler for when user click on courses button
*/
handleCoursesToggle = () => {
this.setCookie();
this.setState({ coursesOpen: !this.state.coursesOpen });
};
/**
* Handler for when user click on tour button
*/
handleTourToggle = () => {
this.setCookie();
this.setState({ tourOpen: !this.state.tourOpen });
};
/**
* Helper function for creating buttons for navigating project, courses, etc.
* @returns {HTMLElement} Elements containing 6 buttons
*/
helperButtons = () => {
return (
<div className="row">
<div className="col-xs-12 col-md-6 col-lg-4" >
<Button
variant="contained"
onClick={this.handleCoursesToggle}
className="welcome-btn">
<Icon className="material-icons">school</Icon>
View Courses
</Button >
</div >
<div className="col-xs-12 col-md-6 col-lg-4" >
<Button
variant="contained"
onClick={this.handleProjectToggle}
className="welcome-btn">
<Icon className="material-icons">perm_media</Icon>
View Example Scenes
</Button >
</div >
<div className="col-xs-12 col-md-6 col-lg-4" >
<Button
variant="contained"
href="/reference"
target="_blank"
rel="noopener noreferrer"
onClick={this.setCookie}
className="welcome-btn">
<Icon className="material-icons">help</Icon>
Open the Reference
</Button >
</div >
<div className="col-xs-12 col-md-6 col-lg-4 d-none d-md-block" >
<Button
variant="contained"
onClick={() => {
this.props.handleTourToggle();
this.props.handleWelcomeToggle();
}}
className="welcome-btn">
<Icon className="material-icons">map</Icon>
Take the Tour
</Button >
</div >
<div className="col-xs-12 col-md-6 col-lg-4" >
<Button
variant="contained"
href="/about/support"
target="_blank"
rel="noopener noreferrer"
onClick={this.setCookie}
className="welcome-btn">
<Icon className="material-icons">alternate_email</Icon>
Get Support
</Button >
</div >
<div className="col-xs-12 col-md-6 col-lg-4" >
<Button
variant="contained"
href="https://github.com/engaging-computing/MYR"
target="_blank"
rel="noopener noreferrer"
onClick={this.setCookie}
className="welcome-btn">
<Icon className="material-icons">code</Icon>
Visit our GitHub
</Button >
</div >
</div>
);
}
/**
* Modals for handling when user clicked on the "exmaple scene" or "coruese" button
* @returns {HTMLElement} Elements of ProjectView and CourseSelect component
*/
handleModals = () => {
return (
<div id="modals">
<ProjectView
deleteFunc={this.props.deleteProj}
userProjs={this.props.userProjs}
exampleProjs={this.props.exampleProjs}
projectsOpen={this.state.projectsOpen}
handleProjectToggle={this.handleProjectToggle}
tab={this.state.projectsTab}
hideTooltip={true}
renameScene={this.props.renameScene} />
<CourseSelect
courses={this.props.courses}
coursesOpen={this.state.coursesOpen}
handleCoursesToggle={this.handleCoursesToggle}
hideTooltip={true} />
</div>
);
}
/**
* @returns Paragraph tag to display permission of using cookie
*/
cookieMessage = () => {
return (
<p id="cookie-consent" className="text-center">MYR uses cookies which are necessary for its functioning. You accept the use of cookies by continuing to use MYR per the <a href="/about/privacy" target="_blank" rel="noopener noreferrer">privacy policy</a>.</p>
);
}
/**
* @returns Creates Welcome Screen Modal
*/
render() {
const { classes } = this.props;
return (
<div>
<React.Fragment>
<Modal
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
open={this.props.welcomeOpen}
onClose={this.handleClose}>
<div style={getOuterModalStyle()} className={classes.outer}>
<IconButton
color="default"
style={exitBtnStyle}
onClick={this.handleClose}>
<Icon className="material-icons">close</Icon>
</IconButton>
<h3 className="text-center">Welcome to MYR!</h3>
<Hidden mdUp>
<hr />
<this.cookieMessage />
</Hidden>
<hr />
<div className="row g-0">
<div id="welcome-description" className="col-12 col-md-6 col-lg-8">
<p>MYR is an educational tool that strikes a balance with the ease of use and challenge. We drew inspiration from Logo Turtle and Processing to provide a beginner friendly experience for teaching and learning with MYR. If you want to learn more about MYR itself, visit our <a href="/about" target="_blank" rel="noopener noreferrer">about page</a>.</p>
<p>Within the editor you can create 3D scenes using JavaScript and the MYR API. You can then view your scene in the viewer using a computer, tablet, smartphone, or a VR headset.</p>
<p>MYR is being developed by the <a href="https://sites.uml.edu/engaging-computing" target="_blank" rel="noopener noreferrer">Engaging Computing Group at UMass Lowell</a>. If you ever need support with MYR for any reason, please reach out via our <a href="/about/support/" target="_blank" rel="noopener noreferrer">support page</a>.</p>
<p>The scene "Dropsies" is an interactive scene built in MYR. Click on it to move around!</p>
</div>
<div id="welcome-viewer" className="col-12 col-md-6 col-lg-4">
<WelcomeScene />
</div>
</div>
<hr />
<this.helperButtons />
<hr />
<p className="note">⌘: Command key for macOS user</p>
<div className="right">
<p className="general-commands">General Commands</p>
{
general.map(e => {return this.shortcutHelper(e);})
}
</div>
<div className="right">
<p className="editor-commands">Editor Commands</p>
{
editor.map(e => {return this.shortcutHelper(e);})
}
</div>
<div className="right">
<p className="scene-controls">Scene Controls</p>
{
scene.map(e => {return this.shortcutHelper(e);})
}
</div>
<this.handleModals />
<this.neverAgain />
<Hidden smDown>
<hr />
<this.cookieMessage />
</Hidden>
</div>
</Modal>
</React.Fragment>
</div >
);
}
}
const WelcomeScreen = withStyles(modelStyles)(Welcome);
export default WelcomeScreen;