diff --git a/src/editor/components/Main.js b/src/editor/components/Main.js
index be497e3db..f48a274ee 100644
--- a/src/editor/components/Main.js
+++ b/src/editor/components/Main.js
@@ -1,4 +1,4 @@
-import { HelpButton, GeoPanel, ZoomButtons } from './components';
+import { HelpButton, ZoomButtons } from './components';
import { useState, useEffect } from 'react';
import ComponentsSidebar from './components/Sidebar';
import Events from '../lib/Events';
@@ -155,9 +155,6 @@ export default function Main() {
{isInspectorEnabled && (
<>
-
-
-
diff --git a/src/editor/components/components/AddLayerPanel/AddLayerPanel.component.jsx b/src/editor/components/components/AddLayerPanel/AddLayerPanel.component.jsx
index fbc5f36bb..a6140b37d 100644
--- a/src/editor/components/components/AddLayerPanel/AddLayerPanel.component.jsx
+++ b/src/editor/components/components/AddLayerPanel/AddLayerPanel.component.jsx
@@ -10,7 +10,6 @@ import CardPlaceholder from '../../../../../ui_assets/card-placeholder.svg';
import LockedCard from '../../../../../ui_assets/locked-card.svg';
import mixinCatalog from '../../../../catalog.json';
import posthog from 'posthog-js';
-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';
@@ -270,6 +269,7 @@ const cardMouseLeave = (mixinId) => {
const AddLayerPanel = () => {
const setModal = useStore((state) => state.setModal);
const isOpen = useStore((state) => state.modal === 'addlayer');
+ const startCheckout = useStore((state) => state.startCheckout);
// set the first Layers option when opening the panel
const [selectedOption, setSelectedOption] = useState(LayersOptions[0].value);
const [groupedMixins, setGroupedMixins] = useState([]);
@@ -311,8 +311,7 @@ const AddLayerPanel = () => {
isProUser: isProUser
});
if (card.requiresPro && !isProUser) {
- Events.emit('hideAddLayerPanel');
- Events.emit('openpaymentmodal');
+ startCheckout('addlayer');
} else if (card.mixinId) {
createEntity(card.mixinId);
} else if (card.handlerFunction) {
diff --git a/src/editor/components/components/GeoPanel/GeoPanel.component.jsx b/src/editor/components/components/GeoPanel/GeoPanel.component.jsx
deleted file mode 100644
index 67f984235..000000000
--- a/src/editor/components/components/GeoPanel/GeoPanel.component.jsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import GeoImg from '../../../../../ui_assets/geo.png';
-import styles from './GeoPanel.module.scss';
-import { useAuthContext, useGeoContext } from '../../../contexts/index.js';
-import posthog from 'posthog-js';
-import useStore from '@/store';
-/**
- * GeoPanel component.
- *
- * @author Rostyslav Nahornyi
- * @category Components.
- */
-const GeoPanel = () => {
- const { currentUser } = useAuthContext();
- const setModal = useStore((state) => state.setModal);
-
- const onClick = () => {
- posthog.capture('geo_panel_clicked');
- if (!currentUser) {
- setModal('signin');
- } else if (currentUser.isPro) {
- setModal('geo');
- } else {
- setModal('payment');
- }
- };
-
- const streetGeo = useGeoContext();
- let coordinateInfo = null;
-
- if (streetGeo) {
- coordinateInfo = `Latitude: ${streetGeo.latitude}, Longitude: ${streetGeo.longitude}, Elevation: ${streetGeo.ellipsoidalHeight}m`;
- }
-
- return (
-
- );
-};
-export { GeoPanel };
diff --git a/src/editor/components/components/GeoPanel/GeoPanel.module.scss b/src/editor/components/components/GeoPanel/GeoPanel.module.scss
deleted file mode 100644
index be62c0564..000000000
--- a/src/editor/components/components/GeoPanel/GeoPanel.module.scss
+++ /dev/null
@@ -1,29 +0,0 @@
-@use '../../../style/variables.scss';
-
-.geo {
- display: flex;
- column-gap: 24px;
- position: absolute;
- bottom: 32px;
- left: 32px;
-
- & > img {
- cursor: pointer;
- }
-
- & > p,
- & > a {
- margin-top: 8px;
- font-size: 16px !important;
- max-width: 80vw;
- text-overflow: ellipsis;
- overflow: hidden;
- white-space: nowrap;
- }
- & > a {
- filter: brightness(90%);
- }
- & > a:hover {
- filter: brightness(100%);
- }
-}
diff --git a/src/editor/components/components/GeoPanel/index.js b/src/editor/components/components/GeoPanel/index.js
deleted file mode 100644
index ddf027699..000000000
--- a/src/editor/components/components/GeoPanel/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { GeoPanel } from './GeoPanel.component.jsx';
diff --git a/src/editor/components/components/index.js b/src/editor/components/components/index.js
index 31d0f68d6..46fc13f6d 100644
--- a/src/editor/components/components/index.js
+++ b/src/editor/components/components/index.js
@@ -10,4 +10,3 @@ export { Logo } from './Logo';
export { ProfileButton } from './ProfileButton';
export { SceneCard } from './SceneCard';
export { SceneEditTitle } from './SceneEditTitle';
-export { GeoPanel } from './GeoPanel';
diff --git a/src/editor/components/modals/PaymentModal/PaymentModal.component.jsx b/src/editor/components/modals/PaymentModal/PaymentModal.component.jsx
index 8f9a18c63..aa4923a38 100644
--- a/src/editor/components/modals/PaymentModal/PaymentModal.component.jsx
+++ b/src/editor/components/modals/PaymentModal/PaymentModal.component.jsx
@@ -20,14 +20,23 @@ const getStripe = () => {
return stripePromise;
};
+const resetPaymentQueryParam = () => {
+ const newUrl = window.location.href.replace(/\?payment=(success|cancel)/, '');
+ window.history.replaceState({}, '', newUrl);
+};
+
const PaymentModal = () => {
const { currentUser } = useAuthContext();
const [isLoading, setIsLoading] = useState(false);
const setModal = useStore((state) => state.setModal);
const modal = useStore((state) => state.modal);
+ const postCheckout = useStore((state) => state.postCheckout);
+ const checkoutSuccess = location.hash.includes('success');
- if (location.hash.includes('success')) {
+ if (checkoutSuccess) {
posthog.capture('checkout_finished');
+ } else if (location.hash.includes('cancel')) {
+ posthog.capture('checkout_canceled');
}
const startCheckout = async () => {
@@ -63,8 +72,12 @@ const PaymentModal = () => {
};
const onClose = () => {
- window.location.hash = '#';
- setModal(null);
+ resetPaymentQueryParam();
+ if (checkoutSuccess && postCheckout) {
+ setModal(postCheckout);
+ } else {
+ setModal(null);
+ }
};
return (
@@ -74,11 +87,8 @@ const PaymentModal = () => {
onClose={onClose}
>
-
Unlock Geospatial Features with a free 30 day trial
-
- Create with geospatial maps and share your vision in augmented reality
- with 3DStreet Pro.
-
+
Unlock Pro features with a free 30 day trial
+
Create with intersections and geospatial maps.
-
All features in Free
@@ -89,7 +99,7 @@ const PaymentModal = () => {
-
- Advanced Street Geometry
+ Intersections and Advanced Street Geometry
-
diff --git a/src/editor/components/scenegraph/GeoLayer.js b/src/editor/components/scenegraph/GeoLayer.js
new file mode 100644
index 000000000..ac687344d
--- /dev/null
+++ b/src/editor/components/scenegraph/GeoLayer.js
@@ -0,0 +1,94 @@
+import { useState, useEffect, useRef } from 'react';
+import useStore from '@/store';
+import { useAuthContext, useGeoContext } from '@/editor/contexts/index.js';
+import Events from '@/editor/lib/Events';
+import posthog from 'posthog-js';
+
+const GeoLayer = () => {
+ const [clicked, setClicked] = useState(false);
+ const componentRef = useRef(null);
+
+ useEffect(() => {
+ const handleClickOutside = (event) => {
+ if (
+ componentRef.current &&
+ !componentRef.current.contains(event.target) &&
+ !event.target.closest('#rightPanel')
+ ) {
+ setClicked(false);
+ }
+ };
+
+ document.addEventListener('mousedown', handleClickOutside);
+ return () => {
+ document.removeEventListener('mousedown', handleClickOutside);
+ };
+ }, []);
+ const { currentUser } = useAuthContext();
+ const setModal = useStore((state) => state.setModal);
+ const startCheckout = useStore((state) => state.startCheckout);
+ const streetGeo = useGeoContext();
+ const entity = document.getElementById('reference-layers');
+
+ const onClick = () => {
+ setClicked(true);
+ posthog.capture('geo_layer_clicked');
+ if (!currentUser) {
+ setModal('signin');
+ } else if (currentUser.isPro) {
+ if (streetGeo) {
+ Events.emit('entityselect', entity);
+ } else {
+ setModal('geo');
+ }
+ } else {
+ startCheckout('geo');
+ }
+ };
+
+ const toggleVisibility = (entity) => {
+ const visible =
+ entity.tagName.toLowerCase() === 'a-scene'
+ ? entity.object3D.visible
+ : entity.getAttribute('visible');
+ AFRAME.INSPECTOR.execute('entityupdate', {
+ entity,
+ component: 'visible',
+ value: !visible
+ });
+ };
+
+ const tagName = entity.tagName.toLowerCase();
+
+ const visible =
+ tagName === 'a-scene'
+ ? entity.object3D.visible
+ : entity.getAttribute('visible');
+ const visibilityButton = (
+ toggleVisibility(entity)}
+ />
+ );
+
+ return (
+
+ {visibilityButton}
+
+ {!streetGeo ? (
+ <>
+ Set Location 🌎
+ >
+ ) : (
+ Geospatial Layer 🌎
+ )}
+
+
+ );
+};
+
+export default GeoLayer;
diff --git a/src/editor/components/scenegraph/SceneGraph.js b/src/editor/components/scenegraph/SceneGraph.js
index f3f0ab440..b435ab633 100644
--- a/src/editor/components/scenegraph/SceneGraph.js
+++ b/src/editor/components/scenegraph/SceneGraph.js
@@ -9,7 +9,7 @@ import { ToolbarWrapper } from './ToolbarWrapper';
import { LayersIcon, ArrowLeftIcon } from '../../icons';
import { getEntityDisplayName } from '../../lib/entity';
import posthog from 'posthog-js';
-
+import GeoLayer from './GeoLayer';
const HIDDEN_CLASSES = ['teleportRay', 'hitEntity', 'hideFromSceneGraph'];
const HIDDEN_IDS = ['dropPlane', 'previewEntity'];
@@ -226,6 +226,12 @@ export default class SceneGraph extends React.Component {
};
isVisibleInSceneGraph = (x) => {
+ if (
+ x.id === 'reference-layers' &&
+ !window.location.search.includes('debug=true')
+ ) {
+ return false;
+ }
let curr = x.parentNode;
if (!curr) {
return false;
@@ -360,7 +366,10 @@ export default class SceneGraph extends React.Component {
Layers
- {this.renderEntities()}
+
+
+
{this.renderEntities()}
+
);
diff --git a/src/editor/contexts/Auth.context.js b/src/editor/contexts/Auth.context.js
index 5d349a147..6ca14de8a 100644
--- a/src/editor/contexts/Auth.context.js
+++ b/src/editor/contexts/Auth.context.js
@@ -1,7 +1,7 @@
import { createContext, useContext, useEffect, useState } from 'react';
import { auth } from '../services/firebase';
import PropTypes from 'prop-types';
-import { isUserPro, isUserBeta } from '../api/user';
+import { isUserPro } from '../api/user';
import posthog from 'posthog-js';
const AuthContext = createContext({
@@ -23,8 +23,7 @@ const AuthProvider = ({ children }) => {
localStorage.setItem('token', await user.getIdToken());
const isPro = await isUserPro(user);
- const isBeta = await isUserBeta(user);
- const enrichedUser = { ...user, isPro, isBeta };
+ const enrichedUser = { ...user, isPro };
posthog.identify(user.uid, {
email: user.email,
diff --git a/src/store.js b/src/store.js
index f5f7b5e18..8da62d955 100644
--- a/src/store.js
+++ b/src/store.js
@@ -38,6 +38,12 @@ const useStore = create(
}
set({ modal: newModal });
},
+ startCheckout: (postCheckout) => {
+ posthog.capture('modal_opened', { modal: 'payment' });
+ posthog.capture('start_checkout');
+ set({ modal: 'payment', postCheckout });
+ },
+ postCheckout: null,
isInspectorEnabled: true,
setIsInspectorEnabled: (newIsInspectorEnabled) => {
if (newIsInspectorEnabled) {