-
Greg Fodor authoredGreg Fodor authored
home-root.js 10.61 KiB
import React, { Component } from "react";
import PropTypes from "prop-types";
import { IntlProvider, FormattedMessage, addLocaleData } from "react-intl";
import en from "react-intl/locale-data/en";
import { lang, messages } from "../utils/i18n";
import { playVideoWithStopOnBlur } from "../utils/video-utils.js";
import homeVideoWebM from "../assets/video/home.webm";
import homeVideoMp4 from "../assets/video/home.mp4";
import hubLogo from "../assets/images/hub-preview-light-no-shadow.png";
import mozLogo from "../assets/images/moz-logo-black.png";
import classNames from "classnames";
import { ENVIRONMENT_URLS } from "../assets/environments/environments";
import { connectToReticulum } from "../utils/phoenix-utils";
import styles from "../assets/stylesheets/index.scss";
import HubCreatePanel from "./hub-create-panel.js";
import AuthDialog from "./auth-dialog.js";
import ReportDialog from "./report-dialog.js";
import JoinUsDialog from "./join-us-dialog.js";
import UpdatesDialog from "./updates-dialog.js";
import DialogContainer from "./dialog-container.js";
import { WithHoverSound } from "./wrap-with-audio";
addLocaleData([...en]);
class HomeRoot extends Component {
static propTypes = {
intl: PropTypes.object,
sceneId: PropTypes.string,
authVerify: PropTypes.bool,
authTopic: PropTypes.string,
authToken: PropTypes.string,
authOrigin: PropTypes.string,
listSignup: PropTypes.bool,
report: PropTypes.bool,
initialEnvironment: PropTypes.string
};
state = {
environments: [],
dialog: null,
mailingListEmail: "",
mailingListPrivacy: false
};
componentDidMount() {
this.closeDialog = this.closeDialog.bind(this);
if (this.props.authVerify) {
this.showAuthDialog(true);
this.verifyAuth().then(this.showAuthDialog);
return;
}
if (this.props.sceneId) {
this.loadEnvironmentFromScene();
} else {
this.loadEnvironments();
}
this.loadHomeVideo();
if (this.props.listSignup) {
this.showUpdatesDialog();
} else if (this.props.report) {
this.showReportDialog();
}
}
async verifyAuth() {
const socket = connectToReticulum();
const channel = socket.channel(this.props.authTopic);
await new Promise((resolve, reject) =>
channel
.join()
.receive("ok", resolve)
.receive("error", reject)
);
channel.push("auth_verified", { token: this.props.authToken });
}
showAuthDialog = verifying => {
this.setState({ dialog: <AuthDialog verifying={verifying} authOrigin={this.props.authOrigin} /> });
};
loadHomeVideo = () => {
const videoEl = document.querySelector("#background-video");
videoEl.playbackRate = 0.9;
playVideoWithStopOnBlur(videoEl);
};
closeDialog() {
this.setState({ dialog: null });
}
showJoinUsDialog() {
this.setState({ dialog: <JoinUsDialog onClose={this.closeDialog} /> });
}
showReportDialog() {
this.setState({ dialog: <ReportDialog onClose={this.closeDialog} /> });
}
showUpdatesDialog() {
this.setState({
dialog: <UpdatesDialog onClose={this.closeDialog} onSubmittedEmail={() => this.showEmailSubmittedDialog()} />
});
}
showEmailSubmittedDialog() {
this.setState({
dialog: (
<DialogContainer onClose={this.closeDialog}>
Great! Please check your e-mail to confirm your subscription.
</DialogContainer>
)
});
}
loadEnvironmentFromScene = async () => {
let sceneUrlBase = "/api/v1/scenes";
if (process.env.RETICULUM_SERVER) {
sceneUrlBase = `https://${process.env.RETICULUM_SERVER}${sceneUrlBase}`;
}
const sceneInfoUrl = `${sceneUrlBase}/${this.props.sceneId}`;
const resp = await fetch(sceneInfoUrl).then(r => r.json());
const scene = resp.scenes[0];
const attribution = scene.attribution && scene.attribution.split("\n").join(", ");
const authors = attribution && [{ organization: { name: attribution } }];
// Transform the scene info into a an environment bundle structure.
this.setState({
environments: [
{
scene_id: this.props.sceneId,
meta: {
title: scene.name,
authors,
images: [{ type: "preview-thumbnail", srcset: scene.screenshot_url }]
}
}
]
});
};
loadEnvironments = () => {
const environments = [];
const environmentLoads = ENVIRONMENT_URLS.map(src =>
(async () => {
const res = await fetch(src);
const data = await res.json();
data.bundle_url = src;
environments.push(data);
})()
);
Promise.all(environmentLoads).then(() => this.setState({ environments }));
};
onDialogLinkClicked = trigger => {
return e => {
e.preventDefault();
e.stopPropagation();
trigger();
};
};
render() {
const mainContentClassNames = classNames({
[styles.mainContent]: true,
[styles.noninteractive]: !!this.state.dialog
});
return (
<IntlProvider locale={lang} messages={messages}>
<div className={styles.home}>
<div className={mainContentClassNames}>
<div className={styles.headerContent}>
<div className={styles.titleAndNav} onClick={() => (document.location = "/")}>
<div className={styles.links}>
<WithHoverSound>
<a href="https://github.com/mozilla/hubs" rel="noreferrer noopener">
<FormattedMessage id="home.source_link" />
</a>
</WithHoverSound>
<WithHoverSound>
<a href="https://discord.gg/XzrGUY8" rel="noreferrer noopener">
<FormattedMessage id="home.community_link" />
</a>
</WithHoverSound>
<WithHoverSound>
<a href="/spoke" rel="noreferrer noopener">
Spoke
</a>
</WithHoverSound>
</div>
</div>
</div>
<div className={styles.heroContent}>
<div className={styles.attribution}>
Medieval Fantasy Book by{" "}
<a
target="_blank"
rel="noreferrer noopener"
href="https://sketchfab.com/models/06d5a80a04fc4c5ab552759e9a97d91a?utm_campaign=06d5a80a04fc4c5ab552759e9a97d91a&utm_medium=embed&utm_source=oembed"
>
Pixel
</a>
</div>
<div className={styles.container}>
<img className={styles.logo} src={hubLogo} />
<div className={styles.title}>
<FormattedMessage id="home.hero_title" />
</div>
{this.state.environments.length === 0 && (
<div className="loader-wrap">
<div className="loader">
<div className="loader-center" />
</div>
</div>
)}
</div>
<div className={styles.create}>
<HubCreatePanel
initialEnvironment={this.props.initialEnvironment}
environments={this.state.environments}
/>
</div>
{this.state.environments.length > 1 && (
<div>
<WithHoverSound>
<div className={styles.joinButton}>
<a href="/link">
<FormattedMessage id="home.join_room" />
</a>
</div>
</WithHoverSound>
<WithHoverSound>
<div className={styles.spokeButton}>
<a href="/spoke">
<FormattedMessage id="home.create_with_spoke" />
</a>
</div>
</WithHoverSound>
</div>
)}
</div>
<div className={styles.footerContent}>
<div className={styles.links}>
<div className={styles.top}>
<WithHoverSound>
<a
className={styles.link}
rel="noopener noreferrer"
href="#"
onClick={this.onDialogLinkClicked(this.showJoinUsDialog.bind(this))}
>
<FormattedMessage id="home.join_us" />
</a>
</WithHoverSound>
<WithHoverSound>
<a
className={styles.link}
rel="noopener noreferrer"
href="#"
onClick={this.onDialogLinkClicked(this.showUpdatesDialog.bind(this))}
>
<FormattedMessage id="home.get_updates" />
</a>
</WithHoverSound>
<WithHoverSound>
<a
className={styles.link}
rel="noopener noreferrer"
href="#"
onClick={this.onDialogLinkClicked(this.showReportDialog.bind(this))}
>
<FormattedMessage id="home.report_issue" />
</a>
</WithHoverSound>
<WithHoverSound>
<a
className={styles.link}
target="_blank"
rel="noopener noreferrer"
href="https://github.com/mozilla/hubs/blob/master/TERMS.md"
>
<FormattedMessage id="home.terms_of_use" />
</a>
</WithHoverSound>
<WithHoverSound>
<a
className={styles.link}
target="_blank"
rel="noopener noreferrer"
href="https://github.com/mozilla/hubs/blob/master/PRIVACY.md"
>
<FormattedMessage id="home.privacy_notice" />
</a>
</WithHoverSound>
<img className={styles.mozLogo} src={mozLogo} />
</div>
</div>
</div>
</div>
<video playsInline muted loop autoPlay className={styles.backgroundVideo} id="background-video">
<source src={homeVideoWebM} type="video/webm" />
<source src={homeVideoMp4} type="video/mp4" />
</video>
{this.state.dialog}
</div>
</IntlProvider>
);
}
}
export default HomeRoot;