# Chat Homework ## Этапы. Используя функцию `jsonPost` на адрес http://students.a-level.com.ua:10012 напишите чат-клиент, который: ### Stage 0 Для поиграться скопируйте в консоль функцию `jsonPost` (или запустите её с этой страницы) и вызовите её с теми или иными объектами в качестве второго параметра. см. `RPC`. ```javascript jsonPost("http://students.a-level.com.ua:10012", {func: 'addMessage', nick: "Anon", message: 'Я не умею копипастить в консоль, зато умею жать красную кнопку.'}) ``` Если после объявления функции `jsonPost` запустить пример выше, то вы напишите в чат. ### Stage 1 Отправляет сообщения в чат. Для проверки отслеживайте приходящий с сервера `nextMessageId`, который должен увеличиваться. **Интерфейс:** поле ввода ника, поле ввода сообщения, кнопка отправки. ### Stage 2 Читает только *новые* сообщения из чата. Для этого надо после каждого получения запоминать `nextMessageId` и отправлять его при следующем запросе новых сообщений. Изначально `nextMessageId` равен нулю, что бы вычитать всю историю сообщений с сервера. **Интерфейс:** некий `div`-контейнер, в котором на каждое сообщение создается `div` (или иной блочный контейнерный тэг) и в него помещается ник и сообщение. Также там есть `timestamp` который может показывать время отправки сообщения. ### Stage 3 Делаем **Stage 2** в `setInterval` для периодической проверки сообщений (раз в 2 -5 секунд). ### Stage 4 Напишите асинхронную функцию отправки, которая внутри себя будет делать два запроса: на отправку и на проверку, для того что бы минимизировать задержку между отправкой сообщения пользователя и его появлением в окне чата. Оформите отдельно функции отправки и проверки новых сообщений как асинхронные (`async`, возвращает `Promise`) ## Информация ### `jsonPost` Данная промисифицированная функция (кстати, сделайте из неё асинхронную) умеет общаться с моим чат-сервером отправляя **RPC** запросы используя **JSON**. **R**emote **P**rocedure **C**all - вызов функций удаленно, имя функции и параметры передаются AJAX-ом в формате (например) **JSON**. ```javascript 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`. ```javascript {func: "addMessage", nick: 'msg', message: 'msg'} ``` Поле `func` является **обязательным** и содержит имя функции, которая должна быть вызвана на сервере. Функция на сервере получает параметром остальной объект (поля `nick` и `message`) **В ответ** вы получите так же объект: ```javascript {nextMessageId: 100500} ``` где 100500: новая длина массива сообщений на сервере после добавления вашего сообщения. #### `getMessages` Позволяет прочесть *часть* массива сообщений от отпределенного индекса и до конца. Используется для последовательной *дочитки новых* сообщений. Для этого надо передать в `jsonPost` подобный объект: ```javascript {func: "getMessages", messageId: 0} ``` При значении 0 в `messageId` сервер отдаст сообщения от 0 до конца, т. е. весь массив: ```javascript { "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 Можно глянуть тут: http://gitlab.a-level.com.ua/gitgod/FrontendLectures/src/master/chatServer/server.js Обратите внимание на массив `messages` и на функции в ассоциативном массиве `RPCFuncs`. А также на http://gitlab.a-level.com.ua/gitgod/FrontendLectures/src/master/chatServer/server.js#L43-L51 ### Спойлер http://chat.asmer.fe.a-level.com.ua/ Но вам все равно надо переписать это на `async`, `await`, `Promise` и **DOM**. ### Где это делать? Если у вас не чрезмерно свежий хром, то это можно делать даже на локалхосте и открывать файл из браузера с диска. Сервер позволяет соединятся с собой с любого домена. ## Удачи :-)