From 92453205e465684f387dbfd4e180c0a5aa5ee795 Mon Sep 17 00:00:00 2001 From: Greg Fodor <gfodor@gmail.com> Date: Mon, 19 Mar 2018 16:07:33 -0700 Subject: [PATCH] name entry popup working --- src/react-components/name-entry-panel.js | 48 ++++++++-------- src/react-components/ui-root.js | 32 ++++++++--- src/room.js | 3 +- src/room.scss | 72 ++++++++++++++++-------- src/storage/store.js | 4 ++ 5 files changed, 105 insertions(+), 54 deletions(-) diff --git a/src/react-components/name-entry-panel.js b/src/react-components/name-entry-panel.js index 3e39a7366..44bb7b220 100644 --- a/src/react-components/name-entry-panel.js +++ b/src/react-components/name-entry-panel.js @@ -4,21 +4,25 @@ import { SCHEMA } from "../storage/store"; export default class NameEntryPanel extends Component { static propTypes = { - store: PropTypes.object + store: PropTypes.object, + finished: PropTypes.func } constructor(props) { super(props); window.store = this.props.store; this.state = {name: this.props.store.state.profile.display_name}; - this.props.store.subscribe(() => { - this.setState({name: this.props.store.state.profile.display_name}); - }); + this.props.store.subscribe(this.storeUpdated); + } + + storeUpdated = () => { + this.setState({name: this.props.store.state.profile.display_name}); } saveName = (e) => { e.preventDefault(); this.props.store.update({ profile: { display_name: this.nameInput.value } }); + this.props.finished(); } componentDidMount() { @@ -27,30 +31,28 @@ export default class NameEntryPanel extends Component { this.nameInput.addEventListener('keypress', e => e.stopPropagation()); this.nameInput.addEventListener('keyup', e => e.stopPropagation()); } + + componentWillUnmount() { + this.props.store.unsubscribe(this.storeUpdated); + } render () { return ( - <div class="name-entry"> - <div class="name-entry__box name-entry__box--darkened"> - <div class="name-entry__subtitle"> - Set your identity + <div className="name-entry"> + <form onSubmit={this.saveName}> + <div className="name-entry__box name-entry__box--darkened"> + <div className="name-entry__subtitle"> + Your identity </div> - <div class="name-entry__form"> - <form onSubmit={this.saveName}> - <div class="name-entry__form-fields"> - <input - class="name-entry__form-field-text" - value={this.state.name} onChange={(e) => this.setState({name: e.target.value})} - required pattern={SCHEMA.definitions.profile.properties.display_name.pattern} - title="Alphanumerics and hyphens. At least 3 characters, no more than 32" - ref={inp => this.nameInput = inp}/> - <div class="name-entry__form-submit"> - <input type="submit" value="Save" /> - </div> - </div> - </form> + <input + className="name-entry__form-field-text" + value={this.state.name} onChange={(e) => this.setState({name: e.target.value})} + required pattern={SCHEMA.definitions.profile.properties.display_name.pattern} + title="Alphanumerics and hyphens. At least 3 characters, no more than 32" + ref={inp => this.nameInput = inp}/> + <input className="name-entry__form-submit" type="submit" value="SAVE" /> </div> - </div> + </form> </div> ); } diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index 4d897fa8a..2a3255c0b 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -63,6 +63,12 @@ const AutoExitWarning = (props) => ( </div> ); +const ProfileInfoHeader = (props) => ( + <div className="profile-info-header" onClick={props.onClick}> + {props.name} + </div> +); + // This is a list of regexes that match the microphone labels of HMDs. // // If entering VR mode, and if any of these regexes match an audio device, @@ -109,7 +115,7 @@ class UIRoot extends Component { sceneLoaded: false, exited: false, - showingNameEntry: true + showProfileEntry: false } componentDidMount() { @@ -321,6 +327,10 @@ class UIRoot extends Component { } } + onProfileFinished = () => { + this.setState({ showProfileEntry: false }) + } + beginAudioSetup = async () => { await this.fetchMicDevices(); this.setState({ entryStep: ENTRY_STEPS.audio_setup }); @@ -403,7 +413,8 @@ class UIRoot extends Component { const dialogContents = this.isWaitingForAutoExit() ? (<AutoExitWarning secondsRemaining={this.state.secondsRemainingBeforeAutoExit} onCancel={this.endAutoExitTimer} />) : ( - <div class="entry-dialog"> + <div className="entry-dialog"> + <ProfileInfoHeader name={this.props.store.state.profile.display_name} onClick={(() => this.setState({showProfileEntry: true })) }/> {entryPanel} {micPanel} {audioSetupPanel} @@ -415,9 +426,11 @@ class UIRoot extends Component { 'ui-dialog--darkened': this.state.entryStep !== ENTRY_STEPS.finished }); - const dialogBoxClassNames = classNames({ - 'ui-dialog-box': true, - 'ui-dialog-box--lighter': this.state.showingNameEntry + const dialogBoxClassNames = classNames({ 'ui-dialog-box': true }); + + const dialogBoxContentsClassNames = classNames({ + 'ui-dialog-box-contents': true, + 'ui-dialog-box-contents--backgrounded': this.state.showProfileEntry }); return !this.state.exited ? @@ -427,9 +440,12 @@ class UIRoot extends Component { this.state.entryStep !== ENTRY_STEPS.finished && ( <div className={dialogBoxClassNames}> - {this.state.showingNameEntry && ( - <NameEntryPanel store={this.props.store}></NameEntryPanel>)} - {dialogContents} + <div className={dialogBoxContentsClassNames}> + {dialogContents} + </div> + + {this.state.showProfileEntry && ( + <NameEntryPanel finished={this.onProfileFinished} store={this.props.store}/>)} </div> ) } diff --git a/src/room.js b/src/room.js index 72c52d26b..715dbf7ba 100644 --- a/src/room.js +++ b/src/room.js @@ -82,6 +82,7 @@ import { getAvailableVREntryTypes } from "./utils/vr-caps-detect.js"; import ConcurrentLoadDetector from "./utils/concurrent-load-detector.js"; registerTelemetry(); +AFRAME.registerInputMappings(inputConfig, true); const store = new Store(); const concurrentLoadDetector = new ConcurrentLoadDetector(); @@ -140,7 +141,7 @@ async function enterScene(mediaStream, enterInVR) { AFRAME.registerInputActivator("pressedmove", PressedMove); AFRAME.registerInputActivator("reverseY", ReverseY); AFRAME.registerInputActions(inGameActions, "default"); - AFRAME.registerInputMappings(inputConfig); + document.querySelector("#player-camera").setAttribute("look-controls", "pointerLockEnabled: true;"); const qs = queryString.parse(location.search); diff --git a/src/room.scss b/src/room.scss index 94ead21b3..a4a046c7c 100644 --- a/src/room.scss +++ b/src/room.scss @@ -7,18 +7,29 @@ $light-text: rgba(240, 240, 240, 1.0); $dark-grey: rgba(128, 128, 128, 1.0); $darker-grey: rgba(64, 64, 64, 1.0); +%default-font { + font-family: 'Zilla Slab', sans-serif; +} + +%rounded-border { + border: 3px solid white; + box-sizing: border-box; + border-radius: 14px; +} + .a-enter-vr { display: none; } .ui { + @extend %default-font; + width: 100%; height: 100%; top: 0; left: 0; position: absolute; pointer-events: none; - font-family: 'Zilla Slab', sans-serif; color: white; } @@ -41,15 +52,24 @@ $darker-grey: rgba(64, 64, 64, 1.0); .ui-dialog-box { grid-column: 3; grid-row : 3; + position: relative; +} + +.ui-dialog-box-contents { background-color: $darker-transparent; - border-radius: 4px; + border-radius: 8px; pointer-events: auto; - padding: 10px; - position: relative; + width: 100%; + height: 100%; } -.ui-dialog-box--lighter { - background-color: $dark-transparent; +.ui-dialog-box--backgrounded { +} + +.ui-dialog-box-contents--backgrounded { + filter: blur(1px); + opacity: 0.7; + pointer-events: none; } .loading-panel { @@ -76,15 +96,18 @@ $darker-grey: rgba(64, 64, 64, 1.0); justify-content: center; align-items: center; display: flex; + pointer-events: auto; } .name-entry__box { - width: 80%; - height: 20%; - border-radius: 4px; - max-height: 200px; + height: 150px; + border-radius: 8px; display: flex; flex-direction: column; + justify-content: space-between; + align-items: center; + padding: 25px; + flex: 1 1 100%; } .name-entry__box--darkened { @@ -94,27 +117,32 @@ $darker-grey: rgba(64, 64, 64, 1.0); .name-entry__subtitle { width: 100%; text-align: center; + font-size: 1.2em; color: $grey-text; } -.name-entry__form-fields { - display: flex; - justify-content: flex-start; -} - .name-entry__form-field-text { + @extend %rounded-border; + @extend %default-font; + color: $light-text; - font-size: 0.8em; - background-color: $darker-grey; - border: 3px solid white; - box-sizing: border-box; - border-radius: 12px; + font-size: 1.2em; + background-color: transparent; line-height: 2.0em; padding-left: 1.25em; padding-right: 1.25em; - font-family: 'Zilla Slab', sans-serif; } .name-entry__form-submit { - flex: 1 1 30%; + @extend %default-font; + border: none; + + margin: 8px; + width: 100px; + line-height: 1.5em; + font-size: 1.0em; + + background-color: transparent; + font-weight: bold; + color: white; } diff --git a/src/storage/store.js b/src/storage/store.js index b1b362d53..0963baddd 100644 --- a/src/storage/store.js +++ b/src/storage/store.js @@ -51,6 +51,10 @@ export default class Store { this.subscribers.add(subscriber); } + unsubscribe(subscriber) { + this.subscribers.delete(subscriber); + } + update(newState) { if (newState.id) { console.error("Store id is immutable."); -- GitLab