|
- const State = {
- websocket: null,
- posts: [],
- users: [],
- }
-
- /*
- *
- * WEBRTC
- *
- */
- const rpcCall = async (username) => {
- const constraints = {audio: true, video: true}
- const media = await navigator.mediaDevices.getUserMedia(constraints)
- const rpc = new RTCPeerConnection({iceServers: []})
- // rpc.oniceconnectionstatechange = (e) => {
- // console.log(e)
- // }
- // rpc.onicecandidate = (e) => {
- // e.candidate
- // const candidate = new RTCIceCandidate();
- // rpc.addIceCandidate(candidate);
- // }
- // rpc.ontrack = (e) => {
- // console.log(e)
- // // appendVideo(media)
- // }
-
- const makeOffer = async () => {
- rpc.addStream(media)
- const offer = await rpc.createOffer()
- await rpc.setLocalDescription(offer)
- return {msgType: 'offer', rsd: rpc.localDescription}
- }
-
- const makeAnswer = async (msg) => {
- const offer = new RTCSessionDescription(msg.rsd)
- await rpc.setRemoteDescription(offer)
- const answer = await rpc.createAnswer()
- await rpc.setLocalDescription(answer)
- return {msgType: 'answer', rsd: rpc.localDescription}
- }
-
- const finishShake = async (msg) => {
- const answer = new RTCSessionDescription(msg.rsd)
- await rpc.setRemoteDescription(answer)
-
- }
- finishShake(await makeAnswer(await makeOffer()))
- }
-
- /*
- *
- * GUI
- *
- */
- 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) => {
- 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', value: localStorage.username}),
- 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)
-
- /*
- *
- * WEBSOCKETS
- *
- */
- const connect = (username) => {
- const wsUrl = location.href.replace('http', 'ws')
-
- 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, 1000, username)
- }
- m.redraw()
- }
- }
- if(localStorage.username) {
- connect(localStorage.username)
- }
|