diff --git a/.prettierignore b/.prettierignore index edded1b3e9cda6cef1bf33b9c8e08aa12f1f84a7..4bd6eed9e8bcf23ca49b895c57f0ba6d6290e14b 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,3 @@ package.json *.gltf +src/vendor/ diff --git a/src/behaviours/oculus-touch-joystick-dpad4.js b/src/behaviours/oculus-touch-joystick-dpad4.js index 758fb603c70faceb932539d722e08976e603046d..67d3352338a40d021c1c44cfb36c455205cde8af 100644 --- a/src/behaviours/oculus-touch-joystick-dpad4.js +++ b/src/behaviours/oculus-touch-joystick-dpad4.js @@ -15,11 +15,8 @@ oculus_touch_joystick_dpad4.prototype = { emitDPad4: function(event) { const x = event.detail.axis[0]; const y = event.detail.axis[1]; - const inCenter = - Math.abs(x) < this.centerRadius && Math.abs(y) < this.centerRadius; - const current = inCenter - ? "center" - : this.angleToDirection(Math.atan2(x, -y)); + const inCenter = Math.abs(x) < this.centerRadius && Math.abs(y) < this.centerRadius; + const current = inCenter ? "center" : this.angleToDirection(Math.atan2(x, -y)); if (current !== this.previous) { this.previous = current; event.target.emit(`${this.outputPrefix}_dpad4_${current}`); diff --git a/src/behaviours/vive-trackpad-dpad4.js b/src/behaviours/vive-trackpad-dpad4.js index 3f4e3a275a4c6bad6a74aa6ef3c8836bb3a26500..cf477635f7747434f12dedcc69009c58824afad1 100644 --- a/src/behaviours/vive-trackpad-dpad4.js +++ b/src/behaviours/vive-trackpad-dpad4.js @@ -25,11 +25,8 @@ vive_trackpad_dpad4.prototype = { emitDPad4: function(event) { const x = event.detail.axis[0]; const y = event.detail.axis[1]; - const inCenter = - Math.abs(x) < this.centerRadius && Math.abs(y) < this.centerRadius; - const direction = inCenter - ? "center" - : angleTo4Direction(Math.atan2(x, -y)); + const inCenter = Math.abs(x) < this.centerRadius && Math.abs(y) < this.centerRadius; + const direction = inCenter ? "center" : angleTo4Direction(Math.atan2(x, -y)); const pressed = this.pressed ? "pressed_" : ""; const current = `${pressed + direction}`; // e.g. "pressed_north" diff --git a/src/components/bone-mute-state-indicator.js b/src/components/bone-mute-state-indicator.js index 7c6dc5913e971a3a3981f2d8af7ed3e151dcf0e8..2d79b3f9c37feee028b89afbe6eaffe7bbd654d8 100644 --- a/src/components/bone-mute-state-indicator.js +++ b/src/components/bone-mute-state-indicator.js @@ -12,12 +12,8 @@ AFRAME.registerComponent("bone-mute-state-indicator", { init() { this.onStateToggled = this.onStateToggled.bind(this); this.el.addEventListener("model-loaded", () => { - this.unmutedBone = this.el.object3D.getObjectByName( - this.data.unmutedBoneName - ); - this.mutedBone = this.el.object3D.getObjectByName( - this.data.mutedBoneName - ); + this.unmutedBone = this.el.object3D.getObjectByName(this.data.unmutedBoneName); + this.mutedBone = this.el.object3D.getObjectByName(this.data.mutedBoneName); console.log(this.unmutedBone, this.mutedBone); this.modelLoaded = true; diff --git a/src/components/event-repeater.js b/src/components/event-repeater.js index f099038b48ca692f1e7443e650b59cdfaeb7481e..64e28720698d8aaa26a8f0ce1525301cebd1ee2f 100644 --- a/src/components/event-repeater.js +++ b/src/components/event-repeater.js @@ -26,5 +26,4 @@ AFRAME.registerComponent("event-repeater", { _handleEvent: function(event, e) { this.el.emit(event, e.details); } - }); diff --git a/src/components/networked-counter.js b/src/components/networked-counter.js index c35dcbaa461fa5ca8627d7cf693455d4244ec9fe..c13f621fb5ee2cb2863bd8d2a90dc66ba3a13082 100644 --- a/src/components/networked-counter.js +++ b/src/components/networked-counter.js @@ -20,7 +20,7 @@ AFRAME.registerComponent("networked-counter", { item.el.removeEventListener(this.data.release_event, item.onReleaseHandler); } } - + for (const id in this.timeouts) { this._removeTimeout(id); } diff --git a/src/components/networked-video-player.js b/src/components/networked-video-player.js index 51adcf2f7f2ddd6d0023c54442e03627ea952c70..189a43e393f359a5c2d217b5978132476f6407c6 100644 --- a/src/components/networked-video-player.js +++ b/src/components/networked-video-player.js @@ -25,7 +25,7 @@ AFRAME.registerComponent("networked-video-player", { if (ownerId !== NAF.clientId && rejectScreenShares) { // Toggle material visibility since object visibility is network-synced // TODO: There ought to be a better way to disable network syncs on a remote entity - this.el.setAttribute("material", {visible: false}); + this.el.setAttribute("material", { visible: false }); return; } diff --git a/src/components/offset-relative-to.js b/src/components/offset-relative-to.js index 923eae3e5673488e8eceb726b9e91f592285f546..f940b687284b34677c579e931557fc9671631f41 100644 --- a/src/components/offset-relative-to.js +++ b/src/components/offset-relative-to.js @@ -12,10 +12,7 @@ AFRAME.registerComponent("offset-relative-to", { }, init() { this.updateOffset(); - this.el.sceneEl.addEventListener( - this.data.on, - this.updateOffset.bind(this) - ); + this.el.sceneEl.addEventListener(this.data.on, this.updateOffset.bind(this)); }, updateOffset() { const offsetVector = new THREE.Vector3().copy(this.data.offset); diff --git a/src/components/super-networked-interactable.js b/src/components/super-networked-interactable.js index 4aeafecaa460379852a0ff4e9eb0f0f0d25bf9f3..601eebd55c996839f71d6bdce86d49131c22590c 100644 --- a/src/components/super-networked-interactable.js +++ b/src/components/super-networked-interactable.js @@ -11,7 +11,7 @@ AFRAME.registerComponent("super-networked-interactable", { NAF.utils.getNetworkedEntity(this.el).then(networkedEl => { this.networkedEl = networkedEl; if (!NAF.utils.isMine(networkedEl)) { - this.el.setAttribute("body", {type: "dynamic", mass: 0}); + this.el.setAttribute("body", { type: "dynamic", mass: 0 }); } else { this.counter.register(networkedEl); } @@ -33,7 +33,7 @@ AFRAME.registerComponent("super-networked-interactable", { this.hand = e.detail.hand; if (this.networkedEl && !NAF.utils.isMine(this.networkedEl)) { if (NAF.utils.takeOwnership(this.networkedEl)) { - this.el.setAttribute("body", {mass: this.data.mass}); + this.el.setAttribute("body", { mass: this.data.mass }); this.counter.register(this.networkedEl); } else { this.el.emit("grab-end", { hand: this.hand }); @@ -43,7 +43,7 @@ AFRAME.registerComponent("super-networked-interactable", { }, _onOwnershipLost: function(e) { - this.el.setAttribute("body", {mass: 0}); + this.el.setAttribute("body", { mass: 0 }); this.el.emit("grab-end", { hand: this.hand }); this.hand = null; this.counter.deregister(this.el); diff --git a/src/components/super-spawner.js b/src/components/super-spawner.js index 72d24eff785b9eebed42c60e2e4feb21e659468a..7c17c96c45dc17221a4709bdc25b71a349d0867a 100644 --- a/src/components/super-spawner.js +++ b/src/components/super-spawner.js @@ -39,12 +39,12 @@ AFRAME.registerComponent("super-spawner", { this.entities.set(entity, { hand: hand, - componentInitialized: false, - bodyLoaded: false, - componentinInitializedListener: componentinInitializedListener, + componentInitialized: false, + bodyLoaded: false, + componentinInitializedListener: componentinInitializedListener, bodyLoadedListener: bodyLoadedListener }); - + entity.addEventListener("componentinitialized", componentinInitializedListener); entity.addEventListener("body-loaded", bodyLoadedListener); diff --git a/src/components/water.js b/src/components/water.js index 9152e548435ea8b184e9d1eb0f8cd4c2371217cc..8dd6463efe3146e02e525fda8f48587d09655dea 100644 --- a/src/components/water.js +++ b/src/components/water.js @@ -19,22 +19,13 @@ function MobileWater(geometry, options) { const clipBias = options.clipBias !== undefined ? options.clipBias : 0.0; const time = options.time !== undefined ? options.time : 0.0; - const normalSampler = - options.waterNormals !== undefined ? options.waterNormals : null; + const normalSampler = options.waterNormals !== undefined ? options.waterNormals : null; const sunDirection = - options.sunDirection !== undefined - ? options.sunDirection - : new THREE.Vector3(0.70707, 0.70707, 0.0); - const sunColor = new THREE.Color( - options.sunColor !== undefined ? options.sunColor : 0xffffff - ); - const waterColor = new THREE.Color( - options.waterColor !== undefined ? options.waterColor : 0x7f7f7f - ); - const eye = - options.eye !== undefined ? options.eye : new THREE.Vector3(0, 0, 0); - const distortionScale = - options.distortionScale !== undefined ? options.distortionScale : 20.0; + options.sunDirection !== undefined ? options.sunDirection : new THREE.Vector3(0.70707, 0.70707, 0.0); + const sunColor = new THREE.Color(options.sunColor !== undefined ? options.sunColor : 0xffffff); + const waterColor = new THREE.Color(options.waterColor !== undefined ? options.waterColor : 0x7f7f7f); + const eye = options.eye !== undefined ? options.eye : new THREE.Vector3(0, 0, 0); + const distortionScale = options.distortionScale !== undefined ? options.distortionScale : 20.0; const side = options.side !== undefined ? options.side : THREE.FrontSide; const fog = options.fog !== undefined ? options.fog : false; diff --git a/src/elements/a-gltf-entity.js b/src/elements/a-gltf-entity.js index 33489e2bc1792aa2b9dd4bddca360caaa39c509b..2969d26d955cb2382eb74bb0e2a5efd9d1025bc8 100644 --- a/src/elements/a-gltf-entity.js +++ b/src/elements/a-gltf-entity.js @@ -238,7 +238,7 @@ AFRAME.registerElement("a-gltf-entity", { // If the src attribute is a selector, get the url from the asset item. if (src && src.charAt(0) === "#") { const assetEl = document.getElementById(src.substring(1)); - if (!assetEl) { + if (!assetEl) { console.warn(`Attempted to use non-existent asset ${src} as src for`, this); return; } diff --git a/src/react-components/avatar-selector.js b/src/react-components/avatar-selector.js index 7cf71b7c5e130c2bbd16a54e5b247f3a1cea74d5..d4a187d8cc5be811cd1343ebc11d311e61a4272b 100644 --- a/src/react-components/avatar-selector.js +++ b/src/react-components/avatar-selector.js @@ -1,49 +1,49 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { injectIntl, FormattedMessage } from 'react-intl'; -import FontAwesomeIcon from '@fortawesome/react-fontawesome'; -import faAngleLeft from '@fortawesome/fontawesome-free-solid/faAngleLeft'; -import faAngleRight from '@fortawesome/fontawesome-free-solid/faAngleRight'; -import meetingSpace from '../assets/environments/MeetingSpace1_mesh.glb'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { injectIntl, FormattedMessage } from "react-intl"; +import FontAwesomeIcon from "@fortawesome/react-fontawesome"; +import faAngleLeft from "@fortawesome/fontawesome-free-solid/faAngleLeft"; +import faAngleRight from "@fortawesome/fontawesome-free-solid/faAngleRight"; +import meetingSpace from "../assets/environments/MeetingSpace1_mesh.glb"; class AvatarSelector extends Component { static propTypes = { avatars: PropTypes.array, avatarId: PropTypes.string, - onChange: PropTypes.func, - } + onChange: PropTypes.func + }; - getAvatarIndex = (direction=0) => { + getAvatarIndex = (direction = 0) => { const currAvatarIndex = this.props.avatars.findIndex(avatar => avatar.id === this.props.avatarId); const numAvatars = this.props.avatars.length; return ((currAvatarIndex + direction) % numAvatars + numAvatars) % numAvatars; - } - nextAvatarIndex = () => this.getAvatarIndex(1) - previousAvatarIndex = () => this.getAvatarIndex(-1) + }; + nextAvatarIndex = () => this.getAvatarIndex(1); + previousAvatarIndex = () => this.getAvatarIndex(-1); emitChangeToNext = () => { const nextAvatarId = this.props.avatars[this.nextAvatarIndex()].id; this.props.onChange(nextAvatarId); - } + }; emitChangeToPrevious = () => { const previousAvatarId = this.props.avatars[this.previousAvatarIndex()].id; this.props.onChange(previousAvatarId); - } + }; componentDidUpdate(prevProps) { - if (this.props.avatarId !== prevProps.avatarId) { + if (this.props.avatarId !== prevProps.avatarId) { // HACK - a-animation ought to restart the animation when the `to` attribute changes, but it doesn't // so we need to force it here. - const currRot = this.animation.parentNode.getAttribute('rotation'); - this.animation.setAttribute('from', `${currRot.x} ${currRot.y} ${currRot.z}`); + const currRot = this.animation.parentNode.getAttribute("rotation"); + this.animation.setAttribute("from", `${currRot.x} ${currRot.y} ${currRot.z}`); this.animation.stop(); this.animation.handleMixinUpdate(); this.animation.start(); } } - render () { + render() { const avatarAssets = this.props.avatars.map(avatar => ( <a-progressive-asset id={avatar.id} @@ -51,74 +51,63 @@ class AvatarSelector extends Component { response-type="arraybuffer" high-src={`${avatar.models.high}`} low-src={`${avatar.models.low}`} - ></a-progressive-asset> + /> )); const avatarEntities = this.props.avatars.map((avatar, i) => ( <a-entity key={avatar.id} position="0 0 0" rotation={`0 ${360 * -i / this.props.avatars.length} 0`}> - <a-gltf-entity position="0 0 5" rotation="0 0 0" src={'#' + avatar.id} inflate="true"> + <a-gltf-entity position="0 0 5" rotation="0 0 0" src={"#" + avatar.id} inflate="true"> <template data-selector=".RootScene"> - <a-entity animation-mixer></a-entity> + <a-entity animation-mixer /> </template> <a-animation attribute="rotation" dur="2000" to={`0 ${this.getAvatarIndex() === i ? 360 : 0} 0`} - repeat="indefinite"> - </a-animation> + repeat="indefinite" + /> </a-gltf-entity> </a-entity> )); return ( <div className="avatar-selector"> - <span className="avatar-selector__loading"> - <FormattedMessage id="profile.avatar-selector.loading"/> - </span> - <a-scene vr-mode-ui="enabled: false" ref={sce => this.scene = sce}> - <a-assets> - {avatarAssets} - <a-asset-item - id="meeting-space1-mesh" - response-type="arraybuffer" - src={meetingSpace} - ></a-asset-item> - </a-assets> + <span className="avatar-selector__loading"> + <FormattedMessage id="profile.avatar-selector.loading" /> + </span> + <a-scene vr-mode-ui="enabled: false" ref={sce => (this.scene = sce)}> + <a-assets> + {avatarAssets} + <a-asset-item id="meeting-space1-mesh" response-type="arraybuffer" src={meetingSpace} /> + </a-assets> - <a-entity> - <a-animation - ref={anm => this.animation = anm} - attribute="rotation" - dur="1000" - easing="ease-out" - to={`0 ${360 * this.getAvatarIndex() / this.props.avatars.length + 180} 0`}> - </a-animation> - {avatarEntities} - </a-entity> + <a-entity> + <a-animation + ref={anm => (this.animation = anm)} + attribute="rotation" + dur="1000" + easing="ease-out" + to={`0 ${360 * this.getAvatarIndex() / this.props.avatars.length + 180} 0`} + /> + {avatarEntities} + </a-entity> - <a-entity position="0 1.5 -5.6" rotation="-10 180 0" camera></a-entity> + <a-entity position="0 1.5 -5.6" rotation="-10 180 0" camera /> - <a-entity - hide-when-quality="low" - light="type: directional; color: #F9FFCE; intensity: 0.6" - position="0 5 -15" - ></a-entity> - <a-entity - hide-when-quality="low" - light="type: ambient; color: #FFF" - ></a-entity> - <a-gltf-entity - id="meeting-space" - src="#meeting-space1-mesh" - position="0 0 0" - ></a-gltf-entity> - </a-scene> - <button className="avatar-selector__previous-button" onClick={this.emitChangeToPrevious}> - <FontAwesomeIcon icon={faAngleLeft} /> - </button> - <button className="avatar-selector__next-button" onClick={this.emitChangeToNext}> - <FontAwesomeIcon icon={faAngleRight} /> - </button> + <a-entity + hide-when-quality="low" + light="type: directional; color: #F9FFCE; intensity: 0.6" + position="0 5 -15" + /> + <a-entity hide-when-quality="low" light="type: ambient; color: #FFF" /> + <a-gltf-entity id="meeting-space" src="#meeting-space1-mesh" position="0 0 0" /> + </a-scene> + <button className="avatar-selector__previous-button" onClick={this.emitChangeToPrevious}> + <FontAwesomeIcon icon={faAngleLeft} /> + </button> + <button className="avatar-selector__next-button" onClick={this.emitChangeToNext}> + <FontAwesomeIcon icon={faAngleRight} /> + </button> </div> ); } diff --git a/src/react-components/entry-buttons.js b/src/react-components/entry-buttons.js index e567c6e1111f362ab4175087794af1f38a6a1c24..e2754712def40c875e9842a2b27800f90c002f86 100644 --- a/src/react-components/entry-buttons.js +++ b/src/react-components/entry-buttons.js @@ -1,28 +1,28 @@ import React, { Component } from "react"; import { FormattedMessage } from "react-intl"; import PropTypes from "prop-types"; -import MobileDetect from 'mobile-detect'; +import MobileDetect from "mobile-detect"; -import MobileScreenEntryImg from '../assets/images/mobile_screen_entry.svg'; -import DesktopScreenEntryImg from '../assets/images/desktop_screen_entry.svg'; -import GenericVREntryImg from '../assets/images/generic_vr_entry.svg'; -import GearVREntryImg from '../assets/images/gearvr_entry.svg'; -import DaydreamEntyImg from '../assets/images/daydream_entry.svg'; +import MobileScreenEntryImg from "../assets/images/mobile_screen_entry.svg"; +import DesktopScreenEntryImg from "../assets/images/desktop_screen_entry.svg"; +import GenericVREntryImg from "../assets/images/generic_vr_entry.svg"; +import GearVREntryImg from "../assets/images/gearvr_entry.svg"; +import DaydreamEntyImg from "../assets/images/daydream_entry.svg"; const mobiledetect = new MobileDetect(navigator.userAgent); -const EntryButton = (props) => ( - <div className="entry-button" onClick={ props.onClick }> - <img src={props.iconSrc} className="entry-button__icon"/> +const EntryButton = props => ( + <div className="entry-button" onClick={props.onClick}> + <img src={props.iconSrc} className="entry-button__icon" /> <div className="entry-button__label"> <div className="entry-button__label__contents"> <span> - <FormattedMessage id={ props.prefixMessageId }/> + <FormattedMessage id={props.prefixMessageId} /> </span> <span className="entry-button--bolded"> - <FormattedMessage id={ props.mediumMessageId }/> + <FormattedMessage id={props.mediumMessageId} /> </span> - { props.subtitle && (<div className="entry-button__subtitle">{props.subtitle}</div>) } + {props.subtitle && <div className="entry-button__subtitle">{props.subtitle}</div>} </div> </div> </div> @@ -34,20 +34,20 @@ EntryButton.propTypes = { prefixMessageId: PropTypes.string, mediumMessageId: PropTypes.string, subtitle: PropTypes.string -} +}; -export const TwoDEntryButton = (props) => { +export const TwoDEntryButton = props => { const entryButtonProps = { ...props, iconSrc: mobiledetect.mobile() ? MobileScreenEntryImg : DesktopScreenEntryImg, prefixMessageId: "entry.screen-prefix", - mediumMessageId: mobiledetect.mobile() ? "entry.mobile-screen" : "entry.desktop-screen" + mediumMessageId: mobiledetect.mobile() ? "entry.mobile-screen" : "entry.desktop-screen" }; - return (<EntryButton {...entryButtonProps}/>); -} + return <EntryButton {...entryButtonProps} />; +}; -export const GenericEntryButton = (props) => { +export const GenericEntryButton = props => { const entryButtonProps = { ...props, iconSrc: GenericVREntryImg, @@ -55,10 +55,10 @@ export const GenericEntryButton = (props) => { mediumMessageId: "entry.generic-medium" }; - return (<EntryButton {...entryButtonProps}/>); + return <EntryButton {...entryButtonProps} />; }; -export const GearVREntryButton = (props) => { +export const GearVREntryButton = props => { const entryButtonProps = { ...props, iconSrc: GearVREntryImg, @@ -66,10 +66,10 @@ export const GearVREntryButton = (props) => { mediumMessageId: "entry.gearvr-medium" }; - return (<EntryButton {...entryButtonProps}/>); + return <EntryButton {...entryButtonProps} />; }; -export const DaydreamEntryButton = (props) => { +export const DaydreamEntryButton = props => { const entryButtonProps = { ...props, iconSrc: DaydreamEntyImg, @@ -77,6 +77,5 @@ export const DaydreamEntryButton = (props) => { mediumMessageId: "entry.daydream-medium" }; - return (<EntryButton {...entryButtonProps}/>); + return <EntryButton {...entryButtonProps} />; }; - diff --git a/src/react-components/profile-entry-panel.js b/src/react-components/profile-entry-panel.js index 91044c27cab1955c7e35698322c618b907a3f95d..990add71c4a84571d451fc3d22be8f56454af668 100644 --- a/src/react-components/profile-entry-panel.js +++ b/src/react-components/profile-entry-panel.js @@ -1,6 +1,6 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { injectIntl, FormattedMessage } from 'react-intl'; +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { injectIntl, FormattedMessage } from "react-intl"; import { SCHEMA } from "../storage/store"; class ProfileEntryPanel extends Component { @@ -8,14 +8,14 @@ class ProfileEntryPanel extends Component { store: PropTypes.object, messages: PropTypes.object, finished: PropTypes.func - } + }; constructor(props) { super(props); window.store = this.props.store; this.state = { display_name: this.props.store.state.profile.display_name, - avatar_id: this.props.store.state.profile.avatar_id, + avatar_id: this.props.store.state.profile.avatar_id }; this.props.store.addEventListener("statechanged", this.storeUpdated); } @@ -23,67 +23,77 @@ class ProfileEntryPanel extends Component { storeUpdated = () => { const { avatar_id, display_name } = this.props.store.state.profile; this.setState({ avatar_id, display_name }); - } + }; - saveStateAndFinish = (e) => { + saveStateAndFinish = e => { e.preventDefault(); - this.props.store.update({profile: { - display_name: this.state.display_name, - avatar_id: this.state.avatar_id - }}); + this.props.store.update({ + profile: { + display_name: this.state.display_name, + avatar_id: this.state.avatar_id + } + }); this.props.finished(); - } + }; - stopPropagation = (e) => { + stopPropagation = e => { e.stopPropagation(); - } + }; - setAvatarStateFromIframeMessage = (e) => { - if (e.source !== this.avatarSelector.contentWindow) { return; } - this.setState({avatar_id: e.data.avatarId}); - } + setAvatarStateFromIframeMessage = e => { + if (e.source !== this.avatarSelector.contentWindow) { + return; + } + this.setState({ avatar_id: e.data.avatarId }); + }; componentDidMount() { // stop propagation so that avatar doesn't move when wasd'ing during text input. - this.nameInput.addEventListener('keydown', this.stopPropagation); - this.nameInput.addEventListener('keypress', this.stopPropagation); - this.nameInput.addEventListener('keyup', this.stopPropagation); - window.addEventListener('message', this.setAvatarStateFromIframeMessage); + this.nameInput.addEventListener("keydown", this.stopPropagation); + this.nameInput.addEventListener("keypress", this.stopPropagation); + this.nameInput.addEventListener("keyup", this.stopPropagation); + window.addEventListener("message", this.setAvatarStateFromIframeMessage); } componentWillUnmount() { - this.props.store.removeEventListener('statechanged', this.storeUpdated); - this.nameInput.removeEventListener('keydown', this.stopPropagation); - this.nameInput.removeEventListener('keypress', this.stopPropagation); - this.nameInput.removeEventListener('keyup', this.stopPropagation); - window.removeEventListener('message', this.setAvatarStateFromIframeMessage); + this.props.store.removeEventListener("statechanged", this.storeUpdated); + this.nameInput.removeEventListener("keydown", this.stopPropagation); + this.nameInput.removeEventListener("keypress", this.stopPropagation); + this.nameInput.removeEventListener("keyup", this.stopPropagation); + window.removeEventListener("message", this.setAvatarStateFromIframeMessage); } - render () { + render() { const { formatMessage } = this.props.intl; return ( <div className="profile-entry"> <form onSubmit={this.saveStateAndFinish}> - <div className="profile-entry__box profile-entry__box--darkened"> - <div className="profile-entry__subtitle"> - <FormattedMessage id="profile.header"/> + <div className="profile-entry__box profile-entry__box--darkened"> + <div className="profile-entry__subtitle"> + <FormattedMessage id="profile.header" /> + </div> + <input + className="profile-entry__form-field-text" + value={this.state.display_name} + onChange={e => this.setState({ display_name: e.target.value })} + required + pattern={SCHEMA.definitions.profile.properties.display_name.pattern} + title={formatMessage({ id: "profile.display_name.validation_warning" })} + ref={inp => (this.nameInput = inp)} + /> + <iframe + className="profile-entry__avatar-selector" + src={ + /* HACK: Have to account for the smoke test server like this. Feels wrong though. */ + `${/smoke/i.test(location.hostname) ? "smoke-" : ""}avatar-selector.html#avatar_id=${ + this.state.avatar_id + }` + } + ref={ifr => (this.avatarSelector = ifr)} + /> + <input className="profile-entry__form-submit" type="submit" value={formatMessage({ id: "profile.save" })} /> </div> - <input - className="profile-entry__form-field-text" - value={this.state.display_name} onChange={(e) => this.setState({display_name: e.target.value})} - required pattern={SCHEMA.definitions.profile.properties.display_name.pattern} - title={formatMessage({ id: "profile.display_name.validation_warning" })} - ref={inp => this.nameInput = inp}/> - <iframe - className="profile-entry__avatar-selector" - src={ - /* HACK: Have to account for the smoke test server like this. Feels wrong though. */ - `${/smoke/i.test(location.hostname) ? 'smoke-' : ''}avatar-selector.html#avatar_id=${this.state.avatar_id}` - } - ref={ifr => this.avatarSelector = ifr}></iframe> - <input className="profile-entry__form-submit" type="submit" value={formatMessage({ id: "profile.save" }) }/> - </div> </form> </div> ); diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index 26ffdfbf3d55227218ea4c36f3752fbe888461df..bca79ffa35eb0fa2188c557b830aab3eca960b95 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -210,18 +210,17 @@ class UIRoot extends Component { hasGrantedMicPermissions = async () => { if (this.state.requestedScreen) { - // There is no way to tell if you've granted mic permissions in a previous session if we've + // There is no way to tell if you've granted mic permissions in a previous session if we've // already prompted for screen sharing permissions, so we have to assume that we've never granted permissions. - // Fortunately, if you *have* granted permissions permanently, there won't be a second browser prompt, but we + // Fortunately, if you *have* granted permissions permanently, there won't be a second browser prompt, but we // can't determine that before hand. // See https://bugzilla.mozilla.org/show_bug.cgi?id=1449783 for a potential solution in the future. return false; - } - else { + } else { // If we haven't requested the screen in this session, check if we've granted permissions in a previous session. return (await grantedMicLabels()).length > 0; } - } + }; performDirectEntryFlow = async enterInVR => { this.startTestTone(); @@ -286,35 +285,36 @@ class UIRoot extends Component { const constraints = { audio: { deviceId: { exact: [ev.target.value] } } }; await this.fetchAudioTrack(constraints); await this.setupNewMediaStream(); - } + }; setMediaStreamToDefault = async () => { await this.fetchAudioTrack({ audio: true }); await this.setupNewMediaStream(); - } + }; setStateAndRequestScreen = async e => { const checked = e.target.checked; await this.setState({ requestedScreen: true, shareScreen: checked }); if (checked) { - this.fetchVideoTrack({ video: { - mediaSource: "screen", - // Work around BMO 1449832 by calculating the width. This will break for multi monitors if you share anything - // other than your current monitor that has a different aspect ratio. - width: screen.width / screen.height * 720, - height: 720, - frameRate: 30 - } }); - } - else { + this.fetchVideoTrack({ + video: { + mediaSource: "screen", + // Work around BMO 1449832 by calculating the width. This will break for multi monitors if you share anything + // other than your current monitor that has a different aspect ratio. + width: screen.width / screen.height * 720, + height: 720, + frameRate: 30 + } + }); + } else { this.setState({ videoTrack: null }); } - } + }; fetchVideoTrack = async constraints => { const mediaStream = await navigator.mediaDevices.getUserMedia(constraints); this.setState({ videoTrack: mediaStream.getVideoTracks()[0] }); - } + }; fetchAudioTrack = async constraints => { if (this.state.audioTrack) { @@ -322,12 +322,12 @@ class UIRoot extends Component { } const mediaStream = await navigator.mediaDevices.getUserMedia(constraints); this.setState({ audioTrack: mediaStream.getAudioTracks()[0] }); - } + }; setupNewMediaStream = async constraints => { const mediaStream = new MediaStream(); - // we should definitely have an audioTrack at this point. + // we should definitely have an audioTrack at this point. mediaStream.addTrack(this.state.audioTrack); if (this.state.videoTrack) { @@ -380,10 +380,8 @@ class UIRoot extends Component { fetchMicDevices = async () => { const mediaDevices = await navigator.mediaDevices.enumerateDevices(); - this.setState({ - micDevices: mediaDevices. - filter(d => d.kind === "audioinput"). - map(d => ({ deviceId: d.deviceId, label: d.label })) + this.setState({ + micDevices: mediaDevices.filter(d => d.kind === "audioinput").map(d => ({ deviceId: d.deviceId, label: d.label })) }); }; @@ -468,45 +466,44 @@ class UIRoot extends Component { // Only show this in desktop firefox since other browsers/platforms will ignore the "screen" media constraint and // will attempt to share your webcam instead! - const screenSharingCheckbox = ( - this.props.enableScreenSharing && - !mobiledetect.mobile() && - /firefox/i.test(navigator.userAgent) && - ( + const screenSharingCheckbox = this.props.enableScreenSharing && + !mobiledetect.mobile() && + /firefox/i.test(navigator.userAgent) && ( <label className="entry-panel__screen-sharing"> - <input className="entry-panel__screen-sharing-checkbox" type="checkbox" + <input + className="entry-panel__screen-sharing-checkbox" + type="checkbox" value={this.state.shareScreen} onChange={this.setStateAndRequestScreen} /> <FormattedMessage id="entry.enable-screen-sharing" /> </label> - ) - ); + ); - const entryPanel = + const entryPanel = this.state.entryStep === ENTRY_STEPS.start ? ( <div className="entry-panel"> <TwoDEntryButton onClick={this.enter2D} /> - { this.state.availableVREntryTypes.generic !== VR_DEVICE_AVAILABILITY.no && ( - <GenericEntryButton onClick={this.enterVR} /> + {this.state.availableVREntryTypes.generic !== VR_DEVICE_AVAILABILITY.no && ( + <GenericEntryButton onClick={this.enterVR} /> )} - { this.state.availableVREntryTypes.gearvr !== VR_DEVICE_AVAILABILITY.no && ( - <GearVREntryButton onClick={this.enterGearVR} /> + {this.state.availableVREntryTypes.gearvr !== VR_DEVICE_AVAILABILITY.no && ( + <GearVREntryButton onClick={this.enterGearVR} /> )} - { this.state.availableVREntryTypes.daydream !== VR_DEVICE_AVAILABILITY.no && ( + {this.state.availableVREntryTypes.daydream !== VR_DEVICE_AVAILABILITY.no && ( <DaydreamEntryButton onClick={this.enterDaydream} subtitle={ - this.state.availableVREntryTypes.daydream == VR_DEVICE_AVAILABILITY.maybe ? daydreamMaybeSubtitle : "" + this.state.availableVREntryTypes.daydream == VR_DEVICE_AVAILABILITY.maybe ? daydreamMaybeSubtitle : "" } - /> + /> )} {this.state.availableVREntryTypes.cardboard !== VR_DEVICE_AVAILABILITY.no && ( <div className="entry-panel__secondary" onClick={this.enterVR}> <FormattedMessage id="entry.cardboard" /> </div> )} - { screenSharingCheckbox } + {screenSharingCheckbox} </div> ) : null; diff --git a/src/storage/store.js b/src/storage/store.js index 037e27bf1d9603a210423baa5ee6543c2ee77fdb..b73825ef3a03c9971a7a9e49d612aee4582abf17 100644 --- a/src/storage/store.js +++ b/src/storage/store.js @@ -4,7 +4,7 @@ import { Validator } from "jsonschema"; const LOCAL_STORE_KEY = "___mozilla_duck"; const STORE_STATE_CACHE_KEY = Symbol(); const validator = new Validator(); -import { EventTarget } from "event-target-shim" +import { EventTarget } from "event-target-shim"; // Durable (via local-storage) schema-enforced state that is meant to be consumed via forward data flow. // (Think flux but with way less incidental complexity, at least for now :)) @@ -17,7 +17,7 @@ export const SCHEMA = { additionalProperties: false, properties: { display_name: { type: "string", pattern: "^[A-Za-z0-9-]{3,32}$" }, - avatar_id: { type: "string" }, + avatar_id: { type: "string" } } } }, @@ -26,11 +26,11 @@ export const SCHEMA = { properties: { id: { type: "string", pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" }, - profile: { "$ref": "#/definitions/profile" }, + profile: { $ref: "#/definitions/profile" } }, additionalProperties: false -} +}; export default class Store extends EventTarget { constructor() { diff --git a/src/utils/concurrent-load-detector.js b/src/utils/concurrent-load-detector.js index f05f619a3f51fab56a33acaec2eeb3b75e48ccea..53fc2d1fa37c1f236966211ba2abaaf4631960de 100644 --- a/src/utils/concurrent-load-detector.js +++ b/src/utils/concurrent-load-detector.js @@ -3,7 +3,7 @@ // events. const LOCAL_STORE_KEY = "___concurrent_load_detector"; -import { EventTarget } from "event-target-shim" +import { EventTarget } from "event-target-shim"; export default class ConcurrentLoadDetector extends EventTarget { constructor(instanceKey) { @@ -20,17 +20,17 @@ export default class ConcurrentLoadDetector extends EventTarget { // Check for concurrent load every second this.interval = setInterval(this._step, 1000); - } + }; stop = () => { if (this.interval) { clearInterval(this.interval); } - } + }; localStorageKey = () => { return `${LOCAL_STORE_KEY}_${this.instanceKey}`; - } + }; _step = () => { const currentState = JSON.parse(localStorage.getItem(this.localStorageKey())); @@ -40,5 +40,5 @@ export default class ConcurrentLoadDetector extends EventTarget { this.dispatchEvent(new CustomEvent("concurrentload")); this.stop(); } - } + }; } diff --git a/src/utils/identity.js b/src/utils/identity.js index 118fe30812c1013cb64c47a335c24465a12f6636..def830cbc4df4d9d1a71b0c10d7b54c5a5610b4a 100644 --- a/src/utils/identity.js +++ b/src/utils/identity.js @@ -164,7 +164,7 @@ const names = [ ]; function selectRandom(arr) { - return arr[Math.floor(Math.random() * arr.length)] + return arr[Math.floor(Math.random() * arr.length)]; } export const avatarIds = avatars.map(av => av.id); @@ -172,7 +172,7 @@ export const avatarIds = avatars.map(av => av.id); export function generateDefaultProfile() { const name = selectRandom(names); return { - display_name: name.replace(/^./, name[0].toUpperCase()) , + display_name: name.replace(/^./, name[0].toUpperCase()), avatar_id: selectRandom(avatarIds) }; }