|
@@ -0,0 +1,207 @@
|
|
|
+// Chat Homework
|
|
|
+
|
|
|
+// Этапы.
|
|
|
+
|
|
|
+// Используя функцию jsonPost на адрес http://students.a-level.com.ua:10012 напишите чат-клиент, который:
|
|
|
+
|
|
|
+// Stage 0
|
|
|
+
|
|
|
+// Для поиграться скопируйте в консоль функцию jsonPost (или запустите её с этой страницы) и вызовите её с теми или иными
|
|
|
+// объектами в качестве второго параметра. см. RPC.
|
|
|
+
|
|
|
+// jsonPost('http://students.a-level.com.ua:10012', {func: 'addMessage', nick: 'Anon', message: 'Я не умею копипастить в консоль, зато умею жать красную кнопку.'})
|
|
|
+
|
|
|
+// Если после объявления функции jsonPost запустить пример выше, то вы напишите в чат.
|
|
|
+
|
|
|
+// Stage 1
|
|
|
+
|
|
|
+// Отправляет сообщения в чат. Для проверки отслеживайте приходящий с сервера nextMessageId, который должен увеличиваться.
|
|
|
+// Интерфейс: поле ввода ника, поле ввода сообщения, кнопка отправки.
|
|
|
+
|
|
|
+// Stage 2
|
|
|
+
|
|
|
+// Читает все сообщения из чата и выводит их в отдельном контейнере.
|
|
|
+// Интерфейс: некий div-контейнер, в котором на каждое сообщение создается div (или иной блочный контейнерный тэг)
|
|
|
+// и в него помещается ник и сообщение. Также там есть timestamp который может показывать время отправки сообщения.
|
|
|
+
|
|
|
+// Stage 3
|
|
|
+
|
|
|
+// Читает только новые сообщения из чата. Для этого надо после каждого получения запоминать nextMessageId и отправлять его
|
|
|
+// при следующем запросе новых сообщений. Изначально nextMessageId равен нулю, что бы вычитать всю историю сообщений с сервера.
|
|
|
+
|
|
|
+// Stage 4
|
|
|
+
|
|
|
+// Делаем Stage 3 в setInterval для периодической проверки сообщений (раз в 2 -5 секунд).
|
|
|
+
|
|
|
+// Stage 5
|
|
|
+
|
|
|
+// Напишите асинхронную функцию отправки, которая внутри себя будет делать два запроса: на отправку и на проверку, для того
|
|
|
+// что бы минимизировать задержку между отправкой сообщения пользователя и его появлением в окне чата.
|
|
|
+// Оформите отдельно функции отправки и проверки новых сообщений как асинхронные (async, возвращает Promise):
|
|
|
+
|
|
|
+// async function sendMessage(nick, message) отсылает сообщение.
|
|
|
+// async function getMessages() получает сообщения и отрисовывает их в DOM
|
|
|
+// async function sendAndCheck() использует две предыдущие для минимизации задержки между отправкой сообщения и приходом их.
|
|
|
+// Именно эта функция должна запускаться по кнопке.
|
|
|
+// async function checkLoop() использует delay и бесконечный цикл для периодического запуска getMessages().
|
|
|
+
|
|
|
+// Stage 6
|
|
|
+
|
|
|
+// Прогуглить и разобраться с fetch и заменить внутренности jsonPost на код, использующий fetch вместо XMLHttpRequest.
|
|
|
+
|
|
|
+// Информация
|
|
|
+
|
|
|
+// jsonPost
|
|
|
+
|
|
|
+// Данная промисифицированная функция (кстати, сделайте из неё асинхронную) умеет общаться с моим чат-сервером отправляя
|
|
|
+// RPC запросы используя JSON. Remote Procedure Call - вызов функций удаленно, имя функции и параметры передаются AJAX-ом
|
|
|
+// в формате (например) JSON.
|
|
|
+
|
|
|
+// function jsonPost(url, data)
|
|
|
+// {
|
|
|
+// return new Promise((resolve, reject) => {
|
|
|
+// var x = new XMLHttpRequest();
|
|
|
+// x.onerror = () => reject(new Error('jsonPost failed'))
|
|
|
+// //x.setRequestHeader('Content-Type', 'application/json');
|
|
|
+// x.open('POST', url, true);
|
|
|
+// x.send(JSON.stringify(data))
|
|
|
+
|
|
|
+// x.onreadystatechange = () => {
|
|
|
+// if (x.readyState == XMLHttpRequest.DONE && x.status == 200){
|
|
|
+// resolve(JSON.parse(x.responseText))
|
|
|
+// }
|
|
|
+// else if (x.status != 200){
|
|
|
+// reject(new Error('status is not 200'))
|
|
|
+// }
|
|
|
+// }
|
|
|
+// })
|
|
|
+// }
|
|
|
+
|
|
|
+// Первым параметром указывается URL (см. выше) на который отправляется методом POST с JSON, вторым - готовый к JSONификации
|
|
|
+// объект, который вы хотите отправить на сервер.
|
|
|
+
|
|
|
+// RPC
|
|
|
+
|
|
|
+// addMessage
|
|
|
+
|
|
|
+// Если вы отправите подобный JSON на сервер (см jsonPost), то сервер запишет ваше сообщение во внутренний массив и отдаст
|
|
|
+// новую длину массива. После этого другие могут прочесть это сообщение, используя метод getMessages.
|
|
|
+
|
|
|
+// {func: 'addMessage', nick: 'msg', message: 'msg'}
|
|
|
+
|
|
|
+// Поле func является обязательным и содержит имя функции, которая должна быть вызвана на сервере. Функция на сервере получает
|
|
|
+// параметром остальной объект (поля nick и message) В ответ вы получите так же объект:
|
|
|
+
|
|
|
+// {nextMessageId: 100500}
|
|
|
+// где 100500: новая длина массива сообщений на сервере после добавления вашего сообщения.
|
|
|
+
|
|
|
+// getMessages
|
|
|
+
|
|
|
+// Позволяет прочесть часть массива сообщений от отпределенного индекса и до конца.
|
|
|
+// Используется для последовательной дочитки новых сообщений. Для этого надо передать в jsonPost подобный объект:
|
|
|
+
|
|
|
+// {func: 'getMessages', messageId: 0}
|
|
|
+
|
|
|
+// При значении 0 в messageId сервер отдаст сообщения от 0 до конца, т. е. весь массив:
|
|
|
+
|
|
|
+// {
|
|
|
+// 'data': [
|
|
|
+// {
|
|
|
+// 'nick': 'test',
|
|
|
+// 'message': 'test',
|
|
|
+// 'timestamp': 1524225450317
|
|
|
+// },
|
|
|
+// {
|
|
|
+// 'nick': 'test',
|
|
|
+// 'message': 'test2',
|
|
|
+// 'timestamp': 1524225460973
|
|
|
+// },
|
|
|
+// {
|
|
|
+// 'nick': 'test',
|
|
|
+// 'message': 'test3',
|
|
|
+// 'timestamp': 1524225504849
|
|
|
+// },
|
|
|
+// {
|
|
|
+// 'nick': 'SirkoSobaka',
|
|
|
+// 'message': 'Hello!',
|
|
|
+// 'timestamp': 1524226323310
|
|
|
+// },
|
|
|
+// {
|
|
|
+// 'nick': 'SirkoSobaka',
|
|
|
+// 'message': 'Hello!',
|
|
|
+// 'timestamp': 1524226326628
|
|
|
+// },
|
|
|
+// ],
|
|
|
+// 'nextMessageId': 5
|
|
|
+// }
|
|
|
+
|
|
|
+// После чего вы итерируете по data и выводите каждый отдельный объект как DOM-элемент с текстом, и, возможно, вложенной
|
|
|
+// версткой (время в отдельном элементе и т.п.)
|
|
|
+// nextMessageId вы запоминаете для того, что бы отправить следующий запрос начиная с него (т. е. с 68 в примере выше).
|
|
|
+// Тогда сервер отдаст вам только сообщения новее последнего запроса.
|
|
|
+
|
|
|
+// Chat Server
|
|
|
+
|
|
|
+// Если у вас не чрезмерно свежий хром, то это можно делать даже на локалхосте и открывать файл из браузера с диска.
|
|
|
+// Сервер позволяет соединятся с собой с любого домена.
|
|
|
+
|
|
|
+// Удачи :-)
|
|
|
+
|
|
|
+const url = 'http://students.a-level.com.ua:10012';
|
|
|
+const nick = document.getElementById('nick');
|
|
|
+const message = document.getElementById('message');
|
|
|
+const btnSendMessage = document.getElementById('button');
|
|
|
+const container = document.getElementById('container');
|
|
|
+let nextMessageId = 1;
|
|
|
+let delay = 1000;
|
|
|
+setInterval(getMessages, delay);
|
|
|
+
|
|
|
+function getMessages() {
|
|
|
+ jsonPost(url, { func: 'getMessages', messageId: nextMessageId })
|
|
|
+ .then((response) => response.json())
|
|
|
+ .then((data) => {
|
|
|
+ nextMessageId = data.nextMessageId;
|
|
|
+ updateMessages(data.data);
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function jsonPost(url, data) {
|
|
|
+ return fetch(url, {
|
|
|
+ method: 'POST',
|
|
|
+ body: JSON.stringify(data),
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function updateMessages(messageList) {
|
|
|
+ messageList.forEach((msg) => {
|
|
|
+ newGotMessage(msg);
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function newGotMessage({ nick, message, timestamp }) {
|
|
|
+ const newBlock = document.createElement('div');
|
|
|
+ newBlock.classList.add('block');
|
|
|
+ const time = new Date(timestamp);
|
|
|
+ let html = '';
|
|
|
+ html += `<p>${nick}<p>`;
|
|
|
+ html += `<p class='message'>'${message}'</p>`;
|
|
|
+ html += `<p>${time.toString()}</p>`;
|
|
|
+ newBlock.innerHTML = html;
|
|
|
+ container.prepend(newBlock);
|
|
|
+}
|
|
|
+
|
|
|
+btnSendMessage.addEventListener('click', sendAndCheck);
|
|
|
+
|
|
|
+async function sendAndCheck() {
|
|
|
+ await sendMessage();
|
|
|
+ getMessages();
|
|
|
+ message.value = '';
|
|
|
+}
|
|
|
+
|
|
|
+async function sendMessage() {
|
|
|
+ jsonPost(url, {
|
|
|
+ func: 'addMessage',
|
|
|
+ nick: nick.value,
|
|
|
+ message: message.value,
|
|
|
+ });
|
|
|
+}
|