const State = { websocket: null, posts: [], users: [], } const autoFocus = (vnode) => { vnode.dom.focus() } const scrollIntoView = (vnode) => { vnode.dom.scrollIntoView() } const prettyTime = (ts) => { return ts.slice(11, 19) } const Login = { sendLogin: (e) => { e.preventDefault() const username = e.target.username.value localStorage.username = username connect(username) }, sendLogout: (e) => { localStorage.removeItem('username') State.websocket.send(JSON.stringify({action: 'logout'})) State.posts = [] }, view() { return m('.login', m('form', {onsubmit: Login.sendLogin}, m('input', {oncreate: autoFocus, name: 'username', autocomplete: 'off'}), m('button', 'Login'), ), State.kind === 'error' && m('.error', State.info), ) }, } const Chat = { sendPost: (e) => { e.preventDefault() const field = e.target.text State.websocket.send(JSON.stringify({action: 'post', text: field.value})) field.value = '' }, view() { return m('.chat', m('.posts', State.posts.map(post => m('.post', {oncreate: scrollIntoView}, m('.ts', prettyTime(post.ts)), m('.source', post.source), m('.text', post.text), )), ), m('form.actions', {onsubmit: Chat.sendPost}, m('input', {oncreate: autoFocus, name: 'text', autocomplete: 'off'}), m('button', 'Send'), ), m('.users', m('button', {onclick: Login.sendLogout}, 'Logout'), m('ul.user-list', State.users.map(username => m('li', username))) ), ) }, } const Main = { view() { const connected = State.websocket && State.websocket.readyState === 1 return connected ? m(Chat) : m(Login) }, } m.mount(document.body, Main) const connect = (username) => { State.websocket = new WebSocket(wsUrl) State.websocket.onopen = (e) => { State.websocket.send(JSON.stringify({action: 'login', username})) } State.websocket.onmessage = (e) => { const message = JSON.parse(e.data) if(message.kind === 'post') { State.posts.push(message) } else if(message.kind === 'update') { Object.assign(State, message) if(message.info) { State.posts.push({ts: message.ts, source: '~', text: message.info}) } } else if(message.kind === 'error') { Object.assign(State, message) Login.sendLogout() } else { console.log(message) } m.redraw() } State.websocket.onclose = (e) => { if(!e.wasClean) { setTimeout(connect, 100, username) } m.redraw() } } const wsUrl = location.toString().replace('http', 'ws') if(localStorage.username) { connect(localStorage.username) }