|
@@ -0,0 +1,223 @@
|
|
|
+//CHAT
|
|
|
+
|
|
|
+// -------------УСЛОВИЕ-------------
|
|
|
+// Этапы.
|
|
|
+// Используя функцию 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 вы запоминаете для того, что бы отправить следующий запрос начиная с него(т.е.с 5 в примере выше).
|
|
|
+// Тогда сервер отдаст вам только сообщения новее последнего запроса.
|
|
|
+// Chat Server
|
|
|
+// Можно глянуть тут: сервер Обратите внимание на массив messages и на функции в ассоциативном массиве RPCFuncs.
|
|
|
+// А также на эти строки кода
|
|
|
+
|
|
|
+// Спойлер
|
|
|
+// чатик
|
|
|
+// Но вам все равно надо переписать это на async, await, Promise и DOM.
|
|
|
+// Где это делать?
|
|
|
+// Если у вас не чрезмерно свежий хром, то это можно делать даже на локалхосте и открывать файл из браузера с диска.
|
|
|
+// Сервер позволяет соединятся с собой с любого домена.
|
|
|
+
|
|
|
+// -------------РЕШЕНИЕ-------------
|
|
|
+// Stage 0
|
|
|
+let lastMessageId = 0;
|
|
|
+// 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'))
|
|
|
+// }
|
|
|
+// }
|
|
|
+// })
|
|
|
+// };
|
|
|
+
|
|
|
+ // Stage 1
|
|
|
+// const sendMessage = async (nick, message) => {
|
|
|
+ async function sendMessage(nick, message){
|
|
|
+ let res = await jsonPost("http://students.a-level.com.ua:10012", {
|
|
|
+ func: 'addMessage', nick: nick,
|
|
|
+ message: message
|
|
|
+ })
|
|
|
+ console.log(res.nextMessageId);
|
|
|
+}
|
|
|
+
|
|
|
+send.onclick = () => {
|
|
|
+ let nickInput = nick.value;
|
|
|
+ let msgInput = msg.value;
|
|
|
+ if (nickInput && msgInput) { sendAndCheck(nickInput, msgInput) }
|
|
|
+ msg.value = '';
|
|
|
+};
|
|
|
+ // Stage 2
|
|
|
+ async function getMessages(){
|
|
|
+ let res = await jsonPost("http://students.a-level.com.ua:10012", {
|
|
|
+ func: 'getMessages', messageId: lastMessageId
|
|
|
+ })
|
|
|
+ lastMessageId = res.nextMessageId;
|
|
|
+ historyDraw(res.data);
|
|
|
+}
|
|
|
+ //Отрисовывает данные
|
|
|
+function historyDraw(array) {
|
|
|
+ let history = array.reverse().map(item => `<div class='postWrapper'><div class='timeWrapper'>${dateTransform(item.timestamp)}</div> <b>${item.nick}:</b>  ${item.message}</div>`);
|
|
|
+ chat.insertAdjacentHTML('afterbegin', history.join(''));
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+//Приводит дату к нужному виду
|
|
|
+function dateTransform(date) {
|
|
|
+ date = new Date(date);
|
|
|
+ return ((date.getHours() < 10 ? ('0' + date.getHours() ) : date.getHours()) + ':' + (date.getMinutes() < 10 ? ('0' + date.getMinutes() ) : date.getMinutes()) + ' ' + (date.getDate() < 10 ? ('0' + (date.getDate())) : date.getDate() )+ '.' + ((date.getMonth() + 1) < 10 ? ('0' + (date.getMonth() + 1)) : (date.getMonth() + 1)) + '.' + date.getFullYear());
|
|
|
+}
|
|
|
+
|
|
|
+// Stage 4
|
|
|
+// setInterval(() => getMessages(lastMessageId), 5000);
|
|
|
+
|
|
|
+
|
|
|
+// Stage 5
|
|
|
+async function sendAndCheck(nick, message) {
|
|
|
+ sendMessage(nick, message);
|
|
|
+ getMessages();
|
|
|
+}
|
|
|
+const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms));
|
|
|
+
|
|
|
+// async function checkLoop() { setInterval(() => getMessages(lastMessageId), 5000); }
|
|
|
+async function checkLoop() {
|
|
|
+ while (true) {
|
|
|
+ await delay(5000);
|
|
|
+ getMessages(lastMessageId);
|
|
|
+}
|
|
|
+}
|
|
|
+checkLoop();
|
|
|
+
|
|
|
+// Stage 6
|
|
|
+async function jsonPost(url, data) {
|
|
|
+ let response=await fetch(url, {
|
|
|
+ method: 'POST',
|
|
|
+ body: JSON.stringify(data) })
|
|
|
+
|
|
|
+ if (response.ok) {
|
|
|
+ return await response.json();
|
|
|
+} else {
|
|
|
+ alert("Ошибка HTTP: " + response.status);
|
|
|
+}
|
|
|
+};
|
|
|
+
|
|
|
+
|