Home Reference Source

src/components/structural/header/ProjectView.js

import React from "react";
import QRCode from "qrcode.react";
import {
    Button,
    ButtonBase,
    Icon,
    IconButton,
    ListItemIcon,
    ListItemText,
    Menu,
    MenuItem,
    Modal,
    TextField,
    Tooltip,
    Tabs,
    Tab
} from "@material-ui/core";

import { withStyles } from "@material-ui/core/styles";
import "../../../css/ProjectView.css";


function getOuterModalStyle() {
    const top = 50;
    const left = 50;

    return {
        top: `${top}%`,
        left: `${left}%`,
        transform: `translate(-${top}%, -${left}%)`,
        maxWidth: "90%",
        maxHeight: "90%"
    };
}

function getModalStyle() {
    const top = 50;
    const left = 50;

    return {
        top: `${top}%`,
        left: `${left}%`,
        transform: `translate(-${top}%, -${left}%)`,
    };
}

function getInfoModalStyle() {
    const top = 50;
    const left = 50;

    return {
        top: `${top}%`,
        left: `${left}%`,
        transform: `translate(-${top}%, -${left}%)`,
    };
}

// CSS for modal
const modelStyles = theme => ({
    outer: {
        position: "absolute",
        width: theme.spacing(150),
        backgroundColor: theme.palette.background.paper,
        boxShadow: theme.shadows[5],
        padding: theme.spacing(4),
        overflow: "scroll"
    },
    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,
};

/**
 * Create component with show 
 */
class Project extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            deleteFunc: this.props.deleteFunc,
            showImg: false,
            anchorEl: null,
            qrCodeOpen: false,
            shareOpen: false,
            infoOpen: false,
            email: "",
            sendTo: [],
            value: this.props.tab,
        };
        this.emailRef = React.createRef();
    }

    /**
     * Handles when user click on the project
     * @param {*} event 
     */
    handleClick = event => {
        this.setState({ anchorEl: event.currentTarget, projectId: event.currentTarget.id });
    };

    /**
     * Handles when user clicks on the info in the user project
     * @param {*} event 
     */
    handleInfoUserClick = event => {
        this.setState({ projectId: event.currentTarget.id, isUserProj: true });
        this.handleInfoToggle();
    };

    /**
     * Handles when user clicks on the info in the example project
     * @param {*} event 
     */
    handleInfoExampleClick = event => {
        this.setState({ projectId: event.currentTarget.id, isUserProj: false });
        this.handleInfoToggle();
    };

    /**
     * Handles when the projectview is closed
     * @param {*} event 
     */
    handleClose = () => {
        this.setState({ anchorEl: null });
    };

    /**
     * Handles when the text change in the textfield
     * @param {string} name Place where it saved in state
     */
    handleTextChange = name => event => {
        this.setState({
            [name]: event.target.value,
        });
    };

    /**
     * Handles when the user add the new email address to send
     */
    handleAddEmail = () => {
        let arr = [].concat(this.state.sendTo);
        arr.push(this.state.email);
        this.emailRef.current.value = "";
        this.setState({ sendTo: arr, email: "" });
    }

    /**
     * Handles toggle the project info
     */
    handleInfoToggle = () => {
        this.setState({ infoOpen: !this.state.infoOpen });
    }

    /**
     * Handles toggle the qrcode menu
     */
    handleQrToggle = () => {
        this.setState({ qrCodeOpen: !this.state.qrCodeOpen });
    }

    /**
     * Handles toggle the share menu
     */
    handleShrToggle = () => {
        this.setState({ shareOpen: !this.state.shareOpen, sendTo: [] });
    }

    /**
     * @returns Email field where user enters addresses they want to share it with
     */
    shareOptions = () => (
        <div>
            <h5>Enter one or more email addresses</h5>
            {
                this.state.sendTo.map(x => {
                    return <p>{x}</p>;
                })
            }
            <TextField
                id="standard-name"
                label="Email"
                value={this.state.email}
                inputRef={this.emailRef}
                onChange={this.handleTextChange("email")}
                margin="normal"
            />
            <IconButton
                variant="raised"
                onClick={this.handleAddEmail}
                color="primary">
                <Icon className="material-icons">add</Icon>
            </IconButton>
            <Button
                color="primary"
                onClick={this.handleAddEmail}
                href={`mailto:${this.state.sendTo.join("; ")}?subject=Check out my VR Scene in MYR&body=You can find my scene at ${window.origin + "/scene/" + this.state.projectId}`}>
                Send
            </Button>
        </div>
    );

    /**
     * @returns Returns the info of the project if it exists
     */
    infoOpen = () => {
        let projectId = this.state.projectId;
        let project;
        if (this.state.isUserProj) {
            project = this.props.userProjs.find(function (project) {
                return project._id === projectId;
            });
        }
        else {
            project = this.props.exampleProjs.find(function (project) {
                return project._id === projectId;
            });
        }
        if (!project) {
            return (
                <div>
                    <h5>Project Information</h5>
                    <h6>Error: Unable to load project information</h6>
                </div>
            );
        }
        let lastMod = new Date(project.updateTime);
        return (
            <div>
                <h3>{project.name}</h3>
                <p id="info-description">{project.desc}</p>
                <small>Last Modified: {lastMod.toDateString()}</small>
            </div>
        );
    };

    /**
     * @returns QR Code of the link to the project
     */
    qrCodeOpen = () => {
        return (
            <div>
                <h5>QR Code to Your Project</h5>
                <QRCode size={330} value={window.origin + "/scene/" + this.state.projectId} />
            </div>
        );
    };

    /**
     * Allows user to change the title and description for a scene they gave saved
     */
    redoScene = (id) => {
        if(window.location.href !== `${window.origin}/scene/${id}` && window.location.href !== `${window.origin}/scene/${id}/`)
        {
            alert("You must have the project open in order to edit it!");
        }
        else
        {
            this.props.renameScene();
            this.props.handleProjectToggle();
        }
    }
    
    /**
     * Helper for creating the project card
     * @param {object} proj Porject info
     * @param {boolean} canDelete whehter project can be deleted
     * @returns Elements of the project card
     */
    helper = (proj, canDelete) => {
        if (proj) {
            let id = proj._id;
            let name = proj.name;
            return (
                <div key={id} id="scene-card" title={name}
                    className="proj col-xs-12 col-md-6 col-lg-4 pt-2 pl-0" >
                    <a href={`/scene/${id}`} className = "text-decoration-none">
                        <span className="project-span">{name}</span>
                        <img id={id} alt={id} src={proj.url}
                            className={"img-thumbnail " + (this.state.showImg && "d-none")} />
                    </a>
                    {canDelete ?
                        <span className="scene-btns">
                            <IconButton
                                id={id}
                                title="Info"
                                color="primary"
                                onClick={this.handleInfoUserClick}
                                className="" >
                                <Icon className="material-icons">info</Icon>
                            </IconButton>
                            <IconButton
                                id={id}
                                title="Share"
                                color="primary"
                                onClick={this.handleClick}
                                className="" >
                                <Icon className="material-icons">share</Icon>
                            </IconButton>
                            <IconButton
                                label="delete Project"
                                title="Delete Project"
                                color="secondary"
                                fullwidth={String(!this.state.showImg)}
                                onClick={() => this.props.deleteFunc(this.props.user.uid, id, proj.name)}>
                                <Icon className="material-icons">delete</Icon>
                            </IconButton>
                            <IconButton
                                id={id}
                                title="Edit"
                                color="primary"
                                onClick={() => this.redoScene(id)}>
                                <Icon className="material-icons">edit</Icon>
                            </IconButton>
                        </span>
                        : <span className="scene-btns">
                            <IconButton
                                id={id}
                                title="Info"
                                color="primary"
                                onClick={this.handleInfoExampleClick}
                                className="" >
                                <Icon className="material-icons">info</Icon>
                            </IconButton>
                            <IconButton
                                id={id}
                                title="Share"
                                color="primary"
                                onClick={this.handleClick}
                                className="" >
                                <Icon className="material-icons">share</Icon>
                            </IconButton>
                        </span>
                    }
                </div>
            );
        } else {
            return null;
        }
    }

    /**
     * @returns Create a share menu for QR Code and email 
     */
    sceneMenu = () => (
        <Menu
            id="simple-menu"
            anchorEl={this.state.anchorEl}
            open={Boolean(this.state.anchorEl)}
            onClose={this.handleClose} >
            <MenuItem
                onClick={() => { this.handleClose(); this.handleQrToggle(); }}>
                <ListItemIcon >
                    <Icon className="material-icons">gradient</Icon>
                </ListItemIcon>
                <ListItemText inset primary="QR Code" />
            </MenuItem>
            <MenuItem
                onClick={() => { this.handleClose(); this.handleShrToggle(); }}>
                <ListItemIcon >
                    <Icon className="material-icons">send</Icon>
                </ListItemIcon>
                <ListItemText inset primary="Send" />
            </MenuItem>
        </Menu>
    );

    /**
     * Handles the switch between user and example project tab
     * @param {*} event 
     * @param {string} value New tab string 
     */
    handleChange = (event, value) => {
        this.setState({ value });
    };
    /**
 * This function sorts projects alphabetically, regardless of case. 
 * If two projects have the same name, they are sorted by their update time
 * @param {*} a a project that will be compared to project b
 * @param {*} b a project that will be compared to project a
 * @returns 1 if project a should come before project b, -1 if b should come before a 
 */
    projectSort = (a,b)=>{
        if(a.name && b.name) {
            if(a.name.toUpperCase() < b.name.toUpperCase())
            {
                return -1;
            }
            else if(a.name.toUpperCase() > b.name.toUpperCase())
            {
                return 1;
            }
        }
        else if(a.updateTime > b.updateTime)
        {
            return -1;
        }
        else if(a.updateTime < b.updateTime)
        {
            return 1;
        }
        
        return 0;
    }  

    /**
     * Create project view
     */
    render() {
        const { classes } = this.props;
        let previewToggle = {
            //paddingtop: 10,
            position: "fixed",
            top: 10,
            right: "46%",
        };
        const style = {
            default: {
                margin: 2,
                color: "#fff",
            }
        };
        const userProjs = [].concat(this.props.userProjs);
        const exampleProjs = [].concat(this.props.exampleProjs);
        return (
            <div>
                <React.Fragment>
                    {
                        !this.props.hideTooltip ?
                            <Tooltip title="Open" placement="bottom-start">
                                <IconButton
                                    id="open-btn"
                                    onClick={this.props.handleProjectToggle}
                                    className="header-btn"
                                    aria-haspopup="true"
                                    style={style.default}>
                                    <Icon className="material-icons">perm_media</Icon>
                                </IconButton>
                            </Tooltip>
                            : null
                    }
                    <Modal
                        aria-labelledby="simple-modal-title"
                        aria-describedby="simple-modal-description"
                        open={this.props.projectsOpen}
                        onClose={this.props.handleProjectToggle}>
                        <div style={getOuterModalStyle()} className={classes.outer}>
                            <IconButton
                                color="default"
                                style={exitBtnStyle}
                                onClick={this.props.handleProjectToggle}>
                                <Icon className="material-icons">close</Icon>
                            </IconButton>
                            <div>
                                <Button
                                    style={previewToggle}
                                    onClick={() => this.setState({ showImg: !this.state.showImg })}>
                                    { // If we are showing the img, show the proper icon
                                        this.state.showImg
                                            ?
                                            <Icon className="material-icons">visibility_off</Icon>
                                            :
                                            <Icon className="material-icons">visibility</Icon>
                                    }
                                    <span>&nbsp;</span>Preview
                                </Button>
                                <hr />
                            </div>
                            <Tabs
                                value={this.state.value}
                                onChange={this.handleChange} >
                                <Tab
                                    label="Your Projects"
                                    value="a" />
                                <Tab
                                    label="Example Scenes"
                                    value="b" />
                            </Tabs>
                            {this.state.value === "a" &&
                                <div id="project-list" style={{ marginTop: 0, overflow: "scroll" }}>
                                    <div className="row" id="user-proj" style={{ width: "100%" }}>
                                        { // Sort the users projects in alphabetical order
                                            userProjs.sort(this.projectSort).map(proj => {
                                                return this.helper(proj, true);
                                            })
                                        }
                                    </div>
                                </div>}
                            {this.state.value === "b" &&
                                <div id="project-list" style={{ marginTop: 0, overflow: "scroll" }}>
                                    <div className="row mt-2" id="sample-proj" style={{ width: "100%" }}>
                                        <p>Want to see <b>YOUR</b> scenes here? Submit them using this <a href="https://docs.google.com/forms/d/e/1FAIpQLSfAgmEobjFWAaiAvkCISLgIv1i4OH6jgUGLqtcZ4ktPIXc1Ew/viewform" rel="noreferrer" target="_blank">form!</a></p>
                                        {
                                            exampleProjs.sort(this.projectSort).map(proj => {
                                                return this.helper(proj, false);
                                            })
                                        }
                                    </div>
                                </div>}
                            <this.sceneMenu />

                            <Modal
                                aria-labelledby="simple-modal-title"
                                aria-describedby="simple-modal-description"
                                open={this.state.infoOpen}
                                onClose={this.handleInfoToggle} >
                                <div style={getInfoModalStyle()} className={classes.info}>
                                    <ButtonBase
                                        style={{ position: "absolute", right: 15, top: 15 }}
                                        onClick={() => this.handleInfoToggle()} >
                                        <Icon className="material-icons">clear</Icon>
                                    </ButtonBase >
                                    <this.infoOpen />
                                </div>
                            </Modal>
                            <Modal
                                aria-labelledby="simple-modal-title"
                                aria-describedby="simple-modal-description"
                                open={this.state.qrCodeOpen}
                                onClose={this.handleQrToggle} >
                                <div style={getModalStyle()} className={classes.paper}>
                                    <ButtonBase
                                        style={{ position: "absolute", right: 15, top: 15 }}
                                        onClick={() => this.handleQrToggle()} >
                                        <Icon className="material-icons">clear</Icon>
                                    </ButtonBase >
                                    <this.qrCodeOpen />
                                </div>
                            </Modal>
                            <Modal
                                aria-labelledby="simple-modal-title"
                                aria-describedby="simple-modal-description"
                                open={this.state.shareOpen}
                                onClose={this.handleShrToggle} >
                                <div style={getModalStyle()} className={classes.paper}>
                                    <ButtonBase
                                        style={{ position: "absolute", right: 15, top: 15 }}
                                        onClick={() => this.handleShrToggle()} >
                                        <Icon className="material-icons">clear</Icon>
                                    </ButtonBase >
                                    <this.shareOptions />
                                </div>
                            </Modal>
                        </div>
                    </Modal>
                </React.Fragment>
            </div>
        );
    }
}

const ProjectView = withStyles(modelStyles)(Project);

export default ProjectView;