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.

151 lines
3.8KB

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