Browse Source

HW17 done

OLGASTUPNIK 3 years ago
commit
926c4bcc21
4 changed files with 484 additions and 0 deletions
  1. 113 0
      css/style.css
  2. 30 0
      index.html
  3. 213 0
      script.js
  4. 128 0
      server.js

+ 113 - 0
css/style.css

@@ -0,0 +1,113 @@
+#container{
+    max-width: 1200px;
+    margin: auto;
+}
+.black{
+background: black;
+}
+.light{
+    background: white;
+}
+.chatik{
+    display: flex;
+}
+@keyframes ldio-kk0sdst3cs {
+    0% {
+        opacity: 1;
+        backface-visibility: hidden;
+        transform: translateZ(0) scale(1.5,1.5);
+    } 100% {
+          opacity: 0;
+          backface-visibility: hidden;
+          transform: translateZ(0) scale(1,1);
+      }
+}
+.ldio-kk0sdst3cs div > div {
+    position: absolute;
+    width: 12px;
+    height: 12px;
+    border-radius: 50%;
+    background: #93dbe9;
+    animation: ldio-kk0sdst3cs 1.8518518518518516s linear infinite;
+}.ldio-kk0sdst3cs div:nth-child(1) > div {
+     left: 74px;
+     top: 44px;
+     animation-delay: -1.6203703703703702s;
+ }
+.ldio-kk0sdst3cs > div:nth-child(1) {
+    transform: rotate(0deg);
+    transform-origin: 80px 50px;
+}.ldio-kk0sdst3cs div:nth-child(2) > div {
+     left: 65px;
+     top: 65px;
+     animation-delay: -1.3888888888888888s;
+ }
+.ldio-kk0sdst3cs > div:nth-child(2) {
+    transform: rotate(45deg);
+    transform-origin: 71px 71px;
+}.ldio-kk0sdst3cs div:nth-child(3) > div {
+     left: 44px;
+     top: 74px;
+     animation-delay: -1.1574074074074074s;
+ }
+.ldio-kk0sdst3cs > div:nth-child(3) {
+    transform: rotate(90deg);
+    transform-origin: 50px 80px;
+}.ldio-kk0sdst3cs div:nth-child(4) > div {
+     left: 23px;
+     top: 65px;
+     animation-delay: -0.9259259259259258s;
+ }
+.ldio-kk0sdst3cs > div:nth-child(4) {
+    transform: rotate(135deg);
+    transform-origin: 29px 71px;
+}.ldio-kk0sdst3cs div:nth-child(5) > div {
+     left: 14px;
+     top: 44px;
+     animation-delay: -0.6944444444444444s;
+ }
+.ldio-kk0sdst3cs > div:nth-child(5) {
+    transform: rotate(180deg);
+    transform-origin: 20px 50px;
+}.ldio-kk0sdst3cs div:nth-child(6) > div {
+     left: 23px;
+     top: 23px;
+     animation-delay: -0.4629629629629629s;
+ }
+.ldio-kk0sdst3cs > div:nth-child(6) {
+    transform: rotate(225deg);
+    transform-origin: 29px 29px;
+}.ldio-kk0sdst3cs div:nth-child(7) > div {
+     left: 44px;
+     top: 14px;
+     animation-delay: -0.23148148148148145s;
+ }
+.ldio-kk0sdst3cs > div:nth-child(7) {
+    transform: rotate(270deg);
+    transform-origin: 50px 20px;
+}.ldio-kk0sdst3cs div:nth-child(8) > div {
+     left: 65px;
+     top: 23px;
+     animation-delay: 0s;
+ }
+.ldio-kk0sdst3cs > div:nth-child(8) {
+    transform: rotate(315deg);
+    transform-origin: 71px 29px;
+}
+.loadingio-spinner-spin-po1sunno34 {
+    width: 38px;
+    height: 38px;
+    display: inline-block;
+    overflow: hidden;
+    background: none;
+}
+.ldio-kk0sdst3cs {
+    width: 100%;
+    height: 100%;
+    position: relative;
+    transform: translateZ(0) scale(0.38);
+    backface-visibility: hidden;
+    transform-origin: 0 0; /* see note above */
+}
+.ldio-kk0sdst3cs div { box-sizing: content-box; }
+/* generated by https://loading.io/ */

+ 30 - 0
index.html

@@ -0,0 +1,30 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport"
+          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+    <meta http-equiv="X-UA-Compatible" content="ie=edge">
+    <title>Document</title>
+    <link rel="stylesheet" href="css/style.css">
+
+</head>
+<body>
+<div id="container">
+
+<div class="chatik">
+<input type="text" id="nick" placeholder="nick" style="width: 25%;font-size: 2em"><br>
+<input type="text" id="msg" placeholder="message" style="width: 70%;font-size: 2em">
+    <button id="send" style="width: 5%;font-size: 2em">
+        <div id="spinner" class="loadingio-spinner-spin-po1sunno34"><div class="ldio-kk0sdst3cs">
+            <div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div><div><div></div></div>
+        </div></div>
+
+    </button>
+</div>
+<br>
+<div id="chat"></div>
+</div>
+<script src="script.js"></script>
+</body>
+</html>

+ 213 - 0
script.js

@@ -0,0 +1,213 @@
+const URL = "http://localhost:4000/message"
+const delay = ms => new Promise((resolve) => setTimeout(() => resolve(ms), ms));
+
+
+function createStore(reducer) {
+    let state = reducer(undefined, {})
+    let subscribers = []
+
+    function dispatch(action) {
+        if (typeof action === 'function') {
+            return action(dispatch)
+        }
+        const newState = reducer(state, action)
+        if (state !== newState) {
+            state = newState
+            subscribers.forEach(cb => cb())
+        }
+    }
+
+    return {
+        getState() {
+            return state;
+        },
+        subscribe(cb) {
+            subscribers.push(cb)
+            return () => subscribers = subscribers.filter(c => c !== cb)
+        },
+        dispatch
+    }
+}
+
+let btnSend = document.querySelector('#send');
+let inputNick = document.querySelector('#nick');
+let inputMessage = document.querySelector('#msg');
+let lastMessageId =0;
+
+async function getHistory() {
+    let msg = await jsonPost(
+        URL,
+        {func: 'getMessages',
+               messageId: lastMessageId})
+    lastMessageId = msg.nextMessageId
+    msg.data.forEach(i => showMessage(i.nick, i.message))
+}
+
+getHistory().then();
+
+function showMessage(inputNick, inputMessage) {
+    let divChat = document.querySelector('#chat');
+    let msgDiv = document.createElement('div');
+    msgDiv.style.display = 'inline-block';
+    let nickSpan = document.createElement('span');
+    nickSpan.innerHTML= '<b/>' + inputNick + ' : '
+    let messSpan = document.createElement('span');
+    messSpan.innerHTML= inputMessage
+
+    msgDiv.style.cssText = 'border-bottom: 1px solid gray; padding: 5px;'
+    divChat.prepend(msgDiv);
+    msgDiv.prepend(messSpan);
+    msgDiv.prepend(nickSpan);
+
+}
+
+
+function chatReducer(
+    state = {
+        messages: [],
+        sendStatus: "",
+        getStatus: "",
+        nextMessageId: 0
+    },
+    {type, messages=[]}) {
+
+    if (type === 'SEND_PENDING') {
+        return {...state, sendStatus: 'PENDING'}
+    }
+    if (type === 'SEND_RESOLVED') {
+        return {...state, sendStatus: 'RESOLVED'}
+    }
+    if (type === 'SEND_REJECTED') {
+        return {...state, sendStatus: 'REJECTED'}
+    }
+    if (type === 'SEND_CLEAR') {
+        return {...state, sendStatus: 'CLEAR'}
+    }
+    if (type === 'GET_PENDING') {
+        return {...state, getStatus: 'PENDING'}
+    }
+    if (type === 'GET_RESOLVED') {
+        return {...state, getStatus: 'PENDING', messages: [...state.messages, ...messages]}
+    }
+    if (type === 'GET_REJECTED') {
+        return {...state, getStatus: 'REJECTED'}
+    }
+
+
+    return state;
+}
+
+const actionSendPending = () => ({type: "SEND_PENDING"})
+const actionSendResolved = () => ({type: "SEND_RESOLVED"})
+const actionSendRejected = () => ({type: "SEND_REJECTED"})
+
+const actionGetPending = () => ({type: "GET_PENDING"})
+const actionGetResolved = (messages, nextMessageId) => ({type: "GET_RESOLVED", messages,nextMessageId})
+const actionGetRejected = () => ({type: "GET_REJECTED"})
+const actionSendClear = () => ({type: "SEND_CLEAR"})
+
+const store = createStore(chatReducer)
+
+store.subscribe(() => console.log(store.getState()));
+
+store.subscribe(() => {}
+    // btnSend.disabled = store.getState().sendStatus === "PENDING";
+    // store.getState().sendStatus === "RESOLVED"  && store.dispatch(actionSendClear())}
+    // store.dispatch(actionSendClear())
+ )
+
+const actionSend = (nick, message) =>
+    async dispatch => {
+        try {
+            dispatch(actionSendPending())
+            await jsonPost(
+                URL,
+                {
+                    func: 'addMessage',
+                    nick: nick,
+                    message: message
+                }
+            )
+            dispatch(actionSendResolved())
+        } catch (e) {
+            dispatch(actionSendRejected(e))
+        }
+    }
+
+const actionGet = () =>
+
+    async dispatch => {
+        try {
+
+            dispatch(actionGetPending())
+            let data= await jsonPost(
+                URL,
+                {
+                    func: 'getMessages',
+                    messageId: lastMessageId
+                }
+            )
+            dispatch(actionGetResolved(data.data,data.nextMessageId))
+            lastMessageId = data.nextMessageId;
+            data.data.forEach(i => showMessage(i.nick, i.message))
+        } catch (e) {
+            dispatch(actionGetRejected(e))
+        }
+        finally {
+            // checkLoop().then()
+        }
+
+    }
+store.getState()
+const sendAndCheck=async()=>{
+    if(inputNick.value.trim() && inputMessage.value.trim()){
+    await store.dispatch(actionSend(inputNick.value, inputMessage.value))
+    await store.dispatch(actionGet())}
+}
+async function checkLoop() {
+    while (true) {
+        store.dispatch(actionGet())
+        await delay(3000);
+        // await getMessages();
+    }
+}
+checkLoop().then()
+
+
+store.subscribe(() => {
+    let currentState = store.getState()
+    if ("PENDING") {
+        msg.placeholder = 'enter your message'
+        spinner.style.display = "inline-block";
+
+    } else if (currentState === "REJECTED") {
+
+        msg.placeholder = 'error'
+        msg.style.background='red'
+        spinner.style.display = "none";
+    } else if (currentState === "RESOLVED") {
+        msg.placeholder = 'enter your message'
+        spinner.style.display = "none";
+    }
+})
+
+
+btnSend.onclick = () => {store.dispatch(sendAndCheck())
+    inputMessage.value=''
+};
+
+
+
+async function jsonPost(url, data) {
+    let response = await fetch(url, {
+        method: 'POST',
+        body: JSON.stringify(data),
+    });
+
+    if (response.status == 200) {
+        let data2 = await response.json();
+        return data2;
+    }
+
+    throw new Error('failed');
+}

+ 128 - 0
server.js

@@ -0,0 +1,128 @@
+#!/usr/bin/node
+
+const http = require('http')
+
+const history = [{nick: 'server', message: 'HELLO'}]
+
+const routes = {
+
+    '/message': {
+        POST(req, res) {
+            res.statusCode = 201
+            let json = ''
+
+            req.on('data', partOfJson => {
+                json += partOfJson
+            })
+
+            req.on('end', () => {
+                try {
+                    const message = JSON.parse(json)
+                    message.timestamp = (new Date).toISOString()
+                    history.push(message)
+                    res.end(JSON.stringify(message))
+                } catch (e) {
+                    res.statusCode=403
+                        res.end('WRONG JSON')
+                }
+            })
+        },
+        GET(req, res) {
+            res.statusCode=200
+            res.end(JSON.stringify(history))
+            // res.setHeader('Access-Control-Allow-Origin', '*');
+            // res.setHeader('Access-Control-Allow-Headers', 'origin, content-type, accept');
+        }
+    },
+    '/message-by-id': {
+        POST(req, res) {
+            res.statusCode = 201
+            let json = ''
+            req.on('data', data => {
+                json += data
+            })
+            req.on('end', () => {
+                try {
+                    const message = JSON.parse(json)
+                    res.end(JSON.stringify(history.find((item) => {
+                        return item.timestamp === message.timestamp
+                    })))
+                } catch (e) {
+                    res.statusCode=403
+                        res.end('NEMA')
+                }
+            })
+            //выколупать из req JSON вида {timestamp: 'ДАТА'}
+            //произвести поиск оного сообщения
+            //отдать его одно
+        }
+    },
+    '/message-after': {
+        POST(req, res) {
+            res.statusCode = 201
+            let json = ''
+            req.on('data', data => {
+                json += data
+            })
+            req.on('end', () => {
+                try {
+                    const message = JSON.parse(json)
+                    let arr = []
+                    const index = history.findIndex((item) => {
+                        return item.timestamp === message.timestamp
+                    })
+
+                    for (let i = index + 1; i < history.length; i++) {
+                        arr.push[i]
+                    }
+                    res.end(JSON.stringify(arr))
+                } catch (e) {
+                    res.statusCode=403
+                        res.end('NEMA')
+                }
+            })
+            //выколупать из req JSON вида {timestamp: 'ДАТА'}
+            //произвести поиск всех сообщений после
+            //отдать их
+        }
+    },
+    '/message-before': {
+        POST(req, res) {
+            res.statusCode = 201
+            let json = ''
+            req.on('data', data => {
+                json += data
+            })
+            req.on('end', () => {
+                try {
+                    const message = JSON.parse(json)
+                    let arr = []
+                    const index = history.findIndex((item) => {
+                        return item.timestamp === message.timestamp
+                    })
+
+                    for (let i = index - 1; i >= 0; i--) {
+                        arr.push[i]
+                    }
+                    res.end(JSON.stringify(arr))
+                } catch (e) {
+                    res.statusCode=403;
+                        res.end('NEMA')
+                }
+            })
+            //выколупать из req JSON вида {timestamp: 'ДАТА'}
+            //произвести поиск всех сообщений до даты
+            //отдать их
+        }
+    }
+}
+//+обновите фронт под этот бэк.
+
+http.createServer((req, res) => {
+    if (req.url in routes && req.method in routes[req.url]) {
+        routes[req.url][req.method](req, res)
+    } else {
+        res.statusCode = 404
+        res.end('<h1>НЭМА</h1>')
+    }
+}).listen(4000)