diff --git a/package.json b/package.json index a97713d44790787795d671f30d6f5d9a384ac8ae..0d9ed6b57e38dc448a3da828e3acb5cc24a33035 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "classnames": "^2.2.5", "detect-browser": "^2.1.0", "event-target-shim": "^3.0.1", + "form-urlencoded": "^2.0.4", "jsonschema": "^1.2.2", "minijanus": "^0.5.0", "mobile-detect": "^1.4.1", diff --git a/scripts/default.env b/scripts/default.env index a1186bbf1f20d84c6883a36bedea1e6ac88e4e7c..b5dbe2c7c9ec3b22eba8d36f07c2fbd700bf0cb0 100644 --- a/scripts/default.env +++ b/scripts/default.env @@ -3,3 +3,4 @@ ORIGIN_TRIAL_TOKEN="ArEZ0vY0uMo3pj+oY8Up4u4Hy8QolJwKxG4/2WRhSPnTZRrviiGhzP6/y72nBdsIhdEyoundxqg//KLbs2vGnQoAAABkeyJvcmlnaW4iOiJodHRwczovL3JldGljdWx1bS5pbzo0NDMiLCJmZWF0dXJlIjoiV2ViVlIxLjFNNjIiLCJleHBpcnkiOjE1MjYzNDg2MjEsImlzU3ViZG9tYWluIjp0cnVlfQ==" ORIGIN_TRIAL_EXPIRES="2018-05-15" JANUS_SERVER="wss://prod-janus.reticulum.io" +DEV_RETICULUM_SERVER="dev.reticulum.io" diff --git a/src/assets/stylesheets/hub-create.scss b/src/assets/stylesheets/hub-create.scss index 636ab5d656725522d5a407d93c8a1d92361d61ca..31e49358ec78660c6bd9b10b1ce321bc6225c876 100644 --- a/src/assets/stylesheets/hub-create.scss +++ b/src/assets/stylesheets/hub-create.scss @@ -192,9 +192,13 @@ font-size: 1.4em; @media (max-width: 520px) { - display: none; + display: none; } - } + } + + a { + pointer-events: all; + } } } diff --git a/src/assets/stylesheets/index.scss b/src/assets/stylesheets/index.scss index 77615e9d341f4ca5688ebe6f0e209163f2c884cf..38833657208e9a58a19ef8b10add9e8a5ceaa25d 100644 --- a/src/assets/stylesheets/index.scss +++ b/src/assets/stylesheets/index.scss @@ -34,6 +34,10 @@ body { display: flex; flex-direction: column; z-index: 2; + + &--noninteractive { + pointer-events: none; + } } .background-video { @@ -49,7 +53,7 @@ body { .header-content { padding: 1.5em 2.5em 1.5em 2.5em; background-color: rgba(0, 0, 0, 0.85); - min-height: 90px; + height: 90px; display: flex; border-bottom: 2px solid #242424; @@ -217,3 +221,111 @@ body { } } } + +.overlay { + width: 100%; + height: 100%; + top: 0; + left: 0; + position: absolute; + pointer-events: none; + color: white; + z-index: 2; +} + +.mailing-list-form { + display: flex; + height: 100%; + flex-direction: column; + justify-content: center; + text-align: center; + margin: 0; + + &__first { + width: 100%; + } + + &__email_field { + @extend %rounded-border; + @extend %default-font; + color: $light-text; + font-size: 1.2em; + background-color: transparent; + line-height: 2.0em; + padding-left: 1.25em; + padding-right: 1.25em; + margin: 0.5em 0; + width: 100%; + } + + &__submit { + @extend %bottom-button; + border: 0; + margin-top: 16px; + } + + &__privacy { + margin-top: 10px; + font-size: 0.7em; + } +} + +.dialog { + display: grid; + grid-template-columns: 1fr 20px minmax(200px,500px) 20px 1fr; + grid-template-rows: 1fr 20px 275px 20px 1fr; + width: 100%; + height: 100%; + background-color: rgba(0,0,0,.6); + + &__box { + grid-column: 3; + grid-row: 3; + position: relative; + pointer-events: auto; + + &__contents { + background-color: rgba(0,0,0,0.8); + border-radius: 8px; + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + position: relative; + + &__title { + @extend %top-title; + margin-top: 20px; + } + + &__body { + margin: 40px; + font-size: 1.1em; + margin-top: 20px; + color: $grey-text; + display: flex; + flex-direction: column; + + a { color: white } + } + + &__close { + position: absolute; + left: 12px; + top: 6px; + color: white; + font-size: 1.4em; + + background: none; + cursor: pointer; + border: none; + } + } + } +} + + + diff --git a/src/assets/translations.data.json b/src/assets/translations.data.json index bf513559ad159824e95b16df5984e11899611d25..e2d73a361c9c11b16943c1fe6390b014e29706fc 100644 --- a/src/assets/translations.data.json +++ b/src/assets/translations.data.json @@ -1,6 +1,5 @@ { - "en": - { + "en": { "entry.screen-prefix": "Enter on ", "entry.desktop-screen": "Screen", "entry.mobile-screen": "Phone", @@ -34,6 +33,7 @@ "audio.granted-subtitle": "You can still mute yourself in-game", "audio.granted-next": "NEXT", "exit.subtitle.exited": "Your session has ended.", + "exit.subtitle.exited": "Your session has ended. Refresh your browser to start a new one.", "exit.subtitle.closed": "This room is no longer available.", "exit.subtitle.full": "This room is full, please try again later.", "exit.subtitle.connect_error": "Unable to connect to this room, please try again later.", @@ -48,12 +48,15 @@ "home.webvr_disclaimer_post": " experiment by ", "home.webvr_disclaimer_mr_team": "Mozilla Mixed Reality", "home.view_source": "View Source", - "home.join_on_slack": "Join us on Slack", + "home.join_us": "Join the Conversation", "home.report_issue": "Report an Issue", "home.get_updates": "Get Updates", "home.hero_title": "A new way to get together online.", "home.hero_subtitle": "Laugh, play, get stuff done, or just hang out.", - "home.made_with_love": "made with â¤ï¸ by ", - "home.environment_author_by": " by " + "home.made_with_love": "made with 🦆 by ", + "home.environment_author_by": " by ", + "home.dialog.close": "CLOSE", + "mailing_list.privacy_label": "I'm okay with Mozilla handling my info as explained in", + "mailing_list.privacy_link": "this Privacy Notice" } } diff --git a/src/components/stats-plus.css b/src/components/stats-plus.css new file mode 100644 index 0000000000000000000000000000000000000000..fc4417bf506b5a5916c286b72aeaf86189790732 --- /dev/null +++ b/src/components/stats-plus.css @@ -0,0 +1,26 @@ +:global(.rs-header) { + display: flex; + justify-content: space-between; + border-bottom: 1px rgba(255,255,255,0.1) solid; + margin-bottom: 8px; +} + +:global(.rs-collapse-btn) { + cursor: pointer; + font-size: 12px; +} + +:global(.rs-fps-counter) { + cursor: pointer; + position: absolute; + bottom: 0; + left: 0; + padding: 8px; + color: #aaa; + font-size: 10px; +} + +:global(.rs-mobile) { + bottom: auto; + top: 0; +} \ No newline at end of file diff --git a/src/components/stats-plus.js b/src/components/stats-plus.js new file mode 100644 index 0000000000000000000000000000000000000000..c64100eeafa0e069a10ff2b005181a2dc446435e --- /dev/null +++ b/src/components/stats-plus.js @@ -0,0 +1,130 @@ +import "./stats-plus.css"; +// Adapted from https://github.com/aframevr/aframe/blob/master/src/components/scene/stats.js + +function createStats(scene) { + const threeStats = new window.threeStats(scene.renderer); + const aframeStats = new window.aframeStats(scene); + const plugins = scene.isMobile ? [] : [threeStats, aframeStats]; + return new window.rStats({ + css: [], // Our stylesheet is injected from AFrame. + values: { + fps: { caption: "fps", below: 30 } + }, + groups: [{ caption: "Framerate", values: ["fps", "raf"] }], + plugins: plugins + }); +} + +const HIDDEN_CLASS = "a-hidden"; + +AFRAME.registerComponent("stats-plus", { + // Whether or not the stats panel is expanded. + // Shows FPS counter when collapsed. + schema: { default: false }, + init() { + this.onExpand = this.onExpand.bind(this); + this.onCollapse = this.onCollapse.bind(this); + this.onEnterVr = this.onEnterVr.bind(this); + this.onExitVr = this.onExitVr.bind(this); + + const scene = this.el.sceneEl; + this.stats = createStats(scene); + this.statsEl = document.querySelector(".rs-base"); + + // Add header to stats panel so we can collapse it + const statsHeaderEl = document.createElement("div"); + statsHeaderEl.classList.add("rs-header"); + + const statsTitleEl = document.createElement("h1"); + statsTitleEl.innerHTML = "Stats"; + statsHeaderEl.appendChild(statsTitleEl); + + const collapseEl = document.createElement("div"); + collapseEl.classList.add("rs-collapse-btn"); + collapseEl.innerHTML = "X"; + collapseEl.addEventListener("click", this.onCollapse); + statsHeaderEl.appendChild(collapseEl); + + this.statsEl.insertBefore(statsHeaderEl, this.statsEl.firstChild); + + // Add fps counter to the page + this.fpsEl = document.createElement("div"); + this.fpsEl.addEventListener("click", this.onExpand); + this.fpsEl.classList.add("rs-fps-counter"); + document.body.appendChild(this.fpsEl); + this.lastFpsUpdate = performance.now(); + this.frameCount = 0; + + if (scene.isMobile) { + this.statsEl.classList.add("rs-mobile"); + this.fpsEl.classList.add("rs-mobile"); + } + + scene.addEventListener("enter-vr", this.onEnterVr); + scene.addEventListener("exit-vr", this.onExitVr); + }, + update(oldData) { + if (oldData !== this.data) { + if (this.data) { + this.statsEl.classList.remove(HIDDEN_CLASS); + this.fpsEl.classList.add(HIDDEN_CLASS); + } else { + this.statsEl.classList.add(HIDDEN_CLASS); + this.fpsEl.classList.remove(HIDDEN_CLASS); + } + } + }, + tick() { + if (this.data) { + // Update rStats + const stats = this.stats; + stats("rAF").tick(); + stats("FPS").frame(); + stats().update(); + } else { + // Update the fps counter + const now = performance.now(); + this.frameCount++; + + // Update the fps counter text once a second + if (now >= this.lastFpsUpdate + 1000) { + const fps = this.frameCount / ((now - this.lastFpsUpdate) / 1000); + this.fpsEl.innerHTML = Math.round(fps) + " FPS"; + this.lastFpsUpdate = now; + this.frameCount = 0; + } + } + }, + onEnterVr() { + // Hide all stats elements when entering VR on mobile + if (this.el.sceneEl.isMobile) { + this.statsEl.classList.add(HIDDEN_CLASS); + this.fpsEl.classList.add(HIDDEN_CLASS); + } + }, + onExitVr() { + // Revert to previous state whe exiting VR on mobile + if (this.el.sceneEl.isMobile) { + if (this.data) { + this.statsEl.classList.remove(HIDDEN_CLASS); + } else { + this.fpsEl.classList.remove(HIDDEN_CLASS); + } + } + }, + onExpand() { + this.el.setAttribute(this.name, true); + }, + onCollapse() { + this.el.setAttribute(this.name, false); + }, + remove() { + this.el.sceneEl.removeListener("enter-vr", this.hide); + this.el.sceneEl.removeListener("exit-vr", this.show); + + if (this.statsEl) { + this.statsEl.parentNode.removeChild(this.statsEl); + this.fpsEl.parentNode.removeChild(this.fpsEl); + } + } +}); diff --git a/src/hub.js b/src/hub.js index e9e8372004750e203848299409fc3b69936711df..bdf37034aa1ee7647b088e616ebb8718ba6f14ba 100644 --- a/src/hub.js +++ b/src/hub.js @@ -49,6 +49,7 @@ import "./components/hand-poses"; import "./components/gltf-model-plus"; import "./components/gltf-bundle"; import "./components/hud-controller"; +import "./components/stats-plus"; import ReactDOM from "react-dom"; import React from "react"; @@ -57,6 +58,7 @@ import HubChannel from "./utils/hub-channel"; import "./systems/personal-space-bubble"; import "./systems/app-mode"; +import "./systems/exit-on-blur"; import "./gltf-component-mappings"; @@ -174,6 +176,9 @@ const onReady = async () => { }; const exitScene = () => { + if (NAF.connection.adapter && NAF.connection.adapter.localMediaStream) { + NAF.connection.adapter.localMediaStream.getTracks().forEach(t => t.stop()); + } hubChannel.disconnect(); const scene = document.querySelector("a-scene"); scene.renderer.animate(null); // Stop animation loop, TODO A-Frame should do this @@ -199,9 +204,7 @@ const onReady = async () => { serverURL: process.env.JANUS_SERVER }); - if (!qsTruthy("no_stats")) { - scene.setAttribute("stats", true); - } + scene.setAttribute("stats-plus", false); if (isMobile || qsTruthy("mobile")) { playerRig.setAttribute("virtual-gamepad-controls", {}); @@ -308,8 +311,10 @@ const onReady = async () => { console.log(`Hub ID: ${hubId}`); const socketProtocol = document.location.protocol === "https:" ? "wss:" : "ws:"; - const socketPort = qs.phx_port || document.location.port; - const socketHost = qs.phx_host || document.location.hostname; + const socketPort = qs.phx_port || (process.env.NODE_ENV === "production" ? document.location.port : 443); + const socketHost = + qs.phx_host || + (process.env.NODE_ENV === "production" ? document.location.hostname : process.env.DEV_RETICULUM_SERVER); const socketUrl = `${socketProtocol}//${socketHost}${socketPort ? `:${socketPort}` : ""}/socket`; console.log(`Phoenix Channel URL: ${socketUrl}`); diff --git a/src/react-components/home-root.js b/src/react-components/home-root.js index af896b643db9e3cf38ac5403aa58e143f28c58ce..36f1a7fbab31bf43653416432b0c54b25fd176a4 100644 --- a/src/react-components/home-root.js +++ b/src/react-components/home-root.js @@ -3,6 +3,8 @@ import PropTypes from "prop-types"; import { IntlProvider, FormattedMessage, addLocaleData } from "react-intl"; import en from "react-intl/locale-data/en"; import homeVideo from "../assets/video/home.webm"; +import classNames from "classnames"; +import formurlencoded from "form-urlencoded"; import HubCreatePanel from "./hub-create-panel.js"; @@ -17,9 +19,10 @@ const messages = localeData[lang] || localeData.en; const ENVIRONMENT_URLS = [ "https://asset-bundles-prod.reticulum.io/rooms/meetingroom/MeetingRoom.bundle.json", - "https://asset-bundles-prod.reticulum.io/rooms/theater/TheaterMeshes.bundle.json", - "https://asset-bundles-prod.reticulum.io/rooms/atrium/AtriumMeshes.bundle.json", - "https://asset-bundles-prod.reticulum.io/rooms/courtyard/CourtyardMeshes.bundle.json" + "https://asset-bundles-prod.reticulum.io/rooms/theater/Theater.bundle.json", + "https://asset-bundles-prod.reticulum.io/rooms/atrium/Atrium.bundle.json", + "https://asset-bundles-prod.reticulum.io/rooms/courtyard/Courtyard.bundle.json", + "https://asset-bundles-prod.reticulum.io/rooms/MedievalFantasyBook/MedievalFantasyBook.bundle.json" ]; class HomeRoot extends Component { @@ -28,7 +31,10 @@ class HomeRoot extends Component { }; state = { - environments: [] + environments: [], + dialogType: null, + mailingListEmail: "", + mailingListPrivacy: false }; componentDidMount() { @@ -36,6 +42,40 @@ class HomeRoot extends Component { document.querySelector("#background-video").playbackRate = 0.5; } + showDialog = dialogType => { + return e => { + e.preventDefault(); + e.stopPropagation(); + this.setState({ dialogType }); + }; + }; + + closeDialog = () => { + this.setState({ dialogType: null }); + }; + + signUpForMailingList = async e => { + e.preventDefault(); + e.stopPropagation(); + if (!this.state.mailingListPrivacy) return; + + const url = "https://www.mozilla.org/en-US/newsletter/"; + + const payload = { + email: this.state.mailingListEmail, + newsletters: "mixed-reality", + privacy: true, + fmt: "H", + source_url: document.location.href + }; + + await fetch(url, { + body: formurlencoded(payload), + method: "POST", + headers: { "content-type": "application/x-www-form-urlencoded" } + }).then(() => this.setState({ dialogType: "email_submitted" })); + }; + loadEnvironments = () => { const environments = []; @@ -52,10 +92,98 @@ class HomeRoot extends Component { }; render() { + let dialogTitle = null; + let dialogBody = null; + + switch (this.state.dialogType) { + // TODO i18n, FormattedMessage doesn't play nicely with links + case "slack": + dialogTitle = "Get in Touch"; + dialogBody = ( + <span> + Want to join the conversation? + <p /> + Join us on the{" "} + <a href="https://webvr-slack.herokuapp.com/" target="_blank" rel="noopener noreferrer"> + WebVR Slack + </a>{" "} + in the #social channel.<br />VR meetups every Friday at noon PST! + <p /> Or, tweet at{" "} + <a href="https://twitter.com/mozillareality" target="_blank" rel="noopener noreferrer"> + @mozillareality + </a>{" "} + on Twitter. + </span> + ); + break; + case "email_submitted": + dialogTitle = ""; + dialogBody = "Great! Please check your e-mail to confirm your subscription."; + break; + case "updates": + dialogTitle = ""; + dialogBody = ( + <span> + Sign up to get release notes about new features. + <p /> + <form onSubmit={this.signUpForMailingList}> + <div className="mailing-list-form"> + <input + type="email" + value={this.state.mailingListEmail} + onChange={e => this.setState({ mailingListEmail: e.target.value })} + className="mailing-list-form__email_field" + required + placeholder="Your email here" + /> + <label className="mailing-list-form__privacy"> + <input + className="mailing-list-form__privacy_checkbox" + type="checkbox" + required + value={this.state.mailingListPrivacy} + onChange={e => this.setState({ mailingListPrivacy: e.target.checked })} + /> + <span className="mailing-list-form__privacy_label"> + <FormattedMessage id="mailing_list.privacy_label" />{" "} + <a target="_blank" rel="noopener noreferrer" href="https://www.mozilla.org/en-US/privacy/"> + <FormattedMessage id="mailing_list.privacy_link" /> + </a> + </span> + </label> + <input className="mailing-list-form__submit" type="submit" value="Sign Up Now" /> + </div> + </form> + </span> + ); + break; + case "report": + dialogTitle = "Report an Issue"; + dialogBody = ( + <span> + Need to report a problem? + <p /> + You can file a{" "} + <a href="https://github.com/mozilla/mr-social-client/issues" target="_blank" rel="noopener noreferrer"> + Github Issue + </a>{" "} + or e-mail us for support at <a href="mailto:hubs@mozilla.com">hubs@mozilla.com</a>. + <p /> + You can also find us in #social on the{" "} + <a href="http://webvr-slack.herokuapp.com/" target="_blank" rel="noopener noreferrer"> + WebVR Slack + </a>. + </span> + ); + break; + } + + const mainContentClassNames = classNames({ "main-content": true, "main-content--noninteractive": !!dialogTitle }); + return ( <IntlProvider locale={lang} messages={messages}> <div className="home"> - <div className="main-content"> + <div className={mainContentClassNames}> <div className="header-content"> <div className="header-content__title"> <img className="header-content__title__name" src="../assets/images/logo.svg" /> @@ -110,13 +238,28 @@ class HomeRoot extends Component { <div className="footer-content"> <div className="footer-content__links"> <div className="footer-content__links__top"> - <a className="footer-content__links__link" rel="noopener noreferrer" target="_blank" href="#"> - <FormattedMessage id="home.join_on_slack" /> + <a + className="footer-content__links__link" + rel="noopener noreferrer" + href="#" + onClick={this.showDialog("slack")} + > + <FormattedMessage id="home.join_us" /> </a> - <a className="footer-content__links__link" rel="noopener noreferrer" target="_blank" href="#"> + <a + className="footer-content__links__link" + rel="noopener noreferrer" + href="#" + onClick={this.showDialog("updates")} + > <FormattedMessage id="home.get_updates" /> </a> - <a className="footer-content__links__link" rel="noopener noreferrer" target="_blank" href="#"> + <a + className="footer-content__links__link" + rel="noopener noreferrer" + href="#" + onClick={this.showDialog("report")} + > <FormattedMessage id="home.report_issue" /> </a> </div> @@ -130,6 +273,22 @@ class HomeRoot extends Component { <video playsInline autoPlay muted loop className="background-video" id="background-video"> <source src={homeVideo} type="video/webm" /> </video> + {this.state.dialogType && ( + <div className="overlay"> + <div className="dialog"> + <div className="dialog__box"> + <div className="dialog__box__contents"> + <button className="dialog__box__contents__close" onClick={this.closeDialog}> + <span>🗙</span> + </button> + <div className="dialog__box__contents__title">{dialogTitle}</div> + <div className="dialog__box__contents__body">{dialogBody}</div> + <div className="dialog__box__contents__button-container" /> + </div> + </div> + </div> + </div> + )} </div> </IntlProvider> ); diff --git a/src/react-components/hub-create-panel.js b/src/react-components/hub-create-panel.js index 32fd19b4edfcb343b0cada0d4f0abf0052344d8e..6e689653bcc4129547ec780cacbe220cb9f954da 100644 --- a/src/react-components/hub-create-panel.js +++ b/src/react-components/hub-create-panel.js @@ -38,14 +38,25 @@ class HubCreatePanel extends Component { hub: { name: this.state.name, default_environment_gltf_bundle_url: environment.bundle_url } }; - const res = await fetch("/api/v1/hubs", { + let createUrl = "/api/v1/hubs"; + + if (process.env.NODE_ENV === "development") { + createUrl = `https://${process.env.DEV_RETICULUM_SERVER}${createUrl}`; + } + + const res = await fetch(createUrl, { body: JSON.stringify(payload), headers: { "content-type": "application/json" }, method: "POST" }); const hub = await res.json(); - document.location = hub.url; + + if (process.env.NODE_ENV === "production") { + document.location = hub.url; + } else { + document.location = `/hub.html?hub_id=${hub.hub_id}`; + } }; isHubNameValid = () => { @@ -149,12 +160,22 @@ class HubCreatePanel extends Component { {environmentTitle} </span> {environmentAuthor && - environmentAuthor.name && ( + environmentAuthor.name && + (environmentAuthor.url ? ( + <a + href={environmentAuthor.url} + target="_blank" + className="create-panel__form__environment__picker__labels__header__author" + > + <FormattedMessage id="home.environment_author_by" /> + <span>{environmentAuthor.name}</span> + </a> + ) : ( <span className="create-panel__form__environment__picker__labels__header__author"> <FormattedMessage id="home.environment_author_by" /> <span>{environmentAuthor.name}</span> </span> - )} + ))} </div> <div className="create-panel__form__environment__picker__labels__footer"> <FormattedMessage id="home.environment_picker_footer" /> diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index 90d08830d616ecc1a8d0f7b0ebd18eb4241b2e0e..eef65a7448aefe0647ba46d411710906220e82bd 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -108,6 +108,12 @@ class UIRoot extends Component { this.props.scene.addEventListener("loaded", this.onSceneLoaded); this.props.scene.addEventListener("stateadded", this.onAframeStateChanged); this.props.scene.addEventListener("stateremoved", this.onAframeStateChanged); + this.props.scene.addEventListener("exit", this.exit); + } + + componentWillUnmount() { + this.props.scene.removeEventListener("loaded", this.onSceneLoaded); + this.props.scene.removeEventListener("exit", this.exit); } componentDidUpdate(prevProps) { diff --git a/src/systems/exit-on-blur.js b/src/systems/exit-on-blur.js new file mode 100644 index 0000000000000000000000000000000000000000..e6263a101f5a2fd1f71cd4b122a12431104e2457 --- /dev/null +++ b/src/systems/exit-on-blur.js @@ -0,0 +1,30 @@ +AFRAME.registerSystem("exit-on-blur", { + init() { + this.onBlur = this.onBlur.bind(this); + this.onFocus = this.onFocus.bind(this); + + window.addEventListener("blur", this.onBlur); + window.addEventListener("focus", this.onFocus); + + this.exitTimeout = null; + }, + + onBlur() { + if (this.el.isMobile) { + this.exitTimeout = setTimeout(() => { + this.el.dispatchEvent(new CustomEvent("exit")); + }, 10 * 1000); + } + }, + + onFocus() { + if (this.el.isMobile) { + clearTimeout(this.exitTimeout); + } + }, + + remove() { + clearTimeout(this.exitTimeout); + window.removeEventListener("blur", this.onBlur); + } +}); diff --git a/src/telemetry.js b/src/telemetry.js index 6eed4d002b96c507dcdaea78b6a5ab58f9791441..9fa4ae9ce79009d203fb4992cd6d643460633794 100644 --- a/src/telemetry.js +++ b/src/telemetry.js @@ -2,6 +2,6 @@ import Raven from "raven-js"; export default function registerTelemetry() { if (process.env.NODE_ENV === "production") { - Raven.config("https://f571beaf5cee4e3085e0bf436f3eb158@sentry.io/256771").install(); + Raven.config("https://013d6a364fed43cdb0539a61d520597a@sentry.prod.mozaws.net/370").install(); } } diff --git a/webpack.config.js b/webpack.config.js index 4f9b26d26cab4c3959a5b1796696f7a0b531c086..55bae82132e8d8611ba8b01ffcddf1ac49109c4b 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -87,7 +87,7 @@ const config = { mode: "development", devtool: process.env.NODE_ENV === "production" ? "source-map" : "inline-source-map", devServer: { - open: true, + open: false, https: createHTTPSConfig(), host: "0.0.0.0", useLocalIp: true, @@ -216,7 +216,8 @@ const config = { new webpack.DefinePlugin({ "process.env": JSON.stringify({ NODE_ENV: process.env.NODE_ENV, - JANUS_SERVER: process.env.JANUS_SERVER + JANUS_SERVER: process.env.JANUS_SERVER, + DEV_RETICULUM_SERVER: process.env.DEV_RETICULUM_SERVER }) }) ] diff --git a/yarn.lock b/yarn.lock index 8820642a27070f29460b409b87626ac173c298f5..6ae1ce2d8e2b6fb237724d975e27a2145a189ca7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -118,7 +118,7 @@ accepts@1.3.3: mime-types "~2.1.11" negotiator "0.6.1" -accepts@~1.3.4, accepts@~1.3.5: +accepts@~1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" dependencies: @@ -2007,18 +2007,14 @@ colormin@^1.0.5: css-color-names "0.0.4" has "^1.0.1" -colors@*: - version "1.2.1" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.2.1.tgz#f4a3d302976aaf042356ba1ade3b1a2c62d9d794" +colors@*, colors@^1.1.2, colors@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" colors@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" -colors@^1.1.2, colors@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" - combine-source-map@~0.7.1: version "0.7.2" resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.7.2.tgz#0870312856b307a87cc4ac486f3a9a62aeccc09e" @@ -2836,17 +2832,7 @@ error@^7.0.2: string-template "~0.2.1" xtend "~4.0.0" -es-abstract@^1.5.1: - version "1.11.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.11.0.tgz#cce87d518f0496893b1a30cd8461835535480681" - dependencies: - es-to-primitive "^1.1.1" - function-bind "^1.1.1" - has "^1.0.1" - is-callable "^1.1.3" - is-regex "^1.0.4" - -es-abstract@^1.7.0: +es-abstract@^1.5.1, es-abstract@^1.7.0: version "1.10.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864" dependencies: @@ -3076,42 +3062,7 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -express@^4.10.7: - version "4.16.3" - resolved "https://registry.yarnpkg.com/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53" - dependencies: - accepts "~1.3.5" - array-flatten "1.1.1" - body-parser "1.18.2" - content-disposition "0.5.2" - content-type "~1.0.4" - cookie "0.3.1" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.1.1" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.2" - path-to-regexp "0.1.7" - proxy-addr "~2.0.3" - qs "6.5.1" - range-parser "~1.2.0" - safe-buffer "5.1.1" - send "0.16.2" - serve-static "1.13.2" - setprototypeof "1.1.0" - statuses "~1.4.0" - type-is "~1.6.16" - utils-merge "1.0.1" - vary "~1.1.2" - -express@^4.16.2: +express@^4.10.7, express@^4.16.2: version "4.16.2" resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c" dependencies: @@ -3317,18 +3268,6 @@ finalhandler@1.1.0: statuses "~1.3.1" unpipe "~1.0.0" -finalhandler@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.2" - statuses "~1.4.0" - unpipe "~1.0.0" - find-cache-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" @@ -3416,6 +3355,10 @@ form-data@~2.1.1: combined-stream "^1.0.5" mime-types "^2.1.12" +form-urlencoded@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/form-urlencoded/-/form-urlencoded-2.0.4.tgz#dbcd590a49ae35d5e9516bbba8567242d0291fe5" + forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -6391,7 +6334,7 @@ prop-types@^15.5.4, prop-types@^15.6.0: loose-envify "^1.3.1" object-assign "^4.1.1" -proxy-addr@~2.0.2, proxy-addr@~2.0.3: +proxy-addr@~2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341" dependencies: @@ -7159,7 +7102,7 @@ serve-static@1.13.1: parseurl "~1.3.2" send "0.16.1" -serve-static@1.13.2, serve-static@^1.10.0, serve-static@^1.8.0: +serve-static@^1.10.0, serve-static@^1.8.0: version "1.13.2" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" dependencies: @@ -7971,7 +7914,7 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-is@~1.6.15, type-is@~1.6.16: +type-is@~1.6.15: version "1.6.16" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" dependencies: