script.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. // Chat Homework
  2. // Этапы.
  3. // Используя функцию jsonPost на адрес http://students.a-level.com.ua:10012 напишите чат-клиент, который:
  4. // Stage 0
  5. // Для поиграться скопируйте в консоль функцию jsonPost (или запустите её с этой страницы) и вызовите её с теми или иными
  6. // объектами в качестве второго параметра. см. RPC.
  7. // jsonPost('http://students.a-level.com.ua:10012', {func: 'addMessage', nick: 'Anon', message: 'Я не умею копипастить в консоль, зато умею жать красную кнопку.'})
  8. // Если после объявления функции jsonPost запустить пример выше, то вы напишите в чат.
  9. // Stage 1
  10. // Отправляет сообщения в чат. Для проверки отслеживайте приходящий с сервера nextMessageId, который должен увеличиваться.
  11. // Интерфейс: поле ввода ника, поле ввода сообщения, кнопка отправки.
  12. // Stage 2
  13. // Читает все сообщения из чата и выводит их в отдельном контейнере.
  14. // Интерфейс: некий div-контейнер, в котором на каждое сообщение создается div (или иной блочный контейнерный тэг)
  15. // и в него помещается ник и сообщение. Также там есть timestamp который может показывать время отправки сообщения.
  16. // Stage 3
  17. // Читает только новые сообщения из чата. Для этого надо после каждого получения запоминать nextMessageId и отправлять его
  18. // при следующем запросе новых сообщений. Изначально nextMessageId равен нулю, что бы вычитать всю историю сообщений с сервера.
  19. // Stage 4
  20. // Делаем Stage 3 в setInterval для периодической проверки сообщений (раз в 2 -5 секунд).
  21. // Stage 5
  22. // Напишите асинхронную функцию отправки, которая внутри себя будет делать два запроса: на отправку и на проверку, для того
  23. // что бы минимизировать задержку между отправкой сообщения пользователя и его появлением в окне чата.
  24. // Оформите отдельно функции отправки и проверки новых сообщений как асинхронные (async, возвращает Promise):
  25. // async function sendMessage(nick, message) отсылает сообщение.
  26. // async function getMessages() получает сообщения и отрисовывает их в DOM
  27. // async function sendAndCheck() использует две предыдущие для минимизации задержки между отправкой сообщения и приходом их.
  28. // Именно эта функция должна запускаться по кнопке.
  29. // async function checkLoop() использует delay и бесконечный цикл для периодического запуска getMessages().
  30. // Stage 6
  31. // Прогуглить и разобраться с fetch и заменить внутренности jsonPost на код, использующий fetch вместо XMLHttpRequest.
  32. // Информация
  33. // jsonPost
  34. // Данная промисифицированная функция (кстати, сделайте из неё асинхронную) умеет общаться с моим чат-сервером отправляя
  35. // RPC запросы используя JSON. Remote Procedure Call - вызов функций удаленно, имя функции и параметры передаются AJAX-ом
  36. // в формате (например) JSON.
  37. // function jsonPost(url, data)
  38. // {
  39. // return new Promise((resolve, reject) => {
  40. // var x = new XMLHttpRequest();
  41. // x.onerror = () => reject(new Error('jsonPost failed'))
  42. // //x.setRequestHeader('Content-Type', 'application/json');
  43. // x.open('POST', url, true);
  44. // x.send(JSON.stringify(data))
  45. // x.onreadystatechange = () => {
  46. // if (x.readyState == XMLHttpRequest.DONE && x.status == 200){
  47. // resolve(JSON.parse(x.responseText))
  48. // }
  49. // else if (x.status != 200){
  50. // reject(new Error('status is not 200'))
  51. // }
  52. // }
  53. // })
  54. // }
  55. // Первым параметром указывается URL (см. выше) на который отправляется методом POST с JSON, вторым - готовый к JSONификации
  56. // объект, который вы хотите отправить на сервер.
  57. // RPC
  58. // addMessage
  59. // Если вы отправите подобный JSON на сервер (см jsonPost), то сервер запишет ваше сообщение во внутренний массив и отдаст
  60. // новую длину массива. После этого другие могут прочесть это сообщение, используя метод getMessages.
  61. // {func: 'addMessage', nick: 'msg', message: 'msg'}
  62. // Поле func является обязательным и содержит имя функции, которая должна быть вызвана на сервере. Функция на сервере получает
  63. // параметром остальной объект (поля nick и message) В ответ вы получите так же объект:
  64. // {nextMessageId: 100500}
  65. // где 100500: новая длина массива сообщений на сервере после добавления вашего сообщения.
  66. // getMessages
  67. // Позволяет прочесть часть массива сообщений от отпределенного индекса и до конца.
  68. // Используется для последовательной дочитки новых сообщений. Для этого надо передать в jsonPost подобный объект:
  69. // {func: 'getMessages', messageId: 0}
  70. // При значении 0 в messageId сервер отдаст сообщения от 0 до конца, т. е. весь массив:
  71. // {
  72. // 'data': [
  73. // {
  74. // 'nick': 'test',
  75. // 'message': 'test',
  76. // 'timestamp': 1524225450317
  77. // },
  78. // {
  79. // 'nick': 'test',
  80. // 'message': 'test2',
  81. // 'timestamp': 1524225460973
  82. // },
  83. // {
  84. // 'nick': 'test',
  85. // 'message': 'test3',
  86. // 'timestamp': 1524225504849
  87. // },
  88. // {
  89. // 'nick': 'SirkoSobaka',
  90. // 'message': 'Hello!',
  91. // 'timestamp': 1524226323310
  92. // },
  93. // {
  94. // 'nick': 'SirkoSobaka',
  95. // 'message': 'Hello!',
  96. // 'timestamp': 1524226326628
  97. // },
  98. // ],
  99. // 'nextMessageId': 5
  100. // }
  101. // После чего вы итерируете по data и выводите каждый отдельный объект как DOM-элемент с текстом, и, возможно, вложенной
  102. // версткой (время в отдельном элементе и т.п.)
  103. // nextMessageId вы запоминаете для того, что бы отправить следующий запрос начиная с него (т. е. с 68 в примере выше).
  104. // Тогда сервер отдаст вам только сообщения новее последнего запроса.
  105. // Chat Server
  106. // Если у вас не чрезмерно свежий хром, то это можно делать даже на локалхосте и открывать файл из браузера с диска.
  107. // Сервер позволяет соединятся с собой с любого домена.
  108. // Удачи :-)
  109. const url = 'http://students.a-level.com.ua:10012';
  110. const nick = document.getElementById('nick');
  111. const message = document.getElementById('message');
  112. const btnSendMessage = document.getElementById('button');
  113. const container = document.getElementById('container');
  114. let nextMessageId = 1;
  115. let delay = 1000;
  116. setInterval(getMessages, delay);
  117. function getMessages() {
  118. jsonPost(url, { func: 'getMessages', messageId: nextMessageId })
  119. .then((response) => response.json())
  120. .then((data) => {
  121. nextMessageId = data.nextMessageId;
  122. updateMessages(data.data);
  123. });
  124. }
  125. function jsonPost(url, data) {
  126. return fetch(url, {
  127. method: 'POST',
  128. body: JSON.stringify(data),
  129. });
  130. }
  131. function updateMessages(messageList) {
  132. messageList.forEach((msg) => {
  133. newGotMessage(msg);
  134. });
  135. }
  136. function newGotMessage({ nick, message, timestamp }) {
  137. const newBlock = document.createElement('div');
  138. newBlock.classList.add('block');
  139. const time = new Date(timestamp);
  140. let html = '';
  141. html += `<p>${nick}<p>`;
  142. html += `<p class='message'>'${message}'</p>`;
  143. html += `<p>${time.toString()}</p>`;
  144. newBlock.innerHTML = html;
  145. container.prepend(newBlock);
  146. }
  147. btnSendMessage.addEventListener('click', sendAndCheck);
  148. async function sendAndCheck() {
  149. await sendMessage();
  150. getMessages();
  151. message.value = '';
  152. }
  153. async function sendMessage() {
  154. jsonPost(url, {
  155. func: 'addMessage',
  156. nick: nick.value,
  157. message: message.value,
  158. });
  159. }