From 80c9915a0121b73b99f9f329f1b994ee317c58fd Mon Sep 17 00:00:00 2001 From: Rahul Gupta Date: Fri, 15 Nov 2024 10:06:05 -0800 Subject: [PATCH] Zustand (#914) * add zustand to 3dstreet --------- Co-authored-by: Rahul Gupta Co-authored-by: Kieran Farr Co-authored-by: Vincent Fretin --- index.html | 36 +--- package-lock.json | 37 +++- package.json | 4 +- src/components/screentock.js | 17 +- src/components/streetplan-loader.js | 19 +- src/editor/components/Collapsible.js | 2 - src/editor/components/Main.js | 3 +- src/editor/components/components/Component.js | 3 - src/editor/components/components/Mixins.js | 3 - .../SceneEditTitle.component.jsx | 41 ++-- src/editor/components/components/Sidebar.js | 2 - .../ScenesModal/ScenesModal.component.jsx | 4 +- src/editor/components/scenegraph/Toolbar.js | 12 +- .../components/viewport/TransformToolbar.js | 2 - src/editor/index.js | 9 +- src/editor/lib/viewport.js | 2 - src/editor/services/ga.js | 11 -- src/index.js | 24 +-- src/json-utils_1.1.js | 64 +------ src/store.js | 18 ++ src/street-utils.js | 1 - src/viewer-styles.css | 177 ++---------------- webpack.config.js | 4 - webpack.prod.config.js | 4 +- 24 files changed, 109 insertions(+), 390 deletions(-) delete mode 100644 src/editor/services/ga.js create mode 100644 src/store.js diff --git a/index.html b/index.html index 479451227..2be1e2acd 100644 --- a/index.html +++ b/index.html @@ -35,16 +35,6 @@
-
- entities -
-
-
- car - bus - bike -
-
Loading 3DStreet
@@ -76,8 +66,8 @@ @@ -129,27 +119,5 @@ }); }); - - \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 0ce76434f..c4323aa4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,11 +29,11 @@ "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-ga4": "^2.1.0", "react-select": "^5.4.0", "stripe": "^15.8.0", "three": "0.145.0", - "uuid": "^9.0.0" + "uuid": "^9.0.0", + "zustand": "^5.0.1" }, "devDependencies": { "@babel/eslint-parser": "^7.24.5", @@ -25768,11 +25768,6 @@ "integrity": "sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==", "dev": true }, - "node_modules/react-ga4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/react-ga4/-/react-ga4-2.1.0.tgz", - "integrity": "sha512-ZKS7PGNFqqMd3PJ6+C2Jtz/o1iU9ggiy8Y8nUeksgVuvNISbmrQtJiZNvC/TjDsqD0QlU5Wkgs7i+w9+OjHhhQ==" - }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -30861,6 +30856,34 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.1.tgz", + "integrity": "sha512-pRET7Lao2z+n5R/HduXMio35TncTlSW68WsYBq2Lg1ASspsNGjpwLAsij3RpouyV6+kHMwwwzP0bZPD70/Jx/w==", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index cec398ec9..851aee952 100644 --- a/package.json +++ b/package.json @@ -48,11 +48,11 @@ "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-ga4": "^2.1.0", "react-select": "^5.4.0", "stripe": "^15.8.0", "three": "0.145.0", - "uuid": "^9.0.0" + "uuid": "^9.0.0", + "zustand": "^5.0.1" }, "devDependencies": { "@babel/eslint-parser": "^7.24.5", diff --git a/src/components/screentock.js b/src/components/screentock.js index 2de075300..ded69ace2 100644 --- a/src/components/screentock.js +++ b/src/components/screentock.js @@ -1,18 +1,5 @@ /* AFRAME */ - -// function buttonScreenshotTock() { -// AFRAME.scenes[0].setAttribute('screentock', 'type', 'jpg'); -// AFRAME.scenes[0].setAttribute('screentock', 'takeScreenshot', true); -// } -// function buttonScreenshotTockPNG() { -// AFRAME.scenes[0].setAttribute('screentock', 'type', 'png'); -// AFRAME.scenes[0].setAttribute('screentock', 'takeScreenshot', true); -// } -// function buttonCaptureImage() { -// AFRAME.scenes[0].setAttribute('screentock', 'type', 'img'); -// AFRAME.scenes[0].setAttribute('screentock', 'imgElementSelector', '#captureImg'); -// AFRAME.scenes[0].setAttribute('screentock', 'takeScreenshot', true); -// } +import useStore from '../store'; AFRAME.registerComponent('screentock', { schema: { @@ -62,7 +49,7 @@ AFRAME.registerComponent('screentock', { ctx.textAlign = 'center'; ctx.fillStyle = '#FFF'; ctx.fillText( - STREET.utils.getCurrentSceneTitle(), + useStore.getState().sceneTitle, screenWidth - screenWidth / 2, screenHeight - 43 ); diff --git a/src/components/streetplan-loader.js b/src/components/streetplan-loader.js index 55666234b..7e7a5dfc2 100644 --- a/src/components/streetplan-loader.js +++ b/src/components/streetplan-loader.js @@ -1,6 +1,9 @@ /* global AFRAME, XMLHttpRequest */ +import useStore from '../store.js'; var streetplanUtils = require('../streetplan/streetplan-utils.js'); +const state = useStore.getState(); + AFRAME.registerComponent('streetplan-loader', { dependencies: ['street'], schema: { @@ -25,20 +28,8 @@ AFRAME.registerComponent('streetplan-loader', { // streetplan alternative name // const streetplanAltName = streetData.altName; - console.log('streetplanName', streetplanName); - - let currentSceneTitle; - const sceneEl = this.el.sceneEl; - if (sceneEl && sceneEl.getAttribute('metadata')) { - currentSceneTitle = sceneEl.getAttribute('metadata').sceneTitle; - } - if (!currentSceneTitle) { - // only set title from streetplan if none exists - sceneEl.setAttribute('metadata', 'sceneTitle', streetplanName); - console.log( - 'therefore setting metadata sceneTitle as streetplanName', - streetplanName - ); + if (!state.sceneTitle) { + state.setSceneTitle(streetplanName); } el.setAttribute('data-layer-name', 'StreetPlan • ' + streetplanName); diff --git a/src/editor/components/Collapsible.js b/src/editor/components/Collapsible.js index 08743566d..ce806fe10 100644 --- a/src/editor/components/Collapsible.js +++ b/src/editor/components/Collapsible.js @@ -1,7 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; -import { sendMetric } from '../services/ga'; export default class Collapsible extends React.Component { static propTypes = { @@ -27,7 +26,6 @@ export default class Collapsible extends React.Component { // Don't collapse if we click on actions like clipboard if (event.target.nodeName === 'A') return; this.setState({ collapsed: !this.state.collapsed }); - sendMetric('Components', 'collapse'); }; render() { diff --git a/src/editor/components/Main.js b/src/editor/components/Main.js index c801ff56c..aafcdcf67 100644 --- a/src/editor/components/Main.js +++ b/src/editor/components/Main.js @@ -237,7 +237,6 @@ export default class Main extends Component { render() { const scene = this.state.sceneEl; const isEditor = !!this.state.inspectorEnabled; - const sceneData = AFRAME.scenes[0].getAttribute('metadata', 'sceneTitle'); return (
@@ -316,7 +315,7 @@ export default class Main extends Component { )} {this.state.inspectorEnabled && (
- +
)} {this.state.inspectorEnabled && ( diff --git a/src/editor/components/components/Component.js b/src/editor/components/components/Component.js index e919a7243..1b6af7652 100644 --- a/src/editor/components/components/Component.js +++ b/src/editor/components/components/Component.js @@ -5,7 +5,6 @@ import PropTypes from 'prop-types'; import PropertyRow from './PropertyRow'; import React from 'react'; import { getComponentClipboardRepresentation } from '../../lib/entity'; -import { sendMetric } from '../../services/ga'; const isSingleProperty = AFRAME.schema.isSingleProperty; @@ -45,7 +44,6 @@ export default class Component extends React.Component { var componentName = trigger .getAttribute('data-component') .toLowerCase(); - sendMetric('Components', 'copyComponentToClipboard', componentName); return getComponentClipboardRepresentation( this.state.entity, componentName @@ -85,7 +83,6 @@ export default class Component extends React.Component { entity: this.props.entity, component: componentName }); - sendMetric('Components', 'removeComponent', componentName); } }; diff --git a/src/editor/components/components/Mixins.js b/src/editor/components/components/Mixins.js index 8eb17b494..d3f951542 100644 --- a/src/editor/components/components/Mixins.js +++ b/src/editor/components/components/Mixins.js @@ -3,7 +3,6 @@ import PropTypes from 'prop-types'; import Select, { components } from 'react-select'; import Events from '../../lib/Events'; import { DropdownArrowIcon } from '../../icons'; -import { sendMetric } from '../../services/ga'; export default class Mixin extends React.Component { static propTypes = { @@ -80,7 +79,6 @@ export default class Mixin extends React.Component { entity: entity, value: mixinStr }); - sendMetric('Components', 'addMixin'); }; updateMixinSingle = (value) => { @@ -92,7 +90,6 @@ export default class Mixin extends React.Component { entity: entity, value: mixinStr }); - sendMetric('Components', 'addMixin'); }; render() { diff --git a/src/editor/components/components/SceneEditTitle/SceneEditTitle.component.jsx b/src/editor/components/components/SceneEditTitle/SceneEditTitle.component.jsx index 131416a1c..94970333c 100644 --- a/src/editor/components/components/SceneEditTitle/SceneEditTitle.component.jsx +++ b/src/editor/components/components/SceneEditTitle/SceneEditTitle.component.jsx @@ -1,26 +1,13 @@ -import { useEffect, useState } from 'react'; import styles from './SceneEditTitle.module.scss'; import { useAuthContext } from '../../../contexts/index.js'; import { updateSceneIdAndTitle } from '../../../api/scene'; +import useStore from '../../../../store.js'; const SceneEditTitle = ({ sceneData }) => { - const [title, setTitle] = useState(sceneData?.sceneTitle); + const title = useStore((state) => state.sceneTitle); + const setTitle = useStore((state) => state.setSceneTitle); const { currentUser } = useAuthContext(); - const sceneId = STREET.utils.getCurrentSceneId(); - - useEffect(() => { - if (sceneData.sceneId === sceneId) { - setTitle(sceneData.sceneTitle); - } - }, [sceneData?.sceneTitle, sceneData?.sceneId, sceneId]); - - useEffect(() => { - AFRAME.scenes[0].addEventListener('newTitle', (event) => { - setTitle(event.detail.sceneTitle ?? ''); - }); - }, []); - const handleEditClick = () => { const newTitle = prompt('Edit the title:', title); @@ -34,14 +21,10 @@ const SceneEditTitle = ({ sceneData }) => { const saveNewTitle = async (newTitle) => { try { - if (sceneData?.sceneId) { - if (currentUser.uid === STREET.utils.getAuthorId()) { - await updateSceneIdAndTitle(sceneData?.sceneId, newTitle); - } + if (currentUser.uid === STREET.utils.getAuthorId()) { + await updateSceneIdAndTitle(STREET.utils.getCurrentSceneId(), newTitle); + STREET.notify.successMessage(`New scene title saved: ${newTitle}`); } - AFRAME.scenes[0].setAttribute('metadata', 'sceneTitle', newTitle); - AFRAME.scenes[0].setAttribute('metadata', 'sceneId', sceneData?.sceneId); - STREET.notify.successMessage(`New scene title saved: ${newTitle}`); } catch (error) { console.error('Error with update title', error); STREET.notify.errorMessage(`Error updating scene title: ${error}`); @@ -50,13 +33,11 @@ const SceneEditTitle = ({ sceneData }) => { return (
- { -
-

- {title || 'Untitled'} -

-
- } +
+

+ {title || 'Untitled'} +

+
); }; diff --git a/src/editor/components/components/Sidebar.js b/src/editor/components/components/Sidebar.js index 10d525337..e0b53a62c 100644 --- a/src/editor/components/components/Sidebar.js +++ b/src/editor/components/components/Sidebar.js @@ -8,7 +8,6 @@ import React from 'react'; import capitalize from 'lodash-es/capitalize'; import classnames from 'classnames'; import { ArrowRightIcon, LayersIcon } from '../../icons'; -import { sendMetric } from '../../services/ga'; import GeoSidebar from './GeoSidebar'; // Make sure to create and import this new component export default class Sidebar extends React.Component { @@ -62,7 +61,6 @@ export default class Sidebar extends React.Component { // additional toggle for hide/show panel by clicking the button toggleRightBar = () => { this.setState({ rightBarHide: !this.state.rightBarHide }); - sendMetric('Components', 'toggleSidebar'); }; render() { diff --git a/src/editor/components/modals/ScenesModal/ScenesModal.component.jsx b/src/editor/components/modals/ScenesModal/ScenesModal.component.jsx index b21045cfd..4d647c1a5 100644 --- a/src/editor/components/modals/ScenesModal/ScenesModal.component.jsx +++ b/src/editor/components/modals/ScenesModal/ScenesModal.component.jsx @@ -12,7 +12,7 @@ import { getCommunityScenes, getUserScenes } from '../../../api/scene'; import { Load24Icon, Loader, Upload24Icon } from '../../../icons'; import { signIn } from '../../../api'; import posthog from 'posthog-js'; - +import useStore from '../../../../store.js'; const SCENES_PER_PAGE = 20; const tabs = [ { @@ -65,7 +65,7 @@ const ScenesModal = ({ isOpen, onClose, initialTab = 'owner', delay }) => { const sceneId = scene.id; const sceneTitle = sceneData.title; AFRAME.scenes[0].setAttribute('metadata', 'sceneId', sceneId); - AFRAME.scenes[0].setAttribute('metadata', 'sceneTitle', sceneTitle); + useStore.getState().setSceneTitle(sceneTitle); AFRAME.scenes[0].setAttribute('metadata', 'authorId', sceneData.author); STREET.notify.successMessage('Scene loaded from 3DStreet Cloud.'); onClose(); diff --git a/src/editor/components/scenegraph/Toolbar.js b/src/editor/components/scenegraph/Toolbar.js index bb3b3478a..12336ba0a 100644 --- a/src/editor/components/scenegraph/Toolbar.js +++ b/src/editor/components/scenegraph/Toolbar.js @@ -14,11 +14,11 @@ import { } from '../../icons'; import Events from '../../lib/Events'; import { Button, ProfileButton, Logo } from '../components'; -import { sendMetric } from '../../services/ga.js'; import posthog from 'posthog-js'; import { UndoRedo } from '../components/UndoRedo'; import debounce from 'lodash-es/debounce'; import { CameraToolbar } from '../viewport/CameraToolbar'; +import useStore from '../../../store'; // const LOCALSTORAGE_MOCAP_UI = "aframeinspectormocapuienabled"; /** @@ -42,7 +42,6 @@ export default class Toolbar extends Component { document.addEventListener('click', this.handleClickOutsideSave); Events.on('historychanged', (cmd) => { if (cmd) { - console.log('historychanged', cmd); // Debounce the cloudSaveHandler call this.debouncedCloudSaveHandler(); } @@ -130,7 +129,7 @@ export default class Toolbar extends Component { } // if there is no current user, show sign in modal let currentSceneId = STREET.utils.getCurrentSceneId(); - let currentSceneTitle = STREET.utils.getCurrentSceneTitle(); + let currentSceneTitle = useStore.getState().sceneTitle; posthog.capture('save_scene_clicked', { save_as: doSaveAs, @@ -180,12 +179,8 @@ export default class Toolbar extends Component { if (newSceneTitle) { currentSceneTitle = newSceneTitle; } - AFRAME.scenes[0].setAttribute( - 'metadata', - 'sceneTitle', - currentSceneTitle - ); + useStore.getState().setSceneTitle(currentSceneTitle); console.log( 'no urlSceneId or doSaveAs is true, therefore generate new one' ); @@ -223,7 +218,6 @@ export default class Toolbar extends Component { const notification = STREET.notify.successMessage('Scene saved'); this.setState({ notification }); - sendMetric('SaveSceneAction', doSaveAs ? 'saveAs' : 'save'); return currentSceneId; } catch (error) { STREET.notify.errorMessage( diff --git a/src/editor/components/viewport/TransformToolbar.js b/src/editor/components/viewport/TransformToolbar.js index 795135ffa..dc71174b2 100644 --- a/src/editor/components/viewport/TransformToolbar.js +++ b/src/editor/components/viewport/TransformToolbar.js @@ -1,7 +1,6 @@ import React from 'react'; import classNames from 'classnames'; import Events from '../../lib/Events'; -import { sendMetric } from '../../services/ga'; var TransformButtons = [ { value: 'translate', icon: 'fa-arrows-alt' }, @@ -43,7 +42,6 @@ export default class TransformToolbar extends React.Component { changeTransformMode = (mode) => { this.setState({ selectedTransform: mode }); Events.emit('transformmodechange', mode); - sendMetric('Toolbar', 'selectHelper', mode); }; onLocalChange = (e) => { diff --git a/src/editor/index.js b/src/editor/index.js index a368689b2..28ba866ee 100644 --- a/src/editor/index.js +++ b/src/editor/index.js @@ -10,9 +10,7 @@ import { Config } from './lib/config'; import { History } from './lib/history'; import { Shortcuts } from './lib/shortcuts'; import { Viewport } from './lib/viewport'; -import { firebaseConfig } from './services/firebase.js'; import './style/index.scss'; -import ReactGA from 'react-ga4'; import posthog from 'posthog-js'; import { commandsByType } from './lib/commands/index.js'; @@ -24,7 +22,6 @@ function Inspector() { this.isFirstOpen = true; this.modules = {}; this.opened = false; - // Wait for stuff. const doInit = () => { if (!AFRAME.scenes.length) { @@ -72,6 +69,7 @@ Inspector.prototype = { this.selected = null; // Init React. + const div = document.createElement('div'); div.id = 'aframeInspector'; div.setAttribute('data-aframe-inspector', 'app'); @@ -180,6 +178,9 @@ Inspector.prototype = { }, initEvents: function () { + // Remove inspector component to properly unregister keydown listener when the inspector is loaded via a script tag, + // otherwise the listener will be registered twice and we can't toggle the inspector from viewer mode with the shortcut. + this.sceneEl.removeAttribute('inspector'); window.addEventListener('keydown', (evt) => { // Alt + Ctrl + i: Shorcut to toggle the inspector const shortcutPressed = @@ -358,9 +359,7 @@ Inspector.prototype = { } }; -ReactGA.initialize(firebaseConfig.measurementId); const inspector = (AFRAME.INSPECTOR = new Inspector()); - posthog.init('phc_Yclai3qykyFi8AEFOrZsh6aS78SSooLzpDz9wQ9YAH9', { api_host: 'https://us.i.posthog.com', person_profiles: 'identified_only' // or 'always' to create profiles for anonymous users as well diff --git a/src/editor/lib/viewport.js b/src/editor/lib/viewport.js index dc37cdf01..1455a4743 100644 --- a/src/editor/lib/viewport.js +++ b/src/editor/lib/viewport.js @@ -4,7 +4,6 @@ import EditorControls from './EditorControls.js'; import { initRaycaster } from './raycaster'; import Events from './Events'; -import { sendMetric } from '../services/ga.js'; // variables used by OrientedBoxHelper const auxEuler = new THREE.Euler(); @@ -383,6 +382,5 @@ export function Viewport(inspector) { element.style.display = 'block'; }); } - sendMetric('Viewport', 'toggleEditor', active); }); } diff --git a/src/editor/services/ga.js b/src/editor/services/ga.js deleted file mode 100644 index 2b5de4a72..000000000 --- a/src/editor/services/ga.js +++ /dev/null @@ -1,11 +0,0 @@ -import ReactGA from 'react-ga4'; - -const sendMetric = (category, action, label) => { - ReactGA.event({ - category, - action, - label: label - }); -}; - -export { sendMetric }; diff --git a/src/index.js b/src/index.js index de43a4a9f..b8817c5e1 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,7 @@ /* global AFRAME, XMLHttpRequest, VERSION */ import 'aframe-cursor-teleport-component'; import 'aframe-extras/controls/index.js'; +import useStore from './store.js'; var streetmixParsers = require('./aframe-streetmix-parsers'); var streetmixUtils = require('./tested/streetmix-utils'); require('./json-utils_1.1.js'); @@ -20,6 +21,9 @@ require('./components/street-geo.js'); require('./components/street-environment.js'); require('./components/intersection.js'); require('./components/obb-clipping.js'); +require('./editor/index.js'); + +const state = useStore.getState(); if (typeof VERSION !== 'undefined') { console.log(`3DStreet Version: ${VERSION}`); @@ -192,26 +196,10 @@ AFRAME.registerComponent('streetmix-loader', { const streetmixSegments = streetData.segments; const streetmixName = streetmixResponseObject.name; - console.log('streetmixName', streetmixName); el.setAttribute('streetmix-loader', 'name', streetmixName); - - let currentSceneTitle; - if (AFRAME.scenes[0] && AFRAME.scenes[0].getAttribute('metadata')) { - currentSceneTitle = - AFRAME.scenes[0].getAttribute('metadata').sceneTitle; - } - if (!currentSceneTitle) { - // only set title from streetmix if none exists - AFRAME.scenes[0].setAttribute( - 'metadata', - 'sceneTitle', - streetmixName - ); - console.log( - 'therefore setting metadata sceneTitle as streetmixName', - streetmixName - ); + if (!state.sceneTitle) { + state.setSceneTitle(streetmixName); } el.setAttribute('data-layer-name', 'Streetmix • ' + streetmixName); diff --git a/src/json-utils_1.1.js b/src/json-utils_1.1.js index 7ce33aded..f3e3bfd05 100644 --- a/src/json-utils_1.1.js +++ b/src/json-utils_1.1.js @@ -1,9 +1,10 @@ +import useStore from './store'; + /* global AFRAME, Node */ /* version: 1.0 */ window.STREET = {}; var assetsUrl; STREET.utils = {}; - function getSceneUuidFromURLHash() { const currentHash = window.location.hash; const match = currentHash.match(/#\/scenes\/([a-zA-Z0-9-]+)\.json/); @@ -32,14 +33,6 @@ function getAuthorId() { } STREET.utils.getAuthorId = getAuthorId; -const getCurrentSceneTitle = () => { - const currentSceneTitle = - AFRAME.scenes[0].getAttribute('metadata').sceneTitle; - console.log('currentSceneTitle', currentSceneTitle); - return currentSceneTitle; -}; -STREET.utils.getCurrentSceneTitle = getCurrentSceneTitle; - /* Takes one or more elements (from a DOM queryselector call) and returns a Javascript object @@ -61,7 +54,7 @@ function convertDOMElToObject(entity) { } return { - title: STREET.utils.getCurrentSceneTitle(), + title: useStore.getState().sceneTitle, version: '1.0', data: data }; @@ -469,57 +462,10 @@ function createEntityFromObj(entityData, parentEl) { AFRAME.registerComponent('metadata', { schema: { - sceneTitle: { default: '' }, sceneId: { default: '' }, authorId: { default: '' } }, - init: function () {}, - update: function (oldData) { - const sceneTitle = this.data.sceneTitle; - if (sceneTitle !== oldData.sceneTitle) { - this.el.emit('newTitle', { sceneTitle: sceneTitle }); - } - } -}); - -AFRAME.registerComponent('scene-title', { - schema: { - titleText: { default: '' } - }, - init: function () { - this.titleElement = undefined; - this.el.addEventListener('newTitle', (evt) => { - this.el.setAttribute('scene-title', 'titleText', evt.detail.sceneTitle); - }); - }, - createTitleElement: function (titleText) { - const titleDiv = (this.titleElement = document.createElement('div')); - const newContent = document.createTextNode(titleText); - titleDiv.setAttribute('id', 'sceneTitle'); - titleDiv.appendChild(newContent); - document.body.append(titleDiv); - }, - updateTitleText: function (titleText) { - this.titleElement.textContent = titleText; - }, - update: function (oldData) { - // If `oldData` is empty, then this means we're in the initialization process. - // No need to update. - if (Object.keys(oldData).length === 0) { - return; - } - - const titleText = this.data.titleText; - const titleElement = this.titleElement; - - if (titleText !== oldData.titleText) { - if (!titleElement) { - this.createTitleElement(titleText); - } else { - this.updateTitleText(titleText); - } - } - } + init: function () {} }); AFRAME.registerComponent('set-loader-from-hash', { @@ -691,7 +637,7 @@ function createElementsFromJSON(streetJSON, clearUrlHash) { const sceneTitle = streetObject.title; if (sceneTitle) { console.log('sceneTitle from createElementsFromJSON', sceneTitle); - AFRAME.scenes[0].setAttribute('metadata', 'sceneTitle', sceneTitle); + useStore.getState().setSceneTitle(sceneTitle); } const streetContainerEl = document.getElementById('street-container'); diff --git a/src/store.js b/src/store.js new file mode 100644 index 000000000..99b340225 --- /dev/null +++ b/src/store.js @@ -0,0 +1,18 @@ +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; + +const useStore = create( + devtools( + (set) => ({ + sceneId: null, + setSceneId: (newSceneId) => set({ sceneId: newSceneId }), + sceneTitle: null, + setSceneTitle: (newSceneTitle) => set({ sceneTitle: newSceneTitle }), + authorId: null, + setAuthorId: (newAuthorId) => set({ authorId: newAuthorId }) + }), + { name: 'MyZustandStore' } + ) +); + +export default useStore; diff --git a/src/street-utils.js b/src/street-utils.js index 872f52e6a..300c0384e 100644 --- a/src/street-utils.js +++ b/src/street-utils.js @@ -69,7 +69,6 @@ function newScene( // clear metadata if (clearMetaData) { AFRAME.scenes[0].setAttribute('metadata', 'sceneId', ''); - AFRAME.scenes[0].setAttribute('metadata', 'sceneTitle', ''); AFRAME.scenes[0].setAttribute('metadata', 'authorId', ''); } diff --git a/src/viewer-styles.css b/src/viewer-styles.css index 122431903..32e1315c2 100644 --- a/src/viewer-styles.css +++ b/src/viewer-styles.css @@ -222,188 +222,43 @@ body.aframe-inspector-opened #sceneTitle { .loader__wrapper { width: 100vw; height: 100vh; - background: rgba(14, 14, 14, 0.69); - display: flex; justify-content: center; align-items: center; } .loader { - display: flex; - align-items: center; - flex-direction: column; position: relative; z-index: 999999; - } - .road { width: 320px; - display: flex; align-items: center; justify-content: center; padding-top: 12px; - - border-top: 4px solid #fff; - + /* Remove the solid border and replace with animated background */ + border-top: none; + background: repeating-linear-gradient( + 90deg, + #6100FF 0px, + #ae7cff 320px, + transparent 0px, + transparent 320px + ); + background-size: 200% 4px; + background-repeat: repeat-x; + background-position: top; + animation: moveStripes 1s linear infinite; color: #fff; font-weight: normal; } -/* ENTITIES */ - -/* - entities width = 1050px - loader width = 320px - animation-duration = 5840ms - */ -.entities { - position: absolute; - bottom: 40px; - right: 0; - - /* 730px = 1050px - 320px */ - clip-path: inset(0 0px 0 730px); - transform: translateX(0px); - animation: topMoving 5840ms linear infinite; -} - -@keyframes topMoving { - 0% { - clip-path: inset(0 0px 0 730px); - transform: translateX(0px); - } - - 100% { - clip-path: inset(0 730px 0 0px); - transform: translateX(730px); - } -} - -/* TRANSPORT */ -.transport { - position: absolute; - left: 0; - top: 2px; -} - -.wrapper__transport { - display: flex; - align-items: flex-end; - position: relative; -} - -.wrapper__transport img { - position: absolute; -} - -/* BIKE */ -.transport .bike { - position: absolute; - animation: bike 5840ms linear infinite; -} - -@keyframes bike { - 0% { - left: -27px; - clip-path: inset(0 0 0 100%); - } - - 4% { - left: 0px; - clip-path: inset(0 0 0 0); - } - - 32% { - left: 294px; - clip-path: inset(0 0 0 0); - } - - 36% { - clip-path: inset(0 100% 0 0); - left: 320px; - } - - 100% { - clip-path: inset(0 100% 0 0); - left: 320px; - } -} - -/* BUS */ -.transport .bus { - position: absolute; - animation: bus 5840ms linear infinite; -} - -@keyframes bus { +@keyframes moveStripes { 0% { - left: -36px; - clip-path: inset(0 0 0 100%); - } - - 8% { - left: -36px; - clip-path: inset(0 0 0 100%); - } - - 12% { - left: 0px; - clip-path: inset(0 0 0 0); - } - - 44% { - left: 284px; - clip-path: inset(0 0 0 0); - } - - 48% { - clip-path: inset(0 100% 0 0); - left: 320px; + background-position: 0 0; } - - 100% { - clip-path: inset(0 100% 0 0); - left: 320px; - } -} - -/* CAR */ -.transport .car { - position: absolute; - animation: car 5840ms linear infinite; -} - -@keyframes car { - 0% { - left: -30px; - clip-path: inset(0 0 0 100%); - } - - 20% { - left: -30px; - clip-path: inset(0 0 0 100%); - } - - 24% { - left: 0px; - clip-path: inset(0 0 0 0); - } - - 76% { - left: 290px; - clip-path: inset(0 0 0 0); - } - - 80% { - clip-path: inset(0 100% 0 0); - left: 320px; - } - 100% { - clip-path: inset(0 100% 0 0); - left: 320px; + background-position: 320px 0; } } \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index 63eb4bc93..bc50c66c9 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -17,10 +17,6 @@ module.exports = { }, devtool: 'source-map', entry: { - editor: { - import: ['./src/editor/index.js'], - filename: '3dstreet-editor.js' - }, core: { import: './src/index.js', filename: 'aframe-street-component.js' } }, output: { diff --git a/webpack.prod.config.js b/webpack.prod.config.js index 54325d7a9..9b6be64c6 100644 --- a/webpack.prod.config.js +++ b/webpack.prod.config.js @@ -7,8 +7,8 @@ const DEPLOY_ENV = process.env.DEPLOY_ENV ?? 'production'; module.exports = { performance: { - maxAssetSize: 2777777, // 2.4 MiB - maxEntrypointSize: 2777777, // 2.4 MiB + maxAssetSize: 2999999, // 2.8 MiB + maxEntrypointSize: 2999999, // 2.8 MiB hints: 'error' }, mode: 'production',