diff --git a/.gitignore b/.gitignore index ceafe15fc..f8b7d6013 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,4 @@ npm-debug.log* build/ *.sw[pomn] # That's /src/lib/aframe-loader-3dtiles-component.min.js -dist/48406973e3ad4d65cbdd.js* -dist/aframe-mapbox-component.min.js -dist/notyf.min.css -dist/viewer-styles.css +dist \ No newline at end of file diff --git a/src/editor/api/index.js b/src/editor/api/index.js index f7d2052f0..63e82dff9 100644 --- a/src/editor/api/index.js +++ b/src/editor/api/index.js @@ -3,7 +3,8 @@ export { updateScene, isSceneAuthor, getCommunityScenes, - checkIfImagePathIsEmpty + checkIfImagePathIsEmpty, + uploadGlbScene } from './scene'; export { signIn } from './auth'; diff --git a/src/editor/api/scene.js b/src/editor/api/scene.js index 1a8fbfd5c..4f0581c05 100644 --- a/src/editor/api/scene.js +++ b/src/editor/api/scene.js @@ -14,7 +14,8 @@ import { where } from 'firebase/firestore'; import { v4 as uuidv4 } from 'uuid'; -import { db } from '../services/firebase'; +import { getDownloadURL, ref, uploadBytesResumable } from 'firebase/storage'; +import { db, storage } from '../services/firebase'; const generateSceneId = async (authorId) => { const userScenesRef = collection(db, 'scenes'); @@ -189,6 +190,51 @@ const checkIfImagePathIsEmpty = async (sceneId) => { } }; +const uploadGlbScene = async (glbBlobFile, sceneId) => { + if (!sceneId || !glbBlobFile) { + throw new Error('Scene id or blob file is not exist'); + } + + try { + const thumbnailRef = ref(storage, `scenes/${sceneId}/files/scene.glb`); + const uploadedFile = uploadBytesResumable(thumbnailRef, glbBlobFile); + + uploadedFile.on( + 'state_changed', + (snapshot) => { + const progress = + (snapshot.bytesTransferred / snapshot.totalBytes) * 100; + console.info(`uploading: ${progress}%`); + }, + (error) => { + console.log({ error }); + }, + async () => { + const downloadURL = await getDownloadURL(uploadedFile.snapshot.ref); + + const userScenesRef = collection(db, 'scenes'); + const sceneDocRef = doc(userScenesRef, sceneId); + const sceneSnapshot = await getDoc(sceneDocRef); + + if (sceneSnapshot.exists()) { + await updateDoc(sceneDocRef, { + glbPath: downloadURL, + updateTimestamp: serverTimestamp() + }); + STREET.notify.successMessage( + 'glTF has successfully uploaded to cloud!.' + ); + console.log('Firebase updateDoc fired'); + } else { + throw new Error('No existing sceneSnapshot exists.'); + } + } + ); + } catch (error) { + console.error(error); + } +}; + export { checkIfImagePathIsEmpty, deleteScene, @@ -197,5 +243,6 @@ export { getUserScenes, isSceneAuthor, updateScene, - updateSceneIdAndTitle + updateSceneIdAndTitle, + uploadGlbScene }; diff --git a/src/editor/components/modals/ScreenshotModal/ScreenshotModal.component.jsx b/src/editor/components/modals/ScreenshotModal/ScreenshotModal.component.jsx index 07a1fdda6..d52383ab9 100644 --- a/src/editor/components/modals/ScreenshotModal/ScreenshotModal.component.jsx +++ b/src/editor/components/modals/ScreenshotModal/ScreenshotModal.component.jsx @@ -9,7 +9,7 @@ import { updateDoc } from 'firebase/firestore'; -import { signIn } from '../../../api'; +import { signIn, uploadGlbScene } from '../../../api'; import { getDownloadURL, ref, uploadBytes } from 'firebase/storage'; import PropTypes from 'prop-types'; import { useAuthContext } from '../../../contexts'; @@ -18,7 +18,6 @@ import { db, storage } from '../../../services/firebase'; import { Button, Dropdown, Input } from '../../components'; import Toolbar from '../../scenegraph/Toolbar'; import Modal from '../Modal.jsx'; -// import { loginHandler } from '../SignInModal'; export const uploadThumbnailImage = async (uploadedFirstTime) => { try { @@ -99,6 +98,22 @@ export const uploadThumbnailImage = async (uploadedFirstTime) => { } }; +const uploadGlbToCloud = async (sceneId) => { + try { + uploadGlbScene(await Toolbar.convertSceneToGLTFBlob(), sceneId); + } catch (error) { + console.error('Error uploading and updating Firestore:', error); + + let errorMessage = `Error updating scene thumbnail: ${error}`; + if (error.code === 'storage/unauthorized') { + errorMessage = + 'Error updating glb file: only the scene author may upload glb file. Save this scene as your own for uploading.'; + } + + AFRAME.scenes[0].components['notify'].message(errorMessage, 'error'); + } +}; + const saveScreenshot = async (value) => { const screenshotEl = document.getElementById('screenshot'); screenshotEl.play(); @@ -148,7 +163,12 @@ function ScreenshotModal({ isOpen, onClose }) { { value: 'GLB glTF', label: 'GLB glTF', - onClick: Toolbar.exportSceneToGLTF + onClick: () => Toolbar.exportGLTFScene() + }, + { + value: 'Upload glTF to cloud', + label: 'Upload glTF to cloud', + onClick: () => uploadGlbToCloud(sceneId) }, { value: '.3dstreet.json', diff --git a/src/editor/components/scenegraph/Toolbar.js b/src/editor/components/scenegraph/Toolbar.js index 2cf2db943..86a1e98ea 100644 --- a/src/editor/components/scenegraph/Toolbar.js +++ b/src/editor/components/scenegraph/Toolbar.js @@ -271,24 +271,35 @@ export default class Toolbar extends Component { // AFRAME.INSPECTOR.close(); // } - static exportSceneToGLTF() { + static async convertSceneToGLTFBlob() { + const scene = AFRAME.scenes[0].object3D; + + try { + filterHelpers(scene, false); + const buffer = await AFRAME.INSPECTOR.exporters.gltf.parseAsync(scene, { + binary: true + }); + + const blobGlTFScene = new Blob([buffer], { + type: 'application/octet-stream' + }); + + return blobGlTFScene; + } catch (error) { + console.error(error); + } finally { + filterHelpers(scene, true); + } + } + + static async exportGLTFScene() { try { sendMetric('SceneGraph', 'exportGLTF'); const sceneName = getSceneName(AFRAME.scenes[0]); - const scene = AFRAME.scenes[0].object3D; - filterHelpers(scene, false); - AFRAME.INSPECTOR.exporters.gltf.parse( - scene, - function (buffer) { - filterHelpers(scene, true); - const blob = new Blob([buffer], { type: 'application/octet-stream' }); - saveBlob(blob, sceneName + '.glb'); - }, - function (error) { - console.error(error); - }, - { binary: true } - ); + + const blob = await this.convertSceneToGLTFBlob(); + saveBlob(blob, sceneName + '.glb'); + STREET.notify.successMessage('3DStreet scene exported as glTF file.'); } catch (error) { STREET.notify.errorMessage(