xrfragment/src/3rd/js/aframe/meeting.js

354 lines
11 KiB
JavaScript
Raw Normal View History

2023-12-13 19:09:58 +01:00
AFRAME.registerComponent('meeting', {
schema:{
2023-12-15 16:23:12 +01:00
id:{ required:true, type:'string'},
visitorname:{required:false,type:'string'},
parentRoom:{required:false,type:'string'}
},
remove: function(){
if( this.room ) this.room.leave()
this.meeting.remove()
2023-12-13 19:09:58 +01:00
},
init: function(){
// embed https://github.com/dmotz/trystero (trystero-torrent.min.js build)
// add css+html
2023-12-15 16:23:12 +01:00
let el = this.meeting = document.createElement("div")
el.id = 'meeting'
2023-12-13 19:09:58 +01:00
el.innerHTML += `<style type="text/css">
#videos{
display:grid-auto-columns;
grid-column-gap:5px;
margin-bottom:15px;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: 15px;
z-index:1500;
}
#videos > video{
border-radius:7px;
display:inline-block;
background:black;
width:80px;
height:60px;
margin-right:5px;
margin-bottom:5px;
vertical-align:top;
pointer-events:all;
}
#videos > video:hover{
filter: brightness(1.8);
cursor:pointer;
}
#chatbar {
z-index: 1500;
position: fixed;
bottom: 20px;
left: 20px;
width: 48%;
background: white;
2023-12-14 18:13:40 +01:00
padding: 0px 0px 0px 15px;
2023-12-13 19:09:58 +01:00
border-radius: 30px;
max-width: 500px;
box-sizing: border-box;
box-shadow: 0px 0px 5px 5px #0002;
}
#chatbar input{
border:none;
2023-12-14 18:13:40 +01:00
width:90%;
box-sizing:border-box;
2023-12-13 19:09:58 +01:00
}
#chat{
position: absolute;
top: 100px;
left: 0;
right: 0;
2023-12-15 16:23:12 +01:00
bottom: 88px;
2023-12-13 19:09:58 +01:00
padding: 15px;
pointer-events: none;
2023-12-15 16:23:12 +01:00
overflow-y:auto;
2023-12-13 19:09:58 +01:00
}
#chat .msg{
2023-12-15 16:23:12 +01:00
background: #fff;
2023-12-13 19:09:58 +01:00
display: inline-block;
padding: 6px 17px;
border-radius: 20px;
color: #000c;
margin-bottom: 10px;
2023-12-15 16:23:12 +01:00
line-height:23px;
pointer-events:visible;
border: 1px solid #ccc;
}
#chat .msg.info{
background: #333;
color: #FFF;
font-size: 14px;
font-weight: bold;
padding: 0px 16px;
}
#chat .msg.info a,
#chat .msg.info a:visited{
color: #aaf;
text-decoration: none;
transition:0.3s;
}
#chat .msg.info a:hover,
#chat button:hover{
filter: brightness(1.8);
text-decoration: underline;
}
#chat button {
margin: 0px 15px 10px 0px;
background: #07F;
color: #FFF;
border-radius: 7px;
padding: 11px 15px;
border: 0;
font-weight: bold;
box-shadow: 0px 0px 5px 5px #0002;
pointer-events:all;
2023-12-13 19:09:58 +01:00
}
#chat,#chatbar,#chatbar *, #chat *{
font-family:monospace;
2023-12-14 18:13:40 +01:00
font-size:15px;
2023-12-13 19:09:58 +01:00
}
</style>
<div id="videos" style="pointer-events:none"></div>
2023-12-14 18:13:40 +01:00
<div id="chat" aria-live="assertive" aria-relevant></div>
2023-12-13 19:09:58 +01:00
<div id="chatbar">
2023-12-15 16:23:12 +01:00
<input id="chatline" type="text" placeholder="enter your name"></input>
2023-12-13 19:09:58 +01:00
</div>`
document.body.appendChild(el)
2023-12-15 16:23:12 +01:00
let chatline = this.chatline = document.querySelector("#chatline")
let chat = this.chat = document.querySelector("#chat")
chat.log = [] // save raw chatlog to prime new visitors
chat.append = (str,classes,buttons) => this.chatAppend(str,classes,buttons)
this.initChatLine()
if( !this.data.visitorname ) this.chat.append("💁 Hi there! Please enter your name")
else{
if( this.data.parentRoom ) this.chat.append(`leaving ${this.data.parentRoom}`,["info"]);
this.trysteroInit()
}
2023-12-13 19:09:58 +01:00
},
2023-12-15 16:23:12 +01:00
chatAppend: function(str,classes,buttons){
if( str ){
str = str.replace('\n', "<br>")
.replace(/^[a-zA-Z-0-9]+?[:\.]/,'<b>$&</b>')
let el = this.createMsg(str)
if( classes ) classes.map( (c) => el.classList.add(c) )
this.chat.appendChild(el) // send to screen
this.chat.innerHTML += '<br>'
this.chat.log.push(str)
}
if( buttons ){
for( let i in buttons ){
let btn = document.createElement("button")
btn.innerText = i
btn.addEventListener('click', () => buttons[i]() )
this.chat.appendChild(btn)
}
this.chat.innerHTML += '<br>'
}
this.chat.scrollTop = this.chat.scrollHeight; // scroll to bottom
},
2023-12-13 19:09:58 +01:00
trysteroInit: async function(){
2023-12-13 19:27:37 +01:00
const { joinRoom } = await import("./../../../dist/trystero-torrent.min.js");
2023-12-13 19:09:58 +01:00
2023-12-15 16:23:12 +01:00
if( !document.location.hash.match(/meet/) ){
document.location.hash += document.location.hash.match(/#/) ? '&meet' : '#meet'
}
let roomname = this.roomname = document.location.href
2023-12-13 19:09:58 +01:00
const config = this.config = {appId: this.data.id }
const room = this.room = joinRoom(config, roomname )
2023-12-15 16:23:12 +01:00
this.chat.append("joined meeting at "+roomname,["info"]);
this.chat.append("copied meeting link to clipboard",["info"]);
2023-12-13 19:09:58 +01:00
const idsToNames = this.idsToNames = {}
const [sendName, getName] = room.makeAction('name')
const [sendChat, getChat] = this.room.makeAction('chat')
this.sendChat = sendChat
this.sendName = sendName
this.getChat = getChat
this.getName = getName
// tell other peers currently in the room our name
2023-12-15 16:23:12 +01:00
idsToNames[ room.selfId ] = this.data.visitorname.substr(0,15)
sendName( this.data.visitorname )
2023-12-13 19:09:58 +01:00
// listen for peers naming themselves
getName((name, peerId) => (idsToNames[peerId] = name))
this.initChat()
// this object can store audio instances for later
const peerAudios = this.peerAudios = {}
const peerVideos = this.peerVideos = {}
// get a local audio stream from the microphone
const selfStream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: {
width: 320,
height: 240,
frameRate: {
ideal: 30,
min: 10
}
}
})
let meVideo = this.addVideo(selfStream, room.selfId)
meVideo.muted = true
// send stream to peers currently in the room
room.addStream(selfStream)
// send stream + chatlog to peers who join later
room.onPeerJoin( (peerId) => {
room.addStream(selfStream, peerId)
sendName( name, peerId)
this.sendChat({prime: this.chat.log}, peerId )
})
room.onPeerLeave( (peerId) => {
console.log(`${idsToNames[peerId] || 'a visitor'} left`)
if( peerVideos[peerId] ){
peerVideos[peerId].remove()
delete peerVideos[peerId]
}
delete idsToNames[peerId]
})
// handle streams from other peers
room.onPeerStream((stream, peerId) => {
// create an audio instance and set the incoming stream
const audio = new Audio()
audio.srcObject = stream
audio.autoplay = true
// add the audio to peerAudio object if you want to address it for something
// later (volume, etc.)
peerAudios[peerId] = audio
})
room.onPeerStream((stream, peerId) => {
this.addVideo(stream,peerId)
})
},
addVideo: function(stream,peerId){
let video = this.peerVideos[peerId]
const videoContainer = document.getElementById('videos')
// if this peer hasn't sent a stream before, create a video element
if (!video) {
video = document.createElement('video')
video.autoplay = true
// add video element to the DOM
videoContainer.appendChild(video)
}
video.srcObject = stream
video.resize = (state) => {
if( video.resize.state == undefined ) video.resize.state = false
if( state == undefined ) state = (video.resize.state = !video.resize.state )
video.style.width = state ? '320px' : '80px'
video.style.height = state ? '200px' : '60px'
}
video.addEventListener('click', () => video.resize() )
this.peerVideos[peerId] = video
return video
},
2023-12-15 16:23:12 +01:00
createMsg: function(str){
2023-12-13 19:09:58 +01:00
let el = document.createElement("div")
el.className = "msg"
el.innerHTML = this.linkify(str)
return el
},
// central function to broadcast stuff to chat
2023-12-15 16:23:12 +01:00
send: function(str,classes,buttons){
2023-12-13 19:09:58 +01:00
if( !this.sendChat ) return
2023-12-15 16:23:12 +01:00
this.sendChat({content:str}) // send to network
this.chat.append(str,classes,buttons) // send to screen
2023-12-13 19:09:58 +01:00
},
2023-12-15 16:23:12 +01:00
initChatLine: function(){
2023-12-13 19:09:58 +01:00
let chatline = this.chatline = document.querySelector("#chatline")
chatline.addEventListener("keydown", (e) => {
if( e.key !== "Enter" ) return
2023-12-15 16:23:12 +01:00
if( !this.data.visitorname ){
this.data.visitorname = chatline.value
this.chat.append("btw. camera/mic access is totally optional ♥️")
this.trysteroInit()
}else{
let str = `${this.idsToNames[ this.room.selfId ]}: ${chatline.value.substr(0,65515).trim()}`
this.send(str)
}
2023-12-13 19:09:58 +01:00
chatline.value = ''
2023-12-14 18:13:40 +01:00
event.preventDefault();
event.target.blur()
2023-12-13 19:09:58 +01:00
})
2023-12-15 16:23:12 +01:00
},
2023-12-13 19:09:58 +01:00
2023-12-15 16:23:12 +01:00
initChat: function(){
2023-12-13 19:09:58 +01:00
// listen for chatmsg
this.getChat((data, peerId) => {
if( data.prime ){
if( this.chat.primed ) return // only prime once
console.log("receiving prime")
data.prime.map( (l) => chat.append(l) ) // send log to screen
this.chat.primed = true
}
2023-12-15 16:23:12 +01:00
chat.append(data.content,data.classes,data.buttons) // send to screen
2023-12-13 19:09:58 +01:00
})
2023-12-15 16:23:12 +01:00
this.notifyTeleport()
2023-12-13 19:09:58 +01:00
return this
},
2023-12-15 16:23:12 +01:00
notifyTeleport: function(buttons){
// send to network
this.sendChat({
content: `${this.data.visitorname} teleported to ${this.roomname}`,
classes: ["info"],
buttons
})
},
2023-12-13 19:09:58 +01:00
linkify: function(t){
2023-12-15 16:23:12 +01:00
const m = t.match(/(?<=\s|^)[a-zA-Z0-9-:/]+[?\.][a-zA-Z0-9-].+?(?=[.,;:?!-]?(?:\s|$))/g)
2023-12-13 19:09:58 +01:00
if (!m) return t
const a = []
m.forEach(x => {
const [t1, ...t2] = t.split(x)
a.push(t1)
t = t2.join(x)
2023-12-15 16:23:12 +01:00
let y = (!(x.match(/:\/\//)) ? 'https://' : '') + x
if (isNaN(x) ){
let url_human = y.split('/')[2]
let isXRFragment = y.match("pos=")
if( isXRFragment ){ // detect xr fragments
url_human = y.replace(/.*[?\?]/,'') // shorten xr fragment links
.replace(/[?\&]meet/,'')
y = y.replace(/.*[\?]/, '?') // start from search (to prevent page-refresh)
}
a.push(`<a href="${y}" ${isXRFragment ? '' : `target="_blank"`} style="pointer-events:all">${url_human}</a>`)
}else
2023-12-13 19:09:58 +01:00
a.push(x)
})
a.push(t)
return a.join('')
}
});