diff --git a/src/editor/components/Main.js b/src/editor/components/Main.js index aafcdcf67..be497e3db 100644 --- a/src/editor/components/Main.js +++ b/src/editor/components/Main.js @@ -1,5 +1,5 @@ import { HelpButton, GeoPanel, ZoomButtons } from './components'; -import { Component } from 'react'; +import { useState, useEffect } from 'react'; import ComponentsSidebar from './components/Sidebar'; import Events from '../lib/Events'; import ModalTextures from './modals/ModalTextures'; @@ -17,64 +17,75 @@ import { PaymentModal } from './modals/PaymentModal'; import { SceneEditTitle } from './components/SceneEditTitle'; import { AddLayerPanel } from './components/AddLayerPanel'; import { IntroModal } from './modals/IntroModal'; -import posthog from 'posthog-js'; import { ToolbarWrapper } from './scenegraph/ToolbarWrapper.js'; +import useStore from '@/store'; THREE.ImageUtils.crossOrigin = ''; -const isStreetLoaded = window.location.hash.length; -const isPaymentModalOpened = window.location.hash.includes('payment'); - // Define the libraries array as a constant outside of the component const GOOGLE_MAPS_LIBRARIES = ['places']; -export default class Main extends Component { - constructor(props) { - super(props); - this.state = { - entity: null, - inspectorEnabled: true, - isModalTexturesOpen: false, - isSignInModalOpened: false, - isProfileModalOpened: false, - isAddLayerPanelOpen: false, - isGeoModalOpened: false, - isIntroModalOpened: false, - isScenesModalOpened: !isStreetLoaded, - isPaymentModalOpened: isPaymentModalOpened, - sceneEl: AFRAME.scenes[0], - visible: { - scenegraph: true, - attributes: true - } - }; +export default function Main() { + const [state, setState] = useState({ + entity: null, + isModalTexturesOpen: false, + sceneEl: AFRAME.scenes[0], + visible: { + scenegraph: true, + attributes: true + } + }); + + useEffect(() => { + const htmlEditorButton = document?.querySelector( + '.viewer-logo-start-editor-button' + ); + htmlEditorButton && htmlEditorButton.remove(); + handleStreetMixURL(); + window.addEventListener('hashchange', () => handleStreetMixURL()); + Events.on('opentexturesmodal', function (selectedTexture, textureOnClose) { + setState((prevState) => ({ + ...prevState, + selectedTexture: selectedTexture, + isModalTexturesOpen: true, + textureOnClose: textureOnClose + })); + }); + Events.on('entityselect', (entity) => { + setState((prevState) => ({ + ...prevState, + entity: entity + })); + }); Events.on('togglesidebar', (event) => { if (event.which === 'all') { - if (this.state.visible.scenegraph || this.state.visible.attributes) { - this.setState({ + if (state.visible.scenegraph || state.visible.attributes) { + setState((prevState) => ({ + ...prevState, visible: { scenegraph: false, attributes: false } - }); + })); } else { - this.setState({ + setState((prevState) => ({ + ...prevState, visible: { scenegraph: true, attributes: true } - }); + })); } } else if (event.which === 'attributes') { - this.setState((prevState) => ({ + setState((prevState) => ({ visible: { ...prevState.visible, attributes: !prevState.visible.attributes } })); } else if (event.which === 'scenegraph') { - this.setState((prevState) => ({ + setState((prevState) => ({ visible: { ...prevState.visible, scenegraph: !prevState.visible.scenegraph @@ -82,257 +93,86 @@ export default class Main extends Component { })); } }); - } + }, []); - handleStreetMixURL() { + const handleStreetMixURL = () => { const isStreetMix = window.location.hash.includes('streetmix'); if (isStreetMix) { - const shownIntro = localStorage.getItem('shownIntro'); - if (!shownIntro) { - this.setState({ isIntroModalOpened: true }); - } STREET.notify.warningMessage( 'Hit save if you want to save changes to the scene. Otherwise changes will be lost' ); } - } - - componentDidMount() { - const htmlEditorButton = document?.querySelector( - '.viewer-logo-start-editor-button' - ); - htmlEditorButton && htmlEditorButton.remove(); - - this.handleStreetMixURL(); - window.addEventListener('hashchange', () => this.handleStreetMixURL()); - Events.on( - 'opentexturesmodal', - function (selectedTexture, textureOnClose) { - this.setState({ - selectedTexture: selectedTexture, - isModalTexturesOpen: true, - textureOnClose: textureOnClose - }); - }.bind(this) - ); - Events.on('entityselect', (entity) => { - this.setState({ entity: entity }); - }); - Events.on('inspectortoggle', (enabled) => { - posthog.capture('inspector_toggled', { enabled: enabled }); - this.setState({ inspectorEnabled: enabled }); - }); - Events.on('openscreenshotmodal', () => { - posthog.capture('screenshot_modal_opened'); - this.setState({ isScreenshotOpen: true }); - }); - Events.on('opensigninmodal', () => { - posthog.capture('signin_modal_opened'); - this.setState({ isSignInModalOpened: true }); - }); - Events.on('openscenesmodal', () => { - posthog.capture('scenes_modal_opened'); - this.setState({ isScenesModalOpened: true }); - }); - Events.on('openprofilemodal', () => { - posthog.capture('profile_modal_opened'); - this.setState({ isProfileModalOpened: true }); - }); - Events.on('opengeomodal', () => { - posthog.capture('geo_modal_opened'); - this.setState({ isGeoModalOpened: true }); - }); - Events.on('openpaymentmodal', () => { - posthog.capture('payment_modal_opened'); - this.setState({ isPaymentModalOpened: true }); - }); - Events.on('hideAddLayerPanel', () => { - this.setState({ isAddLayerPanelOpen: false }); - }); - } - - toggleAddLayerPanel = () => { - posthog.capture('add_layer_panel_opened'); - this.setState((prevState) => ({ - isAddLayerPanelOpen: !prevState.isAddLayerPanelOpen - })); }; - onCloseScreenshotModal = (value) => { - this.setState({ isScreenshotOpen: false }); - }; - - onModalTextureOnClose = (value) => { - this.setState({ isModalTexturesOpen: false }); - if (this.state.textureOnClose) { - this.state.textureOnClose(value); + const onModalTextureOnClose = (value) => { + setState((prevState) => ({ + ...prevState, + isModalTexturesOpen: false + })); + if (state.textureOnClose) { + state.textureOnClose(value); } }; - onCloseSignInModal = () => { - this.setState({ isSignInModalOpened: false }); - }; - - onCloseScenesModal = () => { - this.setState({ isScenesModalOpened: false }); - }; - - onCloseProfileModal = () => { - this.setState({ isProfileModalOpened: false }); - }; - - onCloseGeoModal = () => { - this.setState({ isGeoModalOpened: false }); - }; - - onCloseIntroModal = () => { - this.setState({ isIntroModalOpened: false }); - localStorage.setItem('shownIntro', true); - }; - - onClosePaymentModal = () => { - window.location.hash = '#'; - this.setState({ isPaymentModalOpened: false }); - }; - - renderComponentsToggle() { - if ( - !this.state.inspectorEnabled || - !this.state.entity || - this.state.visible.attributes - ) { - return null; - } - - return ( -
- { - Events.emit('togglesidebar', { which: 'attributes' }); - }} - className="fa fa-plus" - title="Show components" - /> -
- ); - } - - renderSceneGraphToggle() { - if (!this.state.inspectorEnabled || this.state.visible.scenegraph) { - return null; - } - return ( -
- { - Events.emit('togglesidebar', { which: 'scenegraph' }); - }} - className="fa fa-plus" - title="Show scenegraph" - /> -
- ); - } - - render() { - const scene = this.state.sceneEl; - const isEditor = !!this.state.inspectorEnabled; - - return ( -
- {this.renderSceneGraphToggle()} - {this.renderComponentsToggle()} - - {isEditor && ( -
- state.isInspectorEnabled); + + return ( +
+ + {isInspectorEnabled && ( +
+ +
+ -
- -
- )} - - - - - - - - - - - - {this.state.inspectorEnabled && ( +
+ )} + + + + + + + + + + + + {isInspectorEnabled && ( + <>
- )} - {this.state.inspectorEnabled && (
- +
- )} - {this.state.inspectorEnabled && (
- )} - {this.state.inspectorEnabled && (
+
+ +
- )} - {this.state.inspectorEnabled && ( -
- -
- )} -
- ); - } + + )} +
+ ); } diff --git a/src/editor/components/components/ActionBar/ActionBar.component.jsx b/src/editor/components/components/ActionBar/ActionBar.component.jsx index 5f5e5831a..0bfc1d891 100644 --- a/src/editor/components/components/ActionBar/ActionBar.component.jsx +++ b/src/editor/components/components/ActionBar/ActionBar.component.jsx @@ -7,8 +7,12 @@ import { Button } from '../Button'; import { useState, useEffect } from 'react'; import posthog from 'posthog-js'; import { Rotate24Icon, Translate24Icon } from '../../../icons'; +import useStore from '@/store.js'; + +const ActionBar = ({ selectedEntity }) => { + const setModal = useStore((state) => state.setModal); + const isOpen = useStore((state) => state.modal === 'addlayer'); -const ActionBar = ({ handleAddClick, isAddLayerPanelOpen, selectedEntity }) => { const [cursorEnabled, setCursorEnabled] = useState( AFRAME.INSPECTOR.cursor.isPlaying ); @@ -42,7 +46,7 @@ const ActionBar = ({ handleAddClick, isAddLayerPanelOpen, selectedEntity }) => { return (
- {!isAddLayerPanelOpen && ( + {!isOpen && (
-
diff --git a/src/editor/components/components/AddLayerPanel/AddLayerPanel.component.jsx b/src/editor/components/components/AddLayerPanel/AddLayerPanel.component.jsx index 5719e5980..fbc5f36bb 100644 --- a/src/editor/components/components/AddLayerPanel/AddLayerPanel.component.jsx +++ b/src/editor/components/components/AddLayerPanel/AddLayerPanel.component.jsx @@ -14,6 +14,7 @@ import Events from '../../../lib/Events'; import pickPointOnGroundPlane from '../../../lib/pick-point-on-ground-plane'; import { customLayersData, streetLayersData } from './layersData.js'; import { LayersOptions } from './LayersOptions.js'; +import useStore from '@/store.js'; // Create an empty image const emptyImg = new Image(); @@ -266,13 +267,19 @@ const cardMouseLeave = (mixinId) => { } }; -const AddLayerPanel = ({ onClose, isAddLayerPanelOpen }) => { +const AddLayerPanel = () => { + const setModal = useStore((state) => state.setModal); + const isOpen = useStore((state) => state.modal === 'addlayer'); // set the first Layers option when opening the panel const [selectedOption, setSelectedOption] = useState(LayersOptions[0].value); const [groupedMixins, setGroupedMixins] = useState([]); const { currentUser } = useAuthContext(); const isProUser = currentUser && currentUser.isPro; + const onClose = () => { + setModal(null); + }; + useEffect(() => { // call getGroupedMixinOptions once time for getting mixinGroups const data = getGroupedMixinOptions(); @@ -398,7 +405,7 @@ const AddLayerPanel = ({ onClose, isAddLayerPanelOpen }) => { return (
{createPortal( diff --git a/src/editor/components/components/GeoPanel/GeoPanel.component.jsx b/src/editor/components/components/GeoPanel/GeoPanel.component.jsx index ad9d01599..67f984235 100644 --- a/src/editor/components/components/GeoPanel/GeoPanel.component.jsx +++ b/src/editor/components/components/GeoPanel/GeoPanel.component.jsx @@ -1,8 +1,8 @@ import GeoImg from '../../../../../ui_assets/geo.png'; import styles from './GeoPanel.module.scss'; -import Events from '../../../lib/Events'; import { useAuthContext, useGeoContext } from '../../../contexts/index.js'; import posthog from 'posthog-js'; +import useStore from '@/store'; /** * GeoPanel component. * @@ -11,14 +11,16 @@ import posthog from 'posthog-js'; */ const GeoPanel = () => { const { currentUser } = useAuthContext(); + const setModal = useStore((state) => state.setModal); + const onClick = () => { posthog.capture('geo_panel_clicked'); if (!currentUser) { - Events.emit('opensigninmodal'); + setModal('signin'); } else if (currentUser.isPro) { - Events.emit('opengeomodal'); + setModal('geo'); } else { - Events.emit('openpaymentmodal'); + setModal('payment'); } }; diff --git a/src/editor/components/components/Logo/Logo.component.jsx b/src/editor/components/components/Logo/Logo.component.jsx index 5cfdad60d..0647c6373 100644 --- a/src/editor/components/components/Logo/Logo.component.jsx +++ b/src/editor/components/components/Logo/Logo.component.jsx @@ -1,27 +1,32 @@ import { Button } from '../Button'; -import PropTypes from 'prop-types'; import styles from './Logo.module.scss'; - +import useStore from '@/store'; /** * Logo component. * * @author Oleksii Medvediev * @category Components */ -const Logo = ({ onToggleEdit, isEditor }) => ( -
-
- 3DStreet Logo -
- -
-); +const Logo = () => { + const setIsInspectorEnabled = useStore( + (state) => state.setIsInspectorEnabled + ); + const isInspectorEnabled = useStore((state) => state.isInspectorEnabled); -Logo.propTypes = { - onToggleEdit: PropTypes.func, - isEditor: PropTypes.bool + return ( +
+
+ 3DStreet Logo +
+ +
+ ); }; export { Logo }; diff --git a/src/editor/components/components/ProfileButton/ProfileButton.component.jsx b/src/editor/components/components/ProfileButton/ProfileButton.component.jsx index a2b8ab4a2..0dfc85240 100644 --- a/src/editor/components/components/ProfileButton/ProfileButton.component.jsx +++ b/src/editor/components/components/ProfileButton/ProfileButton.component.jsx @@ -1,12 +1,11 @@ import styles from './ProfileButton.module.scss'; import { Button } from '../Button'; -import Events from '../../../lib/Events.js'; import { Profile32Icon } from './icons.jsx'; import { useAuthContext } from '../../../contexts'; import posthog from 'posthog-js'; import MsftProfileImg from '../../../../../ui_assets/profile-microsoft.png'; - +import useStore from '@/store'; /** * ProfileButton component. * @@ -43,14 +42,14 @@ const renderProfileIcon = (currentUser) => { const ProfileButton = () => { const { currentUser } = useAuthContext(); - + const setModal = useStore((state) => state.setModal); const onClick = async () => { posthog.capture('profile_button_clicked', { is_logged_in: !!currentUser }); if (currentUser) { - return Events.emit('openprofilemodal'); + setModal('profile'); + } else { + setModal('signin'); } - - return Events.emit('opensigninmodal'); }; return ( diff --git a/src/editor/components/modals/GeoModal/GeoModal.component.jsx b/src/editor/components/modals/GeoModal/GeoModal.component.jsx index 4b651f9da..646ccf7d2 100644 --- a/src/editor/components/modals/GeoModal/GeoModal.component.jsx +++ b/src/editor/components/modals/GeoModal/GeoModal.component.jsx @@ -17,8 +17,9 @@ import { import GeoImg from '../../../../../ui_assets/geo.png'; import { roundCoord } from '../../../../../src/utils.js'; import { QrCode } from '../../components/QrCode'; +import useStore from '@/store.js'; -const GeoModal = ({ isOpen, onClose }) => { +const GeoModal = () => { const { isLoaded } = useJsApiLoader({ googleMapsApiKey: firebaseConfig.apiKey }); @@ -30,6 +31,12 @@ const GeoModal = ({ isOpen, onClose }) => { const [autocomplete, setAutocomplete] = useState(null); const [qrCodeUrl, setQrCodeUrl] = useState(null); const [isWorking, setIsWorking] = useState(false); + const setModal = useStore((state) => state.setModal); + const isOpen = useStore((state) => state.modal === 'geo'); + + const onClose = () => { + setModal(null); + }; useEffect(() => { if (isOpen) { diff --git a/src/editor/components/modals/IntroModal/IntroModal.component.jsx b/src/editor/components/modals/IntroModal/IntroModal.component.jsx index 4037bd253..bbd77cfca 100644 --- a/src/editor/components/modals/IntroModal/IntroModal.component.jsx +++ b/src/editor/components/modals/IntroModal/IntroModal.component.jsx @@ -1,7 +1,13 @@ import Modal from '../Modal.jsx'; import MuxPlayer from '@mux/mux-player-react'; +import useStore from '@/store.js'; -const IntroModal = ({ isOpen, onClose }) => { +const IntroModal = () => { + const isOpen = useStore((state) => state.modal === 'intro'); + const onClose = () => { + useStore.getState().setModal(null); + localStorage.setItem('shownIntro', true); + }; return ( { @@ -19,9 +20,11 @@ const getStripe = () => { return stripePromise; }; -const PaymentModal = ({ isOpen, onClose }) => { +const PaymentModal = () => { const { currentUser } = useAuthContext(); const [isLoading, setIsLoading] = useState(false); + const setModal = useStore((state) => state.setModal); + const modal = useStore((state) => state.modal); if (location.hash.includes('success')) { posthog.capture('checkout_finished'); @@ -59,8 +62,17 @@ const PaymentModal = ({ isOpen, onClose }) => { setIsLoading(false); }; + const onClose = () => { + window.location.hash = '#'; + setModal(null); + }; + return ( - +

Unlock Geospatial Features with a free 30 day trial

diff --git a/src/editor/components/modals/ProfileModal/ProfileModal.component.jsx b/src/editor/components/modals/ProfileModal/ProfileModal.component.jsx index df3f50664..e521545f0 100644 --- a/src/editor/components/modals/ProfileModal/ProfileModal.component.jsx +++ b/src/editor/components/modals/ProfileModal/ProfileModal.component.jsx @@ -6,16 +6,23 @@ import { Button } from '../../components'; import { useAuthContext } from '../../../contexts'; import { signOut } from 'firebase/auth'; import { auth, functions } from '../../../services/firebase'; -import Events from '../../../lib/Events.js'; import { Action24, Loader } from '../../../icons'; import { httpsCallable } from 'firebase/functions'; import posthog from 'posthog-js'; import { renderProfileIcon } from '../../components/ProfileButton'; +import useStore from '@/store'; -const ProfileModal = ({ isOpen, onClose }) => { +const ProfileModal = () => { const { currentUser, setCurrentUser } = useAuthContext(); + const setModal = useStore((state) => state.setModal); + const modal = useStore((state) => state.modal); + const [isLoading, setIsLoading] = useState(false); + const onClose = () => { + setModal(null); + }; + const logOutHandler = async () => { onClose(); await signOut(auth); @@ -40,7 +47,11 @@ const ProfileModal = ({ isOpen, onClose }) => { }; return ( - +

3DStreet Cloud Account

@@ -110,7 +121,7 @@ const ProfileModal = ({ isOpen, onClose }) => {