work in progress [might break]

This commit is contained in:
Leon van Kammen 2023-12-27 21:31:42 +00:00
parent fceaf07c1f
commit 21ef1ded86
7 changed files with 220 additions and 84 deletions

View file

@ -135,7 +135,8 @@ chatComponent.css = `
button#showchat{ button#showchat{
z-index: 1500; z-index: 1500;
position: fixed; position: fixed;
bottom: 20px; bottom: 24px;
height: 34px;
left: 20px; left: 20px;
width: 48%; width: 48%;
background: white; background: white;
@ -158,6 +159,8 @@ chatComponent.css = `
border:none; border:none;
width:90%; width:90%;
box-sizing:border-box; box-sizing:border-box;
height: 24px;
font-size: var(--xrf-font-size-2);
} }
#messages{ #messages{
position: absolute; position: absolute;
@ -207,6 +210,9 @@ chatComponent.css = `
#messages .msg.multiline { #messages .msg.multiline {
padding: 2px 14px; padding: 2px 14px;
} }
#messages .msg.config {
background:#
}
#messages button { #messages button {
text-decoration:none; text-decoration:none;
margin: 0px 15px 10px 0px; margin: 0px 15px 10px 0px;
@ -223,15 +229,6 @@ chatComponent.css = `
#messages,#chatbar,#chatbar *, #messages *{ #messages,#chatbar,#chatbar *, #messages *{
} }
#messages input{
padding: 7px 15px;
border-block: none;
border-inline: none;
border: 1px solid #888;
background: var(--xrf-lighter-gray);
height: 18px;
max-width:168px;
}
#messages button.emoticon, #messages button.emoticon,
#messages .btn.emoticon { #messages .btn.emoticon {
@ -253,7 +250,7 @@ chatComponent.css = `
background:#EEE; background:#EEE;
} }
nomargin{ .nomargin{
margin:0; margin:0;
} }
</style>` </style>`

View file

@ -26,21 +26,25 @@ connectionsComponent = {
<div id="settings"></div> <div id="settings"></div>
<br> <br>
<button id="connect" onclick="$connections.connect()">📡 Connect!</button> <button id="connect" onclick="$connections.connect()">📡 Connect!</button>
<br> <br><br>
</div> </div>
`, `,
init: (el) => new Proxy({ init: (el) => new Proxy({
webcam: [{plugin:{name:"No thanks"},config(){}}], webcam: [{plugin:{name:"No thanks"},config: () => document.createElement('div')}],
chatnetwork: [{plugin:{name:"No thanks"},config(){}}], chatnetwork: [{plugin:{name:"No thanks"},config: () => document.createElement('div')}],
scene: [{plugin:{name:"No thanks"},config(){}}], scene: [{plugin:{name:"No thanks"},config: () => document.createElement('div')}],
selectedWebcam: '',
selectedChatnetwork:'',
selectedScene: '',
$webcam: $webcam = el.querySelector("#webcam"), $webcam: $webcam = el.querySelector("#webcam"),
$chatnetwork: $chatnetwork = el.querySelector("#chatnetwork"), $chatnetwork: $chatnetwork = el.querySelector("#chatnetwork"),
$scene: $scene = el.querySelector("#scene"), $scene: $scene = el.querySelector("#scene"),
$settings: $settings = el.querySelector("#settings"), $settings: $settings = el.querySelector("#settings"),
$connect: $connect = el.querySelector("#connect"),
install(opts){ install(opts){
this.opts = opts this.opts = opts
@ -49,49 +53,62 @@ connectionsComponent = {
$webcam.addEventListener('change', () => this.renderSettings() ) $webcam.addEventListener('change', () => this.renderSettings() )
$chatnetwork.addEventListener('change', () => this.renderSettings() ) $chatnetwork.addEventListener('change', () => this.renderSettings() )
$scene.addEventListener('change', () => this.renderSettings() ) $scene.addEventListener('change', () => this.renderSettings() )
}, },
show(){ show(){
$chat.visible = true $chat.visible = true
if( !network.connected ){ if( !network.connected ){
if( el.parentElement ) el.parentElement.remove()
$chat.send({message:"", el}) $chat.send({message:"", el})
this.renderSettings() this.renderSettings()
if( !network.meetinglink ){ // set default
$webcam.value = 'Peer2Peer'
$chatnetwork.value = 'Peer2Peer'
$scene.value = 'Peer2Peer'
}
}else $chat.send({message:"you are already connected, refresh page to create new connection",class:['info']}) }else $chat.send({message:"you are already connected, refresh page to create new connection",class:['info']})
}, },
connect(){ connect(){
navigator.share({ this.update()
url: 'https://foo.com', this.webcam.selected.connect({webcam:true})
title: 'your meeting link' this.chatnetwork.selected.connect({chat:true})
}) this.scene.selected.connect({scene:true})
this.$connect.setAttribute('disabled','disabled')
this.$connect.classList.add('disabled')
window.notify("🪐 connecting to awesomeness..")
},
update(){
this.selectedWebcam = $webcam.value
this.selectedChatnetwork = $chatnetwork.value
this.selectedScene = $scene.value
},
forSelectedPluginsDo(cb){
// this function looks weird but it's handy to prevent the same plugins rendering duplicate configurations
let plugins = {}
let select = (name) => (o) => o.plugin.name == name ? plugins[ o.plugin.name ] = o : ''
this.webcam.find( select(this.selectedWebcam) )
this.chatnetwork.find( select(this.selectedChatnetwork) )
this.scene.find( select(this.selectedScene) )
for( let i in plugins ){
try{ cb(plugins[i]) }catch(e){ console.error(e) }
}
}, },
renderSettings(){ renderSettings(){
let opts = {webcam: $webcam.value, chatnetwork: $chatnetwork.value, scene: $scene.value } let opts = {webcam: $webcam.value, chatnetwork: $chatnetwork.value, scene: $scene.value }
let theWebcam = this.webcam.find( (p) => p.plugin.name == $webcam.value ) this.update()
let theChatnetwork = this.chatnetwork.find( (p) => p.plugin.name == $chatnetwork.value )
let theScene = this.scene.find( (p) => p.plugin.name == $scene.value )
$settings.innerHTML = '' $settings.innerHTML = ''
$settings.appendChild(theWebcam.config(opts)) this.forSelectedPluginsDo( (plugin) => {
if( theChatnetwork.plugin.name != theWebcam.plugin.name ) $settings.appendChild( theChatnetwork.config(opts) ) console.log("configuring "+plugin.plugin.name)
if( theScene.plugin.name != theWebcam.plugin.name && theScene.plugin.name != theChatnetwork.plugin.name ) console.dir(plugin)
$settings.appendChild( scene.config(opts) ) $settings.appendChild( plugin.config(opts) )
})
}, },
randomName(){
var names = []
let add = (s) => s.length < 6 && !s.match(/[0-9$]/) && !s.match(/_/) ? names.push(s) : false
for ( var i in window ) add(i)
for ( var i in Object.prototype ) add(i)
for ( var i in Function.prototype ) add(i)
for ( var i in Array.prototype ) add(i)
for ( var i in String.prototype ) add(i)
var a = names[Math.floor(Math.random() * names.length)];
var b = names[Math.floor(Math.random() * names.length)];
var c = names[Math.floor(Math.random() * names.length)];
return String(`${a}-${b}-${c}`).toLowerCase()
}
},{ },{
get(data,k,v){ return data[k] }, get(data,k,v){ return data[k] },

View file

@ -352,6 +352,13 @@ let utils = {
QR.canvas = document.getElementById('qrcode') QR.canvas = document.getElementById('qrcode')
QR.draw( url, QR.canvas ) QR.draw( url, QR.canvas )
},0) },0)
// mobile share
if( typeof navigator.share != 'undefined'){
navigator.share({
url,
title: 'your meeting link'
})
}
} }
} }
@ -444,7 +451,7 @@ $menu.css = `
--xrf-lighter-gray: #e4e2fb96; --xrf-lighter-gray: #e4e2fb96;
--xrf-font-sans-serif: system-ui, -apple-system, segoe ui, roboto, ubuntu, helvetica, cantarell, noto sans, sans-serif; --xrf-font-sans-serif: system-ui, -apple-system, segoe ui, roboto, ubuntu, helvetica, cantarell, noto sans, sans-serif;
--xrf-font-monospace: menlo, monaco, lucida console, liberation mono, dejavu sans mono, bitstream vera sans mono, courier new, monospace, serif; --xrf-font-monospace: menlo, monaco, lucida console, liberation mono, dejavu sans mono, bitstream vera sans mono, courier new, monospace, serif;
--xrf-font-size-0: 12px; --xrf-font-size-0: 11px;
--xrf-font-size-1: 14px; --xrf-font-size-1: 14px;
--xrf-font-size-2: 17px; --xrf-font-size-2: 17px;
--xrf-font-size-3: 21px; --xrf-font-size-3: 21px;
@ -805,19 +812,42 @@ $menu.css = `
font-size: var(--xrf-font-size-0); font-size: var(--xrf-font-size-0);
margin-right:10px margin-right:10px
} }
.ruler{
width:97%;
margin:7px 0px;
}
a.badge { a.badge {
text-decoration:none; text-decoration:none;
} }
.xrf select{ .xrf select{
min-width: 200px;oborder-inline: none; min-width: 200px;
border-inline: none;
border-inline: none; border-inline: none;
border-block: none; border-block: none;
border: 1px solid #AAA; border: 3px solid var(--xrf-primary);
height: 31px;
border-radius: 5px; border-radius: 5px;
background: var(--xrf-lighter-gray); background: none;
border-radius:30px;
}
.xrf select,
.xrf option{
padding: 0px 16px; padding: 0px 16px;
min-width: 200px;
height: 35px;
}
.xrf input{
border-radius:30px;
padding: 7px 15px;
border-block: none;
border-inline: none;
border: 1px solid #888;
background: transparent;
height: 18px;
max-width:168px;
} }
.xrf table tr td { .xrf table tr td {
@ -825,10 +855,18 @@ $menu.css = `
text-align:right; text-align:right;
} }
.xrf table tr td:nth-child(1){ .xrf table tr td:nth-child(1){
min-width:95px; min-width:115px;
height:40px;
padding-right:15px; padding-right:15px;
} }
.xrf small{
font-size: var(--xrf-font-size-0);
}
.disabled{
opacity:0.5
}
</style> </style>
` `

View file

@ -17,6 +17,7 @@ window.accessibility = (opts) => new Proxy({
settings(){ settings(){
this.toggle() // *TODO* should show settings screen this.toggle() // *TODO* should show settings screen
if( this.enabled ) window.notify(`accessibility boosted, click <a href="#">here</a> to tweak settings`)
}, },
speak(str, override){ speak(str, override){

View file

@ -3,6 +3,7 @@
window.network = (opts) => new Proxy({ window.network = (opts) => new Proxy({
connected: false, connected: false,
meetinglink: "",
peers: {}, peers: {},
plugin: {}, plugin: {},
opts, opts,

View file

@ -1,10 +1,13 @@
window.matrix = (opts) => new Proxy({ window.matrix = (opts) => new Proxy({
el: null, // HTML element
plugin:{ plugin:{
type: 'network', type: 'network',
name: '[matrix] channel', name: '[matrix] channel',
description: '[matrix] is a standardized decentralized privacy-friendly protocol', description: 'a standardized decentralized privacy-friendly protocol',
url: 'https://matrix.org', url: 'https://matrix.org',
protocol: 'matrix://',
video: false, video: false,
audio: false, audio: false,
chat: true, chat: true,
@ -12,14 +15,45 @@ window.matrix = (opts) => new Proxy({
}, },
html: { html: {
generic: (opts) => `<table> generic: (opts) => `<div>
<a href="${opts.url}" target="_blank" class="badge ruler">matrix</a>
<table>
<tr> <tr>
<td><a href="${opts.url}" target="_blank" class="badge">matrix</a></td> <td>channel</td>
<td> <td>
<input type="text" id="channelname" placeholder="channel name"/> <input type="text" id="channel" placeholder="#xrfragment:matrix.org"/>
</td>
</tr>
<tr>
<td>server</td>
<td>
<input type="text" id="server" placeholder="https://matrix.org"/>
</td>
</tr>
<tr>
<td>user</td>
<td>
<input type="text" id="username" placeholder="@you:matrix.org"/>
</td>
</tr>
<tr>
<td>authenticate</td>
<td>
<select>
<option>via password</option>
<option>via access token</option>
</select>
</td>
</tr>
<tr>
<td></td>
<td>
<input type="text" id="secret" placeholder="password or token"/>
</td> </td>
</tr> </tr>
</table> </table>
<small style="display:inline-block;float:right">Support for Oauth / OpenID is <a href="https://matrix.org/blog/#openid-connect" target="_blank">in progress</a></small>
<br><br>
</div> </div>
` `
}, },
@ -29,20 +63,44 @@ window.matrix = (opts) => new Proxy({
network.plugin['matrix'] = this network.plugin['matrix'] = this
$connections.chatnetwork = $connections.chatnetwork.concat([this]) $connections.chatnetwork = $connections.chatnetwork.concat([this])
$connections.scene = $connections.scene.concat([this]) $connections.scene = $connections.scene.concat([this])
this.reactToConnectionHrefs()
},
connect(opts){
console.log("connecting "+this.plugin.name)
console.dir(opts)
}, },
config(opts){ config(opts){
opts = {...opts, ...this.plugin } opts = {...opts, ...this.plugin }
let el = document.createElement('div') this.el = document.createElement('div')
let html = this.html.generic(opts) let html = this.html.generic(opts)
for( let i in opts ){ for( let i in opts ){
if( this.html[i] ) html += this.html[i](opts) if( this.html[i] ) html += this.html[i](opts)
} }
el.innerHTML = html this.el.innerHTML = html
el.querySelector('.badge').addEventListener('mouseover', () => { 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`)
window.notify(`${opts.name} is ${opts.description}.<br>You can basically make up a new channelname or use an existing one`) return this.el
},
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
$connections.$chatnetwork.value = this.plugin.name
$connections.$scene.value = this.plugin.name
$connections.show()
let server = parts.split("/")[1].replace(/:.*/,'')
let channel = parts.split("/")[1].replace(/.*:/,'')
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)
}) })
return el
} }
}, },
{ {
@ -51,9 +109,9 @@ window.matrix = (opts) => new Proxy({
set(data,k,v){ set(data,k,v){
let from = data[k] let from = data[k]
data[k] = v data[k] = v
switch( k ){ //switch( k ){
default: matrix.opts.scene.dispatchEvent({type:`matrix.${k}.change`, from, to:v}) // default: matrix.opts.scene.dispatchEvent({type:`matrix.${k}.change`, from, to:v})
} //}
} }
}) })

View file

@ -5,6 +5,7 @@ window.trystero = (opts) => new Proxy({
name: 'Peer2Peer', name: 'Peer2Peer',
description: 'P2P using WebRTC over bittorrent for signaling & encryption', description: 'P2P using WebRTC over bittorrent for signaling & encryption',
url: 'https://github.com/dmotz/trystero', url: 'https://github.com/dmotz/trystero',
protocol: 'trystero://',
video: true, video: true,
audio: true, audio: true,
chat: true, chat: true,
@ -12,23 +13,12 @@ window.trystero = (opts) => new Proxy({
}, },
html: { html: {
generic: (opts) => `<table id="trystero"> generic: (opts) => ``
<tr>
<td>
<button class="emoticon" id="randomize" aria-label="button" aria-title="randomize" onclick="$('#trystero #channelname').value = $connections.randomName()">🎲</button>
<a href="${opts.url}" target="_blank" class="badge nomargin">P2P</a>
</td>
<td>
<input type="text" id="channelname" placeholder="channel name"/>
</td>
</tr>
</table>
</div>
`
}, },
handle: null, // { selfId: .... } when connected handle: null, // { selfId: .... } when connected
link: null, ip: null,
roomid: '',
selfId: null, selfId: null,
connected: false, connected: false,
names: {}, names: {},
@ -41,17 +31,32 @@ window.trystero = (opts) => new Proxy({
$connections.webcam = $connections.webcam.concat([this]) $connections.webcam = $connections.webcam.concat([this])
$connections.chatnetwork = $connections.chatnetwork.concat([this]) $connections.chatnetwork = $connections.chatnetwork.concat([this])
$connections.scene = $connections.scene.concat([this]) $connections.scene = $connections.scene.concat([this])
this.reactToConnectionHrefs()
}, },
connect(){ connect(opts){
// embedded https://github.com/dmotz/trystero (trystero-torrent.min.js build) // embedded https://github.com/dmotz/trystero (trystero-torrent.min.js build)
console.log("connecting "+this.plugin.name)
console.dir(opts) console.dir(opts)
this.handle = joinRoom( room.config, room.link ) //this.handle = joinRoom( room.config, room.link )
this.send({message:'📡 [trystero] opening P2P WebRTC-channel via bittorrent',class:['info']}) //this.send({message:'📡 [trystero] opening P2P WebRTC-channel via bittorrent',class:['info']})
}, },
send(opts){ $chat.send({...opts, source: 'trystero'}) }, send(opts){ $chat.send({...opts, source: 'trystero'}) },
createLink(opts){
this.link = document.location.href.replace(/#.*/,'')
if( this.link.match(/localhost/) ){
fetch('https://api.duckduckgo.com/?q=my+ip&format=json')
.then( (res) => res.json() )
.then( (res) => {
const ipRegex = /Your IP address is ([0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)/g;
const ip = ipRegex.exec(res.Answer)[1]
this.link = this.link.replace(/localhost/, ip )
})
}
},
config(opts){ config(opts){
opts = {...opts, ...this.plugin } opts = {...opts, ...this.plugin }
let el = document.createElement('div') let el = document.createElement('div')
@ -59,12 +64,31 @@ window.trystero = (opts) => new Proxy({
for( let i in opts ){ for( let i in opts ){
if( this.html[i] ) html += this.html[i](opts) if( this.html[i] ) html += this.html[i](opts)
} }
el.innerHTML = html 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.`)
el.querySelector('#randomize').addEventListener('mouseover', () => window.notify("generate random channel name") ) // resolve ip
el.querySelector('.badge').addEventListener('mouseover', () => { if( !this.link ) this.createLink(opts)
window.notify(`${opts.name} is ${opts.description} <br>by using a serverless technology called <a href="${opts.url}" target="_blank">trystero</a>.<br>You can basically make up your own channelname or choose an existing one`)
})
return el return el
},
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,'')
console.dir(parts)
if( parts[0] == 'r' ){ // room
this.roomid = parts.split("/")[1].replace(/:.*/,'')
this.server = parts.split("/")[1].replace(/.*:/,'')
if( this.server != 'bittorrent' ) window.notify("only bittorrent is supported for trystero (for now) :/")
$connections.show()
$connections.$webcam.value = this.plugin.name
$connections.$chatnetwork.value = this.plugin.name
$connections.$scene.value = this.plugin.name
console.log("configured trystero")
}
}else window.notify("malformed connection URI: "+mesh.userData.href)
})
} }
}, },
@ -74,9 +98,9 @@ window.trystero = (opts) => new Proxy({
set(data,k,v){ set(data,k,v){
let from = data[k] let from = data[k]
data[k] = v data[k] = v
switch( k ){ //switch( k ){
default: trystero.opts.scene.dispatchEvent({type:`trystero.${k}.change`, from, to:v}) // default: elcene.dispatchEvent({type:`trystero.${k}.change`, from, to:v})
} //}
} }
}) })