diff --git a/src/assets/stylesheets/profile.scss b/src/assets/stylesheets/profile.scss index 4b01331d346967f8c3c5ca538f7fe4ab332ae205..3911b025f0cdba74825dc6173cd0dff5cd76a0f4 100644 --- a/src/assets/stylesheets/profile.scss +++ b/src/assets/stylesheets/profile.scss @@ -13,6 +13,11 @@ z-index: 10; background-color: rgba(255, 255, 255, 0.95); + + a { + cursor: pointer; + } + :local(.logo) { width: 150px; position: absolute; @@ -35,6 +40,15 @@ .loading-panel { background: transparent; } + + .custom-url-link { + position: absolute; + top: 10px; + right: 30px; + z-index: 100; + text-shadow: 0px 0px 6px #202020; + color: white; + } } :local(.avatar-selector) { diff --git a/src/react-components/avatar-selector.js b/src/react-components/avatar-selector.js index 6ffbe380d160ae375b4171585e7422cd09c8809d..7cf65f4768de6301c5fc707a11c6a64e491529b2 100644 --- a/src/react-components/avatar-selector.js +++ b/src/react-components/avatar-selector.js @@ -167,7 +167,7 @@ class AvatarSelector extends Component { </a-entity> <a-entity position="0 1.5 -5.6" rotation="-10 180 0"> - <a-entity camera="" /> + <a-entity camera="far: 1;" /> </a-entity> <a-entity diff --git a/src/react-components/profile-entry-panel.js b/src/react-components/profile-entry-panel.js index e4d02c70a4bb896816c44d8018aeae2da8ea0139..ba81fc53b0a941d8dafa4a330156eb9ff83de6e1 100644 --- a/src/react-components/profile-entry-panel.js +++ b/src/react-components/profile-entry-panel.js @@ -6,6 +6,7 @@ import styles from "../assets/stylesheets/profile.scss"; import classNames from "classnames"; import hubLogo from "../assets/images/hub-preview-white.png"; import { WithHoverSound } from "./wrap-with-audio"; +import { avatars } from "../assets/avatars/avatars"; class ProfileEntryPanel extends Component { static propTypes = { @@ -18,7 +19,7 @@ class ProfileEntryPanel extends Component { constructor(props) { super(props); const { displayName, avatarId } = this.props.store.state.profile; - this.state = { displayName, avatarId }; + this.state = { displayName, avatarId, customMode: avatarId && avatarId.startsWith("http") }; this.props.store.addEventListener("statechanged", this.storeUpdated); } @@ -39,7 +40,8 @@ class ProfileEntryPanel extends Component { hasChangedName: hasChangedNowOrPreviously }, profile: { - ...this.state + displayName: this.state.displayName, + avatarId: this.state.avatarId } }); this.props.finished(); @@ -94,20 +96,44 @@ class ProfileEntryPanel extends Component { title={formatMessage({ id: "profile.display_name.validation_warning" })} ref={inp => (this.nameInput = inp)} /> - <div className={styles.avatarSelectorContainer}> - <div className="loading-panel"> - <div className="loader-wrap"> - <div className="loader"> - <div className="loader-center" /> + {this.state.customMode ? ( + <div className={styles.avatarSelectorContainer}> + <label htmlFor="#custom-avatar-url">Avatar GLTF/GLB </label> + <input + id="custom-avatar-url" + type="url" + className={styles.formFieldText} + value={this.state.avatarId} + onChange={e => this.setState({ avatarId: e.target.value })} + /> + <div className={styles.links}> + <a onClick={() => this.setState({ customMode: false, avatarId: "botdefault" })}>cancel</a> + </div> + </div> + ) : ( + <div className={styles.avatarSelectorContainer}> + <div className="loading-panel"> + <div className="loader-wrap"> + <div className="loader"> + <div className="loader-center" /> + </div> </div> </div> + <iframe + className={styles.avatarSelector} + src={`/avatar-selector.html#avatar_id=${this.state.avatarId}`} + ref={ifr => (this.avatarSelector = ifr)} + /> + <a + className="custom-url-link" + onClick={() => + this.setState({ customMode: true, avatarId: avatars.find(a => a.id === this.state.avatarId).model }) + } + > + custom url + </a> </div> - <iframe - className={styles.avatarSelector} - src={`/avatar-selector.html#avatar_id=${this.state.avatarId}`} - ref={ifr => (this.avatarSelector = ifr)} - /> - </div> + )} <WithHoverSound> <input className={styles.formSubmit} type="submit" value={formatMessage({ id: "profile.save" })} /> </WithHoverSound> diff --git a/src/scene-entry-manager.js b/src/scene-entry-manager.js index 4c1875e727d5c2cb7d21696f80d371e900782a84..ed34939f2e6ec0a38cd7858d7c2003eb1f48776e 100644 --- a/src/scene-entry-manager.js +++ b/src/scene-entry-manager.js @@ -10,7 +10,7 @@ const isDebug = qsTruthy("debug"); const qs = new URLSearchParams(location.search); const aframeInspectorUrl = require("file-loader?name=assets/js/[name]-[hash].[ext]!aframe-inspector/dist/aframe-inspector.min.js"); -import { addMedia } from "./utils/media-utils"; +import { addMedia, proxiedUrlFor } from "./utils/media-utils"; import { ObjectContentOrigins } from "./object-types"; function requestFullscreen() { @@ -142,10 +142,10 @@ export default class SceneEntryManager { }; _updatePlayerRigWithProfile = () => { - const displayName = this.store.state.profile.displayName; + const { avatarId, displayName } = this.store.state.profile; this.playerRig.setAttribute("player-info", { displayName, - avatarSrc: "#" + (this.store.state.profile.avatarId || "botdefault") + avatarSrc: avatarId && avatarId.startsWith("http") ? proxiedUrlFor(avatarId) : `#${avatarId || "botdefault"}` }); const hudController = this.playerRig.querySelector("[hud-controller]"); hudController.setAttribute("hud-controller", { showTip: !this.store.state.activity.hasFoundFreeze });