Skip to content
Snippets Groups Projects
scene-ui.js 6.47 KiB
Newer Older
Greg Fodor's avatar
Greg Fodor committed
import React, { Component } from "react";
import PropTypes from "prop-types";
Greg Fodor's avatar
Greg Fodor committed
import classNames from "classnames";
import { IntlProvider, FormattedMessage, addLocaleData } from "react-intl";
Greg Fodor's avatar
Greg Fodor committed
import en from "react-intl/locale-data/en";
import styles from "../assets/stylesheets/scene-ui.scss";
import hubLogo from "../assets/images/hub-preview-white.png";
import spokeLogo from "../assets/images/spoke_logo_black.png";
Greg Fodor's avatar
Greg Fodor committed
import { getReticulumFetchUrl } from "../utils/phoenix-utils";
import { generateHubName } from "../utils/name-generation";
import { WithHoverSound } from "./wrap-with-audio";
import CreateRoomDialog from "./create-room-dialog.js";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faEllipsisH } from "@fortawesome/free-solid-svg-icons/faEllipsisH";
Greg Fodor's avatar
Greg Fodor committed

import { lang, messages } from "../utils/i18n";

addLocaleData([...en]);

class SceneUI extends Component {
  static propTypes = {
    scene: PropTypes.object,
    sceneLoaded: PropTypes.bool,
    sceneId: PropTypes.string,
    sceneName: PropTypes.string,
    sceneDescription: PropTypes.string,
Greg Fodor's avatar
Greg Fodor committed
    sceneAttributions: PropTypes.object,
Greg Fodor's avatar
Greg Fodor committed
    sceneScreenshotURL: PropTypes.string
Greg Fodor's avatar
Greg Fodor committed
  state = {
    showScreenshot: false,
    showCustomRoomDialog: false
Greg Fodor's avatar
Greg Fodor committed
  };
Greg Fodor's avatar
Greg Fodor committed

  constructor(props) {
    super(props);

    // Show screenshot if scene isn't loaded in 5 seconds
    setTimeout(() => {
      if (!this.props.sceneLoaded) {
        this.setState({ showScreenshot: true });
      }
    }, 5000);
Greg Fodor's avatar
Greg Fodor committed
  }

  componentDidMount() {
    this.props.scene.addEventListener("loaded", this.onSceneLoaded);
  }

  componentWillUnmount() {
    this.props.scene.removeEventListener("loaded", this.onSceneLoaded);
  }

Greg Fodor's avatar
Greg Fodor committed
  createRoom = async () => {
    const payload = { hub: { name: this.state.customRoomName || generateHubName(), scene_id: this.props.sceneId } };
Greg Fodor's avatar
Greg Fodor committed
    const createUrl = getReticulumFetchUrl("/api/v1/hubs");

    const res = await fetch(createUrl, {
      body: JSON.stringify(payload),
      headers: { "content-type": "application/json" },
      method: "POST"
    });

    const hub = await res.json();

    if (!process.env.RETICULUM_SERVER || document.location.host === process.env.RETICULUM_SERVER) {
      document.location = hub.url;
    } else {
      document.location = `/hub.html?hub_id=${hub.hub_id}`;
    }
  };

Greg Fodor's avatar
Greg Fodor committed
  render() {
Greg Fodor's avatar
Greg Fodor committed
    const sceneUrl = [location.protocol, "//", location.host, location.pathname].join("");
Greg Fodor's avatar
Greg Fodor committed
    const tweetText = `${this.props.sceneName} in #hubs`;
    const tweetLink = `https://twitter.com/share?url=${encodeURIComponent(sceneUrl)}&text=${encodeURIComponent(
      tweetText
    )}`;
Greg Fodor's avatar
Greg Fodor committed

Greg Fodor's avatar
Greg Fodor committed
    let attributions;

    const toAttributionSpan = a => {
      if (a.url) {
        const source =
          a.url.indexOf("sketchfab.com") >= 0
            ? "on Sketchfab"
            : a.url.indexOf("poly.google.com") >= 0
              ? "on Google Poly"
              : "";
Greg Fodor's avatar
Greg Fodor committed

        return (
          <span key={a.url}>
            <a href={a.url} target="_blank" rel="noopener noreferrer">
              {a.name} by {a.author} {source}
johnshaughnessy's avatar
johnshaughnessy committed
            </a>
            &nbsp;
Greg Fodor's avatar
Greg Fodor committed
          </span>
        );
      } else {
        return (
          <span key={`${a.name} ${a.author}`}>
johnshaughnessy's avatar
johnshaughnessy committed
            {a.name} by {a.author}
            &nbsp;
Greg Fodor's avatar
Greg Fodor committed
          </span>
        );
      }
    };

    if (this.props.sceneAttributions) {
      if (!this.props.sceneAttributions.extras) {
        attributions = (
          <span>
johnshaughnessy's avatar
johnshaughnessy committed
            <span>{this.props.sceneAttributions.creator ? `by ${this.props.sceneAttributions.creator}` : ""}</span>
            &nbsp;
Greg Fodor's avatar
Greg Fodor committed
            <br />
            {this.props.sceneAttributions.content && this.props.sceneAttributions.content.map(toAttributionSpan)}
          </span>
        );
      } else {
        // Legacy
        attributions = <span>{this.props.sceneAttributions.extras}</span>;
      }
    }

Greg Fodor's avatar
Greg Fodor committed
    return (
      <IntlProvider locale={lang} messages={messages}>
        <div className={styles.ui}>
          <div
            className={classNames({
              [styles.screenshot]: true,
              [styles.screenshotHidden]: this.props.sceneLoaded
            })}
          >
            {this.state.showScreenshot && <img src={this.props.sceneScreenshotURL} />}
          </div>
          <div className={styles.whiteOverlay} />
          <div className={styles.grid}>
            <div className={styles.mainPanel}>
              <WithHoverSound>
                <a href="/" className={styles.logo}>
                  <img src={hubLogo} />
                </a>
              </WithHoverSound>
              <div className={styles.logoTagline}>
                <FormattedMessage id="scene.logo_tagline" />
              <div className={styles.createButtons}>
                <WithHoverSound>
                  <button className={styles.createButton} onClick={this.createRoom}>
                    <FormattedMessage id="scene.create_button" />
                  </button>
                </WithHoverSound>
                <WithHoverSound>
johnshaughnessy's avatar
johnshaughnessy committed
                  <button
                    className={styles.optionsButton}
                    onClick={() => this.setState({ showCustomRoomDialog: true })}
                  >
                    <FontAwesomeIcon icon={faEllipsisH} />
                  </button>
                </WithHoverSound>
              <WithHoverSound>
                <a href={tweetLink} rel="noopener noreferrer" target="_blank" className={styles.tweetButton}>
                  <img src="../assets/images/twitter.svg" />
                  <div>
                    <FormattedMessage id="scene.tweet_button" />
                  </div>
                </a>
              </WithHoverSound>
          </div>
          <div className={styles.info}>
            <div className={styles.name}>{this.props.sceneName}</div>
Greg Fodor's avatar
Greg Fodor committed
            <div className={styles.attribution}>{attributions}</div>
          <div className={styles.spoke}>
            <div className={styles.madeWith}>made with</div>
            <a href="/spoke">
              <img src={spokeLogo} />
            </a>
          </div>
          {this.state.showCustomRoomDialog && (
            <CreateRoomDialog
              includeScenePrompt={false}
              onClose={() => this.setState({ showCustomRoomDialog: false })}
              onCustomScene={name => {
                this.setState({ showCustomRoomDialog: false, customRoomName: name }, () => this.createRoom());
              }}
            />
          )}
Greg Fodor's avatar
Greg Fodor committed
      </IntlProvider>
    );
  }
}

export default SceneUI;