|
- const State = Object.seal({
- username: null,
- websocket: null,
- online: [],
- messages: [],
- get isConnected() {
- return State.websocket && State.websocket.readyState === 1
- },
- })
-
- /*
- *
- * 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
- State.messages = []
- })
- listen('logout', ({detail}) => {
- State.online = []
- })
- listen('state', ({detail}) => {
- delete detail.ts
- delete detail.kind
- Object.assign(State, detail)
- })
- listen('post', ({detail}) => {
- State.messages.push(detail)
- m.redraw()
- })
- const doNotLog = new Set(['login', 'state', 'post', 'peerInfo', 'join', 'leave'])
-
- /*
- *
- * ALERTS
- *
- */
- State.unseen = 0
- listen('post', () => {State.unseen += !document.hasFocus(); updateTitle()})
- listen('focus', () => {State.unseen = 0; updateTitle()})
- const updateTitle = () => {
- document.title = location.href.split('//')[1] + (State.unseen ? ` (${State.unseen})` : ``)
- }
- /*
- *
- * UTILS
- *
- */
- const autoFocus = (vnode) => {
- vnode.dom.focus()
- }
- /*
- *
- * BASE
- *
- */
- const Base = {
- 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 = {
- display: 'grid',
- gridTemplateRows: 'auto 1fr',
- height: '100vh',
- overflow: 'hidden',
- }
- const headerStyle = {
- display: 'grid',
- gridAutoFlow: 'column',
- justifyItems: 'start',
- marginRight: 'auto',
- }
- return m('main', {style: mainStyle},
- m('header', {style: headerStyle},
- State.isConnected ? null : m('form.login',
- {onsubmit: Base.sendLogin},
- m('input', attrs),
- m('button', 'Login'),
- ),
- State.isConnected ? m('form.logout',
- {onsubmit: Base.sendLogout},
- m('button', 'Logout'),
- m('input[readonly]', {value: location}),
- ) : null,
- State.isConnected ? m(VideoConfig) : null,
- m('span.error', State.info),
- ),
- State.isConnected ? m(StreamContainer) : null,
- )
- },
- }
- m.mount(document.body, Base)
-
- /*
- *
- * 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 => {
- signal({kind: 'post', ts: message.ts, value: `${username} joined`})
- signal({kind: 'join', username: username})
- })
- difference(State.online, message.online).forEach(username => {
- signal({kind: 'post', ts: message.ts, value: `${username} left`})
- signal({kind: 'leave', username: username})
- })
- }
-
- 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()
- }
- }
- if(localStorage.username) {
- connect(localStorage.username)
- }
|