Skip to content
Snippets Groups Projects
spoke.js 8.75 KiB
Newer Older
import ReactDOM from "react-dom";
import React, { Component } from "react";
//import PropTypes from "prop-types";
//import classNames from "classnames";
Greg Fodor's avatar
Greg Fodor committed
import { playVideoWithStopOnBlur } from "./utils/video-utils.js";
Greg Fodor's avatar
Greg Fodor committed
import { IntlProvider, FormattedMessage, addLocaleData } from "react-intl";
import styles from "./assets/stylesheets/spoke.scss";
Greg Fodor's avatar
Greg Fodor committed
import spokeLogo from "./assets/images/spoke_logo.png";
Greg Fodor's avatar
Greg Fodor committed
import spokeVideoMp4 from "./assets/video/spoke.mp4";
Greg Fodor's avatar
Greg Fodor committed
import spokeVideoWebm from "./assets/video/spoke.webm";
Greg Fodor's avatar
Greg Fodor committed
import YouTube from "react-youtube";

//const qs = new URLSearchParams(location.search);

import registerTelemetry from "./telemetry";

registerTelemetry();

import en from "react-intl/locale-data/en";
import { lang, messages } from "./utils/i18n";

addLocaleData([...en]);

Greg Fodor's avatar
Greg Fodor committed
function getPlatform() {
  const platform = window.navigator.platform;

  if (["Macintosh", "MacIntel", "MacPPC", "Mac68K"].indexOf(platform) >= 0) {
    return "macos";
  } else if (["Win32", "Win64", "Windows"].indexOf(platform) >= 0) {
    return "win";
  } else if (/Linux/.test(platform) && !/\WAndroid\W/.test(navigator.userAgent)) {
Greg Fodor's avatar
Greg Fodor committed
    return "linux";
  }

  return "unsupported";
}

class SpokeLanding extends Component {
  static propTypes = {};

  constructor(props) {
    super(props);
Greg Fodor's avatar
Greg Fodor committed
    this.state = {
      platform: getPlatform(),
      downloadClicked: false,
      downloadLinkForCurrentPlatform: {},
      showPlayer: false,
      playerVideoId: "WmQKZJPhV7s"
Greg Fodor's avatar
Greg Fodor committed
    };
Greg Fodor's avatar
Greg Fodor committed
  componentDidMount() {
    this.loadVideo();
Greg Fodor's avatar
Greg Fodor committed
    this.fetchReleases();
Greg Fodor's avatar
Greg Fodor committed
  }

Greg Fodor's avatar
Greg Fodor committed
  tryGetJson = async request => {
    const text = await request.text();
    try {
      return JSON.parse(text);
    } catch (e) {
      console.log(`JSON error parsing response from ${request.url} "${text}"`, e);
    }
  };

  getDownloadUrlForPlatform = (assets, platform) => {
    return assets.find(asset => asset.name.includes(platform)).downloadUrl;
  };

  fetchReleases = async () => {
    // Read-only, public access token.
    const token = "de8cbfb4cc0281c7b731c891df431016c29b0ace";
    const result = await fetch("https://api.github.com/graphql", {
      timeout: 5000,
      method: "POST",
      headers: { authorization: `bearer ${token}` },
      body: JSON.stringify({
        query: `
          {
            repository(owner: "mozillareality", name: "spoke") {
Greg Fodor's avatar
Greg Fodor committed
                orderBy: { field: CREATED_AT, direction: DESC },
                first: 5
              ) {
                nodes {
                  isPrerelease,
                  isDraft,
                  tag { name },
                  releaseAssets(last: 3) {
                    nodes { name, downloadUrl }
                  }
                },
                pageInfo { endCursor, hasNextPage }
              }
            }
          }
        `
      })
    }).then(this.tryGetJson);

    if (!result || !result.data) {
Greg Fodor's avatar
Greg Fodor committed
      this.setState({ platform: "unsupported" });
Greg Fodor's avatar
Greg Fodor committed
      return;
    }

    const releases = result.data.repository.releases;
    const release = releases.nodes.find(release => /*!release.isPrerelease && */ !release.isDraft);

    if (!release) {
Greg Fodor's avatar
Greg Fodor committed
      this.setState({ platform: "unsupported" });
Greg Fodor's avatar
Greg Fodor committed
      return;
    }

    this.setState({
Greg Fodor's avatar
Greg Fodor committed
      downloadLinkForCurrentPlatform: this.getDownloadUrlForPlatform(release.releaseAssets.nodes, this.state.platform),
      spokeVersion: release.tag.name
Greg Fodor's avatar
Greg Fodor committed
    });
  };

Greg Fodor's avatar
Greg Fodor committed
  loadVideo() {
    const videoEl = document.querySelector("#preview-video");
    playVideoWithStopOnBlur(videoEl);
  }

  render() {
Greg Fodor's avatar
Greg Fodor committed
    const platform = this.state.platform;
Greg Fodor's avatar
Greg Fodor committed
    const releasesLink = "https://github.com/MozillaReality/Spoke/releases/latest";
Greg Fodor's avatar
Greg Fodor committed
    const downloadLink = platform === "unsupported" ? releasesLink : this.state.downloadLinkForCurrentPlatform;
Greg Fodor's avatar
Greg Fodor committed

    return (
      <IntlProvider locale={lang} messages={messages}>
Greg Fodor's avatar
Greg Fodor committed
        <div className={styles.ui}>
          <div className={styles.header}>
            <div className={styles.headerLinks}>
              <a href="https://github.com/mozillareality/spoke" rel="noopener noreferrer">
                <FormattedMessage id="home.source_link" />
Greg Fodor's avatar
Greg Fodor committed
              </a>
              <a href="https://discord.gg/XzrGUY8" rel="noreferrer noopener">
                <FormattedMessage id="home.community_link" />
              </a>
Greg Fodor's avatar
Greg Fodor committed
              <a href="/" rel="noreferrer noopener">
                Hubs
              </a>
Greg Fodor's avatar
Greg Fodor committed
            </div>
          </div>
          <div className={styles.content}>
            <div className={styles.heroPane}>
Greg Fodor's avatar
Greg Fodor committed
              <div className={styles.heroMessage}>
                <div className={styles.spokeLogo}>
                  <img src={spokeLogo} />
                  <div className={styles.primaryTagline}>
                    <FormattedMessage id="spoke.primary_tagline" />
                  </div>
                </div>
                <div className={styles.secondaryTagline}>
                  <FormattedMessage id="spoke.secondary_tagline" />
                  <a style={{ fontWeight: "bold" }} href="/">
                    Hubs
                  </a>
Greg Fodor's avatar
Greg Fodor committed
                </div>
                <div className={styles.actionButtons}>
                  {!this.state.downloadClicked ? (
                    <a
                      href={downloadLink}
Greg Fodor's avatar
Greg Fodor committed
                      onClick={() => this.setState({ downloadClicked: platform !== "unsupported" })}
                      className={styles.downloadButton}
                    >
Greg Fodor's avatar
Greg Fodor committed
                      <div>
                        <FormattedMessage id={"spoke.download_" + this.state.platform} />
                      </div>
Greg Fodor's avatar
Greg Fodor committed
                      {platform !== "unsupported" && (
                        <div className={styles.version}>{this.state.spokeVersion} Beta</div>
                      )}
Greg Fodor's avatar
Greg Fodor committed
                    </a>
                  ) : (
                    <div className={styles.thankYou}>
                      <p>
                        <FormattedMessage id="spoke.thank_you" />
                      </p>

                      <p>
                        You can also <a href="https://discord.gg/XzrGUY8/">join our community</a> on Discord.
                      </p>
                    </div>
Greg Fodor's avatar
Greg Fodor committed
                  )}

                  {platform !== "unsupported" &&
                    !this.state.downloadClicked && (
                      <a href={releasesLink} className={styles.browseVersions}>
                        <FormattedMessage id="spoke.browse_all_versions" />
                      </a>
                    )}
                  <div className={styles.tutorialButtons}>
                    <button
                      className={styles.playButton}
                      onClick={() => this.setState({ showPlayer: true, playerVideoId: "WmQKZJPhV7s" })}
                    >
                      <FormattedMessage id="spoke.beginner_tutorial_button" />
                    </button>
                    <button
                      className={styles.playButton}
                      onClick={() => this.setState({ showPlayer: true, playerVideoId: "1Yg5x4Plz_4" })}
                    >
                      <FormattedMessage id="spoke.advanced_tutorial_button" />
                    </button>
                  </div>
Greg Fodor's avatar
Greg Fodor committed
                </div>
              </div>
Greg Fodor's avatar
Greg Fodor committed
              <div className={styles.heroVideo}>
                <video playsInline muted loop autoPlay className={styles.previewVideo} id="preview-video">
Greg Fodor's avatar
Greg Fodor committed
                  <source src={spokeVideoMp4} type="video/mp4" />
Greg Fodor's avatar
Greg Fodor committed
                  <source src={spokeVideoWebm} type="video/webm" />
Greg Fodor's avatar
Greg Fodor committed
                </video>
                <div className={styles.attribution}>Low Poly Campfire by Minzkraut</div>
Greg Fodor's avatar
Greg Fodor committed
              </div>
            </div>
          </div>
          <div className={styles.bg} />
Greg Fodor's avatar
Greg Fodor committed
          {this.state.showPlayer && (
            <div className={styles.playerOverlay}>
              <div className={styles.playerContent}>
                <YouTube
                  className={styles.playerVideo}
                  opts={{ rel: 0 }}
                  videoId={this.state.playerVideoId}
Greg Fodor's avatar
Greg Fodor committed
                  onReady={e => e.target.playVideo()}
                />
                {platform !== "unsupported" && (
                  <a href={downloadLink} className={styles.downloadButton}>
                    <div>
                      <FormattedMessage id={"spoke.download_" + this.state.platform} />
                    </div>
                    <div className={styles.version}>{this.state.spokeVersion} Beta</div>
                  </a>
                )}
                <a onClick={() => this.setState({ showPlayer: false })} className={styles.closeVideo}>
                  <FormattedMessage id="spoke.close" />
                </a>
              </div>
            </div>
          )}
Greg Fodor's avatar
Greg Fodor committed
        </div>
      </IntlProvider>
    );
  }
}

document.addEventListener("DOMContentLoaded", () => {
  ReactDOM.render(<SpokeLanding />, document.getElementById("ui-root"));
});