diff --git a/src/3rd/js/plugin/frontend/css.js b/src/3rd/js/plugin/frontend/css.js
index 5dd51b7..a44c976 100644
--- a/src/3rd/js/plugin/frontend/css.js
+++ b/src/3rd/js/plugin/frontend/css.js
@@ -364,8 +364,9 @@ document.head.innerHTML += `
opacity:0.5
}
- body.menu .js-snackbar__wrapper {
- top: 64px;
+ body.menu .js-snackbar__wrapper,
+ body.topbar .js-snackbar__wrapper {
+ transform: translateY(40px);
}
.transcript{
diff --git a/src/3rd/js/plugin/frontend/frontend.js b/src/3rd/js/plugin/frontend/frontend.js
index 995d189..5faf924 100644
--- a/src/3rd/js/plugin/frontend/frontend.js
+++ b/src/3rd/js/plugin/frontend/frontend.js
@@ -43,7 +43,8 @@ window.frontend = (opts) => new Proxy({
.setupCapture()
.setupUserHints()
.setupNetworkListeners()
- .hidetopbarWhenMenuCollapse()
+ .setupTopbar()
+ .hideTopbarWhenMenuCollapse()
.hideUIWhenNavigating()
window.notify = this.notify
@@ -68,6 +69,16 @@ window.frontend = (opts) => new Proxy({
return this
},
+ setupTopbar(){
+ // setup topbar handle
+ this.$topbar = this.el.querySelector("#topbar")
+ this.$topbar.toggle = (state) => {
+ this.$topbar.style.display = state ? 'block' : 'none'
+ document.body.classList[ state ? 'add' : 'remove' ](['topbar'])
+ }
+ return this
+ },
+
setupIframeUrlHandler(){
// allow iframe to open url
window.addEventListener('message', (event) => {
@@ -175,9 +186,9 @@ window.frontend = (opts) => new Proxy({
return this
},
- hidetopbarWhenMenuCollapse(){
+ hideTopbarWhenMenuCollapse(){
// hide topbar when menu collapse button is pressed
- document.addEventListener('$menu:collapse', (e) => this.el.querySelector("#topbar").style.display = e.detail === true ? 'block' : 'none')
+ document.addEventListener('$menu:collapse', (e) => this.$topbar.toggle( e.detail === true ) )
return this
},
@@ -250,9 +261,9 @@ window.frontend = (opts) => new Proxy({
window.frontend.emit("notify",opts)
},
- download(){
+ download(cb){
- function exportScene(model,ext,file){
+ function exportScene(model,ext,file,cb){
document.dispatchEvent( new CustomEvent('frontend.export',{detail:{ scene: model.scene,ext}}) )
xrf.emit('export', {scene: model.scene, ext})
@@ -266,15 +277,17 @@ window.frontend = (opts) => new Proxy({
return false;
}
+ if( !cb ) cb = download
+
// setup exporters
let defaultExporter = THREE.GLTFExporter
if( !xrf.loaders['gltf'].exporter ) xrf.loaders['gltf'].exporter = defaultExporter
if( !xrf.loaders['glb'].exporter ) xrf.loaders['glb'].exporter = defaultExporter
const exporter = new xrf.loaders[ext].exporter()
- debugger
+
exporter.parse(
model.scene,
- function ( glb ) { download(glb, `${file}`) }, // ready
+ function ( glb ) { cb(glb, `${file}`) }, // ready
function ( error ) { console.error(error) }, // error
{
binary:true,
@@ -293,9 +306,15 @@ window.frontend = (opts) => new Proxy({
const Loader = xrf.loaders[fileExt]
loader = new Loader().setPath( dir )
notify('exporting scene
please wait..')
- loader.load(url, (model) => {
- exportScene(model,fileExt,file)
- }, console.error )
+
+ fetch(url, { method: 'HEAD' })
+ .then( (res) => { // url exists
+ if( res.ok ){
+ loader.load( url, (model) => exportScene(model,fileExt,file,cb), console.error )
+ }else{
+ exportScene(xrf.model,fileExt,file,cb)
+ }
+ })
},
updateHashPosition(randomize){
diff --git a/src/3rd/js/plugin/remotestorage/remotestorage-module-webXR.js b/src/3rd/js/plugin/remotestorage/remotestorage-module-webXR.js
new file mode 100644
index 0000000..3728eda
--- /dev/null
+++ b/src/3rd/js/plugin/remotestorage/remotestorage-module-webXR.js
@@ -0,0 +1,30 @@
+const WebXR = { name: 'webxr', builder: function(privateClient, publicClient) {
+ return {
+ exports: {
+ add: function(data,opts) {
+ if( !data || !opts.filename || !opts.mimetype) throw 'webxr.add() needs filedata + filename + mimetype'
+ const client = opts.client = opts.public ? publicClient : privateClient;
+ return client.storeFile(opts.mimetype, opts.filename,data)
+ },
+
+ getListing: function(a,opts){
+ opts = opts || {}
+ const client = opts.public ? publicClient : privateClient;
+ return client.getListing(a)
+ },
+
+ getFile: function(file,opts){
+ opts = opts || {}
+ const client = opts.public ? publicClient : privateClient;
+ return client.getFile(file)
+ },
+
+ remove: function(file,opts){
+ opts = opts || {}
+ const client = opts.public ? publicClient : privateClient;
+ return client.remove(file)
+ }
+
+ }
+ }
+}};
diff --git a/src/3rd/js/plugin/remotestorage/remotestorage-module-webXRF.js b/src/3rd/js/plugin/remotestorage/remotestorage-module-webXRF.js
deleted file mode 100644
index 2b11695..0000000
--- a/src/3rd/js/plugin/remotestorage/remotestorage-module-webXRF.js
+++ /dev/null
@@ -1,7 +0,0 @@
-const WebXRF = { name: 'webxr', builder: function(privateClient, publicClient) {
- return {
- exports: {
- addScene: function() {}
- }
- }
-}};
diff --git a/src/3rd/js/plugin/remotestorage/remotestorage.js b/src/3rd/js/plugin/remotestorage/remotestorage.js
index c277557..2a5315e 100644
--- a/src/3rd/js/plugin/remotestorage/remotestorage.js
+++ b/src/3rd/js/plugin/remotestorage/remotestorage.js
@@ -14,33 +14,68 @@ remoteStorageComponent = (el) => new Proxy({
#files .rs-button{
background:#CCC;
}
+ #files input {
+ min-width:345px;
+ }
+ #files #buttons select,
+ #files #buttons button{
+ width:255px;
+ max-width:unset;
+ }
+ #files #listing{
+ margin-bottom:15px;
+ }
+ #files #delete{
+ display: none;
+ transform: translate(10px, 5px);
+ }
+ #links{
+ display:none
+ }
`),
connected: false,
+ $listing: false,
+ $links: false,
init(opts){
// create HTML element
- $files.tabs = $files.tabs.concat({id:"remoteFiles", name: "online"})
+ $files.tabs = $files.tabs.concat({id:"remoteFiles", name: "remote storage"})
el.innerHTML = this.html(this)
el.className = "tab"
document.querySelector("#files .tab-frame").appendChild(el);
+ // setup references
+ this.$listing = document.querySelector('#files .tab-frame select#listing');
+ this.$links = document.querySelector('#files .tab-frame #links');
+
// setup input listeners
(['click']).map( (e) => el.addEventListener(e, (ev) => typeof this[e] == 'function' && this[e](ev.target.id,ev) ) )
-
+
// signal ready
setTimeout( () => {
document.dispatchEvent( new CustomEvent("remotestorage:ready", {detail: {$files:this,xrf}}) )
@@ -68,21 +103,27 @@ remoteStorageComponent = (el) => new Proxy({
dropbox: "4jc8nx1lbarp472"
}
const modules = []
- if( typeof WebXRF != undefined ){
- modules.push(WebXRF) // defined in remotestorage-module-webXRF.js
+ if( typeof WebXR != undefined ){
+ modules.push(WebXR) // defined in remotestorage-module-webXRF.js
}
window.remoteStorage = new RemoteStorage({logging: true, modules })
if( Object.keys(apis).length ) remoteStorage.setApiKeys(apis)
- remoteStorage.on('connected', (e) => { this.connected = true })
- //remoteStorage.on('network-offline', (e) => this.el.sceneEl.emit('remoteStorage.network-offline',e) )
- //remoteStorage.on('network-online', (e) => this.el.sceneEl.emit('remoteStorage.network-online',e) )
- //remoteStorage.on('error', (e) => this.el.sceneEl.emit('remoteStorage.error',e) )
- remoteStorage.on('ready', (e) => { } )
+ remoteStorage.on('not-connected', (e) => { this.connected = false })
+ remoteStorage.on('ready', (e) => { })
+ remoteStorage.on('connected', (e) => {
+ this.connected = true
+ // force open dialog and click remote-tab
+ frontend.$topbar.toggle(true)
+ $files.toggle(true)
+ document.querySelector("#files input#remoteFiles").click()
- remoteStorage.access.claim( `webxr`, 'rw'); // our data dir
- remoteStorage.caching.enable( `/webxr/` ) // local-first, remotestorage-second
- remoteStorage.caching.enable( `/public/webxr/` ) // local-first, remotestorage-second
+ this.updateFiles()
+ })
+
+ remoteStorage.access.claim( `webxr`, 'rw'); // our data dir
+ remoteStorage.caching.enable( `/webxr/` ) // local-first, remotestorage-second
+ remoteStorage.caching.enable( `/public/webxr/` ) // local-first, remotestorage-second
// create widget
let opts = {}
@@ -93,6 +134,103 @@ remoteStorageComponent = (el) => new Proxy({
},
+ savePrivate(){
+ frontend.download( (data,filename) => {
+ filename = prompt('save-as filename', filename)
+ remoteStorage.webxr.add(data,{public:false,filename,mimetype: 'model/glb-binary'})
+ .then( () => window.notify(`saved webxr/${filename} to remote storage`) )
+ .catch( (e) => {
+ console.error(e)
+ window.notify(`failed to save webxr/${filename} to remote storage`,{status:'error'})
+ })
+ })
+ },
+
+ savePublic(){
+ frontend.download( (data,filename) => {
+ filename = prompt('save-as filename', filename)
+ opts = {public:true,filename,mimetype: 'model/glb-binary'}
+ remoteStorage.webxr.add(data,opts)
+ .then( (res) => {
+ window.notify(`saved webxr/${filename} to remote storage`)
+ const link = opts.client.storage.remote.href + opts.client.base + filename
+ const linkWebView = document.location.href.replace(/(\?|#).*/,'') + `?${link}`
+ this.$links.querySelector("#file").value = link
+ this.$links.querySelector("#webviewer").value = linkWebView
+ this.$links.style.display = 'block'
+ })
+ .catch( (e) => {
+ console.error(e)
+ window.notify(`failed to save webxr/${filename} to remote storage`,{status:'error'})
+ })
+ })
+ },
+
+ openPrivate(file){
+ if( !confirm(`teleport to ${file} on your remotestorage?`) ) return
+ remoteStorage.webxr.getFile(file)
+ .then( (res) => {
+ for( var i in xrf.loaders ){
+ if( file.replace(/.*\./).match(i) ){
+ xrf.navigator.URI.file = '' // bypass cached file (easy refresh same file for testing)
+ xrf.navigator.to(file,null, (new xrf.loaders[i]()), res.data)
+ return
+ }
+ }
+ throw 'unknown filetype: '+file
+ })
+ .catch( (e) => {
+ console.error(e)
+ window.notify("could not load webxr/"+file)
+ })
+ },
+
+ remove(){
+ const currentFile = el.querySelector('select#listing').value
+ if( confirm("remove "+currentFile+" from remote storage?") ){
+ remoteStorage.webxr.remove(currentFile)
+ .then( () => {
+ window.notify(`removed webxr/${filename} from remote storage`)
+ this.updateFiles()
+ })
+ .catch( (e) => {
+ console.error(e)
+ window.notify(`could not webxr/${filename} from remote storage`,{status:'error'})
+ })
+ }
+ },
+
+ updateFiles(){
+ remoteStorage.webxr.getListing()
+ .then( (listing) => {
+
+ this.$listing.innerHTML = '' // empty
+
+ const addOption = (value,text) => {
+ let opt = document.createElement("option")
+ opt.text = text
+ opt.value = value
+ this.$listing.appendChild(opt)
+ }
+
+ addOption("","--- your experiences ---")
+ for( let file in listing ){
+ if( file.match(/\.(glb|gltf|usd|obj|col|fbx)$/) ) addOption(file,file)
+ }
+
+ // autoload selection
+ if( !this.updateFiles.autoload ){ // run once
+ this.$listing.addEventListener('change', () => {
+ if( this.$listing.options.selectedIndex > 0 ) this.openPrivate(this.$listing.value)
+ document.querySelector("#delete").style.display = this.$listing.options.selectedIndex == 0 ? "none" : "inline-block"
+ this.$links.style.display = 'none'
+ })
+ this.updateFiles.autoload = true
+ }
+ })
+
+ },
+
click(id,e){
//switch(id){
// case "more": return this.toggle(); break;