wip: remotestorage

This commit is contained in:
Leon van Kammen 2025-05-09 18:19:10 +02:00
parent 076a6ca86a
commit 52f79a44c5
7 changed files with 358 additions and 85 deletions

View file

@ -86,6 +86,7 @@
<script src="./../../../dist/xrfragment.plugin.matrix.js"></script> <!-- matrix connectivity -->
<script src="./../../../dist/xrfragment.plugin.network.js"></script> <!-- matrix and webrtc chat/scene examples -->
<script src="./../../../dist/xrfragment.plugin.editor.js"></script> <!-- basic editor example -->
<script src="./../../../dist/xrfragment.plugin.remotestorage.js"></script><!-- basic remotestorage example -->
<script src="./../../../dist/xrfragment.plugin.frontend.css.js"></script> <!-- basic menu interface css -->
<script src="./../../../dist/xrfragment.plugin.frontend.js"></script> <!-- basic menu interface -->

3
make
View file

@ -123,10 +123,11 @@ build(){
cp src/3rd/js/plugin/frontend/\$editor.js dist/xrfragment.plugin.editor.js
cp src/3rd/js/plugin/frontend/css.js dist/xrfragment.plugin.frontend.css.js
jscat src/3rd/js/plugin/frontend/{snackbar,accessibility,\$menu,frontend,chatcommand/*,joystick,tab-to-href,remotestorage}.js > dist/xrfragment.plugin.frontend.js
jscat src/3rd/js/plugin/frontend/{snackbar,accessibility,\$menu,\$files,frontend,chatcommand/*,joystick,tab-to-href}.js > dist/xrfragment.plugin.frontend.js
jscat src/3rd/js/plugin/matrix/{matrix-crdt,matrix}.js > dist/xrfragment.plugin.matrix.js
jscat src/3rd/js/plugin/p2p/{trystero-torrent.min,trystero}.js > dist/xrfragment.plugin.p2p.js
jscat src/3rd/js/plugin/remotestorage/*.js > dist/xrfragment.plugin.remotestorage.js
# all in one
cat dist/aframe.min.js dist/aframe-blink-controls.min.js dist/xrfragment.aframe.js > dist/xrfragment.aframe.all.js

View file

@ -0,0 +1,113 @@
// reactive component for displaying the menu
filesComponent = (el) => new Proxy({
html: (data) => `
<style type="text/css">
#messages .msg.ui, #messages .msg.ui #files div {
border:none;
padding:0;
margin:0;
box-shadow:none;
}
</style>
<div class="ui envelope">
<div class="msg ui">
<div>
<div id="files">
<i class="gg-close-o" id="close" onclick="$files.visible = false"></i>
<br>
<div class="tab-frame">
${data.tabs.map( (t) =>
` <input type="radio" name="tab" id="${t.id}" ${t.id == "localFiles" ? 'checked="checked"' : '' }>
<label for="${t.id}">${t.name}</label>
`
).join('')
}
<br><br>
<div class="tab">
<div id="localFilesTab">
<button id="uploadFile" ><i class="gg-software-upload"></i> upload</button>
<button id="downloadfile"><i class="gg-software-download"></i> download</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`,
tabs: [
{name: "local files", id: "localFiles"}
],
show: false,
$localFiles: $localFiles = el.querySelector("#localFiles"),
toggle(state){
this.show = state !== undefined ? state : !this.show
},
init(opts){
this.decorateFileButton()
// create HTML element
el.innerHTML = this.html(this)
for( i in this ) el[i] = this[i]
this.toggle(this.show) // trigger visibility
document.querySelector("#messages").appendChild(el);
// setup input listeners
(['click']).map( (e) => el.addEventListener(e, (ev) => this[e] && this[e](ev.target.id,ev) ) )
// signal ready
setTimeout( () => {
document.dispatchEvent( new CustomEvent("$files:ready", {detail: {$files:this,xrf}}) )
},100)
return this
},
decorateFileButton: function(){
// rename button
document.querySelector("#load").setAttribute("value","3D file")
// decorate fileLoaders
window.frontend.fileLoaders = ( (fileLoaders) => {
this.fileLoader = fileLoaders
return () => this.toggle()
})( window.frontend.fileLoaders )
},
click(id,e){
switch(id){
case "icon":
case "more": return this.toggle(); break;
}
}
},
{
get(me,k,v){ return me[k] },
set(me,k,v){
me[k] = v
switch( k ){
case "show":{
el.style.display = v ? '' : 'none';
if( v ) $chat.visible = true
break;
}
case "tabs":{
el.innerHTML = $files.html($files)
break;
}
}
},
renderButtons: (data) => `${data.buttons.join('')}`
})
// reactify component!
document.addEventListener('$chat:ready', (e) => {
window.$files = filesComponent( document.createElement('div') ).init(e.detail)
})

View file

@ -740,6 +740,7 @@ document.head.innerHTML += `
box-sizing: border-box;
position: relative;
display: inline-block;
margin-right:5px;
transform: scale(var(--ggs,1)) translate(3px,3px);
width: 16px;
height: 6px;
@ -847,5 +848,47 @@ document.head.innerHTML += `
left: -11px;
top: -2px
}
.gg-software-upload {
box-sizing: border-box;
position: relative;
display: inline-block;
margin-right:5px;
transform: scale(var(--ggs, 1));
width: 16px;
height: 6px;
border: 2px solid;
border-top: 0;
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
margin-top: 8px;
}
.gg-software-upload::after {
content: "";
display: block;
box-sizing: border-box;
position: absolute;
width: 8px;
height: 8px;
border-left: 2px solid;
border-top: 2px solid;
transform: rotate(45deg);
left: 2px;
bottom: 4px;
}
.gg-software-upload::before {
content: "";
display: block;
box-sizing: border-box;
position: absolute;
border-radius: 3px;
width: 2px;
height: 10px;
background: currentColor;
left: 5px;
bottom: 3px;
}
</style>
`

View file

@ -0,0 +1,94 @@
// reactive component for displaying the menu
filesComponent = (el) => new Proxy({
html: `
<div class="ui envelope">
<div class="msg ui">
<div>
<div id="files">
<i class="gg-close-o" id="close" onclick="$files.visible = false"></i>
<br>
<div class="tab-frame">
<input type="radio" name="tab" id="login" checked="">
<label for="login">local files</label>
<div class="tab">
<div id="localFiles"><div><div>
<a class="badge ruler">Peer2Peer</a><br>
<table>
<tbody><tr>
<td>nickname</td>
<td>
<input type="text" id="nickname" placeholder="your nickname" maxlength="18" onkeydown="trystero.nickname = this.value">
</td>
</tr>
</tbody></table>
</div>
</div></div>
<table>
<tbody><tr>
<td></td>
<td>
<button id="connect" onclick="network.connect( $connections )">📡 Connect!</button>
</td>
</tr>
</tbody></table>
</div>
</div>
</div>
</div>
</div>
</div>
`,
collapsed: false,
$localFiles: $localFiles = el.querySelector("#localFiles"),
toggle(state){
this.collapsed = state !== undefined ? state : !this.collapsed
},
init(opts){
el.innerHTML = this.html
document.querySelector("#messages").appendChild(el);
(['click']).map( (e) => el.addEventListener(e, (ev) => this[e] && this[e](ev.target.id,ev) ) )
setTimeout( () => {
document.dispatchEvent( new CustomEvent("$files:ready", {detail: {$files:this,xrf}}) )
},100)
return this
},
click(id,e){
switch(id){
case "icon":
case "more": return this.toggle(); break;
}
this.toggle(false)
}
},
{
get(me,k,v){ return me[k] },
set(me,k,v){
me[k] = v
switch( k ){
case "buttons": el.querySelector("#buttons").innerHTML = this.renderButtons(me);
document.dispatchEvent( new CustomEvent("$menu:buttons:render", {detail: el.querySelector('.menu') }) )
break;
case "collapsed":
el.querySelector("#buttons").style.display = me.collapsed ? 'block' : 'none'
frontend.emit('$menu:collapse', v)
break;
}
},
renderButtons: (data) => `${data.buttons.join('')}`
})
// reactify component!
document.addEventListener('frontend:ready', (e) => {
window.$files = filesComponent( document.createElement('div') ).init(e.detail)
})

View file

@ -1,84 +0,0 @@
// this demonstrates the remotestorage aframe component
document.addEventListener('frontend:ready', (e) => {
function loadScript(cb){
let el = document.createElement("script")
el.setAttribute("defer","")
el.src = "https://unpkg.com/remotestoragejs@2.0.0-beta.7/release/remotestorage.js"
document.head.appendChild(el)
el = document.createElement("script")
el.src = "https://unpkg.com/remotestorage-widget@1.6.0/build/widget.js"
el.addEventListener('load', () => setTimeout(cb,2000) )
document.head.appendChild(el)
}
function addStyles(){
let el = document.createElement('style')
el.type = 'text/css'
el.innerHTML = `
#remotestorage-widget {
left: 0;
position: fixed;
display:block;
}
body.menu #remotestorage-widget{
top: 50px;
}
body #remotestorage-widget {
top: 0px;
}
`
document.head.appendChild(el)
}
function addButtons(){
const $widget = document.querySelector(".rs-widget")
debugger
const $btnLoad = document.createElement('button')
const $btnSave = document.createElement('button')
$btnLoad.innerText = 'load'
$btnSave.innerText = 'save'
$widget.appendChild($btnLoad)
$widget.appendChild($btnSave)
}
function init(){
let apis = {}
window.remoteStorage = new RemoteStorage({logging: true })
if( Object.keys(apis).length ) remoteStorage.setApiKeys(apis)
remoteStorage.on('connected', (e) => { console.log("connected") } )
//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.access.claim( `webxr`, 'rw'); // our data dir
remoteStorage.caching.enable( `/webxr/` ) // local-first, remotestorage-second
}
addStyles()
loadScript(init)
// decorate fileLoaders
window.frontend.fileLoaders = (function(fileLoaders){
return function(){
// show rs widgets
if( !document.querySelector("#remotestorage-widget") ){
let opts = {}
opts.modalBackdrop = false
widget = new window.Widget(window.remoteStorage, opts)
widget.attach();
addButtons()
}
//fileLoaders()
}
})( window.frontend.fileLoaders )
})

View file

@ -0,0 +1,105 @@
// this demonstrates the remotestorage aframe component
// reactive component for displaying the menu
remoteStorageComponent = (el) => new Proxy({
html: `
<style type="text/css">
body div#remotestorage-widget .rs-button{
background: #AAA;
}
</style>
<div id="remoteFilesTab">
<div id="rswidget"></div>
<br><br>
<button id="uploadFile" ><i class="gg-software-upload"></i> upload</button>
<button id="downloadfile"><i class="gg-software-download"></i> download</button>
</div>
`,
show: false,
toggle(state){
this.show = state !== undefined ? state : !this.show
},
init(opts){
// create HTML element
$files.tabs = $files.tabs.concat({id:"remoteFiles", name: "remote files"})
el.innerHTML = this.html
el.className = "tab"
this.toggle(this.show) // trigger visibility
document.querySelector("#files .tab-frame").appendChild(el);
// 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}}) )
},100)
this.loadScript( () => this.initRemoteStorage() )
return this
},
loadScript(cb){
let el = document.createElement("script")
el.setAttribute("defer","")
el.src = "https://unpkg.com/remotestoragejs@2.0.0-beta.7/release/remotestorage.js"
document.head.appendChild(el)
el = document.createElement("script")
el.src = "https://unpkg.com/remotestorage-widget@1.6.0/build/widget.js"
el.addEventListener('load', () => setTimeout(cb,2000) )
document.head.appendChild(el)
},
initRemoteStorage(){
let apis = {
dropbox: "4jc8nx1lbarp472"
}
window.remoteStorage = new RemoteStorage({logging: true })
if( Object.keys(apis).length ) remoteStorage.setApiKeys(apis)
remoteStorage.on('connected', (e) => { console.log("connected") } )
//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.access.claim( `webxr`, 'rw'); // our data dir
remoteStorage.caching.enable( `/webxr/` ) // local-first, remotestorage-second
// create widget
let opts = {}
opts.modalBackdrop = false
widget = new window.Widget(window.remoteStorage, opts)
widget.attach( "rswidget" );
},
click(id,e){
switch(id){
case "icon":
case "more": return this.toggle(); break;
}
this.toggle(false)
}
},
{
get(me,k,v){ return me[k] },
set(me,k,v){
me[k] = v
},
})
// reactify component!
document.addEventListener('$files:ready', (e) => {
window.$remotestorage = remoteStorageComponent( document.createElement('div') ).init(e.detail)
})