Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

148 lines
3.9KB

  1. const State = Object.seal({
  2. username: null,
  3. websocket: null,
  4. online: [],
  5. get isConnected() {
  6. return State.websocket && State.websocket.readyState === 1
  7. },
  8. })
  9. const params = (new URL(document.location)).searchParams
  10. /*
  11. *
  12. * SIGNALING
  13. *
  14. */
  15. addEventListener('resize', () => m.redraw())
  16. const wire = (message) => State.websocket.send(JSON.stringify(message))
  17. const signal = (message) => dispatchEvent(new CustomEvent(message.kind, {detail: message}))
  18. const listen = (kind, handler) => {
  19. addEventListener(kind, handler)
  20. }
  21. listen('login', ({detail}) => {
  22. State.username = detail.value
  23. })
  24. listen('logout', ({detail}) => {
  25. State.online = []
  26. })
  27. listen('state', ({detail}) => {
  28. delete detail.ts
  29. delete detail.kind
  30. Object.assign(State, detail)
  31. })
  32. const doNotLog = new Set(['login', 'state', 'post', 'peerInfo', 'join', 'leave'])
  33. /*
  34. *
  35. * UTILS
  36. *
  37. */
  38. const autoFocus = (vnode) => {
  39. vnode.dom.focus()
  40. }
  41. /*
  42. *
  43. * WEBSOCKET
  44. *
  45. */
  46. const connect = (username) => {
  47. const wsUrl = location.href.replace('http', 'ws')
  48. State.websocket = new WebSocket(wsUrl)
  49. State.websocket.onopen = (e) => {
  50. wire({kind: 'login', value: username})
  51. }
  52. State.websocket.onmessage = (e) => {
  53. const message = JSON.parse(e.data)
  54. if(message.online) {
  55. const difference = (l1, l2) => l1.filter(u => !l2.includes(u))
  56. difference(message.online, State.online).forEach(username => {
  57. signal({kind: 'post', ts: message.ts, value: `${username} joined`})
  58. signal({kind: 'join', username: username})
  59. })
  60. difference(State.online, message.online).forEach(username => {
  61. signal({kind: 'post', ts: message.ts, value: `${username} left`})
  62. signal({kind: 'leave', username: username})
  63. })
  64. }
  65. if(!doNotLog.has(message.kind)) {
  66. console.log(message)
  67. }
  68. signal(message)
  69. m.redraw()
  70. }
  71. State.websocket.onclose = (e) => {
  72. State.online.forEach(signalPeerStop)
  73. if(!e.wasClean) {
  74. setTimeout(connect, 1000, username)
  75. }
  76. m.redraw()
  77. }
  78. }
  79. /*
  80. *
  81. * BASE
  82. *
  83. */
  84. const Base = {
  85. oncreate: () => {
  86. const randomName = ('' + Math.random()).substring(2)
  87. connect(localStorage.username || randomName)
  88. },
  89. sendLogin: (e) => {
  90. e.preventDefault()
  91. const username = e.target.username.value
  92. localStorage.username = username
  93. connect(username)
  94. },
  95. sendLogout: (e) => {
  96. e.preventDefault()
  97. wire({kind: 'logout'})
  98. signal({kind: 'logout'})
  99. },
  100. view() {
  101. const attrs = {
  102. oncreate: autoFocus,
  103. name: 'username',
  104. autocomplete: 'off',
  105. value: localStorage.username,
  106. }
  107. const mainStyle = {
  108. position: 'fixed',
  109. width: '100%',
  110. display: 'grid',
  111. gridTemplateRows: 'auto 1fr',
  112. height: window.innerHeight + 'px',
  113. overflow: 'hidden',
  114. }
  115. const headerStyle = {
  116. display: 'grid',
  117. gridAutoFlow: 'column',
  118. justifyItems: 'start',
  119. marginRight: 'auto',
  120. }
  121. return m('main', {style: mainStyle},
  122. m('header', {style: headerStyle},
  123. State.isConnected ? null : m('form.login',
  124. {onsubmit: Base.sendLogin},
  125. m('input', attrs),
  126. m('button', 're-join'),
  127. ),
  128. State.isConnected ? [
  129. m('button', {onclick: Base.sendLogout}, 'pick name'),
  130. ] : null,
  131. State.isConnected ? m(VideoConfig) : null,
  132. State.isConnected ? m(ChatConfig) : null,
  133. m('span.error', State.info),
  134. ),
  135. State.isConnected ? m(StreamContainer) : null,
  136. State.isConnected ? m(Chat) : null,
  137. )
  138. },
  139. }
  140. m.mount(document.body, Base)