Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

111 rindas
3.0KB

  1. const State = {
  2. websocket: null,
  3. posts: [],
  4. users: [],
  5. }
  6. const autoFocus = (vnode) => {
  7. vnode.dom.focus()
  8. }
  9. const scrollIntoView = (vnode) => {
  10. vnode.dom.scrollIntoView()
  11. }
  12. const prettyTime = (ts) => {
  13. return ts.slice(11, 19)
  14. }
  15. const Login = {
  16. sendLogin: (e) => {
  17. e.preventDefault()
  18. const username = e.target.username.value
  19. localStorage.username = username
  20. connect(username)
  21. },
  22. sendLogout: (e) => {
  23. State.websocket.send(JSON.stringify({action: 'logout'}))
  24. State.posts = []
  25. },
  26. view() {
  27. return m('.login',
  28. m('form', {onsubmit: Login.sendLogin},
  29. m('input', {oncreate: autoFocus, name: 'username', autocomplete: 'off', value: localStorage.username}),
  30. m('button', 'Login'),
  31. ),
  32. State.kind === 'error' && m('.error', State.info),
  33. )
  34. },
  35. }
  36. const Chat = {
  37. sendPost: (e) => {
  38. e.preventDefault()
  39. const field = e.target.text
  40. State.websocket.send(JSON.stringify({action: 'post', text: field.value}))
  41. field.value = ''
  42. },
  43. view() {
  44. return m('.chat',
  45. m('.posts',
  46. State.posts.map(post => m('.post', {oncreate: scrollIntoView},
  47. m('.ts', prettyTime(post.ts)),
  48. m('.source', post.source),
  49. m('.text', post.text),
  50. )),
  51. ),
  52. m('form.actions', {onsubmit: Chat.sendPost},
  53. m('input', {oncreate: autoFocus, name: 'text', autocomplete: 'off'}),
  54. m('button', 'Send'),
  55. ),
  56. m('.users',
  57. m('button', {onclick: Login.sendLogout}, 'Logout'),
  58. m('ul.user-list', State.users.map(username => m('li', username)))
  59. ),
  60. )
  61. },
  62. }
  63. const Main = {
  64. view() {
  65. const connected = State.websocket && State.websocket.readyState === 1
  66. return connected ? m(Chat) : m(Login)
  67. },
  68. }
  69. m.mount(document.body, Main)
  70. const connect = (username) => {
  71. const wsUrl = location.href.replace('http', 'ws')
  72. State.websocket = new WebSocket(wsUrl)
  73. State.websocket.onopen = (e) => {
  74. State.websocket.send(JSON.stringify({action: 'login', username}))
  75. }
  76. State.websocket.onmessage = (e) => {
  77. const message = JSON.parse(e.data)
  78. if(message.kind === 'post') {
  79. State.posts.push(message)
  80. }
  81. else if(message.kind === 'update') {
  82. Object.assign(State, message)
  83. if(message.info) {
  84. State.posts.push({ts: message.ts, source: '~', text: message.info})
  85. }
  86. }
  87. else if(message.kind === 'error') {
  88. Object.assign(State, message)
  89. Login.sendLogout()
  90. }
  91. else {
  92. console.log(message)
  93. }
  94. m.redraw()
  95. }
  96. State.websocket.onclose = (e) => {
  97. if(!e.wasClean) {
  98. setTimeout(connect, 1000, username)
  99. }
  100. m.redraw()
  101. }
  102. }
  103. if(localStorage.username) {
  104. connect(localStorage.username)
  105. }