2023-12-27 17:25:49 +00:00
chatComponent = {
html : `
2024-01-29 20:17:57 +01:00
< div id = "chat" >
2023-12-27 17:25:49 +00:00
< div id = "videos" style = "pointer-events:none" > < / d i v >
2024-04-23 09:42:33 +00:00
< div id = "messages" aria - live = "assertive" role = "log" aria - relevant = "additions" > < / d i v >
2023-12-27 17:25:49 +00:00
< div id = "chatfooter" >
< div id = "chatbar" >
2024-01-29 20:17:57 +01:00
< input id = "chatline" type = "text" placeholder = "chat here" > < / i n p u t >
2023-12-27 17:25:49 +00:00
< / d i v >
2024-04-23 09:42:33 +00:00
< button id = "chatsend" class = "btn" aria - label = "send message" >
< i class = "gg-chevron-right-o" > < / i >
< / b u t t o n >
2023-12-27 17:25:49 +00:00
< / d i v >
< / d i v >
` ,
init : ( el ) => new Proxy ( {
2024-01-29 20:17:57 +01:00
scene : null ,
visible : true ,
messages : [ ] ,
oneMessagePerUser : false ,
2023-12-27 17:25:49 +00:00
2024-01-10 22:01:21 +01:00
username : '' , // configured by 'network.connected' event
2024-01-03 14:23:34 +00:00
$videos : el . querySelector ( "#videos" ) ,
$messages : el . querySelector ( "#messages" ) ,
$chatline : el . querySelector ( "#chatline" ) ,
$chatbar : el . querySelector ( "#chatbar" ) ,
2024-04-25 15:56:29 +00:00
$chatsend : el . querySelector ( "#chatsend" ) ,
2023-12-27 17:25:49 +00:00
install ( opts ) {
this . opts = opts
this . scene = opts . scene
2024-01-29 20:17:57 +01:00
this . $chatbar . style . display = 'none'
2023-12-27 17:25:49 +00:00
el . className = "xrf"
el . style . display = 'none' // start hidden
document . body . appendChild ( el )
document . dispatchEvent ( new CustomEvent ( "$chat:ready" , { detail : opts } ) )
2024-01-03 14:23:34 +00:00
this . send ( { message : ` Welcome to <b> ${ document . location . search . substr ( 1 ) } </b>, a 3D scene(file) which simply links to other ones.<br>You can start a solo offline exploration in XR right away.<br>Type /help below, or use the arrow- or WASD-keys on your keyboard, and mouse-drag to rotate.<br> ` , class : [ "info" , "guide" , "multiline" ] } )
2023-12-27 17:25:49 +00:00
} ,
2024-04-25 15:56:29 +00:00
sendInput ( value ) {
if ( value [ 0 ] == '#' ) return xrf . navigator . to ( value )
let event = value . match ( /^[!\/]/ ) ? "chat.command" : "network.send"
let message = value . replace ( /^[!\/]/ , '' )
let raw = { detail : { message : value , halt : false } }
document . dispatchEvent ( new CustomEvent ( event , { detail : { message } } ) )
document . dispatchEvent ( new CustomEvent ( "chat.input" , raw ) )
if ( event == "network.send" && ! raw . detail . halt ) this . send ( { message : value } )
this . $chatline . lastValue = value
this . $chatline . value = ''
if ( window . innerHeight < 600 ) this . $chatline . blur ( )
} ,
2023-12-27 17:25:49 +00:00
initListeners ( ) {
2024-01-03 14:23:34 +00:00
let { $chatline } = this
2024-01-10 22:01:21 +01:00
$chatline . addEventListener ( 'click' , ( e ) => this . inform ( ) )
2024-01-29 20:17:57 +01:00
2023-12-27 17:25:49 +00:00
$chatline . addEventListener ( 'keydown' , ( e ) => {
if ( e . key == 'Enter' ) {
2024-04-25 15:56:29 +00:00
this . sendInput ( $chatline . value )
}
if ( e . key == 'ArrowUp' ) {
$chatline . value = $chatline . lastValue || ''
2023-12-27 17:25:49 +00:00
}
} )
2024-01-10 22:01:21 +01:00
2024-01-29 20:17:57 +01:00
document . addEventListener ( 'network.connect' , ( e ) => {
this . visible = true
this . $chatbar . style . display = '' // show
} )
2024-01-10 22:01:21 +01:00
document . addEventListener ( 'network.connected' , ( e ) => {
if ( e . detail . username ) this . username = e . detail . username
} )
2024-04-23 09:42:33 +00:00
document . addEventListener ( 'chat.command' , ( e ) => {
if ( String ( e . detail . message ) . trim ( ) == 'help' ) {
let detail = { message : ` The following commands are available
< br > < br >
< b class = "badge" > / h e l p < / b > s h o w s t h i s h e l p s c r e e n
` }
document . dispatchEvent ( new CustomEvent ( 'chat.command.help' , { detail } ) )
this . send ( { message : detail . message } )
}
} )
2024-04-25 15:56:29 +00:00
this . $chatsend . addEventListener ( 'click' , ( e ) => {
this . sendInput ( $chatline . value )
} )
2023-12-27 17:25:49 +00:00
} ,
2024-01-10 22:01:21 +01:00
inform ( ) {
if ( ! this . inform . informed && ( this . inform . informed = true ) ) {
2024-04-25 15:56:29 +00:00
window . notify ( "You can now type messages in the textfield below." )
2024-01-10 22:01:21 +01:00
}
} ,
2023-12-27 17:25:49 +00:00
toggle ( ) {
this . visible = ! this . visible
if ( this . visible && window . meeting . status == 'offline' ) window . meeting . start ( this . opts )
} ,
2024-01-10 22:01:21 +01:00
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
2023-12-27 17:25:49 +00:00
send ( opts ) {
2024-01-03 14:23:34 +00:00
let { $messages } = this
2023-12-27 17:25:49 +00:00
opts = { linebreak : true , message : "" , class : [ ] , ... opts }
2024-01-03 14:23:34 +00:00
if ( window . frontend && window . frontend . emit ) window . frontend . emit ( '$chat.send' , opts )
2024-01-29 20:17:57 +01:00
opts . pos = opts . pos || network . posName || network . pos
2024-01-03 14:23:34 +00:00
let div = document . createElement ( 'div' )
let msg = document . createElement ( 'div' )
let br = document . createElement ( 'br' )
let nick = document . createElement ( 'div' )
2023-12-27 17:25:49 +00:00
msg . className = "msg"
let html = ` ${ opts . message || '' } ${ opts . html ? opts . html ( opts ) : '' } `
if ( $messages . last == html ) return
msg . innerHTML = html
if ( opts . el ) msg . appendChild ( opts . el )
opts . id = Math . random ( )
if ( opts . class ) {
msg . classList . add . apply ( msg . classList , opts . class )
br . classList . add . apply ( br . classList , opts . class )
2024-01-03 14:23:34 +00:00
div . classList . add . apply ( div . classList , opts . class . concat ( [ "envelope" ] ) )
}
2024-04-17 15:20:21 +00:00
if ( ! msg . className . match ( /(info|guide|ui)/ ) && ! opts . from ) {
2024-04-10 16:38:50 +00:00
let frag = xrf . URI . parse ( document . location . hash ) . XRF
2024-01-29 20:17:57 +01:00
opts . from = 'you'
2024-01-10 22:01:21 +01:00
if ( frag . pos ) opts . pos = frag . pos . string
msg . classList . add ( 'self' )
}
2024-01-03 14:23:34 +00:00
if ( opts . from ) {
nick . className = "user"
nick . innerText = opts . from + ' '
div . appendChild ( nick )
if ( opts . pos ) {
let a = document . createElement ( "a" )
a . href = a . innerText = ` #pos= ${ opts . pos } `
nick . appendChild ( a )
}
2023-12-27 17:25:49 +00:00
}
2023-12-28 09:22:54 +00:00
div . appendChild ( msg )
2024-01-10 22:01:21 +01:00
// force one message per user
2024-01-29 20:17:57 +01:00
if ( this . oneMessagePerUser && opts . from ) {
2024-01-10 22:01:21 +01:00
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
2023-12-28 09:22:54 +00:00
$messages . appendChild ( div )
if ( opts . linebreak ) div . appendChild ( br )
2023-12-27 17:25:49 +00:00
$messages . scrollTop = $messages . scrollHeight // scroll down
$messages . last = msg . innerHTML
2024-01-03 14:23:34 +00:00
} ,
getChatLog ( ) {
return ( [ ... this . $messages . querySelectorAll ( '.envelope' ) ] )
. filter ( ( d ) => ! d . className . match ( /(info|ui)/ ) )
. map ( ( d ) => d . innerHTML )
. join ( '\n' )
2023-12-27 17:25:49 +00:00
}
} , {
2024-01-03 14:23:34 +00:00
get ( me , k , v ) { return me [ k ] } ,
set ( me , k , v ) {
me [ k ] = v
2023-12-27 17:25:49 +00:00
switch ( k ) {
2024-01-03 14:23:34 +00:00
case "visible" : {
el . style . display = me . visible ? 'block' : 'none'
if ( ! el . inited && ( el . inited = true ) ) me . initListeners ( )
break ;
}
2023-12-27 17:25:49 +00:00
}
}
} )
}
// reactify component!
document . addEventListener ( '$menu:ready' , ( opts ) => {
opts = opts . detail
document . head . innerHTML += chatComponent . css
2024-01-03 14:23:34 +00:00
window . $chat = document . createElement ( 'div' )
2023-12-27 17:25:49 +00:00
$chat . innerHTML = chatComponent . html
$chat = chatComponent . init ( $chat )
$chat . install ( opts )
//$menu.buttons = ([`<a class="btn" aria-label="button" aria-description="toggle text" id="meeting" onclick="$chat.toggle()">📜 toggle text</a><br>`])
// .concat($menu.buttons)
} )
// alpine component for displaying meetings
chatComponent . css = `
< style type = "text/css" >
# videos {
display : grid - auto - columns ;
grid - column - gap : 5 px ;
margin - bottom : 15 px ;
position : fixed ;
top : 0 ;
left : 0 ;
bottom : 0 ;
right : 0 ;
margin : 15 px ;
2024-04-16 18:40:49 +02:00
z - index : 1000 ;
2023-12-27 17:25:49 +00:00
}
# videos > video {
border - radius : 7 px ;
display : inline - block ;
background : black ;
width : 80 px ;
height : 60 px ;
margin - right : 5 px ;
margin - bottom : 5 px ;
vertical - align : top ;
pointer - events : all ;
}
# videos > video : hover {
filter : brightness ( 1.8 ) ;
cursor : pointer ;
}
# chatbar ,
2024-04-23 09:42:33 +00:00
button # chatsend {
2023-12-27 17:25:49 +00:00
z - index : 1500 ;
position : fixed ;
2023-12-27 21:31:42 +00:00
bottom : 24 px ;
height : 34 px ;
2023-12-27 17:25:49 +00:00
left : 20 px ;
width : 48 % ;
background : white ;
padding : 0 px 0 px 0 px 15 px ;
border - radius : 30 px ;
max - width : 500 px ;
box - sizing : border - box ;
box - shadow : 0 px 0 px 5 px 5 px # 0002 ;
}
2024-04-23 09:42:33 +00:00
button # chatsend {
line - height : 0 px ;
display : none ;
z - index : 1550 ;
color : white ;
border : 0 ;
height : 35 px ;
background : var ( -- xrf - dark - gray ) ;
font - weight : bold ;
width : 20 px ;
max - width : 20 px ;
border - radius : 20 px 0 px 0 px 20 px ;
overflow : hidden ;
2025-02-10 15:53:41 +01:00
margin : 0 ;
2023-12-27 17:25:49 +00:00
}
# chatbar input {
border : none ;
width : 90 % ;
box - sizing : border - box ;
2023-12-27 21:31:42 +00:00
height : 24 px ;
font - size : var ( -- xrf - font - size - 2 ) ;
2023-12-28 09:22:54 +00:00
max - width : unset ;
2023-12-27 17:25:49 +00:00
}
# messages {
2024-01-29 20:17:57 +01:00
/ *
2024-01-10 22:01:21 +01:00
display : flex ;
2024-01-29 20:17:57 +01:00
flex - direction : column ;
2024-06-15 17:33:08 +02:00
width : 100 % ;
max - width : 40 % ;
2024-01-29 20:17:57 +01:00
* /
width : 100 % ;
2024-04-25 15:56:29 +00:00
box - sizing : border - box ;
2024-01-10 22:01:21 +01:00
align - items : flex - start ;
2023-12-27 17:25:49 +00:00
position : absolute ;
2024-01-03 14:23:34 +00:00
transition : 1 s ;
2024-01-29 20:17:57 +01:00
top : 77 px ;
2023-12-27 17:25:49 +00:00
left : 0 ;
2024-01-10 22:01:21 +01:00
bottom : 49 px ;
padding : 20 px ;
2024-01-03 14:23:34 +00:00
overflow : hidden ;
2024-04-25 15:56:29 +00:00
overflow - y : auto ;
2024-01-03 14:23:34 +00:00
pointer - events : none ;
transition : 1 s ;
z - index : 100 ;
}
body . menu # messages {
top : 50 px ;
}
2024-01-09 11:05:13 +00:00
# messages : hover {
pointer - events : all ;
}
2024-01-03 14:23:34 +00:00
# messages * {
2024-04-25 15:56:29 +00:00
box - sizing : border - box ;
/ *
2024-01-29 20:17:57 +01:00
pointer - events : none ;
- webkit - user - select : none ;
- moz - user - select : - moz - none ;
- ms - user - select : none ;
user - select : none ;
2024-04-25 15:56:29 +00:00
* /
2023-12-27 17:25:49 +00:00
}
# messages . msg {
2024-01-03 14:23:34 +00:00
transition : all 1 s ease ;
2023-12-27 17:25:49 +00:00
background : # fff ;
display : inline - block ;
2024-01-03 14:23:34 +00:00
padding : 1 px 17 px ;
2024-01-10 22:01:21 +01:00
border - radius : 20 px ;
2023-12-27 17:25:49 +00:00
color : # 000 c ;
margin - bottom : 10 px ;
line - height : 23 px ;
line - height : 33 px ;
2024-01-03 14:23:34 +00:00
cursor : grabbing ;
border : 1 px solid # 0002 ;
}
2024-04-17 15:20:21 +00:00
# messages . msg * ,
# messages . user * {
2024-01-29 20:17:57 +01:00
pointer - events : all ;
- webkit - user - select : text ;
- moz - user - select : - moz - text ;
- ms - user - select : text ;
user - select : text ;
}
2024-01-03 14:23:34 +00:00
# messages . msg . self {
2024-01-10 22:01:21 +01:00
border - radius : 20 px ;
2024-04-23 09:42:33 +00:00
background : var ( -- xrf - dark - gray ) ;
2024-01-03 14:23:34 +00:00
}
# messages . msg . self ,
# messages . msg . self div {
color : # FFF ;
2023-12-27 17:25:49 +00:00
}
# messages . msg . info {
2024-04-25 15:56:29 +00:00
background : var ( -- xrf - white ) ;
2024-01-03 14:23:34 +00:00
border - radius : 20 px ;
2024-04-25 15:56:29 +00:00
color : var ( -- xrf - dark - gray ) ;
2024-04-23 09:42:33 +00:00
text - align : left ;
2024-01-03 14:23:34 +00:00
line - height : 19 px ;
}
# messages . msg . info ,
# messages . msg . info * {
font - size : var ( -- xrf - font - size - 0 ) ;
}
# messages . msg a {
text - decoration : underline ;
2024-04-23 09:42:33 +00:00
color : var ( -- xrf - light - xrf - secondary ) ;
2024-01-03 14:23:34 +00:00
font - weight : bold ;
2024-01-10 22:01:21 +01:00
transition : 0.3 s ;
}
# messages . msg . info a ,
2024-01-29 20:17:57 +01:00
# messages a . ruler {
2024-01-10 22:01:21 +01:00
color : # FFF ;
2024-01-03 14:23:34 +00:00
}
# messages . msg a : hover {
2024-01-10 22:01:21 +01:00
color : # 000 ;
2024-01-03 14:23:34 +00:00
}
# messages . msg . ui ,
# messages . msg . ui div {
background : white ;
border : none ;
color : # 333 ;
border - radius : 20 px ;
margin : 0 ;
padding : 0 px 5 px 5 px 5 px ;
2023-12-27 17:25:49 +00:00
}
2023-12-27 18:26:42 +00:00
# messages . guide , . guide {
2023-12-27 17:25:49 +00:00
display : unset ;
}
2023-12-27 18:26:42 +00:00
# messages . guide , . guide {
2023-12-27 17:25:49 +00:00
display : none ;
}
br . guide {
display : inline - block ;
}
# messages . msg . info a : hover ,
# messages button : hover {
filter : brightness ( 1.4 ) ;
}
# messages . msg . multiline {
padding : 2 px 14 px ;
}
# messages button {
text - decoration : none ;
margin : 0 px 15 px 10 px 0 px ;
background : var ( -- xrf - primary ) ;
font - family : var ( -- xrf - font - sans - serif ) ;
color : # FFF ;
border - radius : 7 px ;
padding : 11 px 15 px ;
border : 0 ;
font - weight : bold ;
box - shadow : 0 px 0 px 5 px 5 px # 0002 ;
pointer - events : all ;
}
# messages , # chatbar , # chatbar * , # messages * {
}
2023-12-27 18:26:42 +00:00
# messages button . emoticon ,
# messages . btn . emoticon {
line - height : 2 px ;
width : 20 px ;
display : inline - block ;
padding : 0 px 0 px ;
margin : 0 ;
vertical - align : middle ;
background : none ;
border : none ;
min - width : 31 px ;
box - shadow : none ;
}
# messages button . emoticon : hover ,
# messages . btn . emoticon : hover {
border : 1 px solid # ccc ! important ;
background : # EEE ;
}
2023-12-27 21:31:42 +00:00
. nomargin {
2023-12-27 18:26:42 +00:00
margin : 0 ;
}
2024-01-03 14:23:34 +00:00
2024-04-24 10:18:57 +00:00
. envelope {
margin - right : 15 px ;
2024-06-15 17:33:08 +02:00
width : 100 % ;
max - width : 40 % ;
2024-04-24 10:18:57 +00:00
}
2024-01-29 20:17:57 +01:00
. envelope ,
. envelope * {
2024-01-03 14:23:34 +00:00
transition : 1 s ;
2024-01-29 20:17:57 +01:00
pointer - events : none ;
}
. envelope a ,
. envelope button ,
. envelope input ,
. envelope textarea ,
. envelope msg ,
. envelope msg * {
pointer - events : all ;
2024-01-03 14:23:34 +00:00
}
. user {
margin - left : 13 px ;
font - weight : bold ;
color : var ( -- xrf - dark - gray ) ;
}
. user , . user * {
font - size : var ( -- xrf - font - size - 0 ) ;
}
2024-04-23 09:42:33 +00:00
. gg - chevron - right - o {
color : # FFF ;
box - sizing : border - box ;
position : relative ;
display : block ;
transform : scale ( var ( -- ggs , 1 ) ) ;
width : 22 px ;
height : 22 px ;
border : 2 px solid ;
border - radius : 100 px
}
. gg - chevron - right - o : : after {
color : # FFF ;
content : "" ;
display : block ;
box - sizing : border - box ;
position : absolute ;
width : 6 px ;
height : 6 px ;
border - bottom : 2 px solid ;
border - right : 2 px solid ;
transform : rotate ( - 45 deg ) ;
left : 5 px ;
top : 6 px
}
2023-12-27 17:25:49 +00:00
< / s t y l e > `