Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

pico.js 3.0KB

5 år sedan
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  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. localStorage.removeItem('username')
  24. State.websocket.send(JSON.stringify({action: 'logout'}))
  25. State.posts = []
  26. },
  27. view() {
  28. return m('.login',
  29. m('form', {onsubmit: Login.sendLogin},
  30. m('input', {oncreate: autoFocus, name: 'username', autocomplete: 'off'}),
  31. m('button', 'Login'),
  32. ),
  33. State.kind === 'error' && m('.error', State.info),
  34. )
  35. },
  36. }
  37. const Chat = {
  38. sendPost: (e) => {
  39. e.preventDefault()
  40. const field = e.target.text
  41. State.websocket.send(JSON.stringify({action: 'post', text: field.value}))
  42. field.value = ''
  43. },
  44. view() {
  45. return m('.chat',
  46. m('.posts',
  47. State.posts.map(post => m('.post', {oncreate: scrollIntoView},
  48. m('.ts', prettyTime(post.ts)),
  49. m('.source', post.source),
  50. m('.text', post.text),
  51. )),
  52. ),
  53. m('form.actions', {onsubmit: Chat.sendPost},
  54. m('input', {oncreate: autoFocus, name: 'text', autocomplete: 'off'}),
  55. m('button', 'Send'),
  56. ),
  57. m('.users',
  58. m('button', {onclick: Login.sendLogout}, 'Logout'),
  59. m('ul.user-list', State.users.map(username => m('li', username)))
  60. ),
  61. )
  62. },
  63. }
  64. const Main = {
  65. view() {
  66. const connected = State.websocket && State.websocket.readyState === 1
  67. return connected ? m(Chat) : m(Login)
  68. },
  69. }
  70. m.mount(document.body, Main)
  71. const connect = (username) => {
  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, 100, username)
  99. }
  100. m.redraw()
  101. }
  102. }
  103. const wsUrl = location.toString().replace('http', 'ws')
  104. if(localStorage.username) {
  105. connect(localStorage.username)
  106. }