matrix now connects [crdt disabled]

This commit is contained in:
Leon van Kammen 2024-01-10 22:01:21 +01:00
parent a40d08d8de
commit a170aabfb6
10 changed files with 296 additions and 119 deletions

View file

@ -64,10 +64,11 @@
`,{timeout:false}) `,{timeout:false})
}) })
</script> </script>
<script src="./../../../dist/aframe-blink-controls.min.js"></script> <!-- teleporting using controllers --> <script src="./../../../dist/aframe-blink-controls.min.js"></script> <!-- teleporting using controllers -->
<script src="./../../../dist/xrfragment.plugin.frontend.js"></script> <!-- menu + chat interface --> <script src="https://cdn.jsdelivr.net/npm/handy-work@3.1.9/build/handy-controls.min.js"></script> <!-- hand controllers -->
<script src="./../../../dist/xrfragment.plugin.p2p.js"></script> <!-- serverless p2p connectivity --> <script src="./../../../dist/xrfragment.plugin.p2p.js"></script> <!-- serverless p2p connectivity -->
<script src="./../../../dist/xrfragment.plugin.matrix.js"></script> <!-- matrix connectivity --> <script src="./../../../dist/xrfragment.plugin.matrix.js"></script> <!-- matrix connectivity -->
<script src="./../../../dist/xrfragment.plugin.frontend.js"></script> <!-- menu + chat interface -->
</body> </body>
</html> </html>

View file

@ -52,7 +52,9 @@ window.AFRAME.registerComponent('xrf', {
el.setAttribute("class","floor") el.setAttribute("class","floor")
$('a-scene').appendChild(el) $('a-scene').appendChild(el)
}) })
blinkControls.components['blink-controls'].update({collisionEntities:true}) let com = blinkControls.components['blink-controls']
if( com ) com.update({collisionEntities:true})
else console.warn("xrfragments: blink-controls is not mounted, please run manually: $('[blink-controls]).components['blink-controls'].update({collisionEntities:true})")
} }
}) })

View file

@ -19,6 +19,8 @@ chatComponent = {
visibleChatbar: false, visibleChatbar: false,
messages: [], messages: [],
username: '', // configured by 'network.connected' event
$videos: el.querySelector("#videos"), $videos: el.querySelector("#videos"),
$messages: el.querySelector("#messages"), $messages: el.querySelector("#messages"),
$chatline: el.querySelector("#chatline"), $chatline: el.querySelector("#chatline"),
@ -37,6 +39,8 @@ chatComponent = {
initListeners(){ initListeners(){
let {$chatline} = this let {$chatline} = this
$chatline.addEventListener('click', (e) => this.inform() )
$chatline.addEventListener('keydown', (e) => { $chatline.addEventListener('keydown', (e) => {
if (e.key == 'Enter' ){ if (e.key == 'Enter' ){
if( $chatline.value[0] != '/' ){ if( $chatline.value[0] != '/' ){
@ -47,14 +51,34 @@ chatComponent = {
if( window.innerHeight < 600 ) $chatline.blur() if( window.innerHeight < 600 ) $chatline.blur()
} }
}) })
document.addEventListener('network.connected', (e) => {
if( e.detail.username ) this.username = e.detail.username
})
console.dir(this.scene) console.dir(this.scene)
}, },
inform(){
if( !this.inform.informed && (this.inform.informed = true) ){
window.notify("Here you can update your statusline.<br>Protocols like [Matrix] allow you to view the full transcript<br>in a dedicated client like <a href='https://element.io' target='_blank'>element.io</a>")
}
},
toggle(){ toggle(){
this.visible = !this.visible this.visible = !this.visible
if( this.visible && window.meeting.status == 'offline' ) window.meeting.start(this.opts) if( this.visible && window.meeting.status == 'offline' ) window.meeting.start(this.opts)
}, },
hyphenate(str){
return String(str).replace(/[^a-zA-Z0-9]/g,'-')
},
// sending messages to the #messages div
// every user can post maximum one msg at a time
// it's more like a 'status' which is more friendly
// for accessibility reasons
// for a fullfledged chat/transcript see matrix clients
send(opts){ send(opts){
let {$messages} = this let {$messages} = this
opts = { linebreak:true, message:"", class:[], ...opts } opts = { linebreak:true, message:"", class:[], ...opts }
@ -74,7 +98,12 @@ chatComponent = {
br.classList.add.apply(br.classList, opts.class) br.classList.add.apply(br.classList, opts.class)
div.classList.add.apply(div.classList, opts.class.concat(["envelope"])) div.classList.add.apply(div.classList, opts.class.concat(["envelope"]))
} }
if( !opts.from && !msg.className.match(/(info|guide)/) ) msg.classList.add('self') if( !opts.from && !msg.className.match(/(info|guide)/) ){
let frag = xrf.URI.parse(document.location.hash)
opts.from = this.username
if( frag.pos ) opts.pos = frag.pos.string
msg.classList.add('self')
}
if( opts.from ){ if( opts.from ){
nick.className = "user" nick.className = "user"
nick.innerText = opts.from+' ' nick.innerText = opts.from+' '
@ -86,6 +115,15 @@ chatComponent = {
} }
} }
div.appendChild(msg) div.appendChild(msg)
// force one message per user
if( opts.from ){
div.id = this.hyphenate(opts.from)
let oldMsg = $messages.querySelector(`#${div.id}`)
if( oldMsg ) oldMsg.remove()
}
// remove after timeout
if( opts.timeout ) setTimeout( (div) => div.remove(), opts.timeout, div )
// finally add the message on top
$messages.appendChild(div) $messages.appendChild(div)
if( opts.linebreak ) div.appendChild(br) if( opts.linebreak ) div.appendChild(br)
$messages.scrollTop = $messages.scrollHeight // scroll down $messages.scrollTop = $messages.scrollHeight // scroll down
@ -196,12 +234,15 @@ chatComponent.css = `
max-width:unset; max-width:unset;
} }
#messages{ #messages{
display: flex;
flex-direction: column-reverse;
align-items: flex-start;
position: absolute; position: absolute;
transition:1s; transition:1s;
top: 0px; top: 0px;
left: 0; left: 0;
bottom: 130px; bottom: 49px;
padding: 15px; padding: 20px;
overflow:hidden; overflow:hidden;
pointer-events:none; pointer-events:none;
transition:1s; transition:1s;
@ -217,9 +258,7 @@ chatComponent.css = `
top:50px; top:50px;
} }
#messages:hover { #messages:hover {
background: #FFF5;
pointer-events:all; pointer-events:all;
overflow-y: auto;
} }
#messages *{ #messages *{
pointer-events:all; pointer-events:all;
@ -229,7 +268,7 @@ chatComponent.css = `
background: #fff; background: #fff;
display: inline-block; display: inline-block;
padding: 1px 17px; padding: 1px 17px;
border-radius: 20px 0px 20px 20px; border-radius: 20px;
color: #000c; color: #000c;
margin-bottom: 10px; margin-bottom: 10px;
line-height:23px; line-height:23px;
@ -239,8 +278,8 @@ chatComponent.css = `
border: 1px solid #0002; border: 1px solid #0002;
} }
#messages .msg.self{ #messages .msg.self{
border-radius: 0px 20px 20px 20px; border-radius: 20px;
background:var(--xrf-primary); background:var(--xrf-box-shadow);
} }
#messages .msg.self, #messages .msg.self,
#messages .msg.self div{ #messages .msg.self div{
@ -259,12 +298,16 @@ chatComponent.css = `
} }
#messages .msg a { #messages .msg a {
text-decoration:underline; text-decoration:underline;
color: #EEE; color: var(--xrf-primary);
font-weight:bold; font-weight:bold;
transition:1s; transition:0.3s;
}
#messages .msg.info a,
#messages .ruler a{
color:#FFF;
} }
#messages .msg a:hover{ #messages .msg a:hover{
color:#FFF; color:#000;
} }
#messages .msg.ui, #messages .msg.ui,
#messages .msg.ui div{ #messages .msg.ui div{

View file

@ -82,9 +82,9 @@ connectionsComponent = {
init: (el) => new Proxy({ init: (el) => new Proxy({
webcam: [{plugin:{name:"No thanks"},config: () => document.createElement('div')}], webcam: [{profile:{name:"No thanks"},config: () => document.createElement('div')}],
chatnetwork: [{plugin:{name:"No thanks"},config: () => document.createElement('div')}], chatnetwork: [{profile:{name:"No thanks"},config: () => document.createElement('div')}],
scene: [{plugin:{name:"No thanks"},config: () => document.createElement('div')}], scene: [{profile:{name:"No thanks"},config: () => document.createElement('div')}],
selectedWebcam: '', selectedWebcam: '',
selectedChatnetwork:'', selectedChatnetwork:'',
@ -126,21 +126,27 @@ connectionsComponent = {
} }
}, },
show(){ show(opts){
$chat.visible = true opts = opts || {}
// hide networking settings if entering thru meetinglink if( opts.hide ){
$networking.style.display = document.location.href.match(/meet=/) ? 'none' : 'block' el.parentElement.parentElement.style.display = 'none'
if( !network.connected ){
if( el.parentElement ) el.parentElement.parentElement.remove()
$chat.send({message:"", el, class:['ui']})
if( !network.meetinglink ){ // set default
$webcam.value = 'Peer2Peer'
$chatnetwork.value = 'Peer2Peer'
$scene.value = 'Peer2Peer'
}
this.renderSettings()
}else{ }else{
$chat.send({message:"you are already connected, refresh page to create new connection",class:['info']}) $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()
document.querySelector('body > .xrf').appendChild(el)
$chat.send({message:"", el, class:['ui']})
if( !network.meetinglink ){ // set default
$webcam.value = 'Peer2Peer'
$chatnetwork.value = 'Peer2Peer'
$scene.value = 'Peer2Peer'
}
this.renderSettings()
}else{
$chat.send({message:"you are already connected, refresh page to create new connection",class:['info']})
}
} }
}, },
@ -153,7 +159,7 @@ connectionsComponent = {
forSelectedPluginsDo(cb){ forSelectedPluginsDo(cb){
// this function looks weird but it's handy to prevent the same plugins rendering duplicate configurations // this function looks weird but it's handy to prevent the same plugins rendering duplicate configurations
let plugins = {} let plugins = {}
let select = (name) => (o) => o.plugin.name == name ? plugins[ o.plugin.name ] = o : '' let select = (name) => (o) => o.profile.name == name ? plugins[ o.profile.name ] = o : ''
this.webcam.find( select(this.selectedWebcam) ) this.webcam.find( select(this.selectedWebcam) )
this.chatnetwork.find( select(this.selectedChatnetwork) ) this.chatnetwork.find( select(this.selectedChatnetwork) )
this.scene.find( select(this.selectedScene) ) this.scene.find( select(this.selectedScene) )
@ -223,25 +229,18 @@ connectionsComponent = {
}) })
}, },
reactToNetwork(){ reactToNetwork(){ // *TODO* move to network?
document.addEventListener('network.connected', () => {
console.log("network.connected")
window.notify("🪐 connected to awesomeness..")
$chat.visibleChatbar = true
$chat.send({message:`🎉 connected!`,class:['info']})
})
document.addEventListener('network.connect', () => { document.addEventListener('network.connect', () => {
console.log("network.connect") this.show({hide:true})
el.parentElement.classList.add('connecthide')
window.notify("🪐 connecting to awesomeness..")
$connect.innerText = 'connecting..' $connect.innerText = 'connecting..'
}) })
document.addEventListener('network.disconnect', () => { document.addEventListener('network.disconnect', () => {
window.notify("🪐 disconnecting..")
$connect.innerText = 'disconnecting..' $connect.innerText = 'disconnecting..'
setTimeout( () => $connect.innerText = 'connect', 1000) setTimeout( () => $connect.innerText = 'connect', 1000)
if( !window.accessibility.enabled ) $chat.visibleChatbar = false
}) })
} }
},{ },{
@ -250,9 +249,9 @@ connectionsComponent = {
set(data,k,v){ set(data,k,v){
data[k] = v data[k] = v
switch( k ){ switch( k ){
case "webcam": $webcam.innerHTML = `<option>${data[k].map((p)=>p.plugin.name).join('</option><option>')}</option>`; break; case "webcam": $webcam.innerHTML = `<option>${data[k].map((p)=>p.profile.name).join('</option><option>')}</option>`; break;
case "chatnetwork": $chatnetwork.innerHTML = `<option>${data[k].map((p)=>p.plugin.name).join('</option><option>')}</option>`; break; case "chatnetwork": $chatnetwork.innerHTML = `<option>${data[k].map((p)=>p.profile.name).join('</option><option>')}</option>`; break;
case "scene": $scene.innerHTML = `<option>${data[k].map((p)=>p.plugin.name).join('</option><option>')}</option>`; break; case "scene": $scene.innerHTML = `<option>${data[k].map((p)=>p.profile.name).join('</option><option>')}</option>`; break;
case "selectedScene": $scene.value = v; data.renderSettings(); break; case "selectedScene": $scene.value = v; data.renderSettings(); break;
case "selectedChatnetwork": $chatnetwork.value = v; data.renderSettings(); break; case "selectedChatnetwork": $chatnetwork.value = v; data.renderSettings(); break;
case "selectedWebcam": { case "selectedWebcam": {
@ -297,7 +296,7 @@ connectionsComponent.css = `
display: block; display: block;
position: relative; position: relative;
float: right; float: right;
margin-bottom: 7px; top: 16px;
} }
#messages .msg.ui div.tab-frame > div.tab{ padding:25px 10px 5px 10px;} #messages .msg.ui div.tab-frame > div.tab{ padding:25px 10px 5px 10px;}
</style>` </style>`

View file

@ -83,16 +83,19 @@ window.accessibility = (opts) => new Proxy({
document.addEventListener('network.send', (e) => { document.addEventListener('network.send', (e) => {
let opts = e.detail let opts = e.detail
opts.message = opts.message || '' opts.message = opts.message || ''
if( opts.class && ~opts.class.indexOf('info') ) opts.message = `info: ${opts.message}`
this.speak(opts.message) this.speak(opts.message)
}) })
opts.xrf.addEventListener('pos', (opts) => { opts.xrf.addEventListener('pos', (opts) => {
if( this.enabled ){ if( this.enabled ){
$chat.send({message: this.posToMessage(opts) }) $chat.send({message: this.posToMessage(opts) })
network.send({message: this.posToMessage(opts), class:["info","guide"]})
}
if( opts.frag.pos.string.match(/,/) ){
network.pos = opts.frag.pos.string
}else{
network.posName = opts.frag.pos.string
} }
network.send({message: this.posToMessage(opts), class:["info","guide"]})
network.pos = opts.frag.pos.string
}) })
}, },

View file

@ -535,7 +535,7 @@ document.head.innerHTML += `
position: relative; position: relative;
display: inline-block; display: inline-block;
-moz-transform: rotate(-45deg) scale(var(--ggs,1)); -moz-transform: rotate(-45deg) scale(var(--ggs,1));
transform: translate(4px,-5px) rotate(-45deg) scale(var(--ggs,1)); transform: translate(4px,1px) rotate(-45deg) scale(var(--ggs,1));
width: 8px; width: 8px;
height: 2px; height: 2px;
background: currentColor; background: currentColor;
@ -701,14 +701,14 @@ document.head.innerHTML += `
box-sizing: border-box; box-sizing: border-box;
position: relative; position: relative;
display: inline-block; display: inline-block;
transform: scale(var(--ggs,1)) translate(3px,2px); transform: scale(var(--ggs,1)) translate(3px,9px);
width: 16px; width: 16px;
height: 6px; height: 6px;
border: 2px solid; border: 2px solid;
border-top: 0; border-top: 0;
border-bottom-left-radius: 2px; border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px; border-bottom-right-radius: 2px;
margin-top: 8px line-height:15px;
} }
.gg-software-download::after { .gg-software-download::after {
content: ""; content: "";

View file

@ -41,6 +41,7 @@ window.frontend = (opts) => new Proxy({
.setupIframeUrlHandler() .setupIframeUrlHandler()
.setupCapture() .setupCapture()
.setupUserHints() .setupUserHints()
.setupNetworkListeners()
.hidetopbarWhenMenuCollapse() .hidetopbarWhenMenuCollapse()
window.notify = this.notify window.notify = this.notify
@ -92,6 +93,37 @@ window.frontend = (opts) => new Proxy({
return this return this
}, },
setupNetworkListeners(){
document.addEventListener('network.connect', (e) => {
console.log("network.connect")
window.notify("🪐 connecting to awesomeness..")
$chat.send({message:`🪐 connecting to awesomeness..`,class:['info'], timeout:5000})
})
document.addEventListener('network.connected', (e) => {
window.notify("🪐 connected to awesomeness..")
$chat.visibleChatbar = true
$chat.send({message:`🎉 ${e.detail.plugin.profile.name||''} connected!`,class:['info'], timeout:5000})
})
document.addEventListener('network.disconnect', () => {
window.notify("🪐 disconnecting..")
})
document.addEventListener('network.info', (e) => {
window.notify(e.detail.message)
$chat.send({...e.detail, class:['info'], timeout:5000})
})
document.addEventListener('network.error', (e) => {
window.notify(e.detail.message)
$chat.send({...e.detail, class:['info'], timeout:5000})
})
return this
},
hidetopbarWhenMenuCollapse(){ hidetopbarWhenMenuCollapse(){
// hide topbar when menu collapse button is pressed // 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.el.querySelector("#topbar").style.display = e.detail === true ? 'block' : 'none')
@ -185,12 +217,14 @@ window.frontend = (opts) => new Proxy({
}, },
share(opts){ share(opts){
opts = opts || {notify:true,qr:true,share:true} opts = opts || {notify:true,qr:true,share:true,linkonly:false}
if( network.connected && !document.location.hash.match(/meet=/) ){ if( network.connected && !document.location.hash.match(/meet=/) ){
let p = $connections.chatnetwork.find( (p) => p.plugin.name == $connections.selectedChatnetwork ) let p = $connections.chatnetwork.find( (p) => p.profile.name == $connections.selectedChatnetwork )
if( p.link ) document.location.hash += `&meet=${p.link}` console.dir(p)
if( p.link ) document.location.hash = `${ network.pos ? `pos=${network.pos}` : ''}&meet=${p.link}`
} }
let url = window.location.href let url = window.location.href
if( opts.linkonly ) return url
this.copyToClipboard( url ) this.copyToClipboard( url )
// End of *TODO* // End of *TODO*
if( opts.notify ){ if( opts.notify ){

View file

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

View file

@ -2,7 +2,7 @@ window.matrix = (opts) => new Proxy({
el: null, // HTML element el: null, // HTML element
plugin:{ profile:{
type: 'network', type: 'network',
name: '[Matrix]', name: '[Matrix]',
description: 'a standardized decentralized privacy-friendly protocol', description: 'a standardized decentralized privacy-friendly protocol',
@ -11,9 +11,13 @@ window.matrix = (opts) => new Proxy({
video: false, video: false,
audio: false, audio: false,
chat: true, chat: true,
scene: true scene: false
}, },
useWebcam: false,
useChat: false,
useScene: false,
channel: '#xrfragment-test:matrix.org', channel: '#xrfragment-test:matrix.org',
server: 'https://matrix.org', server: 'https://matrix.org',
username:'', username:'',
@ -22,6 +26,10 @@ window.matrix = (opts) => new Proxy({
client: null, client: null,
roomid: '', roomid: '',
// Matrix-CRDT
ydoc: null,
yhref: null,
html: { html: {
generic: (opts) => `<div> generic: (opts) => `<div>
<div target="_blank" class="badge ruler">matrix <a onclick="frontend.plugin.matrix.info()"><i class="gg-info right"></i></a></div> <div target="_blank" class="badge ruler">matrix <a onclick="frontend.plugin.matrix.info()"><i class="gg-info right"></i></a></div>
@ -60,7 +68,6 @@ window.matrix = (opts) => new Proxy({
</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>
` `
@ -69,65 +76,139 @@ window.matrix = (opts) => new Proxy({
init(){ init(){
frontend.plugin['matrix'] = this frontend.plugin['matrix'] = this
$connections.chatnetwork = $connections.chatnetwork.concat([this]) $connections.chatnetwork = $connections.chatnetwork.concat([this])
$connections.scene = $connections.scene.concat([this])
this.reactToConnectionHrefs() this.reactToConnectionHrefs()
this.nickname = localStorage.getItem("nickname") || `human${String(Math.random()).substr(5,4)}` this.nickname = localStorage.getItem("nickname") || `human${String(Math.random()).substr(5,4)}`
document.addEventListener('network.connect', (e) => this.connect(e.detail) ) document.addEventListener('network.connect', (e) => this.connect(e.detail) )
document.addEventListener('network.init', () => { document.addEventListener('network.init', () => {
let meeting = network.getMeetingFromUrl(document.location.href) let meeting = network.getMeetingFromUrl(document.location.href)
if( meeting.match(this.plugin.protocol) ){ if( meeting.match(this.profile.protocol) ){
this.parseLink( meeting ) this.parseLink( meeting )
} }
}) })
}, },
connect(opts){ connect(opts){
console.log("connecting "+this.plugin.name) if( opts.selectedWebcam == this.profile.name ) this.useWebcam = true
this.channel = document.querySelector("#matrix input#channel").value if( opts.selectedChatnetwork == this.profile.name ) this.useChat = true
this.server = document.querySelector("#matrix input#server").value if( opts.selectedScene == this.profile.name ) this.useScene = true
this.username = document.querySelector("#matrix input#username").value if( this.useWebcam || this.useScene || this.useChat ){
this.auth = document.querySelector("#matrix select#auth").value this.link = `matrix://r/${this.channel.replace(/^#/,'')}`
let secret = document.querySelector("#matrix input#secret").value console.log("connecting "+this.profile.name)
document.querySelector("#matrix input#secret").value = '' 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 } let credentials = { baseUrl: this.server }
if( this.auth == 'via access token'){ if( this.auth == 'via access token'){
credentials.accessToken = secret credentials.accessToken = secret
credentials.userId = this.username credentials.userId = this.username
}
this.client = Matrix.sdk.createClient(credentials)
// Extra configuration needed for certain matrix-js-sdk
// calls to work without calling sync start functions
this.client.canSupportVoip = false;
this.client.clientOpts = {
lazyLoadMembers: true,
};
// auth
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()
} }
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(){ onMatrixConnect(){
// token: this.matrixclient.getAccessToken() // token: this.matrixclient.getAccessToken()
this.client.startClient() frontend.emit('network.info',{message:'🛰 syncing with Matrix (might take a while)',plugin:this})
client.once('sync', function(state, prevState, res) { frontend.emit('network.connected',{plugin:this,username: this.username})
if( state == 'PREPARED' ) this.setupListeners() this.client.startClient({ initialSyncLimit: 4 }) // show last 4 messages?
else console.log("state: "+state) // get roomId of channel
this.client.getRoomIdForAlias(this.channel)
.then( (o) => {
this.roomId = o.room_id
this.setupListeners()
})
.catch((e) => {
frontend.emit('network.error',{plugin:this,message:`channel ${this.channel} cannot be joined: `+String(e)})
})
},
setupCRDT(){
// Create a new Y.Doc and connect the MatrixProvider
this.ydoc = new Matrix.Y.Doc();
const provider = new Matrix.MatrixProvider(this.ydoc, this.client, {
type: "alias",
alias: this.channel
}); });
provider.initialize();
this.yhref = this.ydoc.getText('href')
// observe changes of the sum
this.yhref.observe((event) => {
console.log("new yhref: " + yhref.toString );
});
debugger
},
getNormalizedName(){
return this.channel.replace(/(^#|:.*)/,'')
}, },
setupListeners(){ setupListeners(){
let rooms = this.client.getRooms(); if( this.useChat ) this.setupChat()
rooms.forEach(room => { //if( this.useScene ) this.setupCRDT() /* throws weird errors, perhaps matrix-sdk-js is too new */
console.log(room); return this
}); },
this.client.on("Room.timeline", function(event, room, toStartOfTimeline) {
if( event.room_id && event.room_id == this.roomid ){ setupChat(){
console.dir(event);
// receive receivemessages
this.client.on("Room.timeline", (event, room, toStartOfTimeline) => {
if (event.getType() !== "m.room.message") return // only print messages
if( room.roomId == this.roomId ){
$chat.send({message: event.getContent().body, from: event.getSender()})
} }
}); });
// send chatmessages
document.addEventListener('network.send', (e) => {
let {message} = e.detail
let href = frontend.share({linkonly:true}) // get our meetinglink in there
// convert to absolute links
if( message.match(/href="#/) ){
message = message.replace(/href=['"]#.*?['"]/g, `href="${href}"`)
}else{
let pos = []
if( network.posName ){
pos.push(`<a href="${href}">#${network.posName}</a>`)
}
if( network.pos ){
pos.push(`<a href="${href}">#${`pos=${network.pos}`}</a>`)
}
if( pos.length ) message += `<br>📍 ${pos.join(' ')}`
}
let content = {
body: message,
format: "org.matrix.custom.html",
formatted_body: message,
msgtype:"m.text"
}
this.client.sendEvent( this.roomId, "m.room.message", content, "", (err,res) => console.error(err) )
})
}, },
config(opts){ config(opts){
opts = {...opts, ...this.plugin } opts = {...opts, ...this.profile }
this.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 ){
@ -141,18 +222,18 @@ window.matrix = (opts) => new Proxy({
}, },
info(opts){ 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`) window.notify(`${this.profile.name} is ${this.profile.description}, it is the hottest internet technology available at this moment.<br>Read more about it <a href="${this.profile.url}" target="_blank">here</a>.<br>You can basically make up a new channelname or use an existing one`)
}, },
parseLink(url){ parseLink(url){
if( !url.match(this.plugin.protocol) ) return if( !url.match(this.profile.protocol) ) return
let parts = url.replace(this.plugin.protocol,'').split("/") let parts = url.replace(this.profile.protocol,'').split("/")
if( parts[0] == 'r' ){ // room if( parts[0] == 'r' ){ // room
let server = parts.split("/")[1].replace(/:.*/,'') let server = parts.split("/")[1].replace(/:.*/,'')
let channel = parts.split("/")[1].replace(/.*:/,'') let channel = parts.split("/")[1].replace(/.*:/,'')
$connections.show() $connections.show()
$connections.selectedChatnetwork = this.plugin.name $connections.selectedChatnetwork = this.profile.name
$connections.selectedScene = this.plugin.name $connections.selectedScene = this.profile.name
this.el.querySelector('#channel').value = `#${channel}:${server}` this.el.querySelector('#channel').value = `#${channel}:${server}`
this.el.querySelector('#server').value = server this.el.querySelector('#server').value = server
console.log("configured matrix") console.log("configured matrix")
@ -160,6 +241,15 @@ window.matrix = (opts) => new Proxy({
return false return false
}, },
t0nkr0nst0nreateLink(opts){
let hash = document.location.hash
if( !this.link ){
const meeting = network.getMeetingFromUrl(document.location.href)
this.link = meeting.match("matrix://") ? meeting : ''
}
if( !hash.match('meet=') ) document.location.hash += `${hash.length > 1 ? '&' : '#'}meet=${this.link}`
},
reactToConnectionHrefs(){ reactToConnectionHrefs(){
xrf.addEventListener('href', (opts) => { xrf.addEventListener('href', (opts) => {
let {mesh} = opts let {mesh} = opts
@ -168,7 +258,11 @@ window.matrix = (opts) => new Proxy({
let href = mesh.userData.href let href = mesh.userData.href
let isLocal = href[0] == '#' let isLocal = href[0] == '#'
let isTeleport = href.match(/(pos=|http:)/) let isTeleport = href.match(/(pos=|http:)/)
if( isLocal && !isTeleport && this.href.send ) this.href.send({href}) console.log("href detected")
if( isLocal && !isTeleport && this.client && this.useScene ){
console.log("sending href")
this.yhref.set( document.location.hash )
}
}) })
let hashvars = xrf.URI.parse( document.location.hash ) let hashvars = xrf.URI.parse( document.location.hash )
if( hashvars.meet ) this.parseLink(hashvars.meet.string) if( hashvars.meet ) this.parseLink(hashvars.meet.string)

View file

@ -1,9 +1,9 @@
window.trystero = (opts) => new Proxy({ window.trystero = (opts) => new Proxy({
plugin:{ profile:{
type: 'network', type: 'network',
name: 'Peer2Peer', name: 'Peer2Peer',
description: 'P2P using WebRTC over bittorrent for signaling & encryption', description: 'WebRTC over bittorrent for signaling & encryption',
url: 'https://github.com/dmotz/trystero', url: 'https://github.com/dmotz/trystero',
protocol: 'trystero://', protocol: 'trystero://',
video: true, video: true,
@ -14,7 +14,7 @@ window.trystero = (opts) => new Proxy({
html: { html: {
generic: (opts) => `<div> generic: (opts) => `<div>
<div target="_blank" class="badge ruler">Peer2Peer WebRTC<a onclick="frontend.plugin.trystero.info()"><i class="gg-info right"></i></a></div> <div target="_blank" class="badge ruler">Peer2Peer<a onclick="frontend.plugin.trystero.info()"><i class="gg-info right"></i></a></div>
<table> <table>
<tr> <tr>
<td>nickname</td> <td>nickname</td>
@ -62,7 +62,7 @@ window.trystero = (opts) => new Proxy({
document.addEventListener('network.connect', (e) => this.connect(e.detail) ) document.addEventListener('network.connect', (e) => this.connect(e.detail) )
document.addEventListener('network.init', () => { document.addEventListener('network.init', () => {
let meeting = network.getMeetingFromUrl(document.location.href) let meeting = network.getMeetingFromUrl(document.location.href)
if( meeting.match(this.plugin.protocol) ){ if( meeting.match(this.profile.protocol) ){
this.parseLink( meeting ) this.parseLink( meeting )
} }
}) })
@ -71,25 +71,25 @@ window.trystero = (opts) => new Proxy({
confirmConnected(){ confirmConnected(){
if( !this.connected ){ if( !this.connected ){
this.connected = true this.connected = true
frontend.emit('network.connected',{plugin:this}) frontend.emit('network.connected',{plugin:this,username: this.nickname})
this.names[ this.selfId ] = this.nickname this.names[ this.selfId ] = this.nickname
} }
}, },
async connect(opts){ async 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)
this.createLink() // ensure link this.createLink() // ensure link
if( opts.selectedWebcam == this.plugin.name ) this.useWebcam = true if( opts.selectedWebcam == this.profile.name ) this.useWebcam = true
if( opts.selectedChatnetwork == this.plugin.name ) this.useChat = true if( opts.selectedChatnetwork == this.profile.name ) this.useChat = true
if( opts.selectedScene == this.plugin.name ) this.useScene = true if( opts.selectedScene == this.profile.name ) this.useScene = true
if( this.useWebcam || this.useChat || this.useScene ){ if( this.useWebcam || this.useChat || this.useScene ){
console.log("connecting "+this.profile.name)
console.log("trystero link: "+this.link) console.log("trystero link: "+this.link)
this.room = joinRoom( {appId: 'xrfragment'}, this.link ) this.room = joinRoom( {appId: 'xrfragment'}, this.link )
$chat.send({message:`Share the meeting link <a onclick="$menu.share()">by clicking here</a>`,class:['info']}) $chat.send({message:`Share the meeting link <a onclick="frontend.share()">by clicking here</a>`,class:['info'],timeout:10000})
$chat.send({message:"waiting for other humans..",class:['info']}) $chat.send({message:"waiting for other humans..",class:['info'], timeout:5000})
// setup trystero events // setup trystero events
const [sendPing, getPing] = this.room.makeAction('ping') const [sendPing, getPing] = this.room.makeAction('ping')
@ -121,7 +121,7 @@ window.trystero = (opts) => new Proxy({
if( this.useWebcam ) this.initWebcam() if( this.useWebcam ) this.initWebcam()
if( this.useChat ) this.initChat() if( this.useChat ) this.initChat()
if( this.useScene ) this.initScene() if( this.useScene ) this.initScene()
} }
}, },
@ -151,7 +151,7 @@ window.trystero = (opts) => new Proxy({
}, },
async initWebcam(){ async initWebcam(){
if( !$connections.$audioInput.value && !$connections.$videInput.value ) return // nothing to do if( !$connections.$audioInput.value && !$connections.$videoInput.value ) return // nothing to do
// get a local audio stream from the microphone // get a local audio stream from the microphone
this.selfStream = await navigator.mediaDevices.getUserMedia({ this.selfStream = await navigator.mediaDevices.getUserMedia({
@ -215,7 +215,7 @@ window.trystero = (opts) => new Proxy({
}, },
config(opts){ config(opts){
opts = {...opts, ...this.plugin } opts = {...opts, ...this.profile }
this.el = document.createElement('div') this.el = document.createElement('div')
this.el.innerHTML = this.html.generic(opts) this.el.innerHTML = this.html.generic(opts)
this.el.querySelector('#nickname').value = this.nickname this.el.querySelector('#nickname').value = this.nickname
@ -225,21 +225,21 @@ window.trystero = (opts) => new Proxy({
}, },
info(opts){ 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.`) window.notify(`${this.profile.name} is ${this.profile.description} <br>by using a serverless technology called <a href="https://webrtc.org/" target="_blank">webRTC</a> via <a href="${this.profile.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){ parseLink(url){
if( !url.match(this.plugin.protocol) ) return if( !url.match(this.profile.protocol) ) return
let parts = url.replace(this.plugin.protocol,'').split("/") let parts = url.replace(this.profile.protocol,'').split("/")
if( parts[0] == 'r' ){ // this.room if( parts[0] == 'r' ){ // this.room
let roomid = parts[1].replace(/:.*/,'') let roomid = parts[1].replace(/:.*/,'')
let server = parts[1].replace(/.*:/,'') let server = parts[1].replace(/.*:/,'')
if( server != 'bittorrent' ) return window.notify("only bittorrent is supported for trystero (for now) :/") if( server != 'bittorrent' ) return window.notify("only bittorrent is supported for trystero (for now) :/")
this.link = url this.link = url
$connections.show() $connections.show()
$connections.selectedWebcam = this.plugin.name $connections.selectedWebcam = this.profile.name
$connections.selectedChatnetwork= this.plugin.name $connections.selectedChatnetwork= this.profile.name
$connections.selectedScene = this.plugin.name $connections.selectedScene = this.profile.name
return true return true
} }
return false return false