|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- const State = Object.seal({
- username: null,
- websocket: null,
- online: [],
- get isConnected() {
- return State.websocket && State.websocket.readyState === 1
- },
- })
- const params = (new URL(document.location)).searchParams
- /*
- *
- * SIGNALING
- *
- */
- addEventListener('resize', () => m.redraw())
- const wire = (message) => State.websocket.send(JSON.stringify(message))
- const signal = (message) => dispatchEvent(new CustomEvent(message.kind, {detail: message}))
- const listen = (kind, handler) => {
- addEventListener(kind, handler)
- }
- listen('login', ({detail}) => {
- State.username = detail.value
- })
- listen('logout', ({detail}) => {
- State.online = []
- })
- listen('state', ({detail}) => {
- delete detail.ts
- delete detail.kind
- Object.assign(State, detail)
- })
- const doNotLog = new Set(['login', 'state', 'post', 'peerInfo', 'volumeMapMove'])
- /*
- *
- * UTILS
- *
- */
- const autoFocus = (vnode) => {
- vnode.dom.focus()
- }
- /*
- *
- * WEBSOCKET
- *
- */
- const connect = (username) => {
- const wsUrl = location.href.replace('http', 'ws')
-
- State.websocket = new WebSocket(wsUrl)
-
- State.websocket.onopen = (e) => {
- wire({kind: 'login', value: username})
- }
-
- State.websocket.onmessage = (e) => {
- const message = JSON.parse(e.data)
-
- if(message.online) {
- const difference = (l1, l2) => l1.filter(u => !l2.includes(u))
- difference(message.online, State.online).forEach(username => {
- if(username === State.username) return
- signal({kind: 'post', ts: message.ts, value: `${username} joined`})
- })
- difference(State.online, message.online).forEach(username => {
- if(username === State.username) return
- signal({kind: 'post', ts: message.ts, value: `${username} left`})
- })
- }
-
- if(!doNotLog.has(message.kind)) {
- console.log(message)
- }
- signal(message)
-
- m.redraw()
- }
-
- State.websocket.onclose = (e) => {
- State.online.forEach(signalPeerStop)
- if(!e.wasClean) {
- setTimeout(connect, 1000, username)
- }
- m.redraw()
- }
- }
- /*
- *
- * BASE
- *
- */
- const Shadow = {
- oncreate({dom, attrs}) {
- dom.listener = () => attrs.app.isOn = !attrs.app.isOn
- addEventListener(attrs.key, dom.listener)
- },
- onremove({dom, attrs}) {
- removeEventListener(attrs.key, dom.listener)
- },
- view({attrs}) {
- const style = {
- zIndex: 999,
- backgroundColor: 'rgba(0, 0, 0, 0.8)',
- visibility: attrs.app.isOn ? 'unset' : 'hidden',
- position: 'fixed',
- height: '100vh',
- width: '100vw',
- right: 0,
- top: 0,
- }
- const onclick = ({target: {classList}}) => {
- classList.contains('shadow') && signal({kind: attrs.key})
- }
- return m('.shadow', {style, onclick}, m(attrs.app))
- },
- }
- const Settings = {
- get(key) {
- try {
- return JSON.parse(localStorage.getItem(key))
- }
- catch(error) {
- return null
- }
- },
- set(key, value) {
- localStorage.setItem(key, JSON.stringify(value))
- },
- multiField: ([key, options]) => {
- let current = Settings.get(key)
- if(!options.includes(current)) {
- Settings.set(key, options[0])
- }
- return m('.field',
- m('label', key),
- options.map(value => {
- const style = {
- fontWeight: Settings.get(key) == value ? 'bold' : 'unset'
- }
- const onclick = () => Settings.set(key, value)
- return m('button', {style, onclick}, `${value}`)
- })
- )
- },
- view() {
- return m('.settings',
- Object.entries({
- blackBars: [true, false],
- }).map(Settings.multiField)
- )
- },
- }
- const Base = {
- oncreate: () => {
- const randomName = ('' + Math.random()).substring(2)
- connect(localStorage.username || randomName)
- },
- sendLogin: (e) => {
- e.preventDefault()
- const username = e.target.username.value
- localStorage.username = username
- connect(username)
- },
- sendLogout: (e) => {
- e.preventDefault()
- wire({kind: 'logout'})
- signal({kind: 'logout'})
- },
- view() {
- const attrs = {
- oncreate: autoFocus,
- name: 'username',
- autocomplete: 'off',
- value: localStorage.username,
- }
- const mainStyle = {
- position: 'fixed',
- width: '100%',
- display: 'grid',
- gridTemplateRows: 'auto 1fr',
- height: window.innerHeight + 'px',
- overflow: 'hidden',
- }
- const headerStyle = {
- display: 'grid',
- gridAutoFlow: 'column',
- justifyItems: 'start',
- marginRight: 'auto',
- }
- return m('main', {style: mainStyle},
- m('header', {style: headerStyle},
- State.isConnected ? [
- m('button', {onclick: Base.sendLogout}, 'settings'),
- m(VideoConfig),
- m(ChatConfig),
- m('button', {onclick: VolumeMap.toggle}, 'volume'),
- ] : [
- m('form.login',
- {onsubmit: Base.sendLogin},
- m('input', attrs),
- m('button', 're-join'),
- ),
- ],
- m('span.error', State.info),
- ),
- State.isConnected ? [
- m(StreamContainer, {key: 'lolo'}),
- m(Shadow, {key: 'chat-shadow', app: Chat}),
- m(Shadow, {key: 'map-shadow', app: VolumeMap}),
- ] : m(Settings),
- )
- },
- }
- m.mount(document.body, Base)
|