Skip to content

Commit

Permalink
Merge pull request #957 from 3DStreet/auto-save
Browse files Browse the repository at this point in the history
Auto save
  • Loading branch information
kfarr authored Dec 20, 2024
2 parents 22b5562 + e2c5246 commit 2926a00
Show file tree
Hide file tree
Showing 21 changed files with 574 additions and 441 deletions.
1 change: 0 additions & 1 deletion public/functions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ exports.getScene = functions
const documentId = req.path
.split('/')
.filter(Boolean)[1]
.replace('.json', '');
if (!documentId) {
res.status(400).send({ error: 'Scene ID is required' });
return;
Expand Down
4 changes: 1 addition & 3 deletions src/editor/api/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ import posthog from 'posthog-js';
const signIn = async () => {
try {
const { user } = await signInWithPopup(auth, new GoogleAuthProvider());
STREET.notify.successMessage(
`Successful login with Google authentication.`
);
// first signIn to ga
if (user.metadata.creationTime !== user.metadata.lastSignInTime) return;
posthog.capture('user_signed_up', {
Expand All @@ -32,6 +29,7 @@ const signIn = async () => {
);
console.error(error);
}
throw error;
}
};

Expand Down
4 changes: 1 addition & 3 deletions src/editor/api/scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ const saveScreenshot = async (value) => {
screenshotEl.setAttribute('screentock', 'takeScreenshot', true);
};

const uploadThumbnailImage = async () => {
const uploadThumbnailImage = async (sceneDocId) => {
try {
// saveScreenshot('img');

Expand Down Expand Up @@ -238,8 +238,6 @@ const uploadThumbnailImage = async () => {
const thumbnailDataUrl = resizedCanvas.toDataURL('image/jpeg', 0.5);
const blobFile = await fetch(thumbnailDataUrl).then((res) => res.blob());

const sceneDocId = STREET.utils.getCurrentSceneId();

const thumbnailRef = ref(storage, `scenes/${sceneDocId}/files/preview.jpg`);

const uploadedImg = await uploadBytes(thumbnailRef, blobFile);
Expand Down
13 changes: 2 additions & 11 deletions src/editor/components/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { PaymentModal } from './modals/PaymentModal';
import { SceneEditTitle } from './components/SceneEditTitle';
import { AddLayerPanel } from './components/AddLayerPanel';
import { IntroModal } from './modals/IntroModal';
import { NewModal } from './modals/NewModal';
import { ToolbarWrapper } from './scenegraph/ToolbarWrapper.js';
import useStore from '@/store';

Expand All @@ -42,8 +43,6 @@ export default function Main() {
);
htmlEditorButton && htmlEditorButton.remove();

handleStreetMixURL();
window.addEventListener('hashchange', () => handleStreetMixURL());
Events.on('opentexturesmodal', function (selectedTexture, textureOnClose) {
setState((prevState) => ({
...prevState,
Expand Down Expand Up @@ -95,15 +94,6 @@ export default function Main() {
});
}, []);

Check warning on line 95 in src/editor/components/Main.js

View workflow job for this annotation

GitHub Actions / Test Cases (20.x)

React Hook useEffect has missing dependencies: 'state.visible.attributes' and 'state.visible.scenegraph'. Either include them or remove the dependency array

const handleStreetMixURL = () => {
const isStreetMix = window.location.hash.includes('streetmix');
if (isStreetMix) {
STREET.notify.warningMessage(
'Hit save if you want to save changes to the scene. Otherwise changes will be lost'
);
}
};

const onModalTextureOnClose = (value) => {
setState((prevState) => ({
...prevState,
Expand Down Expand Up @@ -141,6 +131,7 @@ export default function Main() {
<ScenesModal />
<ProfileModal />
<IntroModal />
<NewModal />
<LoadScript
googleMapsApiKey={firebaseConfig.apiKey}
libraries={GOOGLE_MAPS_LIBRARIES}
Expand Down
140 changes: 140 additions & 0 deletions src/editor/components/components/Save/Save.component.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { useState, useEffect } from 'react';
import { saveSceneWithScreenshot } from '@/editor/lib/SceneUtils';
import useStore from '@/store';
import { Button } from '@/editor/components/components';
import { Cloud24Icon, Save24Icon } from '@/editor/icons';
import debounce from 'lodash-es/debounce';
import Events from '@/editor/lib/Events';

export const Save = ({ currentUser }) => {
const [savedScene, setSavedScene] = useState(false);
const [isSaveActionActive, setIsSaveActionActive] = useState(false);
const { isSavingScene, doSaveAs, setModal, saveScene, postSaveScene } =
useStore();

useEffect(() => {
if (savedScene) {
debounce(() => {
setSavedScene(false);
}, 1000);
}
}, [savedScene]); // eslint-disable-line react-hooks/exhaustive-deps

useEffect(() => {
const autoSaveScene = debounce((cmd) => {
if (cmd) {
if (currentUser && STREET.utils.getAuthorId() === currentUser.uid) {
const streetGeo = document
.getElementById('reference-layers')
?.getAttribute('street-geo');
if (
!currentUser.isPro &&
streetGeo &&
streetGeo['latitude'] &&
streetGeo['longitude']
) {
setModal('payment');
return;
}
saveScene(false);
}
}
}, 1000);
Events.on('historychanged', autoSaveScene);
return () => {
Events.off('historychanged', autoSaveScene);
};
}, [currentUser]); // eslint-disable-line react-hooks/exhaustive-deps

useEffect(() => {
if (isSavingScene) {
handleSave(doSaveAs);
}
}, [isSavingScene]); // eslint-disable-line react-hooks/exhaustive-deps
// if (isSavingScene) {
// // Events.on('historychanged', (cmd) => {
// // if (cmd) {
// // // Debounce the cloudSaveHandler call

// // // this.debouncedCloudSaveHandler();
// // }
// // });

const toggleSaveActionState = () => {
setIsSaveActionActive(!isSaveActionActive);
};

const isAuthor = () => {
return currentUser?.uid === STREET.utils.getAuthorId();
};

const handleSave = async (saveAs) => {
try {
await saveSceneWithScreenshot(currentUser, saveAs);
} catch (error) {
STREET.notify.errorMessage(
`Error trying to save 3DStreet scene to cloud. Error: ${error}`
);
console.error(error);
} finally {
postSaveScene();
setSavedScene(true);
}
};

const handleUnsignedSave = () => {
setModal('signin');
};

return (
<div>
{currentUser ? (
<div className="saveButtonWrapper relative w-24">
{isSavingScene ? (
<Button variant="filled">
<div>Saved</div>
</Button>
) : (
<Button
leadingIcon={<Save24Icon />}
onClick={toggleSaveActionState}
disabled={isSavingScene}
variant="toolbtn"
>
<div>Save</div>
</Button>
)}
{isSaveActionActive && (
<div className="dropdownedButtons">
<Button
leadingIcon={<Cloud24Icon />}
variant="white"
onClick={() => saveScene(false)}
disabled={isSavingScene || !isAuthor()}
>
<div>Save</div>
</Button>
<Button
leadingIcon={<Cloud24Icon />}
variant="white"
onClick={() => saveScene(true)}
disabled={isSavingScene}
>
<div>Make a Copy</div>
</Button>
</div>
)}
</div>
) : (
<Button
leadingIcon={<Save24Icon />}
onClick={handleUnsignedSave}
disabled={isSavingScene}
variant="toolbtn"
>
<div>Save</div>
</Button>
)}
</div>
);
};
1 change: 1 addition & 0 deletions src/editor/components/components/Save/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Save } from './Save.component.jsx';
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@
cursor: pointer;

&:hover {
border: 1px solid variables.$purple-100;
background-color: variables.$purple-100;
transition: ease-out 0.3s;
}

&:active {
border-color: variables.$purple-200;
background-color: variables.$purple-200;
transition: all 0.3s;
}

Expand Down
83 changes: 83 additions & 0 deletions src/editor/components/modals/NewModal/NewModal.component.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import Modal from '../Modal.jsx';
import useStore from '@/store.js';
import styles from './NewModal.module.scss';
import ScenePlaceholder from '@/../ui_assets/ScenePlaceholder.svg';
import { inputStreetmix } from '@/editor/lib/SceneUtils.js';
import { Button } from '@/editor/components/components';
import { Upload24Icon } from '@/editor/icons';

export const NewModal = () => {
const setModal = useStore((state) => state.setModal);
const isOpen = useStore((state) => state.modal === 'new');
const saveScene = useStore((state) => state.saveScene);
const onClose = () => {
setModal(null);
};

const onClickNew = () => {
setModal(null);
AFRAME.INSPECTOR.selectEntity(null);
useStore.getState().newScene();
STREET.utils.newScene();
AFRAME.scenes[0].emit('newScene');
};

const scenesData = [
{
title: 'Create Blank Scene',
imagePath: '/ui_assets/cards/new-blank.jpg',
onClick: onClickNew
},
{
title: 'Import From Streetmix',
imagePath: '/ui_assets/cards/new-streetmix-import.jpg',
onClick: inputStreetmix
}
];

return (
<Modal
isOpen={isOpen}
onClose={onClose}
title="Create a New Scene"
titleElement={
<div className="flex items-center justify-between pr-4 pt-4">
<div className="font-large text-center text-2xl">
Create a New Scene
</div>
<Button
onClick={() => {
setModal('scenes');
}}
leadingIcon={<Upload24Icon />}
>
Open Scene
</Button>
</div>
}
>
<div className={styles.wrapper}>
{scenesData?.map((scene, index) => (
<div key={index} className={styles.card} title={scene.title}>
<div
className={styles.img}
onClick={(event) => {
scene.onClick(event);
saveScene(true);
onClose();
}}
style={{
backgroundImage: `url(${scene.imagePath || ScenePlaceholder})`,
backgroundSize: 'cover',
backgroundPosition: 'center'
}}
/>
<div>
<p className={styles.title}>{scene.title}</p>
</div>
</div>
))}
</div>
</Modal>
);
};
Loading

0 comments on commit 2926a00

Please sign in to comment.