Skip to content
Snippets Groups Projects
hub-create-panel.js 9.47 KiB
Newer Older
Greg Fodor's avatar
Greg Fodor committed
import React, { Component } from "react";
import PropTypes from "prop-types";
import { injectIntl, FormattedMessage } from "react-intl";
import { generateHubName } from "../utils/name-generation";
import { faAngleLeft } from "@fortawesome/free-solid-svg-icons/faAngleLeft";
import { faAngleRight } from "@fortawesome/free-solid-svg-icons/faAngleRight";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { resolveURL, extractUrlBase } from "../utils/resolveURL";
Greg Fodor's avatar
Greg Fodor committed
import { getReticulumFetchUrl } from "../utils/phoenix-utils";
import CreateRoomDialog from "./create-room-dialog.js";
import { WithHoverSound } from "./wrap-with-audio";
import default_scene_preview_thumbnail from "../assets/images/default_thumbnail.png";
import styles from "../assets/stylesheets/hub-create.scss";
Greg Fodor's avatar
Greg Fodor committed
const HUB_NAME_PATTERN = "^[A-Za-z0-9-'\":!@#$%^&*(),.?~ ]{4,64}$";

Greg Fodor's avatar
Greg Fodor committed
class HubCreatePanel extends Component {
  static propTypes = {
    intl: PropTypes.object,
    environments: PropTypes.array,
    initialEnvironment: PropTypes.string
Greg Fodor's avatar
Greg Fodor committed
  constructor(props) {
    super(props);
    let environmentIndex = Math.floor(Math.random() * props.environments.length);

    if (props.initialEnvironment) {
      environmentIndex = props.environments.findIndex(
        e => e.name.toLowerCase() === props.initialEnvironment.toLowerCase()
      );
    }

Greg Fodor's avatar
Greg Fodor committed
    this.state = {
      ready: false,
Greg Fodor's avatar
Greg Fodor committed
      name: generateHubName(),
      environmentIndex,
Greg Fodor's avatar
Greg Fodor committed
      showCustomSceneDialog: false,
      customSceneUrl: null
Greg Fodor's avatar
Greg Fodor committed
    };
    // Optimisticly preload all environment thumbnails
    (async () => {
      const environmentThumbnails = props.environments.map((_, i) => this._getEnvironmentThumbnail(i));
      await Promise.all(
        environmentThumbnails.map(environmentThumbnail => this._preloadImage(environmentThumbnail.srcset))
      );
      this.setState({ ready: true });
    })();
  _getEnvironmentThumbnail = environmentIndex => {
    const environment = this.props.environments[environmentIndex];
    const meta = environment.meta || {};

    let environmentThumbnail = {
      srcset: default_scene_preview_thumbnail
    };

    if (meta.images) {
      const thumbnailImage = meta.images.find(i => i.type === "preview-thumbnail");

      if (thumbnailImage) {
        // TODO kill bundles
        if (environment.bundle_url) {
          const baseURL = new URL(extractUrlBase(environment.bundle_url), window.location.href);

          environmentThumbnail = {
            srcset: resolveURL(thumbnailImage.srcset, baseURL)
          };
        } else {
          environmentThumbnail = {
            srcset: thumbnailImage.srcset
          };
        }
    }

    return environmentThumbnail;
Greg Fodor's avatar
Greg Fodor committed
  createHub = async e => {
Greg Fodor's avatar
Greg Fodor committed
    if (e) {
      e.preventDefault();
    }

Greg Fodor's avatar
Greg Fodor committed
    const environment = this.props.environments[this.state.environmentIndex];
Greg Fodor's avatar
Greg Fodor committed

    const payload = {
      hub: { name: this.state.name }
    if (!this.state.customSceneUrl && environment.scene_id) {
      payload.hub.scene_id = environment.scene_id;
    } else {
      const sceneUrl = this.state.customSceneUrl || environment.bundle_url;
      payload.hub.default_environment_gltf_bundle_url = sceneUrl;
    }

Greg Fodor's avatar
Greg Fodor committed
    const createUrl = getReticulumFetchUrl("/api/v1/hubs");
Greg Fodor's avatar
Greg Fodor committed

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

    const hub = await res.json();
Greg Fodor's avatar
Greg Fodor committed

    if (!process.env.RETICULUM_SERVER || document.location.host === process.env.RETICULUM_SERVER) {
Greg Fodor's avatar
Greg Fodor committed
      document.location = hub.url;
    } else {
      document.location = `/hub.html?hub_id=${hub.hub_id}`;
    }
Greg Fodor's avatar
Greg Fodor committed
  isHubNameValid = () => {
    const hubAlphaPattern = "[A-Za-z0-9]{4}";
    return new RegExp(HUB_NAME_PATTERN).test(this.state.name) && new RegExp(hubAlphaPattern).test(this.state.name);
  };

Brian Peiris's avatar
Brian Peiris committed
  _preloadImage = async srcset => {
    const img = new Image();
    const imgLoad = new Promise(resolve => img.addEventListener("load", resolve));
Brian Peiris's avatar
Brian Peiris committed
    img.srcset = srcset;
    await imgLoad;
  };

  setToEnvironmentOffset = async offset => {
Greg Fodor's avatar
Greg Fodor committed
    const numEnvs = this.props.environments.length;

Marshall Quander's avatar
Marshall Quander committed
    const environmentIndex = (((this.state.environmentIndex + offset) % numEnvs) + numEnvs) % numEnvs;
    const environmentThumbnail = this._getEnvironmentThumbnail(environmentIndex);
    await this._preloadImage(environmentThumbnail.srcset);

    this.setState({ environmentIndex });
Greg Fodor's avatar
Greg Fodor committed
  };

Greg Fodor's avatar
Greg Fodor committed
    this.setToEnvironmentOffset(1);
  };

  setToPreviousEnvironment = () => {
Greg Fodor's avatar
Greg Fodor committed
    this.setToEnvironmentOffset(-1);
  };

Greg Fodor's avatar
Greg Fodor committed
  showCustomSceneDialog = e => {
    e.preventDefault();
    e.stopPropagation();
    this.setState({ showCustomSceneDialog: true });
  };

Greg Fodor's avatar
Greg Fodor committed
  shuffle = () => {
    this.setState({
      name: generateHubName(),
      environmentIndex: Math.floor(Math.random() * this.props.environments.length)
    });
  };

Greg Fodor's avatar
Greg Fodor committed
  render() {
    if (!this.state.ready) return null;
Greg Fodor's avatar
Greg Fodor committed

    if (this.props.environments.length == 0) {
Greg Fodor's avatar
Greg Fodor committed
      return <div className={styles.placeholder} />;
    const environment = this.props.environments[this.state.environmentIndex];
    const meta = environment.meta || {};
    const environmentTitle = meta.title || environment.name;
    const environmentAuthor = (meta.authors || [])[0];
    const environmentThumbnail = this._getEnvironmentThumbnail(this.state.environmentIndex);
Greg Fodor's avatar
Greg Fodor committed
    return (
      <div>
        <form onSubmit={this.createHub}>
          <div className={styles.createPanel}>
            <div className={styles.form}>
              <div className={styles.environment}>
                <div className={styles.picker}>
                  <img className={styles.image} srcSet={environmentThumbnail.srcset} />
                  <div className={styles.labels}>
                    <div className={styles.header}>
                      <span className={styles.title}>{environmentTitle}</span>
                      {environmentAuthor &&
                        environmentAuthor.name &&
                        (environmentAuthor.url ? (
                          <WithHoverSound>
                            <a href={environmentAuthor.url} rel="noopener noreferrer" className={styles.author}>
                              <FormattedMessage id="home.environment_author_by" />
                              <span>{environmentAuthor.name}</span>
                            </a>
                          </WithHoverSound>
                        ) : (
                          <span className={styles.author}>
                            <FormattedMessage id="home.environment_author_by" />
                            <span>{environmentAuthor.name}</span>
                          </span>
                        ))}
                      {environmentAuthor &&
                        environmentAuthor.organization &&
                        (environmentAuthor.organization.url ? (
                          <WithHoverSound>
                            <a
                              href={environmentAuthor.organization.url}
                              rel="noopener noreferrer"
                              className={styles.org}
                            >
                              <span>{environmentAuthor.organization.name}</span>
                            </a>
                          </WithHoverSound>
                        ) : (
                          <span className={styles.org}>
                            <span>{environmentAuthor.organization.name}</span>
                          </span>
                        ))}
                    </div>
                    <div className={styles.footer}>
                      <WithHoverSound>
                        <button onClick={this.showCustomSceneDialog} className={styles.customButton}>
                          <FormattedMessage id="home.room_create_options" />
johnshaughnessy's avatar
johnshaughnessy committed
                        </button>
Greg Fodor's avatar
Greg Fodor committed
                    </div>
                  </div>
                  <div className={styles.controls}>
                    <WithHoverSound>
                      <button
                        className={styles.prev}
                        type="button"
                        tabIndex="1"
                        onClick={this.setToPreviousEnvironment}
                      >
                        <FontAwesomeIcon icon={faAngleLeft} />
                      </button>
                    </WithHoverSound>
                    <WithHoverSound>
                      <button className={styles.next} type="button" tabIndex="2" onClick={this.setToNextEnvironment}>
                        <FontAwesomeIcon icon={faAngleRight} />
                      </button>
                    </WithHoverSound>
Greg Fodor's avatar
Greg Fodor committed
                  </div>
                </div>
              </div>
              <div className={styles.container}>
                <WithHoverSound>
                  <button type="submit" tabIndex="5" className={styles.submitButton}>
                    <FormattedMessage id="home.room_create_button" />
                  </button>
                </WithHoverSound>
              </div>
            </div>
Greg Fodor's avatar
Greg Fodor committed
          </div>
        </form>
        {this.state.showCustomSceneDialog && (
          <CreateRoomDialog
            includeScenePrompt={true}
            onClose={() => this.setState({ showCustomSceneDialog: false })}
            onCustomScene={(name, url) => {
              this.setState({ showCustomSceneDialog: false, name: name, customSceneUrl: url }, () => this.createHub());
            }}
          />
Greg Fodor's avatar
Greg Fodor committed
        )}
Greg Fodor's avatar
Greg Fodor committed
    );
  }
}

export default injectIntl(HubCreatePanel);