cleaner connection popup using tabs

This commit is contained in:
Leon van Kammen 2024-01-09 11:05:13 +00:00
parent 07a7db7f7b
commit a40d08d8de
12 changed files with 266 additions and 91 deletions

View file

@ -44,6 +44,7 @@ chatComponent = {
}
this.send({message: $chatline.value })
$chatline.value = ''
if( window.innerHeight < 600 ) $chatline.blur()
}
})
console.dir(this.scene)
@ -215,6 +216,11 @@ chatComponent.css = `
body.menu #messages{
top:50px;
}
#messages:hover {
background: #FFF5;
pointer-events:all;
overflow-y: auto;
}
#messages *{
pointer-events:all;
}

View file

@ -3,61 +3,80 @@ connectionsComponent = {
html: `
<div id="connections">
<i class="gg-close-o" id="close" onclick="$connections.toggle()"></i>
<div id="networking">
<h2>Network channels:</h2>
<table>
<tr>
<td>Webcam</td>
<td>
<select id="webcam"></select>
</td>
</tr>
<tr>
<td>Chat</td>
<td>
<select id="chatnetwork"></select>
</td>
</tr>
<tr>
<td>World sync</td>
<td>
<select id="scene"></select>
</td>
</tr>
<br>
<div class="tab-frame">
<input type="radio" name="tab" id="login" checked>
<label for="login">login</label>
<input type="radio" name="tab" id="networks">
<label for="networks">networks</label>
<input type="radio" name="tab" id="io">
<label for="io">devices</label>
<div class="tab">
<div id="settings"></div>
<table>
<tr>
<td></td>
<td>
<button id="connect" onclick="network.connect( $connections )">📡 Connect!</button>
</td>
</tr>
</table>
</div>
<div class="tab">
<div id="networking">
<table>
<tr>
<td>Webcam</td>
<td>
<select id="webcam"></select>
</td>
</tr>
<tr>
<td>Chat</td>
<td>
<select id="chatnetwork"></select>
</td>
</tr>
<tr>
<td>World sync</td>
<td>
<select id="scene"></select>
</td>
</tr>
</table>
</div>
</div>
<div class="tab">
<div id="devices">
<a class="badge ruler">Webcam and/or Audio</a>
<table>
<tr>
<td>Video</td>
<td>
<select id="videoInput"></select>
</td>
</tr>
<tr>
<td>Mic</td>
<td>
<select id="audioInput"></select>
</td>
</tr>
<tr style="display:none"> <!-- not used (for now) -->
<td>Audio</td>
<td>
<select id="audioOutput"></select>
</td>
</tr>
</table>
</div>
</div>
</div>
<div id="devices">
<a class="badge ruler">Webcam</a>
<table>
<tr>
<td>Video</td>
<td>
<select id="videoInput"></select>
</td>
</tr>
<tr>
<td>Mic</td>
<td>
<select id="audioInput"></select>
</td>
</tr>
<tr style="display:none"> <!-- not used (for now) -->
<td>Audio</td>
<td>
<select id="audioOutput"></select>
</td>
</tr>
</table>
</div>
<div id="settings"></div>
<table>
<tr>
<td></td>
<td>
<button id="connect" onclick="network.connect( $connections )">📡 Connect!</button>
</td>
</tr>
</table>
</div>
`,
@ -91,7 +110,6 @@ connectionsComponent = {
`<a class="btn" aria-label="button" aria-title="connect button" aria-description="use this to talk or chat with other people" id="meeting" onclick="$connections.show()"><i class="gg-user-add"></i>&nbsp;connect</a><br>`
]).concat($menu.buttons)
// hide networking settings if entering thru meetinglink
if( document.location.href.match(/meet=/) ) this.show()
setTimeout( () => document.dispatchEvent( new CustomEvent("$connections:ready", {detail: opts}) ), 1 )
@ -110,6 +128,7 @@ connectionsComponent = {
show(){
$chat.visible = true
// hide networking settings if entering thru meetinglink
$networking.style.display = document.location.href.match(/meet=/) ? 'none' : 'block'
if( !network.connected ){
if( el.parentElement ) el.parentElement.parentElement.remove()
@ -147,7 +166,7 @@ connectionsComponent = {
let opts = {webcam: $webcam.value, chatnetwork: $chatnetwork.value, scene: $scene.value }
this.update()
$settings.innerHTML = ''
this.forSelectedPluginsDo( (plugin) => $settings.appendChild( plugin.config(opts) ) )
this.forSelectedPluginsDo( (plugin) => $settings.appendChild( plugin.config({...opts,plugin}) ) )
this.renderInputs()
},
@ -276,9 +295,9 @@ connectionsComponent.css = `
}
#close{
display: block;
margin-top: 16px;
position: relative;
float: right;
margin-bottom: 7px;
}
#messages .msg.ui div.tab-frame > div.tab{ padding:25px 10px 5px 10px;}
</style>`

View file

@ -46,6 +46,7 @@ document.head.innerHTML += `
white-space:pre;
min-width: 45px;
box-shadow: 0px 0px 10px var(--xrf-box-shadow);
display:inline-block;
}
.xrf button:hover,
@ -148,7 +149,7 @@ document.head.innerHTML += `
.menu .btn{
.footer > .menu .btn{
display:inline-block;
background: var(--xrf-primary);
border-radius: 25px;
@ -287,7 +288,8 @@ document.head.innerHTML += `
text-align:right;
}
.badge {
.badge,
#messages .msg.ui div.badge{
display:inline-block;
color: var(--xrf-white);
font-weight: bold;
@ -356,6 +358,22 @@ document.head.innerHTML += `
top: 64px;
}
.right { float:right }
.left { float:left }
/*
* tabs
*/
div.tab-frame > input{ display:none;}
div.tab-frame > label{ display:block; float:left;padding:5px 10px; cursor:pointer; }
div.tab-frame > input:checked + label{ cursor:default; border-bottom:1px solid #888; font-weight:bold; }
div.tab-frame > div.tab{ display:none; padding:15px 10px 5px 10px;clear:left}
div.tab-frame > input:nth-of-type(1):checked ~ .tab:nth-of-type(1),
div.tab-frame > input:nth-of-type(2):checked ~ .tab:nth-of-type(2),
div.tab-frame > input:nth-of-type(3):checked ~ .tab:nth-of-type(3){ display:block;}
/*
* css icons from https://css.gg
*/

View file

@ -194,15 +194,16 @@ window.frontend = (opts) => new Proxy({
this.copyToClipboard( url )
// End of *TODO*
if( opts.notify ){
window.notify(`<h2>${ network.connected ? 'Meeting link ' : 'Link'} copied to clipboard!</h2> <br>Now share it with your friends ❤️<br>
window.notify(`<h2>${ network.connected ? 'Meeting link ' : 'Link'} copied to clipboard!</h2>
Now share it with your friends <br>
<canvas id="qrcode" width="121" height="121"></canvas><br>
<button onclick="frontend.download()"><i class="gg-software-download"></i>&nbsp;&nbsp;&nbsp;download scene file</button> <br>
<button onclick="alert('this might take a while'); $('a-scene').components.screenshot.capture('equirectangular')"><i class="gg-image"></i>&nbsp;&nbsp;download 360 screenshot</button> <br>
<a class="btn" target="_blank" href="https://github.com/coderofsalvation/xrfragment-helloworld"><i class="gg-serverless"></i>&nbsp;&nbsp;&nbsp;clone & selfhost this experience</a><br>
<br>
To embed this experience in your blog,<br>
copy/paste the following into your HTML:<br><input type="text" value="&lt;iframe src='${document.location.href}'&gt;&lt;/iframe&gt;" id="share"/>
<br>
<br>
`,{timeout:false})
}
// draw QR code

View file

@ -176,7 +176,7 @@ document.head.innerHTML += `
height: auto;
margin: 5px 0;
transition: all ease .5s;
border-radius: 3px;
border-radius: 15px;
box-shadow: 0 0 4px 0 var(--xrf-box-shadow);
right: 20px;
position: fixed;
@ -236,8 +236,8 @@ document.head.innerHTML += `
.js-snackbar__close {
cursor: pointer;
display: flex;
align-items: center;
padding: 0 10px;
align-items: top;
padding: 8px 13px 0px 0px;
user-select: none;
}

View file

@ -1,3 +1,3 @@
export { MatrixProvider } from "matrix-crdt";
//export * as Y from "yjs";
//export sdk from "matrix-js-sdk";
export * as Y from "yjs";
export * as sdk from "matrix-js-sdk";

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,37 @@
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <https://feross.org>
* @license MIT
*/
/*!
* content-type
* Copyright(c) 2015 Douglas Christopher Wilson
* MIT Licensed
*/
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
/*! queue-microtask. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
/*! simple-peer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

View file

@ -14,20 +14,28 @@ window.matrix = (opts) => new Proxy({
scene: true
},
channel: '#xrfragment-test:matrix.org',
server: 'https://matrix.org',
username:'',
auth: 'via password',
authkey: '',
client: null,
roomid: '',
html: {
generic: (opts) => `<div>
<a href="${opts.url}" target="_blank" class="badge ruler">matrix</a>
<table>
<div target="_blank" class="badge ruler">matrix <a onclick="frontend.plugin.matrix.info()"><i class="gg-info right"></i></a></div>
<table id="matrix">
<tr>
<td>channel</td>
<td>
<input type="text" id="channel" placeholder="#xrfragment:matrix.org"/>
<input type="text" id="channel" placeholder="#xrfragment:matrix.org" value="${opts.plugin.channel}"/>
</td>
</tr>
<tr>
<td>server</td>
<td>
<input type="text" id="server" placeholder="https://matrix.org"/>
<td>
<input type="text" id="server" placeholder="https://matrix.org" value="${opts.plugin.server}"/>
</td>
</tr>
<tr>
@ -48,7 +56,7 @@ window.matrix = (opts) => new Proxy({
<tr>
<td></td>
<td>
<input type="text" id="secret" placeholder="enter password"/>
<input type="password" id="secret" placeholder="enter password"/>
</td>
</tr>
</table>
@ -63,11 +71,59 @@ window.matrix = (opts) => new Proxy({
$connections.chatnetwork = $connections.chatnetwork.concat([this])
$connections.scene = $connections.scene.concat([this])
this.reactToConnectionHrefs()
this.nickname = localStorage.getItem("nickname") || `human${String(Math.random()).substr(5,4)}`
document.addEventListener('network.connect', (e) => this.connect(e.detail) )
document.addEventListener('network.init', () => {
let meeting = network.getMeetingFromUrl(document.location.href)
if( meeting.match(this.plugin.protocol) ){
this.parseLink( meeting )
}
})
},
connect(opts){
console.log("connecting "+this.plugin.name)
console.dir(opts)
this.channel = document.querySelector("#matrix input#channel").value
this.server = document.querySelector("#matrix input#server").value
this.username = document.querySelector("#matrix input#username").value
this.auth = document.querySelector("#matrix select#auth").value
let secret = document.querySelector("#matrix input#secret").value
document.querySelector("#matrix input#secret").value = ''
let credentials = { baseUrl: this.server }
if( this.auth == 'via access token'){
credentials.accessToken = secret
credentials.userId = this.username
}
this.client = Matrix.sdk.createClient(credentials)
if( this.auth == 'via password'){
this.client.login("m.login.password",{"user": this.username, password: secret})
.then( () => this.onMatrixConnect() )
.catch( () => window.notify("authentication was not succesful 😞"))
}else this.onMatrixConnect()
},
onMatrixConnect(){
// token: this.matrixclient.getAccessToken()
this.client.startClient()
client.once('sync', function(state, prevState, res) {
if( state == 'PREPARED' ) this.setupListeners()
else console.log("state: "+state)
});
},
setupListeners(){
let rooms = this.client.getRooms();
rooms.forEach(room => {
console.log(room);
});
this.client.on("Room.timeline", function(event, room, toStartOfTimeline) {
if( event.room_id && event.room_id == this.roomid ){
console.dir(event);
}
});
},
config(opts){
@ -81,28 +137,41 @@ window.matrix = (opts) => new Proxy({
this.el.querySelector('#auth').addEventListener('change', (e) => {
this.el.querySelector('#secret').setAttribute('placeholder', `enter ${e.target.value.replace(/.* /,'')}`)
})
window.notify(`${opts.name} is ${opts.description}, it is the hottest internet technology available at this moment.<br>Read more about it <a href="${opts.url}" target="_blank">here</a>.<br>You can basically make up a new channelname or use an existing one`)
return this.el
},
info(opts){
window.notify(`${this.plugin.name} is ${this.plugin.description}, it is the hottest internet technology available at this moment.<br>Read more about it <a href="${this.plugin.url}" target="_blank">here</a>.<br>You can basically make up a new channelname or use an existing one`)
},
parseLink(url){
if( !url.match(this.plugin.protocol) ) return
let parts = url.replace(this.plugin.protocol,'').split("/")
if( parts[0] == 'r' ){ // room
let server = parts.split("/")[1].replace(/:.*/,'')
let channel = parts.split("/")[1].replace(/.*:/,'')
$connections.show()
$connections.selectedChatnetwork = this.plugin.name
$connections.selectedScene = this.plugin.name
this.el.querySelector('#channel').value = `#${channel}:${server}`
this.el.querySelector('#server').value = server
console.log("configured matrix")
}
return false
},
reactToConnectionHrefs(){
xrf.addEventListener('href', (opts) => {
let {mesh} = opts
if( !opts.click ) return
if( mesh.userData.href.match(this.protocol) ){
let parts = mesh.userData.href.replace(this.plugin.protocol,'')
if( parts[0] == 'r' ){ // room
let server = parts.split("/")[1].replace(/:.*/,'')
let channel = parts.split("/")[1].replace(/.*:/,'')
$connections.show()
$connections.selectedChatnetwork = this.plugin.name
$connections.selectedScene = this.plugin.name
this.el.querySelector('#channel').value = `#${channel}:${server}`
this.el.querySelector('#server').value = server
console.log("configured matrix")
}
}else window.notify("malformed connection URI: "+mesh.userData.href)
this.parseLink(mesh.userData.href)
let href = mesh.userData.href
let isLocal = href[0] == '#'
let isTeleport = href.match(/(pos=|http:)/)
if( isLocal && !isTeleport && this.href.send ) this.href.send({href})
})
let hashvars = xrf.URI.parse( document.location.hash )
if( hashvars.meet ) this.parseLink(hashvars.meet.string)
}
},
{

View file

@ -0,0 +1,18 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
# nativeBuildInputs is usually what you want -- tools you need to run
nativeBuildInputs = with pkgs.buildPackages; [
nodejs
haxe
mmark
xml2rfc
esbuild
];
shellHooks = ''
bash
'';
}

View file

@ -3,7 +3,7 @@ module.exports = {
output: {
library: {
type: "umd",
name: "matrix"
name: "Matrix"
},
filename: "matrix-crdt.js",
path: require('path').resolve(__dirname, '.')

View file

@ -14,12 +14,12 @@ window.trystero = (opts) => new Proxy({
html: {
generic: (opts) => `<div>
<a href="${opts.url}" target="_blank" class="badge ruler">P2P</a>
<div target="_blank" class="badge ruler">Peer2Peer WebRTC<a onclick="frontend.plugin.trystero.info()"><i class="gg-info right"></i></a></div>
<table>
<tr>
<td>nickname</td>
<td>
<input type="text" id="nickname" placeholder="your nickname" maxlength="18"/>
<input type="text" id="nickname" placeholder="your nickname" maxlength="18" onkeydown="trystero.nickname = this.value"/>
</td>
</tr>
</table>
@ -151,6 +151,8 @@ window.trystero = (opts) => new Proxy({
},
async initWebcam(){
if( !$connections.$audioInput.value && !$connections.$videInput.value ) return // nothing to do
// get a local audio stream from the microphone
this.selfStream = await navigator.mediaDevices.getUserMedia({
audio: $connections.$audioInput.value,
@ -216,13 +218,16 @@ window.trystero = (opts) => new Proxy({
opts = {...opts, ...this.plugin }
this.el = document.createElement('div')
this.el.innerHTML = this.html.generic(opts)
// window.notify(`${opts.name} is ${opts.description} <br>by using a serverless technology called <a href="https://webrtc.org/" target="_blank">webRTC</a> via <a href="${opts.url}" target="_blank">trystero</a>.<br>You can basically make up your own channelname or choose an existing one.<br>Use this for hasslefree anonymous meetings.`)
this.el.querySelector('#nickname').value = this.nickname
this.el.querySelector('#nickname').addEventListener('change', (e) => localStorage.setItem("nickname",e.target.value) )
// resolve ip
return this.el
},
info(opts){
window.notify(`${this.plugin.name} is ${this.plugin.description} <br>by using a serverless technology called <a href="https://webrtc.org/" target="_blank">webRTC</a> via <a href="${this.plugin.url}" target="_blank">trystero</a>.<br>You can basically make up your own channelname or choose an existing one.<br>Use this for hasslefree anonymous meetings.`)
},
parseLink(url){
if( !url.match(this.plugin.protocol) ) return
let parts = url.replace(this.plugin.protocol,'').split("/")
@ -250,6 +255,8 @@ window.trystero = (opts) => new Proxy({
let isTeleport = href.match(/(pos=|http:)/)
if( isLocal && !isTeleport && this.href.send ) this.href.send({href})
})
let hashvars = xrf.URI.parse( document.location.hash )
if( hashvars.meet ) this.parseLink(hashvars.meet.string)
}
},