- Save and share your street scenes by clicking on a provider below to
- log-in or automatically create a{' '}
-
- 3DStreet Cloud account
- {' '}
- if you don't already have one.
-
+
Sign in to save your project.
{
- signIn();
- onClose();
+ onSignInClick('google');
}}
alt="Sign In with Google Button"
className={styles.signInButton}
@@ -45,8 +46,7 @@ const SignInModal = () => {
{
- signInMicrosoft();
- onClose();
+ onSignInClick('microsoft');
}}
alt="Sign In with Microsoft Button"
className={styles.signInButton}
diff --git a/src/editor/components/modals/SignInModal/SignInModal.module.scss b/src/editor/components/modals/SignInModal/SignInModal.module.scss
index a3762e21..89a18bec 100644
--- a/src/editor/components/modals/SignInModal/SignInModal.module.scss
+++ b/src/editor/components/modals/SignInModal/SignInModal.module.scss
@@ -8,6 +8,7 @@
.contentWrapper {
display: flex;
+ width: 100%;
flex-direction: column;
align-items: center;
row-gap: 28px;
diff --git a/src/editor/components/scenegraph/Toolbar.js b/src/editor/components/scenegraph/Toolbar.js
index 36a5dbbf..c049bf00 100644
--- a/src/editor/components/scenegraph/Toolbar.js
+++ b/src/editor/components/scenegraph/Toolbar.js
@@ -1,385 +1,76 @@
-import React, { Component } from 'react';
-import {
- createScene,
- updateScene,
- checkIfImagePathIsEmpty,
- uploadThumbnailImage
-} from '../../api/scene';
-import {
- Cloud24Icon,
- Save24Icon,
- ScreenshotIcon,
- Upload24Icon,
- Edit24Icon
-} from '../../icons';
-import Events from '../../lib/Events';
+import { ScreenshotIcon, Upload24Icon, Edit24Icon } from '../../icons';
import { Button, ProfileButton, Logo } from '../components';
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';
+import { makeScreenshot } from '@/editor/lib/SceneUtils';
+import { Save } from '@/editor/components/components/Save';
-// const LOCALSTORAGE_MOCAP_UI = "aframeinspectormocapuienabled";
+function Toolbar({ currentUser }) {
+ const { isSavingScene, setModal, isInspectorEnabled } = useStore();
-/**
- * Tools and actions.
- */
-export default class Toolbar extends Component {
- constructor(props) {
- super(props);
- this.state = {
- isSaveActionActive: false,
- showLoadBtn: true,
- isSavingScene: false,
- savedScene: false,
- pendingSceneSave: false,
- inspectorEnabled: true
- };
- this.saveButtonRef = React.createRef();
- }
-
- componentDidMount() {
- document.addEventListener('click', this.handleClickOutsideSave);
- Events.on('historychanged', (cmd) => {
- if (cmd) {
- // Debounce the cloudSaveHandler call
- this.debouncedCloudSaveHandler();
- }
- });
- // Subscribe to store changes
- this.unsubscribe = useStore.subscribe(
- (state) => state.isInspectorEnabled,
- (isInspectorEnabled) => {
- this.setState({ inspectorEnabled: isInspectorEnabled });
- }
- );
- }
-
- componentDidUpdate(prevProps) {
- if (this.props.currentUser !== prevProps.currentUser) {
- if (this.state.pendingSceneSave && this.props.currentUser) {
- // Remove the flag from state, as we're going to handle the save now.
- this.setState({ pendingSceneSave: false });
- this.cloudSaveHandlerWithImageUpload();
- }
- }
- }
-
- componentWillUnmount() {
- document.removeEventListener('click', this.handleClickOutsideSave);
- // Unsubscribe from store changes
- if (this.unsubscribe) {
- this.unsubscribe();
- }
- }
-
- isAuthor = () => {
- return this.props.currentUser?.uid === STREET.utils.getAuthorId();
- };
-
- handleClickOutsideSave = (event) => {
- if (
- this.saveButtonRef.current &&
- !this.saveButtonRef.current.contains(event.target)
- ) {
- this.setState({ isSaveActionActive: false });
- }
- };
-
- cloudSaveHandlerWithImageUpload = async (doSaveAs) => {
- this.makeScreenshot();
- const currentSceneId = await this.cloudSaveHandler({ doSaveAs });
- const isImagePathEmpty = await checkIfImagePathIsEmpty(currentSceneId);
- if (isImagePathEmpty) {
- await uploadThumbnailImage();
- }
- };
-
- newHandler = () => {
+ const newHandler = () => {
posthog.capture('new_scene_clicked');
- AFRAME.INSPECTOR.selectEntity(null);
- useStore.getState().newScene();
- STREET.utils.newScene();
- AFRAME.scenes[0].emit('newScene');
+ useStore.getState().setModal('new');
};
- cloudSaveHandler = async ({ doSaveAs = false }) => {
- try {
- // if there is no current user, show sign in modal
- let currentSceneId = STREET.utils.getCurrentSceneId();
- let currentSceneTitle = useStore.getState().sceneTitle;
-
- posthog.capture('save_scene_clicked', {
- save_as: doSaveAs,
- user_id: this.props.currentUser ? this.props.currentUser.uid : null,
- scene_id: currentSceneId,
- scene_title: currentSceneTitle
- });
-
- if (!this.props.currentUser) {
- console.log('no user');
- useStore.getState().setModal('signin');
- return;
- }
-
- // check if the user is not pro, and if the geospatial has array of values of mapbox
- const streetGeo = document
- .getElementById('reference-layers')
- ?.getAttribute('street-geo');
- if (
- !this.props.currentUser.isPro &&
- streetGeo &&
- streetGeo['latitude'] &&
- streetGeo['longitude']
- ) {
- useStore.getState().setModal('payment');
- return;
- }
- if (!this.isAuthor()) {
- posthog.capture('not_scene_author', {
- scene_id: currentSceneId,
- user_id: this.props.currentUser.uid
- });
- doSaveAs = true;
- }
-
- // generate json from 3dstreet core
- const entity = document.getElementById('street-container');
- const data = STREET.utils.convertDOMElToObject(entity);
- const filteredData = JSON.parse(STREET.utils.filterJSONstreet(data));
- this.setState({ isSavingScene: true });
-
- // we want to save, so if we *still* have no sceneID at this point, then create a new one
- if (!currentSceneId || !!doSaveAs) {
- // ask user for scene title here currentSceneTitle
- let newSceneTitle = prompt('Scene Title:', currentSceneTitle || '');
-
- if (newSceneTitle) {
- currentSceneTitle = newSceneTitle;
- }
-
- useStore.getState().setSceneTitle(currentSceneTitle);
- console.log(
- 'no urlSceneId or doSaveAs is true, therefore generate new one'
- );
- currentSceneId = await createScene(
- this.props.currentUser.uid,
- filteredData.data,
- currentSceneTitle,
- filteredData.version
- );
- console.log('newly generated currentSceneId', currentSceneId);
- } else {
- await updateScene(
- currentSceneId,
- filteredData.data,
- currentSceneTitle,
- filteredData.version
- );
- }
-
- // after all those save shenanigans let's set currentSceneId in state
- this.setState({ currentSceneId });
-
- // save json to firebase with other metadata
+ const isEditor = !!isInspectorEnabled;
- // make sure to update sceneId with new one in metadata component!
- AFRAME.scenes[0].setAttribute('metadata', 'sceneId', currentSceneId);
- AFRAME.scenes[0].setAttribute(
- 'metadata',
- 'authorId',
- this.props.currentUser.uid
- );
-
- // Change the hash URL without reloading
- window.location.hash = `#/scenes/${currentSceneId}.json`;
- this.setState({ isSaveActionActive: false });
- this.setState({ savedScene: true });
- this.setSavedSceneFalse();
-
- return currentSceneId;
- } catch (error) {
- STREET.notify.errorMessage(
- `Error trying to save 3DStreet scene to cloud. Error: ${error}`
- );
- console.error(error);
- } finally {
- this.setState({ isSavingScene: false });
- }
- };
-
- setSavedSceneFalse = debounce(() => {
- this.setState({ savedScene: false });
- }, 500);
-
- debouncedCloudSaveHandler = debounce(() => {
- if (
- this.props.currentUser &&
- STREET.utils.getAuthorId() === this.props.currentUser.uid
- ) {
- const streetGeo = document
- .getElementById('reference-layers')
- ?.getAttribute('street-geo');
- if (
- !this.props.currentUser.isPro &&
- streetGeo &&
- streetGeo['latitude'] &&
- streetGeo['longitude']
- ) {
- useStore.getState().setModal('payment');
- return;
- }
- this.cloudSaveHandler({ doSaveAs: false });
- }
- }, 1000);
-
- handleUnsignedSaveClick = () => {
- posthog.capture('remix_scene_clicked');
- this.setState({ pendingSceneSave: true });
- useStore.getState().setModal('signin');
- };
-
- makeScreenshot = () => {
- const imgHTML = '';
- // Set the screenshot in local storage
- localStorage.setItem('screenshot', JSON.stringify(imgHTML));
- const screenshotEl = document.getElementById('screenshot');
- screenshotEl.play();
-
- screenshotEl.setAttribute('screentock', 'type', 'img');
- screenshotEl.setAttribute(
- 'screentock',
- 'imgElementSelector',
- '#screentock-destination'
- );
- // take the screenshot
- screenshotEl.setAttribute('screentock', 'takeScreenshot', true);
- };
-
- toggleSaveActionState = () => {
- this.setState((prevState) => ({
- isSaveActionActive: !prevState.isSaveActionActive
- }));
- };
-
- render() {
- const isEditor = !!this.state.inspectorEnabled;
- return (
-