diff --git a/package.json b/package.json index 95b04a6073d46ef12bfce28af145e42bd741ad9d..3af88032ac14ddf9d383cf31dfc7b897c742d72e 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,16 @@ { - "name": "mr-social-client", + "name": "hubs", "version": "0.0.1", + "description": "Duck-themed multi-user virtual spaces in WebVR.", "main": "src/index.js", "license": "MPL-2.0", + "homepage": "https://github.com/mozilla/hubs#readme", "repository": { "type": "git", - "url": "https://github.com/mozilla/mr-social-client.git" + "url": "https://github.com/mozilla/hubs.git" + }, + "bugs": { + "url": "https://github.com/mozilla/hubs/issues" }, "scripts": { "postinstall": "node ./scripts/postinstall.js", @@ -33,7 +38,6 @@ "aframe-xr": "github:brianpeiris/aframe-xr#3162aed", "classnames": "^2.2.5", "copy-to-clipboard": "^3.0.8", - "copy-webpack-plugin": "^4.5.1", "deepmerge": "^2.1.1", "detect-browser": "^2.1.0", "event-target-shim": "^3.0.1", @@ -44,12 +48,10 @@ "networked-aframe": "https://github.com/mozillareality/networked-aframe#mr-social-client/master", "nipplejs": "https://github.com/mozillareality/nipplejs#mr-social-client/master", "phoenix": "^1.3.0", - "query-string": "^5.0.1", "raven-js": "^3.20.1", "react": "^16.1.1", "react-dom": "^16.1.1", "react-intl": "^2.4.0", - "react-router-dom": "^4.2.2", "screenfull": "^3.3.2", "super-hands": "https://github.com/mozillareality/aframe-super-hands-component#hubs/master", "uuid": "^3.2.1", @@ -64,6 +66,7 @@ "babel-plugin-transform-react-jsx-img-import": "^0.1.4", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", + "copy-webpack-plugin": "^4.5.1", "cross-env": "^5.1.3", "css-loader": "^0.28.10", "dotenv": "^5.0.1", diff --git a/src/avatar-selector.js b/src/avatar-selector.js index 45b0a7eed21aebe238e86237a540d97dbbdc63fe..296b94e4dd1608abd97e4a6f6e7f4f1270a80301 100644 --- a/src/avatar-selector.js +++ b/src/avatar-selector.js @@ -1,6 +1,5 @@ import ReactDOM from "react-dom"; import React from "react"; -import queryString from "query-string"; import { IntlProvider, addLocaleData } from "react-intl"; import en from "react-intl/locale-data/en"; @@ -26,22 +25,16 @@ addLocaleData([...en]); registerTelemetry(); +const hash = new URLSearchParams(location.hash.replace(/^#/, "?")); window.APP = new App(); -const hash = queryString.parse(location.hash); -const isMobile = AFRAME.utils.device.isMobile(); -if (hash.quality) { - window.APP.quality = hash.quality; -} else { - window.APP.quality = isMobile ? "low" : "high"; -} +window.APP.quality = hash.get("quality") || AFRAME.utils.device.isMobile() ? "low" : "high"; function postAvatarIdToParent(newAvatarId) { window.parent.postMessage({ avatarId: newAvatarId }, location.origin); } function mountUI() { - const hash = queryString.parse(location.hash); - const avatarId = hash.avatar_id; + const avatarId = hash.get("avatar_id"); ReactDOM.render( <IntlProvider locale={lang} messages={messages}> <AvatarSelector {...{ avatars, avatarId, onChange: postAvatarIdToParent }} /> diff --git a/src/components/networked-video-player.js b/src/components/networked-video-player.js index eb4a93e0acc77173a714ca72aee1a29f744a2ffb..912c15bad2e2587960ad6ca484ffc13626009b24 100644 --- a/src/components/networked-video-player.js +++ b/src/components/networked-video-player.js @@ -1,5 +1,3 @@ -import queryString from "query-string"; - import styles from "./networked-video-player.css"; const nafConnected = function() { @@ -25,8 +23,8 @@ AFRAME.registerComponent("networked-video-player", { const ownerId = networkedEl.components.networked.data.owner; - const qs = queryString.parse(location.search); - const rejectScreenShares = qs.accept_screen_shares === undefined; + const qs = new URLSearchParams(location.search); + const rejectScreenShares = !qs.has("accept_screen_shares"); 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 diff --git a/src/hub.js b/src/hub.js index 2d3863f11d8354d72b4eab52967b1193063b8374..2fbef43929b944c95804f823142bbad546bff149 100644 --- a/src/hub.js +++ b/src/hub.js @@ -1,7 +1,6 @@ console.log(`Hubs version: ${process.env.BUILD_VERSION || "?"}`); import "./assets/stylesheets/hub.scss"; -import queryString from "query-string"; import { patchWebGLRenderingContext } from "./utils/webgl"; patchWebGLRenderingContext(); @@ -101,14 +100,10 @@ window.APP.RENDER_ORDER = { }; const store = window.APP.store; -const qs = queryString.parse(location.search); +const qs = new URLSearchParams(location.search); const isMobile = AFRAME.utils.device.isMobile(); -if (qs.quality) { - window.APP.quality = qs.quality; -} else { - window.APP.quality = isMobile ? "low" : "high"; -} +window.APP.quality = qs.get("quality") || isMobile ? "low" : "high"; import "aframe-physics-system"; import "aframe-physics-extras"; @@ -136,9 +131,9 @@ import { getAvailableVREntryTypes, VR_DEVICE_AVAILABILITY } from "./utils/vr-cap import ConcurrentLoadDetector from "./utils/concurrent-load-detector.js"; function qsTruthy(param) { - const val = qs[param]; - // if the param exists but is not set (e.g. "?foo&bar"), its value is null. - return val === null || /1|on|true/i.test(val); + const val = qs.get(param); + // if the param exists but is not set (e.g. "?foo&bar"), its value is the empty string. + return val === "" || /1|on|true/i.test(val); } const isBotMode = qsTruthy("bot"); @@ -167,7 +162,7 @@ store.init(); function mountUI(scene, props = {}) { const disableAutoExitOnConcurrentLoad = qsTruthy("allow_multi"); - const forcedVREntryType = qs.vr_entry_type || null; + const forcedVREntryType = qs.get("vr_entry_type"); const enableScreenSharing = qsTruthy("enable_screen_sharing"); const htmlPrefix = document.body.dataset.htmlPrefix || ""; const showProfileEntry = !store.state.activity.hasChangedName; @@ -269,7 +264,7 @@ const onReady = async () => { applyProfileOnPlayerRig(); store.addEventListener("statechanged", applyProfileOnPlayerRig); - const avatarScale = parseInt(qs.avatar_scale, 10); + const avatarScale = parseInt(qs.get("avatar_scale"), 10); if (avatarScale) { playerRig.setAttribute("scale", { x: avatarScale, y: avatarScale, z: avatarScale }); @@ -433,9 +428,9 @@ const onReady = async () => { return; } - if (qs.required_version && process.env.BUILD_VERSION) { + if (qs.get("required_version") && process.env.BUILD_VERSION) { const buildNumber = process.env.BUILD_VERSION.split(" ", 1)[0]; // e.g. "123 (abcd5678)" - if (qs.required_version !== buildNumber) { + if (qs.get("required_version") !== buildNumber) { remountUI({ roomUnavailableReason: "version_mismatch" }); setTimeout(() => document.location.reload(), 5000); exitScene(); @@ -485,9 +480,9 @@ const onReady = async () => { } }; - if (qs.room) { + if (qs.has("room")) { // If ?room is set, this is `yarn start`, so just use a default environment and query string room. - setRoom(qs.room || "default"); + setRoom(qs.get("room") || "default"); initialEnvironmentEl.setAttribute("gltf-bundle", { src: DEFAULT_ENVIRONMENT_URL }); @@ -495,7 +490,7 @@ const onReady = async () => { } // Connect to reticulum over phoenix channels to get hub info. - const hubId = qs.hub_id || document.location.pathname.substring(1).split("/")[0]; + const hubId = qs.get("hub_id") || document.location.pathname.substring(1).split("/")[0]; console.log(`Hub ID: ${hubId}`); const socket = connectToReticulum(); diff --git a/src/index.js b/src/index.js index d0b5c4557cda8a0e408ef01c6e5fa2ae680a01ed..f61c18337e40519a46693bf09607c105be58efae 100644 --- a/src/index.js +++ b/src/index.js @@ -4,15 +4,14 @@ import ReactDOM from "react-dom"; import registerTelemetry from "./telemetry"; import HomeRoot from "./react-components/home-root"; import InfoDialog from "./react-components/info-dialog.js"; -import queryString from "query-string"; -const qs = queryString.parse(location.search); +const qs = new URLSearchParams(location.search); registerTelemetry(); ReactDOM.render( <HomeRoot - initialEnvironment={qs.initial_environment} - dialogType={qs.list_signup ? InfoDialog.dialogTypes.updates : null} + initialEnvironment={qs.get("initial_environment")} + dialogType={qs.has("list_signup") ? InfoDialog.dialogTypes.updates : null} />, document.getElementById("home-root") ); diff --git a/src/react-components/2d-hud.js b/src/react-components/2d-hud.js index e32a02d3eca931e112eb976b3c1cdcafc55f6dff..93ae3cc44237d8938443b59b9ad6b2990a7c9029 100644 --- a/src/react-components/2d-hud.js +++ b/src/react-components/2d-hud.js @@ -1,18 +1,17 @@ import React from "react"; import PropTypes from "prop-types"; import cx from "classnames"; -import queryString from "query-string"; import styles from "../assets/stylesheets/2d-hud.scss"; import FontAwesomeIcon from "@fortawesome/react-fontawesome"; import faPlus from "@fortawesome/fontawesome-free-solid/faPlus"; -const qs = queryString.parse(location.search); +const qs = new URLSearchParams(location.search); function qsTruthy(param) { - const val = qs[param]; - // if the param exists but is not set (e.g. "?foo&bar"), its value is null. - return val === null || /1|on|true/i.test(val); + const val = qs.get(param); + // if the param exists but is not set (e.g. "?foo&bar"), its value is the empty string. + return val === "" || /1|on|true/i.test(val); } const enableMediaTools = qsTruthy("mediaTools"); diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index ce6358cb8ca3063f11fbb35894e7e69f99469840..e9ff7f529c58672f789bac21b295b70cebb8757b 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -2,7 +2,6 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; import classNames from "classnames"; import { VR_DEVICE_AVAILABILITY } from "../utils/vr-caps-detect"; -import queryString from "query-string"; import { IntlProvider, FormattedMessage, addLocaleData } from "react-intl"; import en from "react-intl/locale-data/en"; import MovingAverage from "moving-average"; @@ -281,12 +280,12 @@ class UIRoot extends Component { // We are not in mobile chrome, so launch into chrome via an Intent URL const location = window.location; - const qs = queryString.parse(location.search); - qs.vr_entry_type = "daydream"; // Auto-choose 'daydream' after landing in chrome + const qs = new URLSearchParams(location.search); + qs.set("vr_entry_type", "daydream"); // Auto-choose 'daydream' after landing in chrome const intentUrl = - `intent://${location.host}${location.pathname || ""}?` + - `${queryString.stringify(qs)}#Intent;scheme=${(location.protocol || "http:").replace(":", "")};` + + `intent://${location.host}${location.pathname}?` + + `${qs}#Intent;scheme=${location.protocol.replace(":", "")};` + `action=android.intent.action.VIEW;package=com.android.chrome;end;`; window.location = intentUrl; diff --git a/src/utils/phoenix-utils.js b/src/utils/phoenix-utils.js index 3c20f8db96aa261e4d8dd27d727b89b499728e53..91317a70d4ea0563842954ef407631c64a1ecacc 100644 --- a/src/utils/phoenix-utils.js +++ b/src/utils/phoenix-utils.js @@ -1,15 +1,14 @@ -import queryString from "query-string"; import uuid from "uuid/v4"; import { Socket } from "phoenix"; export function connectToReticulum() { - const qs = queryString.parse(location.search); + const qs = new URLSearchParams(location.search); - const socketProtocol = qs.phx_protocol || (document.location.protocol === "https:" ? "wss:" : "ws:"); + const socketProtocol = qs.get("phx_protocol") || (document.location.protocol === "https:" ? "wss:" : "ws:"); const [retHost, retPort] = (process.env.DEV_RETICULUM_SERVER || "").split(":"); const isProd = process.env.NODE_ENV === "production"; - const socketPort = qs.phx_port || (isProd ? document.location.port : retPort) || "443"; - const socketHost = qs.phx_host || (isProd ? document.location.hostname : retHost) || ""; + const socketPort = qs.get("phx_port") || (isProd ? document.location.port : retPort) || "443"; + const socketHost = qs.get("phx_host") || (isProd ? document.location.hostname : retHost) || ""; const socketUrl = `${socketProtocol}//${socketHost}${socketPort ? `:${socketPort}` : ""}/socket`; console.log(`Phoenix Socket URL: ${socketUrl}`); diff --git a/yarn.lock b/yarn.lock index ccf9e5ea5c7dc4786625753d1e72ebc27f9d67f7..4bd30225dc8626c30c5a590523bcd70c9626f655 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3971,16 +3971,6 @@ he@1.1.x: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" -history@^4.7.2: - version "4.7.2" - resolved "https://registry.yarnpkg.com/history/-/history-4.7.2.tgz#22b5c7f31633c5b8021c7f4a8a954ac139ee8d5b" - dependencies: - invariant "^2.2.1" - loose-envify "^1.2.0" - resolve-pathname "^2.2.0" - value-equal "^0.4.0" - warning "^3.0.0" - hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -3993,10 +3983,6 @@ hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" -hoist-non-react-statics@^2.3.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz#d2ca2dfc19c5a91c5a6615ce8e564ef0347e2a40" - home-or-tmp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" @@ -4346,7 +4332,7 @@ intl-relativeformat@^2.0.0: dependencies: intl-messageformat "^2.0.0" -invariant@^2.1.1, invariant@^2.2.0, invariant@^2.2.1, invariant@^2.2.2: +invariant@^2.1.1, invariant@^2.2.0, invariant@^2.2.2: version "2.2.3" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.3.tgz#1a827dfde7dcbd7c323f0ca826be8fa7c5e9d688" dependencies: @@ -5058,7 +5044,7 @@ loglevelnext@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/loglevelnext/-/loglevelnext-1.0.3.tgz#0f69277e73bbbf2cd61b94d82313216bf87ac66e" -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" dependencies: @@ -6071,12 +6057,6 @@ path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" -path-to-regexp@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" - dependencies: - isarray "0.0.1" - path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -6487,7 +6467,7 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -prop-types@^15.5.4, prop-types@^15.6.0: +prop-types@^15.6.0: version "15.6.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca" dependencies: @@ -6566,14 +6546,6 @@ query-string@^4.1.0, query-string@^4.2.3: object-assign "^4.1.0" strict-uri-encode "^1.0.0" -query-string@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.0.tgz#9583b15fd1307f899e973ed418886426a9976469" - dependencies: - decode-uri-component "^0.2.0" - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" - querystring-es3@^0.2.0, querystring-es3@~0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -6654,29 +6626,6 @@ react-intl@^2.4.0: intl-relativeformat "^2.0.0" invariant "^2.1.1" -react-router-dom@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.2.2.tgz#c8a81df3adc58bba8a76782e946cbd4eae649b8d" - dependencies: - history "^4.7.2" - invariant "^2.2.2" - loose-envify "^1.3.1" - prop-types "^15.5.4" - react-router "^4.2.0" - warning "^3.0.0" - -react-router@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-4.2.0.tgz#61f7b3e3770daeb24062dae3eedef1b054155986" - dependencies: - history "^4.7.2" - hoist-non-react-statics "^2.3.0" - invariant "^2.2.2" - loose-envify "^1.3.1" - path-to-regexp "^1.7.0" - prop-types "^15.5.4" - warning "^3.0.0" - react@^16.1.1: version "16.2.0" resolved "https://registry.yarnpkg.com/react/-/react-16.2.0.tgz#a31bd2dab89bff65d42134fa187f24d054c273ba" @@ -7043,10 +6992,6 @@ resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" -resolve-pathname@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-2.2.0.tgz#7e9ae21ed815fd63ab189adeee64dc831eefa879" - resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -8357,10 +8302,6 @@ validate-npm-package-license@^3.0.1: spdx-correct "~1.0.0" spdx-expression-parse "~1.0.0" -value-equal@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.4.0.tgz#c5bdd2f54ee093c04839d71ce2e4758a6890abc7" - vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -8413,12 +8354,6 @@ vm-browserify@0.0.4, vm-browserify@~0.0.1: dependencies: indexof "0.0.1" -warning@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c" - dependencies: - loose-envify "^1.0.0" - watchify-middleware@^1.6.0: version "1.8.0" resolved "https://registry.yarnpkg.com/watchify-middleware/-/watchify-middleware-1.8.0.tgz#8f7cb9c528022be8525a7e066c10e2fd8c544be6"