From e1af08d0f8eef7a038b6791b2df6a0df4b3a5806 Mon Sep 17 00:00:00 2001 From: DanielPhilosoph Date: Wed, 29 Dec 2021 16:03:03 +0200 Subject: [PATCH 01/15] css --- front/package-lock.json | 175 +++++++++++++++++++++++++++++++ front/package.json | 1 + front/public/images/chat.png | Bin 0 -> 15311 bytes front/public/images/chatBG.png | Bin 0 -> 110228 bytes front/public/index.html | 26 +++++ front/src/App.jsx | 12 ++- front/src/core/Chat.jsx | 26 +++++ front/src/core/ChatRoom.jsx | 13 +++ front/src/core/EnterNamePage.jsx | 37 ++++++- front/src/core/NavBar.jsx | 21 +++- front/src/helper/functions.js | 17 +++ front/src/index.css | 128 ++++++++++++++++++++++ 12 files changed, 449 insertions(+), 7 deletions(-) create mode 100644 front/public/images/chat.png create mode 100644 front/public/images/chatBG.png create mode 100644 front/src/core/Chat.jsx create mode 100644 front/src/core/ChatRoom.jsx create mode 100644 front/src/helper/functions.js diff --git a/front/package-lock.json b/front/package-lock.json index d1574aa..e92e88a 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -1843,6 +1843,44 @@ } } }, + "@popperjs/core": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.0.tgz", + "integrity": "sha512-zrsUxjLOKAzdewIDRWy9nsV1GQsKBCWaGwsZQlCgr6/q+vjyZhFgqedLfFBuI9anTPEUT4APq9Mu0SZBTzIcGQ==" + }, + "@react-aria/ssr": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.1.0.tgz", + "integrity": "sha512-RxqQKmE8sO7TGdrcSlHTcVzMP450hqowtBSd2bBS9oPlcokVkaGq28c3Rwa8ty5ctw4EBCjXqjP7xdcKMGDzug==", + "requires": { + "@babel/runtime": "^7.6.2" + } + }, + "@restart/hooks": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.5.tgz", + "integrity": "sha512-tLGtY0aHeIfT7aPwUkvQuhIy3+q3w4iqmUzFLPlOAf/vNUacLaBt1j/S//jv/dQhenRh8jvswyMojCwmLvJw8A==", + "requires": { + "dequal": "^2.0.2" + } + }, + "@restart/ui": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-0.2.5.tgz", + "integrity": "sha512-3dP8pMFickPpvAG5MVQW53HnJl0c17h7MwvI4nNy9QF66sHSYVchudlqlI8eOSaqnmc5YVjGura63vMb9LTNbQ==", + "requires": { + "@babel/runtime": "^7.13.16", + "@popperjs/core": "^2.10.1", + "@react-aria/ssr": "^3.0.1", + "@restart/hooks": "^0.4.0", + "@types/warning": "^3.0.0", + "dequal": "^2.0.2", + "dom-helpers": "^5.2.0", + "prop-types": "^15.7.2", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + } + }, "@rollup/plugin-babel": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz", @@ -2331,6 +2369,11 @@ "@types/node": "*" } }, + "@types/invariant": { + "version": "2.2.35", + "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.35.tgz", + "integrity": "sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg==" + }, "@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", @@ -2391,6 +2434,11 @@ "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.2.tgz", "integrity": "sha512-ekoj4qOQYp7CvjX8ZDBgN86w3MqQhLE1hczEJbEIjgFEumDy+na/4AJAbLXfgEWFNB2pKadM5rPFtuSGMWK7xA==" }, + "@types/prop-types": { + "version": "15.7.4", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", + "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==" + }, "@types/q": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", @@ -2406,6 +2454,24 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, + "@types/react": { + "version": "17.0.38", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.38.tgz", + "integrity": "sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-transition-group": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz", + "integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==", + "requires": { + "@types/react": "*" + } + }, "@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -2419,6 +2485,11 @@ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==" }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, "@types/serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", @@ -2462,6 +2533,11 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" }, + "@types/warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI=" + }, "@types/ws": { "version": "8.2.2", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz", @@ -3546,6 +3622,11 @@ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==" }, + "classnames": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + }, "clean-css": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.2.2.tgz", @@ -4063,6 +4144,11 @@ } } }, + "csstype": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", + "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==" + }, "damerau-levenshtein": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz", @@ -4175,6 +4261,11 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" }, + "dequal": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.2.tgz", + "integrity": "sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug==" + }, "destroy": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", @@ -4290,6 +4381,15 @@ "utila": "~0.4" } }, + "dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "dom-serializer": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", @@ -6006,6 +6106,14 @@ "side-channel": "^1.0.4" } }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, "ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -9204,6 +9312,15 @@ "react-is": "^16.13.1" } }, + "prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "requires": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + } + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -9311,6 +9428,29 @@ "whatwg-fetch": "^3.6.2" } }, + "react-bootstrap": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.0.4.tgz", + "integrity": "sha512-sfxhLKY/P5oeqkcxI4Q3SkJKLBq/7pv1wGykGlmCLWp4Pll3HMVl5VRtVbKsE4FzGsZGhXXauhi2HhRbmWLwBA==", + "requires": { + "@babel/runtime": "^7.14.0", + "@restart/hooks": "^0.4.5", + "@restart/ui": "^0.2.5", + "@types/invariant": "^2.2.33", + "@types/prop-types": "^15.7.3", + "@types/react": ">=16.14.8", + "@types/react-transition-group": "^4.4.1", + "@types/warning": "^3.0.0", + "classnames": "^2.3.1", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.7.2", + "prop-types-extra": "^1.1.0", + "react-transition-group": "^4.4.1", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + } + }, "react-dev-utils": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.0.tgz", @@ -9417,6 +9557,11 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, "react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -9629,6 +9774,17 @@ } } }, + "react-transition-group": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", + "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==", + "requires": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + } + }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -10868,6 +11024,17 @@ "which-boxed-primitive": "^1.0.2" } }, + "uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "requires": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + } + }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -11010,6 +11177,14 @@ "makeerror": "1.0.12" } }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, "watchpack": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", diff --git a/front/package.json b/front/package.json index f695245..e766f56 100644 --- a/front/package.json +++ b/front/package.json @@ -7,6 +7,7 @@ "@testing-library/react": "12.1.2", "@testing-library/user-event": "13.5.0", "react": "^17.0.2", + "react-bootstrap": "2.0.4", "react-dom": "^17.0.2", "react-router-dom": "6.2.1", "react-scripts": "5.0.0", diff --git a/front/public/images/chat.png b/front/public/images/chat.png new file mode 100644 index 0000000000000000000000000000000000000000..124f65e51e1474e1be63e8c691fb27a732fd4cdb GIT binary patch literal 15311 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 literal 0 HcmV?d00001 diff --git a/front/public/images/chatBG.png b/front/public/images/chatBG.png new file mode 100644 index 0000000000000000000000000000000000000000..69830374e8aa3705c99d0ff05558f1f988611257 GIT binary patch literal 110228 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
  • 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%0Dl0_^ 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 literal 0 HcmV?d00001 diff --git a/front/public/index.html b/front/public/index.html index d09b05e..3ff8593 100644 --- a/front/public/index.html +++ b/front/public/index.html @@ -5,7 +5,33 @@ + + + + + + + + + + + + Chat App 2.0 diff --git a/front/src/App.jsx b/front/src/App.jsx index 13aadf5..aa160b9 100644 --- a/front/src/App.jsx +++ b/front/src/App.jsx @@ -1,9 +1,19 @@ import React from "react"; +import { BrowserRouter, Routes, Route } from "react-router-dom"; import "./App.css"; +import ChatRoom from "./core/ChatRoom"; +import EnterNamePage from "./core/EnterNamePage"; function App() { - return
    ; + return ( + + + } /> + } /> + + + ); } export default App; diff --git a/front/src/core/Chat.jsx b/front/src/core/Chat.jsx new file mode 100644 index 0000000..2a58ed3 --- /dev/null +++ b/front/src/core/Chat.jsx @@ -0,0 +1,26 @@ +import React from "react"; + +export default function Chat() { + return ( +
    +
    +
    + Online Users +
    +
    +
    +
    + Img + Chat +
    +
    sss
    +
    + + +
    +
    +
    + ); +} diff --git a/front/src/core/ChatRoom.jsx b/front/src/core/ChatRoom.jsx new file mode 100644 index 0000000..ae6ba6a --- /dev/null +++ b/front/src/core/ChatRoom.jsx @@ -0,0 +1,13 @@ +import React from "react"; +import Chat from "./Chat"; +import NavBar from "./NavBar"; + +export default function ChatRoom() { + document.documentElement.style.setProperty("--background-color", "#526377"); + return ( +
    + + +
    + ); +} diff --git a/front/src/core/EnterNamePage.jsx b/front/src/core/EnterNamePage.jsx index acc12e4..c0c68cc 100644 --- a/front/src/core/EnterNamePage.jsx +++ b/front/src/core/EnterNamePage.jsx @@ -1,12 +1,39 @@ -import React from "react"; +import React, { useRef } from "react"; +import { Form, Button } from "react-bootstrap"; +import { useNavigate } from "react-router-dom"; + +import { validateName } from "../helper/functions"; export default function EnterNamePage() { + const navigate = useNavigate(); + const nameInput = useRef(); + const errorRef = useRef(); + + const onLoginClick = (e) => { + e.preventDefault(); + const response = validateName(nameInput.current.value); + if (response.valid) { + // GOOD + navigate("/chat-room"); + } else { + errorRef.current.innerText = response.error; + } + }; + return (
    -

    Enter name:

    -

    - -

    +
    +

    Login To Chat Room!

    +
    + + Name + + +

    + +

    ); } diff --git a/front/src/core/NavBar.jsx b/front/src/core/NavBar.jsx index 859a0cc..0891461 100644 --- a/front/src/core/NavBar.jsx +++ b/front/src/core/NavBar.jsx @@ -1,7 +1,26 @@ import React from "react"; +import { Navbar, Container, Nav } from "react-bootstrap"; function NavBar() { - return
    ; + return ( + + + React-Bootstrap + + + + + + + + ); } export default NavBar; diff --git a/front/src/helper/functions.js b/front/src/helper/functions.js new file mode 100644 index 0000000..1da5c43 --- /dev/null +++ b/front/src/helper/functions.js @@ -0,0 +1,17 @@ +export function validateName(name) { + if (name === "") { + return { valid: false, error: "Can't be null" }; + } + if (name.length < 3) { + return { valid: false, error: "Must be higher then 3 characters" }; + } + if (!/^[a-zA-Z- 0-9]+$/.test(name)) { + return { valid: false, error: "Can't contain special characters" }; + } + return { valid: true }; +} + +export function temp() { + const x = 3; + return x; +} diff --git a/front/src/index.css b/front/src/index.css index f16c44e..f29b273 100644 --- a/front/src/index.css +++ b/front/src/index.css @@ -1,4 +1,132 @@ +:root { + --background-color: linear-gradient( + 90deg, + rgba(45, 226, 255, 1) 0%, + rgba(19, 50, 200, 1) 0%, + rgba(0, 219, 255, 1) 100% + ); +} + +body { + background: var(--background-color); + min-height: 100vh; +} + +.form { + width: 100%; + margin: 0 auto; + top: 50%; +} + .EnterNamePageDiv { + width: 40%; padding: 100px; + background-color: white; + border-radius: 15px; + display: flex; + margin: 0 auto; + position: absolute; + top: 25%; + left: 30%; + justify-content: center; +} + +.loginError { + font-size: 12px; + color: red; +} + +.usersOnlineDiv { + flex-grow: 3; + width: 10%; + background-color: salmon; + height: 800px; +} +.chatDiv { + flex-grow: 10; + width: 10%; + background-image: url("../public/images/chatBG.png"); + height: 800px; + position: relative; +} +.warperChatDiv { + display: flex; + flex-direction: ltr; + width: 80%; margin: auto; + margin-top: 50px; +} + +.usersOnlineHeaderDiv { + background-color: silver; + height: 60px; + font-family: "Raleway", sans-serif; + font-size: 30px; + padding: 5px 10px; +} + +.chatHeaderDiv { + background-color: rgb(114, 114, 114); + height: 60px; + padding: 7px 10px; +} + +.chatLogo { + border-radius: 50%; + width: 40px; + margin-top: -10px; +} + +.usersOnlineText { + padding: 10px; +} + +.chatHeaderText { + font-family: "Raleway", sans-serif; + font-size: 30px; + padding: 0px 10px; +} + +.sendMassageDiv { + left: 0; + bottom: 0; + position: absolute; + width: 100%; + height: 50px; + padding: 10px; + background-color: rgb(161, 161, 161); +} + +.messageInput { + width: 85%; + height: 30px; + border-radius: 5px; + border: 0; +} + +.sendButton { + padding: 7px; + margin-left: 22px; + border: 0.1px solid blue; + border-radius: 50%; + font-size: 16px; + background-color: rgb(45, 45, 253); + color: whitesmoke; +} + +@media screen and (max-width: 900px) { + .warperChatDiv { + flex-direction: column-reverse; + gap: 30px; + } + + .chatDiv { + width: 100%; + height: 500px; + } + + .usersOnlineDiv { + width: 100%; + height: 500px; + } } From 3645e4a70f05ea1563c44d83e5c508ea2875ee02 Mon Sep 17 00:00:00 2001 From: DanielPhilosoph Date: Wed, 29 Dec 2021 16:10:16 +0200 Subject: [PATCH 02/15] css --- front/public/index.html | 8 +++----- front/src/core/Chat.jsx | 6 +++++- front/src/index.css | 18 ++++++++++-------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/front/public/index.html b/front/public/index.html index 3ff8593..7d3cfcf 100644 --- a/front/public/index.html +++ b/front/public/index.html @@ -23,12 +23,10 @@ - diff --git a/front/src/core/Chat.jsx b/front/src/core/Chat.jsx index 2a58ed3..7372c9d 100644 --- a/front/src/core/Chat.jsx +++ b/front/src/core/Chat.jsx @@ -15,7 +15,11 @@ export default function Chat() {
    sss
    - + diff --git a/front/src/index.css b/front/src/index.css index f29b273..fa439f7 100644 --- a/front/src/index.css +++ b/front/src/index.css @@ -39,7 +39,7 @@ body { .usersOnlineDiv { flex-grow: 3; width: 10%; - background-color: salmon; + background-color: #8d99a7; height: 800px; } .chatDiv { @@ -58,17 +58,19 @@ body { } .usersOnlineHeaderDiv { - background-color: silver; + background-color: #315b8a; height: 60px; font-family: "Raleway", sans-serif; font-size: 30px; padding: 5px 10px; + color: whitesmoke; } .chatHeaderDiv { - background-color: rgb(114, 114, 114); + background-color: #165eb1; height: 60px; padding: 7px 10px; + color: whitesmoke; } .chatLogo { @@ -98,19 +100,19 @@ body { } .messageInput { - width: 85%; + width: 92%; height: 30px; border-radius: 5px; border: 0; + background: transparent; } .sendButton { - padding: 7px; + padding: 3px 10px; margin-left: 22px; - border: 0.1px solid blue; + border: 0.1px solid rgb(92, 92, 92); border-radius: 50%; - font-size: 16px; - background-color: rgb(45, 45, 253); + background-color: rgb(92, 92, 92); color: whitesmoke; } From 1f74ba12c2297544edb67a8fbd3b799bf7b2a58f Mon Sep 17 00:00:00 2001 From: DanielPhilosoph Date: Wed, 29 Dec 2021 17:09:18 +0200 Subject: [PATCH 03/15] connect works - back is ready --- front/.gitignore => .gitignore | 2 +- .vscode/settings.json | 3 +- back/app.js | 42 + back/package-lock.json | 1411 ++++++++++++++++++++++++++++++ back/package.json | 19 + front/.eslintrc.json | 5 +- front/package-lock.json | 93 ++ front/package.json | 2 + front/src/App.jsx | 7 +- front/src/core/Chat.jsx | 8 +- front/src/core/ChatRoom.jsx | 23 +- front/src/core/EnterNamePage.jsx | 9 +- 12 files changed, 1609 insertions(+), 15 deletions(-) rename front/.gitignore => .gitignore (95%) create mode 100644 back/app.js create mode 100644 back/package-lock.json create mode 100644 back/package.json diff --git a/front/.gitignore b/.gitignore similarity index 95% rename from front/.gitignore rename to .gitignore index 4d29575..04c5395 100644 --- a/front/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies -/node_modules +node_modules /.pnp .pnp.js diff --git a/.vscode/settings.json b/.vscode/settings.json index 4162b6d..f34a02c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "editor.codeActionsOnSave": { - "source.fixAll.eslint": true + "source.fixAll.eslint": true, + "source.fixAll.tslint": true, }, "eslint.validate": [ "javascript" diff --git a/back/app.js b/back/app.js new file mode 100644 index 0000000..8b0fd81 --- /dev/null +++ b/back/app.js @@ -0,0 +1,42 @@ +const app = require("express")(); +const http = require("http").createServer(app); +const io = require("socket.io")(http); +const cors = require("cors"); +const PORT = 3001; + +//? Middleware +app.use(cors()); + +// //? Static Files +// app.use(express.static("client/build")); +// app.get("/", (req, res) => { +// res.sendFile(path.join(__dirname, "../front/public/index.html")); +// }); + +//? Socket.io Connection +io.on("connection", (socket) => { + console.log("a user connected"); + + socket.on("onConnect", ({ name }) => { + io.emit("newConnection", { name: name, time: new Date() }); + }); + + socket.on("message", (info) => { + io.emit("messageBack", { + name: info.name, + message: info.message, + time: new Date(), + }); + }); + + socket.on("disconnect", () => { + // io.emit("userDisconnect", { }); + console.log("disconnect"); + }); +}); + +//? Routers + +http.listen(PORT, () => { + console.log(`listening on ${PORT}`); +}); diff --git a/back/package-lock.json b/back/package-lock.json new file mode 100644 index 0000000..3842aa9 --- /dev/null +++ b/back/package-lock.json @@ -0,0 +1,1411 @@ +{ + "name": "back", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/component-emitter": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", + "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==" + }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + }, + "@types/node": { + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.5.tgz", + "integrity": "sha512-w3mrvNXLeDYV1GKTZorGJQivK6XLCoGwpnyJFbJVK/aTBQUxOCaa/GlFAAN3OTDFcb7h5tiFG+YXCO2By+riZw==" + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "requires": { + "string-width": "^4.1.0" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base64-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz", + "integrity": "sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA==" + }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + }, + "body-parser": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", + "requires": { + "bytes": "3.1.1", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" + } + }, + "boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "bytes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==" + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } + } + }, + "camelcase": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", + "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==" + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==" + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + } + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "requires": { + "is-obj": "^2.0.0" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "engine.io": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.0.tgz", + "integrity": "sha512-ErhZOVu2xweCjEfYcTdkCnEYUiZgkAcBBAhW4jbIvNG8SLU3orAqoJCiytZjYF7eTpVmmCrLDjLIEaPlUAs1uw==", + "requires": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.0", + "ws": "~8.2.3" + }, + "dependencies": { + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "engine.io-parser": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.2.tgz", + "integrity": "sha512-wuiO7qO/OEkPJSFueuATIXtrxF7/6GTbAO9QLv7nnbjwZ5tYhLm9zxvLwxstRs0dcT0KUlWTjtIOs1T86jt12g==", + "requires": { + "base64-arraybuffer": "~1.0.1" + } + }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", + "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.6", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "optional": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "global-dirs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", + "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "requires": { + "ini": "2.0.0" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" + }, + "http": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/http/-/http-0.0.1-security.tgz", + "integrity": "sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g==" + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=" + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "requires": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + } + }, + "is-npm": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "requires": { + "json-buffer": "3.0.0" + } + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "requires": { + "package-json": "^6.3.0" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" + }, + "mime-types": { + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "requires": { + "mime-db": "1.51.0" + } + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "nodemon": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.15.tgz", + "integrity": "sha512-gdHMNx47Gw7b3kWxJV64NI+Q5nfl0y5DgDbiVtShiwa7Z0IZ07Ll4RLFo6AjrhzMtoEZn5PDE3/c2AbVsiCkpA==", + "requires": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5", + "update-notifier": "^5.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "requires": { + "abbrev": "1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", + "requires": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "requires": { + "escape-goat": "^2.0.0" + } + }, + "qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", + "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", + "requires": { + "bytes": "3.1.1", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + } + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "registry-auth-token": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "requires": { + "rc": "^1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "requires": { + "rc": "^1.2.8" + } + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "signal-exit": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" + }, + "socket.io": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.0.tgz", + "integrity": "sha512-bnpJxswR9ov0Bw6ilhCvO38/1WPtE3eA2dtxi2Iq4/sFebiDJQzgKNYA7AuVVdGW09nrESXd90NbZqtDd9dzRQ==", + "requires": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.1.0", + "socket.io-adapter": "~2.3.3", + "socket.io-parser": "~4.0.4" + }, + "dependencies": { + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "socket.io-adapter": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", + "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==" + }, + "socket.io-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", + "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", + "requires": { + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", + "debug": "~4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "requires": { + "nopt": "~1.0.10" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "update-notifier": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", + "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", + "requires": { + "boxen": "^5.0.0", + "chalk": "^4.1.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.4.0", + "is-npm": "^5.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.1.0", + "pupa": "^2.1.1", + "semver": "^7.3.4", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + }, + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "requires": { + "string-width": "^4.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==" + }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/back/package.json b/back/package.json new file mode 100644 index 0000000..ac8d9d4 --- /dev/null +++ b/back/package.json @@ -0,0 +1,19 @@ +{ + "name": "back", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "nodemon app.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "cors": "2.8.5", + "express": "4.17.2", + "http": "0.0.1-security", + "path": "0.12.7", + "socket.io": "4.4.0" + } +} diff --git a/front/.eslintrc.json b/front/.eslintrc.json index 647bfc4..884af99 100644 --- a/front/.eslintrc.json +++ b/front/.eslintrc.json @@ -27,10 +27,7 @@ "error", "double" ], - "comma-dangle": [ - "error", - "never" - ], + "comma-dangle": 0, "linebreak-style": 0 } } \ No newline at end of file diff --git a/front/package-lock.json b/front/package-lock.json index e92e88a..4edb60a 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -1950,6 +1950,11 @@ "@sinonjs/commons": "^1.7.0" } }, + "@socket.io/component-emitter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz", + "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==" + }, "@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", @@ -3348,11 +3353,21 @@ "babel-plugin-transform-react-remove-prop-types": "^0.4.24" } }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "base64-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz", + "integrity": "sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA==" + }, "batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -4519,6 +4534,37 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "engine.io-client": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz", + "integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==", + "requires": { + "@socket.io/component-emitter": "~3.0.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.0", + "has-cors": "1.1.0", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "ws": "~8.2.3", + "xmlhttprequest-ssl": "~2.0.0", + "yeast": "0.1.2" + }, + "dependencies": { + "ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==" + } + } + }, + "engine.io-parser": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.2.tgz", + "integrity": "sha512-wuiO7qO/OEkPJSFueuATIXtrxF7/6GTbAO9QLv7nnbjwZ5tYhLm9zxvLwxstRs0dcT0KUlWTjtIOs1T86jt12g==", + "requires": { + "base64-arraybuffer": "~1.0.1" + } + }, "enhanced-resolve": { "version": "5.8.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", @@ -5770,6 +5816,11 @@ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -8408,6 +8459,16 @@ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" }, + "parseqs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" + }, + "parseuri": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -10363,6 +10424,28 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" }, + "socket.io-client": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.0.tgz", + "integrity": "sha512-g7riSEJXi7qCFImPow98oT8X++MSsHz6MMFRXkWNJ6uEROSHOa3kxdrsYWMq85dO+09CFMkcqlpjvbVXQl4z6g==", + "requires": { + "@socket.io/component-emitter": "~3.0.0", + "backo2": "~1.0.2", + "debug": "~4.3.2", + "engine.io-client": "~6.1.1", + "parseuri": "0.0.6", + "socket.io-parser": "~4.1.1" + } + }, + "socket.io-parser": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.1.tgz", + "integrity": "sha512-USQVLSkDWE5nbcY760ExdKaJxCE65kcsG/8k5FDGZVVxpD1pA7hABYXYkCUvxUuYYh/+uQw0N/fvBzfT8o07KA==", + "requires": { + "@socket.io/component-emitter": "~3.0.0", + "debug": "~4.3.1" + } + }, "sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -11823,6 +11906,11 @@ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" }, + "xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -11862,6 +11950,11 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/front/package.json b/front/package.json index e766f56..5bd32d0 100644 --- a/front/package.json +++ b/front/package.json @@ -6,11 +6,13 @@ "@testing-library/jest-dom": "5.16.1", "@testing-library/react": "12.1.2", "@testing-library/user-event": "13.5.0", + "prop-types": "15.8.0", "react": "^17.0.2", "react-bootstrap": "2.0.4", "react-dom": "^17.0.2", "react-router-dom": "6.2.1", "react-scripts": "5.0.0", + "socket.io-client": "4.4.0", "web-vitals": "2.1.2" }, "scripts": { diff --git a/front/src/App.jsx b/front/src/App.jsx index aa160b9..e64d313 100644 --- a/front/src/App.jsx +++ b/front/src/App.jsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; import { BrowserRouter, Routes, Route } from "react-router-dom"; import "./App.css"; @@ -6,11 +6,12 @@ import ChatRoom from "./core/ChatRoom"; import EnterNamePage from "./core/EnterNamePage"; function App() { + const [username, setUsername] = useState(""); return ( - } /> - } /> + } /> + } /> ); diff --git a/front/src/core/Chat.jsx b/front/src/core/Chat.jsx index 7372c9d..f69735e 100644 --- a/front/src/core/Chat.jsx +++ b/front/src/core/Chat.jsx @@ -1,12 +1,14 @@ import React from "react"; +import PropTypes from "prop-types"; -export default function Chat() { +export default function Chat({ username }) { return (
    Online Users
    +
    {username}
    @@ -28,3 +30,7 @@ export default function Chat() {
    ); } + +Chat.propTypes = { + username: PropTypes.string.isRequired, +}; diff --git a/front/src/core/ChatRoom.jsx b/front/src/core/ChatRoom.jsx index ae6ba6a..5dd25fb 100644 --- a/front/src/core/ChatRoom.jsx +++ b/front/src/core/ChatRoom.jsx @@ -1,13 +1,30 @@ -import React from "react"; +import React, { useEffect, useRef } from "react"; +import io from "socket.io-client"; +import PropTypes from "prop-types"; + import Chat from "./Chat"; import NavBar from "./NavBar"; -export default function ChatRoom() { +export default function ChatRoom({ username }) { + const socketRef = useRef(); + useEffect(() => { + socketRef.current = io.connect("http://localhost:3001"); + socketRef.current.emit("onConnect", { name: username.toString() }); + + socketRef.current.on("newConnection", (msg) => { + console.log(msg); + }); + }, []); + document.documentElement.style.setProperty("--background-color", "#526377"); return (
    - +
    ); } + +ChatRoom.propTypes = { + username: PropTypes.string.isRequired, +}; diff --git a/front/src/core/EnterNamePage.jsx b/front/src/core/EnterNamePage.jsx index c0c68cc..30b06b2 100644 --- a/front/src/core/EnterNamePage.jsx +++ b/front/src/core/EnterNamePage.jsx @@ -1,10 +1,11 @@ import React, { useRef } from "react"; import { Form, Button } from "react-bootstrap"; import { useNavigate } from "react-router-dom"; +import PropTypes from "prop-types"; import { validateName } from "../helper/functions"; -export default function EnterNamePage() { +export default function EnterNamePage({ setUsername }) { const navigate = useNavigate(); const nameInput = useRef(); const errorRef = useRef(); @@ -13,7 +14,7 @@ export default function EnterNamePage() { e.preventDefault(); const response = validateName(nameInput.current.value); if (response.valid) { - // GOOD + setUsername(nameInput.current.value); navigate("/chat-room"); } else { errorRef.current.innerText = response.error; @@ -37,3 +38,7 @@ export default function EnterNamePage() {
    ); } + +EnterNamePage.propTypes = { + setUsername: PropTypes.func.isRequired, +}; From 254d93bb1cacb6a1956a01c8ab4e5f9a53045971 Mon Sep 17 00:00:00 2001 From: DanielPhilosoph Date: Wed, 29 Dec 2021 18:51:42 +0200 Subject: [PATCH 04/15] css --- .vscode/settings.json | 11 ++++-- front/.eslintrc.json | 3 +- front/package-lock.json | 56 +++++++++++++++++++++++++++ front/package.json | 3 ++ front/src/Actions.js | 4 ++ front/src/core/Chat.jsx | 24 ++++++++---- front/src/core/ChatRoom.jsx | 17 ++++---- front/src/core/EnterNamePage.jsx | 20 ++++++---- front/src/core/Message.jsx | 37 ++++++++++++++++++ front/src/helper/actionsFunctions.js | 39 +++++++++++++++++++ front/src/index.css | 58 +++++++++++++++++++++++++++- front/src/index.jsx | 7 +++- front/src/reducers/MainReducer.js | 46 ++++++++++++++++++++++ front/src/store/store.js | 7 ++++ 14 files changed, 302 insertions(+), 30 deletions(-) create mode 100644 front/src/Actions.js create mode 100644 front/src/core/Message.jsx create mode 100644 front/src/helper/actionsFunctions.js create mode 100644 front/src/reducers/MainReducer.js create mode 100644 front/src/store/store.js diff --git a/.vscode/settings.json b/.vscode/settings.json index f34a02c..44090cd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,11 @@ { - "editor.codeActionsOnSave": { - "source.fixAll.eslint": true, - "source.fixAll.tslint": true, - }, + "editor.formatOnSave": false, + // Runs Prettier, then ESLint + "editor.codeActionsOnSave": [ + "source.formatDocument", + "source.fixAll.eslint" + ], + "vetur.validation.template": false, "eslint.validate": [ "javascript" ] diff --git a/front/.eslintrc.json b/front/.eslintrc.json index 884af99..0f2842a 100644 --- a/front/.eslintrc.json +++ b/front/.eslintrc.json @@ -28,6 +28,7 @@ "double" ], "comma-dangle": 0, - "linebreak-style": 0 + "linebreak-style": 0, + "object-curly-newline": 0 } } \ No newline at end of file diff --git a/front/package-lock.json b/front/package-lock.json index 4edb60a..8dcd171 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -2361,6 +2361,15 @@ "@types/node": "*" } }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -2469,6 +2478,17 @@ "csstype": "^3.0.2" } }, + "@types/react-redux": { + "version": "7.1.21", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.21.tgz", + "integrity": "sha512-bLdglUiBSQNzWVVbmNPKGYYjrzp3/YDPwfOH3nLEz99I4awLlaRAPWjo6bZ2POpxztFWtDDXIPxBLVykXqBt+w==", + "requires": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "@types/react-transition-group": { "version": "4.4.4", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz", @@ -5852,6 +5872,14 @@ "@babel/runtime": "^7.7.6" } }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + }, "hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -9623,6 +9651,26 @@ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, + "react-redux": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz", + "integrity": "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==", + "requires": { + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + }, + "dependencies": { + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } + } + }, "react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -9881,6 +9929,14 @@ "strip-indent": "^3.0.0" } }, + "redux": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz", + "integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", diff --git a/front/package.json b/front/package.json index 5bd32d0..0ac90a7 100644 --- a/front/package.json +++ b/front/package.json @@ -6,12 +6,15 @@ "@testing-library/jest-dom": "5.16.1", "@testing-library/react": "12.1.2", "@testing-library/user-event": "13.5.0", + "nanoid": "3.1.30", "prop-types": "15.8.0", "react": "^17.0.2", "react-bootstrap": "2.0.4", "react-dom": "^17.0.2", + "react-redux": "7.2.6", "react-router-dom": "6.2.1", "react-scripts": "5.0.0", + "redux": "4.1.2", "socket.io-client": "4.4.0", "web-vitals": "2.1.2" }, diff --git a/front/src/Actions.js b/front/src/Actions.js new file mode 100644 index 0000000..540e6c9 --- /dev/null +++ b/front/src/Actions.js @@ -0,0 +1,4 @@ +export const SET_USERNAME_AND_ID = "SET_USERNAME_AND_ID"; +export const REMOVE_CONNECTED_USER = "REMOVE_CONNECTED_USER"; +export const ADD_CONNECTED_USER = "ADD_CONNECTED_USER"; +export const ADD_TO_MESSAGES = "ADD_TO_MESSAGES"; diff --git a/front/src/core/Chat.jsx b/front/src/core/Chat.jsx index f69735e..4e7c4f6 100644 --- a/front/src/core/Chat.jsx +++ b/front/src/core/Chat.jsx @@ -1,21 +1,33 @@ import React from "react"; -import PropTypes from "prop-types"; +import { useSelector } from "react-redux"; +import Message from "./Message"; -export default function Chat({ username }) { +export default function Chat() { + const state = useSelector((state_) => state_); + console.log(state); return (
    Online Users
    -
    {username}
    Img Chat
    -
    sss
    +
    + {" "} + + +
    ); } - -Chat.propTypes = { - username: PropTypes.string.isRequired, -}; diff --git a/front/src/core/ChatRoom.jsx b/front/src/core/ChatRoom.jsx index 5dd25fb..228f309 100644 --- a/front/src/core/ChatRoom.jsx +++ b/front/src/core/ChatRoom.jsx @@ -1,15 +1,20 @@ import React, { useEffect, useRef } from "react"; import io from "socket.io-client"; -import PropTypes from "prop-types"; +import { useSelector } from "react-redux"; import Chat from "./Chat"; import NavBar from "./NavBar"; -export default function ChatRoom({ username }) { +export default function ChatRoom() { const socketRef = useRef(); + const state = useSelector((state_) => state_); useEffect(() => { + const userInfo = JSON.parse(localStorage.getItem("info")); socketRef.current = io.connect("http://localhost:3001"); - socketRef.current.emit("onConnect", { name: username.toString() }); + socketRef.current.emit("onConnect", { + name: state.username ? state.username : userInfo.name, + id: state.id ? state.id : userInfo.id, + }); socketRef.current.on("newConnection", (msg) => { console.log(msg); @@ -20,11 +25,7 @@ export default function ChatRoom({ username }) { return (
    - +
    ); } - -ChatRoom.propTypes = { - username: PropTypes.string.isRequired, -}; diff --git a/front/src/core/EnterNamePage.jsx b/front/src/core/EnterNamePage.jsx index 30b06b2..879d638 100644 --- a/front/src/core/EnterNamePage.jsx +++ b/front/src/core/EnterNamePage.jsx @@ -1,20 +1,30 @@ import React, { useRef } from "react"; import { Form, Button } from "react-bootstrap"; import { useNavigate } from "react-router-dom"; -import PropTypes from "prop-types"; +import { useDispatch } from "react-redux"; + +import { nanoid } from "nanoid"; import { validateName } from "../helper/functions"; +import { setUserNameAndId } from "../helper/actionsFunctions"; -export default function EnterNamePage({ setUsername }) { +export default function EnterNamePage() { const navigate = useNavigate(); const nameInput = useRef(); const errorRef = useRef(); + const dispatch = useDispatch(); const onLoginClick = (e) => { e.preventDefault(); const response = validateName(nameInput.current.value); if (response.valid) { - setUsername(nameInput.current.value); + // ! Setting the name and the id to redux + const id = nanoid(); + dispatch(setUserNameAndId(id, nameInput.current.value)); + localStorage.setItem( + "info", + JSON.stringify({ name: nameInput.current.value, id }) + ); navigate("/chat-room"); } else { errorRef.current.innerText = response.error; @@ -38,7 +48,3 @@ export default function EnterNamePage({ setUsername }) {
    ); } - -EnterNamePage.propTypes = { - setUsername: PropTypes.func.isRequired, -}; diff --git a/front/src/core/Message.jsx b/front/src/core/Message.jsx new file mode 100644 index 0000000..abfa35c --- /dev/null +++ b/front/src/core/Message.jsx @@ -0,0 +1,37 @@ +import React from "react"; +import PropTypes from "prop-types"; + +export default function Message( + { + isMyMessage, + username, + message, + time, + id + } +) { + if (!isMyMessage) { + return ( +
    + {username} + {message} + {time} +
    + ); + } + return ( +
    + {username} + {message} + {time} +
    + ); +} + +Message.propTypes = { + isMyMessage: PropTypes.bool.isRequired, + username: PropTypes.string.isRequired, + message: PropTypes.string.isRequired, + time: PropTypes.string.isRequired, + id: PropTypes.string.isRequired, +}; diff --git a/front/src/helper/actionsFunctions.js b/front/src/helper/actionsFunctions.js new file mode 100644 index 0000000..cdddecd --- /dev/null +++ b/front/src/helper/actionsFunctions.js @@ -0,0 +1,39 @@ +import { + ADD_CONNECTED_USER, + ADD_TO_MESSAGES, + REMOVE_CONNECTED_USER, + SET_USERNAME_AND_ID, +} from "../Actions"; + +export function setUserNameAndId(id, username) { + return { + type: SET_USERNAME_AND_ID, + payload: { username, id }, + }; +} + +export function removeConnectedUser(id) { + return { + type: REMOVE_CONNECTED_USER, + payload: { id }, + }; +} + +export function addConnectedUser(id, name) { + return { + type: ADD_CONNECTED_USER, + payload: { name, id }, + }; +} + +export function addToMessages(id, name, message, time) { + return { + type: ADD_TO_MESSAGES, + payload: { + name, + id, + message, + time, + }, + }; +} diff --git a/front/src/index.css b/front/src/index.css index fa439f7..6c65e92 100644 --- a/front/src/index.css +++ b/front/src/index.css @@ -100,7 +100,7 @@ body { } .messageInput { - width: 92%; + width: 90%; height: 30px; border-radius: 5px; border: 0; @@ -115,6 +115,62 @@ body { background-color: rgb(92, 92, 92); color: whitesmoke; } +/* ----------------- */ +/* Message + Self */ +/* ----------------- */ + +.message{ + margin: 20px; + padding: 15px; + max-width: 60%; + min-width: 20%; + background-color: rgb(212, 212, 212); + border-radius: 5px; + width: -moz-fit-content; + width: fit-content; + word-break: break-word; +} + +.messageName{ + font-size: 14px; + color: cadetblue; +} + +.messageText{ + display: block; +} + +.messageTime{ + display: block; + text-align: right; + font-size: 12px; +} + +.messageSelf{ + float: right; + margin: 20px; + padding: 15px; + max-width: 60%; + min-width: 20%; + background-color: rgb(87, 160, 126); + border-radius: 5px; + word-break: break-word; +} + +.messageNameSelf{ + font-size: 14px; + color: rgb(255, 255, 255); +} + +.messageTextSelf{ + display: block; +} + +.messageTimeSelf{ + display: block; + text-align: right; + font-size: 12px; +} @media screen and (max-width: 900px) { .warperChatDiv { diff --git a/front/src/index.jsx b/front/src/index.jsx index 846ed2c..46b690d 100644 --- a/front/src/index.jsx +++ b/front/src/index.jsx @@ -1,11 +1,16 @@ import React from "react"; import ReactDOM from "react-dom"; import "./index.css"; +import { Provider } from "react-redux"; import App from "./App"; +import store from "./store/store"; + ReactDOM.render( - + + + , document.getElementById("root") ); diff --git a/front/src/reducers/MainReducer.js b/front/src/reducers/MainReducer.js new file mode 100644 index 0000000..24ed200 --- /dev/null +++ b/front/src/reducers/MainReducer.js @@ -0,0 +1,46 @@ +const initialState = { + username: "", + id: "", + connectedUsers: [], // {name: string, id: string} + messages: [], // {name: string, id: string , message: string, time: Date} +}; +export default function MainReducer(state = initialState, action = "") { + switch (action.type) { + case "SET_USERNAME_AND_ID": + return { + ...state, + username: action.payload.username, + id: action.payload.id, + }; + case "REMOVE_CONNECTED_USER": + return { + ...state, + connectedUsers: state.connectedUsers.filter( + (connectedUser) => connectedUser.id !== action.payload.id + ), + }; + case "ADD_CONNECTED_USER": + return { + ...state, + connectedUsers: [ + ...state.connectedUsers, + { name: action.payload.name, id: action.payload.id }, + ], + }; + case "ADD_TO_MESSAGES": + return { + ...state, + messages: [ + ...state.messages, + { + name: action.payload.name, + id: action.payload.id, + message: action.payload.message, + time: action.payload.time, + }, + ], + }; + default: + return state; + } +} diff --git a/front/src/store/store.js b/front/src/store/store.js new file mode 100644 index 0000000..7db7d26 --- /dev/null +++ b/front/src/store/store.js @@ -0,0 +1,7 @@ +import { createStore } from "redux"; + +import MainReducer from "../reducers/MainReducer"; + +const store = createStore(MainReducer); + +export default store; From 67420cb844700a74a21e77212cb1bddbbe540a99 Mon Sep 17 00:00:00 2001 From: DanielPhilosoph Date: Thu, 30 Dec 2021 00:21:02 +0200 Subject: [PATCH 05/15] css, fixed online users --- .vscode/settings.json | 10 +--- back/app.js | 39 ++++++++++-- front/package-lock.json | 5 ++ front/package.json | 1 + front/public/images/person.png | Bin 0 -> 5413 bytes front/src/Actions.js | 1 + front/src/core/Chat.jsx | 82 +++++++++++++++++++++---- front/src/core/ChatRoom.jsx | 33 ++++++++-- front/src/core/ConnectedUser.jsx | 19 ++++++ front/src/core/Message.jsx | 18 ++---- front/src/core/SpecialMessage.jsx | 34 +++++++++++ front/src/helper/actionsFunctions.js | 11 +++- front/src/helper/functions.js | 8 ++- front/src/index.css | 86 +++++++++++++++++++++++---- front/src/index.jsx | 2 +- front/src/reducers/MainReducer.js | 6 ++ 16 files changed, 295 insertions(+), 60 deletions(-) create mode 100644 front/public/images/person.png create mode 100644 front/src/core/ConnectedUser.jsx create mode 100644 front/src/core/SpecialMessage.jsx diff --git a/.vscode/settings.json b/.vscode/settings.json index 44090cd..754b18d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,11 +1,7 @@ { - "editor.formatOnSave": false, - // Runs Prettier, then ESLint - "editor.codeActionsOnSave": [ - "source.formatDocument", - "source.fixAll.eslint" - ], - "vetur.validation.template": false, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true, + }, "eslint.validate": [ "javascript" ] diff --git a/back/app.js b/back/app.js index 8b0fd81..f04df18 100644 --- a/back/app.js +++ b/back/app.js @@ -1,6 +1,12 @@ +// const express = require("express"); +const path = require("path"); const app = require("express")(); const http = require("http").createServer(app); -const io = require("socket.io")(http); +const io = require("socket.io")(http, { + cors: { + origin: ["http://localhost:3000"], + }, +}); const cors = require("cors"); const PORT = 3001; @@ -8,29 +14,50 @@ const PORT = 3001; app.use(cors()); // //? Static Files -// app.use(express.static("client/build")); +// app.use(express.static("front/build")); // app.get("/", (req, res) => { -// res.sendFile(path.join(__dirname, "../front/public/index.html")); +// res.sendFile(path.join(__dirname, "./build/index.html")); // }); +//? Connected users array +let connectedUsers = []; + //? Socket.io Connection io.on("connection", (socket) => { console.log("a user connected"); - socket.on("onConnect", ({ name }) => { - io.emit("newConnection", { name: name, time: new Date() }); + socket.on("onConnect", ({ name, id }) => { + connectedUsers.push({ id: id, name: name, socketId: socket.id }); + console.log(connectedUsers); + io.emit("newConnection", { + id: id, + name: name, + time: new Date(), + connectedUsers: connectedUsers, + }); }); socket.on("message", (info) => { io.emit("messageBack", { name: info.name, + id: info.id, message: info.message, time: new Date(), }); }); + socket.on("privateMessage", (anotherSocketId, msg) => { + socket.to(anotherSocketId).emit("privateMessage", socket.id, msg); + }); + socket.on("disconnect", () => { - // io.emit("userDisconnect", { }); + io.emit( + "userDisconnect", + connectedUsers.find((user) => user.socketId === socket.id) + ); + connectedUsers = connectedUsers.filter( + (user) => user.socketId !== socket.id + ); console.log("disconnect"); }); }); diff --git a/front/package-lock.json b/front/package-lock.json index 8dcd171..b1c5d55 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -8157,6 +8157,11 @@ "minimist": "^1.2.5" } }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/front/package.json b/front/package.json index 0ac90a7..3f2efc1 100644 --- a/front/package.json +++ b/front/package.json @@ -6,6 +6,7 @@ "@testing-library/jest-dom": "5.16.1", "@testing-library/react": "12.1.2", "@testing-library/user-event": "13.5.0", + "moment": "2.29.1", "nanoid": "3.1.30", "prop-types": "15.8.0", "react": "^17.0.2", diff --git a/front/public/images/person.png b/front/public/images/person.png new file mode 100644 index 0000000000000000000000000000000000000000..ca9a393fa4d63ca56f5abeed5f68b682d970009f GIT binary patch literal 5413 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 P00000NkvXXu0mjfFbg=k literal 0 HcmV?d00001 diff --git a/front/src/Actions.js b/front/src/Actions.js index 540e6c9..e323085 100644 --- a/front/src/Actions.js +++ b/front/src/Actions.js @@ -2,3 +2,4 @@ export const SET_USERNAME_AND_ID = "SET_USERNAME_AND_ID"; export const REMOVE_CONNECTED_USER = "REMOVE_CONNECTED_USER"; export const ADD_CONNECTED_USER = "ADD_CONNECTED_USER"; export const ADD_TO_MESSAGES = "ADD_TO_MESSAGES"; +export const SET_CONNECTED_USERS = "SET_CONNECTED_USERS"; diff --git a/front/src/core/Chat.jsx b/front/src/core/Chat.jsx index 4e7c4f6..e159c48 100644 --- a/front/src/core/Chat.jsx +++ b/front/src/core/Chat.jsx @@ -1,16 +1,39 @@ -import React from "react"; +import React, { useRef } from "react"; import { useSelector } from "react-redux"; +import PropTypes from "prop-types"; + import Message from "./Message"; +import SpecialMessage from "./SpecialMessage"; +import ConnectedUser from "./ConnectedUser"; -export default function Chat() { +export default function Chat({ socketRef }) { + const messageRef = useRef(); const state = useSelector((state_) => state_); console.log(state); + + const onSendClick = (e) => { + e.preventDefault(); + socketRef.current.emit("message", { + name: state.username, + id: state.id, + message: messageRef.current.value, + }); + }; + return (
    Online Users
    + {state.connectedUsers.map((user) => ( + + ))}
    @@ -18,27 +41,60 @@ export default function Chat() { Chat
    - {" "} - - + {state.messages.map((message) => { + switch (message.type) { + case "regular": + return ( + + ); + case "connect": + return ( + + ); + case "disconnect": + return ( + + ); + default: + return ""; + } + })}
    -
    ); } + +Chat.propTypes = { + socketRef: PropTypes.func.isRequired, +}; diff --git a/front/src/core/ChatRoom.jsx b/front/src/core/ChatRoom.jsx index 228f309..70be102 100644 --- a/front/src/core/ChatRoom.jsx +++ b/front/src/core/ChatRoom.jsx @@ -1,23 +1,46 @@ import React, { useEffect, useRef } from "react"; import io from "socket.io-client"; -import { useSelector } from "react-redux"; +import { useSelector, useDispatch } from "react-redux"; import Chat from "./Chat"; import NavBar from "./NavBar"; +import { + addToMessages, + removeConnectedUser, + setConnectedUsers, + setUserNameAndId, +} from "../helper/actionsFunctions"; export default function ChatRoom() { const socketRef = useRef(); const state = useSelector((state_) => state_); + const dispatch = useDispatch(); useEffect(() => { const userInfo = JSON.parse(localStorage.getItem("info")); + // ? If state is null (probably refresh), so take from local storage + if (state.username === "" || state.id === "") { + dispatch(setUserNameAndId(userInfo.id, userInfo.name)); + } socketRef.current = io.connect("http://localhost:3001"); socketRef.current.emit("onConnect", { - name: state.username ? state.username : userInfo.name, - id: state.id ? state.id : userInfo.id, + name: state.username !== "" ? state.username : userInfo.name, + id: state.id !== "" ? state.id : userInfo.id, }); socketRef.current.on("newConnection", (msg) => { - console.log(msg); + dispatch(addToMessages(msg.id, msg.name, "", msg.time, "connect")); + dispatch(setConnectedUsers(msg.connectedUsers)); + }); + + socketRef.current.on("messageBack", (msg) => { + dispatch( + addToMessages(msg.id, msg.name, msg.message, msg.time, "regular") + ); + }); + + socketRef.current.on("userDisconnect", (msg) => { + dispatch(addToMessages(msg.id, msg.name, "", msg.time, "disconnect")); + dispatch(removeConnectedUser(msg.id)); }); }, []); @@ -25,7 +48,7 @@ export default function ChatRoom() { return (
    - +
    ); } diff --git a/front/src/core/ConnectedUser.jsx b/front/src/core/ConnectedUser.jsx new file mode 100644 index 0000000..1ec61cb --- /dev/null +++ b/front/src/core/ConnectedUser.jsx @@ -0,0 +1,19 @@ +import React from "react"; +import PropTypes from "prop-types"; + +export default function ConnectedUser({ name, id, socketId }) { + return ( +
    +
    + img +
    +
    {name}
    +
    + ); +} + +ConnectedUser.propTypes = { + name: PropTypes.string.isRequired, + id: PropTypes.string.isRequired, + socketId: PropTypes.string.isRequired, +}; diff --git a/front/src/core/Message.jsx b/front/src/core/Message.jsx index abfa35c..bd4400c 100644 --- a/front/src/core/Message.jsx +++ b/front/src/core/Message.jsx @@ -1,29 +1,23 @@ import React from "react"; import PropTypes from "prop-types"; +import { formatDate } from "../helper/functions"; -export default function Message( - { - isMyMessage, - username, - message, - time, - id - } -) { +export default function Message({ isMyMessage, username, message, time, id }) { + const formattedDate = formatDate(time); if (!isMyMessage) { return (
    {username} {message} - {time} + {formattedDate}
    ); } return (
    {username} - {message} - {time} + {message} + {formattedDate}
    ); } diff --git a/front/src/core/SpecialMessage.jsx b/front/src/core/SpecialMessage.jsx new file mode 100644 index 0000000..1c04327 --- /dev/null +++ b/front/src/core/SpecialMessage.jsx @@ -0,0 +1,34 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { formatDate } from "../helper/functions"; + +export default function SpecialMessage({ type, name, time }) { + const formattedDate = formatDate(time); + let className; + let message; + switch (type) { + case "disconnect": + className = "disconnectMessage"; + message = "has disconnected"; + break; + case "connected": + className = "connectedMessage"; + message = "has connected"; + break; + + default: + break; + } + return ( +
    + {`${name} ${message}`} + {formattedDate} +
    + ); +} + +SpecialMessage.propTypes = { + type: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + time: PropTypes.string.isRequired, +}; diff --git a/front/src/helper/actionsFunctions.js b/front/src/helper/actionsFunctions.js index cdddecd..7cabb78 100644 --- a/front/src/helper/actionsFunctions.js +++ b/front/src/helper/actionsFunctions.js @@ -2,6 +2,7 @@ import { ADD_CONNECTED_USER, ADD_TO_MESSAGES, REMOVE_CONNECTED_USER, + SET_CONNECTED_USERS, SET_USERNAME_AND_ID, } from "../Actions"; @@ -26,10 +27,18 @@ export function addConnectedUser(id, name) { }; } -export function addToMessages(id, name, message, time) { +export function setConnectedUsers(connectedUsers) { + return { + type: SET_CONNECTED_USERS, + payload: { connectedUsers }, + }; +} + +export function addToMessages(id, name, message, time, type) { return { type: ADD_TO_MESSAGES, payload: { + type, name, id, message, diff --git a/front/src/helper/functions.js b/front/src/helper/functions.js index 1da5c43..d9271ec 100644 --- a/front/src/helper/functions.js +++ b/front/src/helper/functions.js @@ -1,3 +1,5 @@ +import moment from "moment"; + export function validateName(name) { if (name === "") { return { valid: false, error: "Can't be null" }; @@ -11,7 +13,7 @@ export function validateName(name) { return { valid: true }; } -export function temp() { - const x = 3; - return x; +export function formatDate(time) { + const date = new Date(time); + return moment(date).format("HH:DD"); } diff --git a/front/src/index.css b/front/src/index.css index 6c65e92..fff2e46 100644 --- a/front/src/index.css +++ b/front/src/index.css @@ -119,7 +119,7 @@ body { /* Message + Self */ /* ----------------- */ -.message{ +.message { margin: 20px; padding: 15px; max-width: 60%; @@ -131,47 +131,109 @@ body { word-break: break-word; } -.messageName{ +.messageName { font-size: 14px; color: cadetblue; } -.messageText{ +.messageText { display: block; } -.messageTime{ +.messageTime { display: block; text-align: right; font-size: 12px; } -.messageSelf{ - float: right; +.messageSelf { margin: 20px; + margin-left: auto; padding: 15px; max-width: 60%; min-width: 20%; background-color: rgb(87, 160, 126); border-radius: 5px; + width: -moz-fit-content; + width: fit-content; word-break: break-word; } -.messageNameSelf{ +.messageNameSelf { font-size: 14px; color: rgb(255, 255, 255); } -.messageTextSelf{ - display: block; +/* ----------------- */ +/* Special Message */ +/* ----------------- */ + +.disconnectMessage > .messageTextSpecial { + color: rgb(255, 52, 52); } -.messageTimeSelf{ - display: block; - text-align: right; +.connectedMessage, +.disconnectMessage { + margin: 20px; + padding: 15px; + max-width: 60%; + min-width: 20%; + background-color: rgb(57, 99, 128); + border-radius: 5px; + width: -moz-fit-content; + width: fit-content; + word-break: break-word; + color: whitesmoke; +} + +.messageTimeSpecial { + padding-left: 30px; + max-width: 60%; + float: right; font-size: 12px; } +.connectedMessage > .messageTextSpecial { + color: lawngreen; +} + +/* ----------------- */ +/* Connected Users */ +/* ----------------- */ + +.connectedUserDiv { + width: 98%; + background-color: #8d99a7; + margin: auto; + display: flex; + position: relative; +} + +.connectedUserDiv:hover { + background-color: #bfc4ca; +} + +.connectedUserName { + width: 80%; + padding: 20px 15px; + border-top: 0.5px solid #bfc4ca; + border-bottom: 0.5px solid #bfc4ca; + font-size: 18px; + word-break: break-word; +} + +.connectedUserImg { + border-radius: 50%; + width: 50px; +} + +.connectedUserImgDiv { + padding: 10px; + display: flex; + justify-items: center; + margin: auto 0; +} + @media screen and (max-width: 900px) { .warperChatDiv { flex-direction: column-reverse; diff --git a/front/src/index.jsx b/front/src/index.jsx index 46b690d..1c3335c 100644 --- a/front/src/index.jsx +++ b/front/src/index.jsx @@ -2,8 +2,8 @@ import React from "react"; import ReactDOM from "react-dom"; import "./index.css"; import { Provider } from "react-redux"; -import App from "./App"; +import App from "./App"; import store from "./store/store"; ReactDOM.render( diff --git a/front/src/reducers/MainReducer.js b/front/src/reducers/MainReducer.js index 24ed200..3b88a3f 100644 --- a/front/src/reducers/MainReducer.js +++ b/front/src/reducers/MainReducer.js @@ -27,12 +27,18 @@ export default function MainReducer(state = initialState, action = "") { { name: action.payload.name, id: action.payload.id }, ], }; + case "SET_CONNECTED_USERS": + return { + ...state, + connectedUsers: action.payload.connectedUsers, + }; case "ADD_TO_MESSAGES": return { ...state, messages: [ ...state.messages, { + type: action.payload.type, name: action.payload.name, id: action.payload.id, message: action.payload.message, From 2ba6281007ce859af33028ea18631ecd55d9e4ce Mon Sep 17 00:00:00 2001 From: DanielPhilosoph Date: Thu, 30 Dec 2021 00:48:22 +0200 Subject: [PATCH 06/15] fixed connections --- back/app.js | 18 ++++++++++++++++-- front/src/core/Chat.jsx | 5 ++++- front/src/core/ChatRoom.jsx | 8 ++++++++ front/src/helper/functions.js | 2 +- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/back/app.js b/back/app.js index f04df18..ba1d2af 100644 --- a/back/app.js +++ b/back/app.js @@ -27,8 +27,18 @@ io.on("connection", (socket) => { console.log("a user connected"); socket.on("onConnect", ({ name, id }) => { - connectedUsers.push({ id: id, name: name, socketId: socket.id }); - console.log(connectedUsers); + const user = connectedUsers.find((user) => user.id === id); + // ? If did find any connected user with the same id, so add + if (!user) { + connectedUsers.push({ id: id, name: name, socketId: socket.id }); + } else { + // ? Else update socket id + connectedUsers.map((user) => { + if (user.id === id) { + user.socketId = socket.id; + } + }); + } io.emit("newConnection", { id: id, name: name, @@ -38,6 +48,7 @@ io.on("connection", (socket) => { }); socket.on("message", (info) => { + console.log(socket.id); io.emit("messageBack", { name: info.name, id: info.id, @@ -46,6 +57,9 @@ io.on("connection", (socket) => { }); }); + //? ----------------------------------- + //! Understand how this works and implement!! + //? ----------------------------------- socket.on("privateMessage", (anotherSocketId, msg) => { socket.to(anotherSocketId).emit("privateMessage", socket.id, msg); }); diff --git a/front/src/core/Chat.jsx b/front/src/core/Chat.jsx index e159c48..94ba481 100644 --- a/front/src/core/Chat.jsx +++ b/front/src/core/Chat.jsx @@ -9,7 +9,6 @@ import ConnectedUser from "./ConnectedUser"; export default function Chat({ socketRef }) { const messageRef = useRef(); const state = useSelector((state_) => state_); - console.log(state); const onSendClick = (e) => { e.preventDefault(); @@ -40,6 +39,10 @@ export default function Chat({ socketRef }) { Img Chat
    + {/* + // TODO when talking to someone private - should change to load private messages + // TODO when to many messages, show create a scroll + */}
    {state.messages.map((message) => { switch (message.type) { diff --git a/front/src/core/ChatRoom.jsx b/front/src/core/ChatRoom.jsx index 70be102..f24aa31 100644 --- a/front/src/core/ChatRoom.jsx +++ b/front/src/core/ChatRoom.jsx @@ -17,6 +17,8 @@ export default function ChatRoom() { const dispatch = useDispatch(); useEffect(() => { const userInfo = JSON.parse(localStorage.getItem("info")); + // TODO fix working with localStorage, need to figure out a way to deal with refresh + // TODO show fix my problems with multiple connection // ? If state is null (probably refresh), so take from local storage if (state.username === "" || state.id === "") { dispatch(setUserNameAndId(userInfo.id, userInfo.name)); @@ -42,6 +44,12 @@ export default function ChatRoom() { dispatch(addToMessages(msg.id, msg.name, "", msg.time, "disconnect")); dispatch(removeConnectedUser(msg.id)); }); + + // TODO should get a private MSG from someone + // TODO add to general state as ${name}ChatMessages. + socketRef.current.on("privateMessage", (msg) => { + console.log(msg); + }); }, []); document.documentElement.style.setProperty("--background-color", "#526377"); diff --git a/front/src/helper/functions.js b/front/src/helper/functions.js index d9271ec..407f11f 100644 --- a/front/src/helper/functions.js +++ b/front/src/helper/functions.js @@ -15,5 +15,5 @@ export function validateName(name) { export function formatDate(time) { const date = new Date(time); - return moment(date).format("HH:DD"); + return moment(date).format("HH:mm"); } From 2f5d2732ba8cff2dde1896ef511222c3b72ab0c1 Mon Sep 17 00:00:00 2001 From: DanielPhilosoph Date: Thu, 30 Dec 2021 15:39:36 +0200 Subject: [PATCH 07/15] fixed user disconnect + rivate messages works, need to implement --- back/app.js | 20 ++++-- front/public/images/chat_1.png | Bin 0 -> 13284 bytes front/src/Actions.js | 2 + front/src/core/Chat.jsx | 96 ++++++++++++--------------- front/src/core/ChatRoom.jsx | 36 ++++++---- front/src/core/ConnectedUser.jsx | 32 ++++++++- front/src/core/EnterNamePage.jsx | 1 - front/src/core/MessagesDiv.jsx | 52 +++++++++++++++ front/src/core/SpecialMessage.jsx | 1 + front/src/helper/actionsFunctions.js | 23 +++++++ front/src/index.css | 17 ++++- front/src/reducers/MainReducer.js | 23 +++++++ front/src/socket/SocketContext.js | 5 ++ 13 files changed, 233 insertions(+), 75 deletions(-) create mode 100644 front/public/images/chat_1.png create mode 100644 front/src/core/MessagesDiv.jsx create mode 100644 front/src/socket/SocketContext.js diff --git a/back/app.js b/back/app.js index ba1d2af..e5a7181 100644 --- a/back/app.js +++ b/back/app.js @@ -25,6 +25,7 @@ let connectedUsers = []; //? Socket.io Connection io.on("connection", (socket) => { console.log("a user connected"); + socket.join(socket.id); socket.on("onConnect", ({ name, id }) => { const user = connectedUsers.find((user) => user.id === id); @@ -60,15 +61,24 @@ io.on("connection", (socket) => { //? ----------------------------------- //! Understand how this works and implement!! //? ----------------------------------- - socket.on("privateMessage", (anotherSocketId, msg) => { - socket.to(anotherSocketId).emit("privateMessage", socket.id, msg); + socket.on("privateMessage", (anotherSocketId, info) => { + console.log("you send a private message to ", anotherSocketId); + socket.to(anotherSocketId).emit("privateMessage", { + from: socket.id, + to: anotherSocketId, + name: info.name, + id: info.id, + message: info.message, + time: new Date(), + }); }); socket.on("disconnect", () => { - io.emit( - "userDisconnect", - connectedUsers.find((user) => user.socketId === socket.id) + const disconnectedUser = connectedUsers.find( + (user) => user.socketId === socket.id ); + disconnectedUser.time = new Date(); + io.emit("userDisconnect", disconnectedUser); connectedUsers = connectedUsers.filter( (user) => user.socketId !== socket.id ); diff --git a/front/public/images/chat_1.png b/front/public/images/chat_1.png new file mode 100644 index 0000000000000000000000000000000000000000..9b4308eda1bb4e9fe418cfc2359e95c98ac7ddb8 GIT binary patch literal 13284 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 wMi7T;F{f5?g2lr9 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 literal 0 HcmV?d00001 diff --git a/front/src/Actions.js b/front/src/Actions.js index e323085..2b2e928 100644 --- a/front/src/Actions.js +++ b/front/src/Actions.js @@ -3,3 +3,5 @@ export const REMOVE_CONNECTED_USER = "REMOVE_CONNECTED_USER"; export const ADD_CONNECTED_USER = "ADD_CONNECTED_USER"; export const ADD_TO_MESSAGES = "ADD_TO_MESSAGES"; export const SET_CONNECTED_USERS = "SET_CONNECTED_USERS"; +export const SET_MESSAGE_TO_SOCKET_ID = "SET_MESSAGE_TO_SOCKET_ID"; +export const ADD_TO_PERSONAL_MESSAGES = "ADD_TO_PERSONAL_MESSAGES"; diff --git a/front/src/core/Chat.jsx b/front/src/core/Chat.jsx index 94ba481..aee66cb 100644 --- a/front/src/core/Chat.jsx +++ b/front/src/core/Chat.jsx @@ -1,22 +1,47 @@ -import React, { useRef } from "react"; +import React, { useRef, useContext } from "react"; import { useSelector } from "react-redux"; -import PropTypes from "prop-types"; -import Message from "./Message"; -import SpecialMessage from "./SpecialMessage"; import ConnectedUser from "./ConnectedUser"; +import { SocketContext } from "../socket/SocketContext"; +import MessagesDiv from "./MessagesDiv"; -export default function Chat({ socketRef }) { +export default function Chat() { const messageRef = useRef(); + const massagesWarperDiv = useRef(); + // Get Socket from context + const socket = useContext(SocketContext); const state = useSelector((state_) => state_); const onSendClick = (e) => { e.preventDefault(); - socketRef.current.emit("message", { - name: state.username, - id: state.id, - message: messageRef.current.value, - }); + if (state.messageTo_socketId === "global") { + socket.emit("message", { + name: state.username, + id: state.id, + message: messageRef.current.value, + }); + } else { + socket.emit("privateMessage", state.messageTo_socketId, { + name: state.username, + id: state.id, + message: messageRef.current.value, + }); + } + + // ? Erase message value after message + messageRef.current.value = ""; + + // ? Scroll down when sending new message + setTimeout(() => { + const objDiv = massagesWarperDiv.current; + objDiv.scrollTop = objDiv.scrollHeight; + }, 50); + }; + + const onKeyPressHandler = (e) => { + if (e.code === "Enter") { + onSendClick(e); + } }; return ( @@ -25,12 +50,13 @@ export default function Chat({ socketRef }) {
    Online Users
    + {state.connectedUsers.map((user) => ( ))}
    @@ -43,48 +69,14 @@ export default function Chat({ socketRef }) { // TODO when talking to someone private - should change to load private messages // TODO when to many messages, show create a scroll */} -
    - {state.messages.map((message) => { - switch (message.type) { - case "regular": - return ( - - ); - case "connect": - return ( - - ); - case "disconnect": - return ( - - ); - default: - return ""; - } - })} -
    +
    ); } - -Chat.propTypes = { - socketRef: PropTypes.func.isRequired, -}; diff --git a/front/src/core/ChatRoom.jsx b/front/src/core/ChatRoom.jsx index f24aa31..e1b9204 100644 --- a/front/src/core/ChatRoom.jsx +++ b/front/src/core/ChatRoom.jsx @@ -1,18 +1,18 @@ -import React, { useEffect, useRef } from "react"; -import io from "socket.io-client"; +import React, { useEffect } from "react"; import { useSelector, useDispatch } from "react-redux"; import Chat from "./Chat"; import NavBar from "./NavBar"; import { addToMessages, + addToPersonalMessages, removeConnectedUser, setConnectedUsers, setUserNameAndId, } from "../helper/actionsFunctions"; +import { socket, SocketContext } from "../socket/SocketContext"; export default function ChatRoom() { - const socketRef = useRef(); const state = useSelector((state_) => state_); const dispatch = useDispatch(); useEffect(() => { @@ -23,40 +23,52 @@ export default function ChatRoom() { if (state.username === "" || state.id === "") { dispatch(setUserNameAndId(userInfo.id, userInfo.name)); } - socketRef.current = io.connect("http://localhost:3001"); - socketRef.current.emit("onConnect", { + + socket.emit("onConnect", { name: state.username !== "" ? state.username : userInfo.name, id: state.id !== "" ? state.id : userInfo.id, }); - socketRef.current.on("newConnection", (msg) => { + socket.on("newConnection", (msg) => { dispatch(addToMessages(msg.id, msg.name, "", msg.time, "connect")); dispatch(setConnectedUsers(msg.connectedUsers)); }); - socketRef.current.on("messageBack", (msg) => { + socket.on("messageBack", (msg) => { dispatch( addToMessages(msg.id, msg.name, msg.message, msg.time, "regular") ); }); - socketRef.current.on("userDisconnect", (msg) => { + socket.on("userDisconnect", (msg) => { dispatch(addToMessages(msg.id, msg.name, "", msg.time, "disconnect")); dispatch(removeConnectedUser(msg.id)); }); // TODO should get a private MSG from someone // TODO add to general state as ${name}ChatMessages. - socketRef.current.on("privateMessage", (msg) => { - console.log(msg); + socket.on("privateMessage", (msg) => { + console.log(`I got a personal message from ${msg.from}`); + dispatch( + addToPersonalMessages( + msg.id, + msg.name, + msg.message, + msg.time, + msg.from, + msg.to + ) + ); }); }, []); document.documentElement.style.setProperty("--background-color", "#526377"); return (
    - - + + + +
    ); } diff --git a/front/src/core/ConnectedUser.jsx b/front/src/core/ConnectedUser.jsx index 1ec61cb..b7f1b5e 100644 --- a/front/src/core/ConnectedUser.jsx +++ b/front/src/core/ConnectedUser.jsx @@ -1,11 +1,39 @@ import React from "react"; import PropTypes from "prop-types"; +import { useDispatch } from "react-redux"; +import { setMessageToSocketId } from "../helper/actionsFunctions"; export default function ConnectedUser({ name, id, socketId }) { + const dispatch = useDispatch(); + + let src = "/images/person.png"; + if (name === "Global Chat") { + src = "/images/chat_1.png"; + } + + const onUserClick = (e) => { + e.preventDefault(); + dispatch(setMessageToSocketId(socketId)); + }; + + const handleKeyUp = (e) => { + e.preventDefault(); + if (e.code === "Enter") { + onUserClick(e); + } + }; + return ( -
    +
    - img + img
    {name}
    diff --git a/front/src/core/EnterNamePage.jsx b/front/src/core/EnterNamePage.jsx index 879d638..bd251e8 100644 --- a/front/src/core/EnterNamePage.jsx +++ b/front/src/core/EnterNamePage.jsx @@ -2,7 +2,6 @@ import React, { useRef } from "react"; import { Form, Button } from "react-bootstrap"; import { useNavigate } from "react-router-dom"; import { useDispatch } from "react-redux"; - import { nanoid } from "nanoid"; import { validateName } from "../helper/functions"; diff --git a/front/src/core/MessagesDiv.jsx b/front/src/core/MessagesDiv.jsx new file mode 100644 index 0000000..0be097a --- /dev/null +++ b/front/src/core/MessagesDiv.jsx @@ -0,0 +1,52 @@ +import React from "react"; +import { useSelector } from "react-redux"; +import Message from "./Message"; +import SpecialMessage from "./SpecialMessage"; + +const MessagesDiv = React.forwardRef((props, ref) => { + const state = useSelector((state_) => state_); + console.log(state); + return ( +
    + {state.messages.map((message) => { + switch (message.type) { + case "regular": + return ( + + ); + case "connect": + return ( + + ); + case "disconnect": + return ( + + ); + default: + return ""; + } + })} +
    + ); +}); + +export default MessagesDiv; diff --git a/front/src/core/SpecialMessage.jsx b/front/src/core/SpecialMessage.jsx index 1c04327..5d3aba1 100644 --- a/front/src/core/SpecialMessage.jsx +++ b/front/src/core/SpecialMessage.jsx @@ -3,6 +3,7 @@ import PropTypes from "prop-types"; import { formatDate } from "../helper/functions"; export default function SpecialMessage({ type, name, time }) { + console.log(time); const formattedDate = formatDate(time); let className; let message; diff --git a/front/src/helper/actionsFunctions.js b/front/src/helper/actionsFunctions.js index 7cabb78..43508b7 100644 --- a/front/src/helper/actionsFunctions.js +++ b/front/src/helper/actionsFunctions.js @@ -1,8 +1,10 @@ import { ADD_CONNECTED_USER, ADD_TO_MESSAGES, + ADD_TO_PERSONAL_MESSAGES, REMOVE_CONNECTED_USER, SET_CONNECTED_USERS, + SET_MESSAGE_TO_SOCKET_ID, SET_USERNAME_AND_ID, } from "../Actions"; @@ -46,3 +48,24 @@ export function addToMessages(id, name, message, time, type) { }, }; } + +export function addToPersonalMessages(id, name, message, time, from, to) { + return { + type: ADD_TO_PERSONAL_MESSAGES, + payload: { + from, + to, + name, + id, + message, + time, + }, + }; +} + +export function setMessageToSocketId(socketId) { + return { + type: SET_MESSAGE_TO_SOCKET_ID, + payload: { messageTo_socketId: socketId }, + }; +} diff --git a/front/src/index.css b/front/src/index.css index fff2e46..bd407f2 100644 --- a/front/src/index.css +++ b/front/src/index.css @@ -49,14 +49,20 @@ body { height: 800px; position: relative; } + .warperChatDiv { display: flex; flex-direction: ltr; - width: 80%; + width: 85%; margin: auto; margin-top: 50px; } +.massagesWarperDiv { + height: 86%; + overflow-y: scroll; +} + .usersOnlineHeaderDiv { background-color: #315b8a; height: 60px; @@ -107,6 +113,10 @@ body { background: transparent; } +.messageInput:focus { + outline: none; +} + .sendButton { padding: 3px 10px; margin-left: 22px; @@ -249,4 +259,9 @@ body { width: 100%; height: 500px; } + + .massagesWarperDiv { + height: 78%; + overflow-y: scroll; + } } diff --git a/front/src/reducers/MainReducer.js b/front/src/reducers/MainReducer.js index 3b88a3f..cc40b83 100644 --- a/front/src/reducers/MainReducer.js +++ b/front/src/reducers/MainReducer.js @@ -3,6 +3,8 @@ const initialState = { id: "", connectedUsers: [], // {name: string, id: string} messages: [], // {name: string, id: string , message: string, time: Date} + personalMessages: [], + messageTo_socketId: "global", // should contain socketId }; export default function MainReducer(state = initialState, action = "") { switch (action.type) { @@ -46,6 +48,27 @@ export default function MainReducer(state = initialState, action = "") { }, ], }; + case "ADD_TO_PERSONAL_MESSAGES": + console.log(action.payload); + return { + ...state, + personalMessages: [ + ...state.personalMessages, + { + from: action.payload.from, + to: action.payload.to, + name: action.payload.name, + id: action.payload.id, + message: action.payload.message, + time: action.payload.time, + }, + ], + }; + case "SET_MESSAGE_TO_SOCKET_ID": + return { + ...state, + messageTo_socketId: action.payload.messageTo_socketId, + }; default: return state; } diff --git a/front/src/socket/SocketContext.js b/front/src/socket/SocketContext.js new file mode 100644 index 0000000..710fe6d --- /dev/null +++ b/front/src/socket/SocketContext.js @@ -0,0 +1,5 @@ +import io from "socket.io-client"; +import React from "react"; + +export const socket = io.connect("http://localhost:3001"); +export const SocketContext = React.createContext(); From 53c0b3b3f580f0242438951fc1ae9da7549e2e63 Mon Sep 17 00:00:00 2001 From: DanielPhilosoph Date: Thu, 30 Dec 2021 18:20:03 +0200 Subject: [PATCH 08/15] personal messages, done --- back/app.js | 8 +- front/src/Actions.js | 1 + front/src/core/Chat.jsx | 31 ++++++- front/src/core/ConnectedUser.jsx | 19 ++++- front/src/core/MessagesDiv.jsx | 120 +++++++++++++++++++-------- front/src/core/SpecialMessage.jsx | 1 - front/src/helper/actionsFunctions.js | 8 ++ front/src/reducers/MainReducer.js | 7 +- 8 files changed, 149 insertions(+), 46 deletions(-) diff --git a/back/app.js b/back/app.js index e5a7181..f139e33 100644 --- a/back/app.js +++ b/back/app.js @@ -43,7 +43,7 @@ io.on("connection", (socket) => { io.emit("newConnection", { id: id, name: name, - time: new Date(), + time: new Date().toString(), connectedUsers: connectedUsers, }); }); @@ -54,7 +54,7 @@ io.on("connection", (socket) => { name: info.name, id: info.id, message: info.message, - time: new Date(), + time: new Date().toString(), }); }); @@ -69,14 +69,14 @@ io.on("connection", (socket) => { name: info.name, id: info.id, message: info.message, - time: new Date(), + time: new Date().toString(), }); }); socket.on("disconnect", () => { const disconnectedUser = connectedUsers.find( (user) => user.socketId === socket.id - ); + ) || { name: "someone", id: "fallback", socketId: "socketId_fallback" }; disconnectedUser.time = new Date(); io.emit("userDisconnect", disconnectedUser); connectedUsers = connectedUsers.filter( diff --git a/front/src/Actions.js b/front/src/Actions.js index 2b2e928..eeb7b23 100644 --- a/front/src/Actions.js +++ b/front/src/Actions.js @@ -5,3 +5,4 @@ export const ADD_TO_MESSAGES = "ADD_TO_MESSAGES"; export const SET_CONNECTED_USERS = "SET_CONNECTED_USERS"; export const SET_MESSAGE_TO_SOCKET_ID = "SET_MESSAGE_TO_SOCKET_ID"; export const ADD_TO_PERSONAL_MESSAGES = "ADD_TO_PERSONAL_MESSAGES"; +export const SET_CHAT_STATE = "SET_CHAT_STATE"; diff --git a/front/src/core/Chat.jsx b/front/src/core/Chat.jsx index aee66cb..1300871 100644 --- a/front/src/core/Chat.jsx +++ b/front/src/core/Chat.jsx @@ -1,17 +1,21 @@ import React, { useRef, useContext } from "react"; -import { useSelector } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import ConnectedUser from "./ConnectedUser"; import { SocketContext } from "../socket/SocketContext"; import MessagesDiv from "./MessagesDiv"; +import { addToPersonalMessages } from "../helper/actionsFunctions"; export default function Chat() { const messageRef = useRef(); const massagesWarperDiv = useRef(); + const dispatch = useDispatch(); // Get Socket from context const socket = useContext(SocketContext); const state = useSelector((state_) => state_); + const objDiv = massagesWarperDiv.current; + const onSendClick = (e) => { e.preventDefault(); if (state.messageTo_socketId === "global") { @@ -26,6 +30,21 @@ export default function Chat() { id: state.id, message: messageRef.current.value, }); + + // ? Find my user to get socketId + const myUser = state.connectedUsers.find((user) => user.id === state.id); + + // ? Add to personal messages + dispatch( + addToPersonalMessages( + state.id, + state.username, + messageRef.current.value, + new Date(), + myUser.socketId, + state.messageTo_socketId + ) + ); } // ? Erase message value after message @@ -33,7 +52,6 @@ export default function Chat() { // ? Scroll down when sending new message setTimeout(() => { - const objDiv = massagesWarperDiv.current; objDiv.scrollTop = objDiv.scrollHeight; }, 50); }; @@ -50,12 +68,19 @@ export default function Chat() {
    Online Users
    - + {state.connectedUsers.map((user) => ( ))} diff --git a/front/src/core/ConnectedUser.jsx b/front/src/core/ConnectedUser.jsx index b7f1b5e..e517f14 100644 --- a/front/src/core/ConnectedUser.jsx +++ b/front/src/core/ConnectedUser.jsx @@ -1,9 +1,9 @@ import React from "react"; import PropTypes from "prop-types"; import { useDispatch } from "react-redux"; -import { setMessageToSocketId } from "../helper/actionsFunctions"; +import { setChatState, setMessageToSocketId } from "../helper/actionsFunctions"; -export default function ConnectedUser({ name, id, socketId }) { +export default function ConnectedUser({ name, id, socketId, isMyself }) { const dispatch = useDispatch(); let src = "/images/person.png"; @@ -14,6 +14,13 @@ export default function ConnectedUser({ name, id, socketId }) { const onUserClick = (e) => { e.preventDefault(); dispatch(setMessageToSocketId(socketId)); + let chatState = "person"; + if (isMyself) { + chatState = "myself"; + } else if (socketId === "global") { + chatState = "global"; + } + dispatch(setChatState(chatState)); }; const handleKeyUp = (e) => { @@ -23,6 +30,11 @@ export default function ConnectedUser({ name, id, socketId }) { } }; + let myself = ""; + if (isMyself) { + myself = "(yourself)"; + } + return (
    img
    -
    {name}
    +
    {`${name} ${myself}`}
    ); } @@ -44,4 +56,5 @@ ConnectedUser.propTypes = { name: PropTypes.string.isRequired, id: PropTypes.string.isRequired, socketId: PropTypes.string.isRequired, + isMyself: PropTypes.bool.isRequired, }; diff --git a/front/src/core/MessagesDiv.jsx b/front/src/core/MessagesDiv.jsx index 0be097a..ba98421 100644 --- a/front/src/core/MessagesDiv.jsx +++ b/front/src/core/MessagesDiv.jsx @@ -1,3 +1,4 @@ +/* eslint-disable operator-linebreak */ import React from "react"; import { useSelector } from "react-redux"; import Message from "./Message"; @@ -6,47 +7,98 @@ import SpecialMessage from "./SpecialMessage"; const MessagesDiv = React.forwardRef((props, ref) => { const state = useSelector((state_) => state_); console.log(state); - return ( -
    - {state.messages.map((message) => { - switch (message.type) { - case "regular": + if (state.chatState === "global") { + return ( +
    + {state.messages.map((message) => { + switch (message.type) { + case "regular": + return ( + + ); + case "connect": + return ( + + ); + case "disconnect": + return ( + + ); + default: + return ""; + } + })} +
    + ); + } + if (state.chatState === "person") { + return ( +
    + {state.personalMessages.map((personalMessage) => { + if ( + personalMessage.to === state.messageTo_socketId || + personalMessage.from === state.messageTo_socketId + ) { return ( ); - case "connect": + } + return ""; + })} +
    + ); + } + if (state.chatState === "myself") { + return ( +
    + {state.personalMessages.map((personalMessage) => { + if ( + personalMessage.to === state.messageTo_socketId && + personalMessage.from === state.messageTo_socketId + ) { return ( - - ); - case "disconnect": - return ( - ); - default: - return ""; - } - })} -
    - ); + } + return ""; + })} +
    + ); + } + return ""; }); export default MessagesDiv; diff --git a/front/src/core/SpecialMessage.jsx b/front/src/core/SpecialMessage.jsx index 5d3aba1..1c04327 100644 --- a/front/src/core/SpecialMessage.jsx +++ b/front/src/core/SpecialMessage.jsx @@ -3,7 +3,6 @@ import PropTypes from "prop-types"; import { formatDate } from "../helper/functions"; export default function SpecialMessage({ type, name, time }) { - console.log(time); const formattedDate = formatDate(time); let className; let message; diff --git a/front/src/helper/actionsFunctions.js b/front/src/helper/actionsFunctions.js index 43508b7..0e2ad0a 100644 --- a/front/src/helper/actionsFunctions.js +++ b/front/src/helper/actionsFunctions.js @@ -3,6 +3,7 @@ import { ADD_TO_MESSAGES, ADD_TO_PERSONAL_MESSAGES, REMOVE_CONNECTED_USER, + SET_CHAT_STATE, SET_CONNECTED_USERS, SET_MESSAGE_TO_SOCKET_ID, SET_USERNAME_AND_ID, @@ -36,6 +37,13 @@ export function setConnectedUsers(connectedUsers) { }; } +export function setChatState(chatState = "global") { + return { + type: SET_CHAT_STATE, + payload: { chatState }, + }; +} + export function addToMessages(id, name, message, time, type) { return { type: ADD_TO_MESSAGES, diff --git a/front/src/reducers/MainReducer.js b/front/src/reducers/MainReducer.js index cc40b83..e9ebaf7 100644 --- a/front/src/reducers/MainReducer.js +++ b/front/src/reducers/MainReducer.js @@ -5,9 +5,15 @@ const initialState = { messages: [], // {name: string, id: string , message: string, time: Date} personalMessages: [], messageTo_socketId: "global", // should contain socketId + chatState: "global", // contain the chat state (what should be displayed) }; export default function MainReducer(state = initialState, action = "") { switch (action.type) { + case "SET_CHAT_STATE": + return { + ...state, + chatState: action.payload.chatState, + }; case "SET_USERNAME_AND_ID": return { ...state, @@ -49,7 +55,6 @@ export default function MainReducer(state = initialState, action = "") { ], }; case "ADD_TO_PERSONAL_MESSAGES": - console.log(action.payload); return { ...state, personalMessages: [ From 4f047163b20b90623065090b29e7a28fad685717 Mon Sep 17 00:00:00 2001 From: DanielPhilosoph Date: Thu, 30 Dec 2021 19:40:45 +0200 Subject: [PATCH 09/15] fixed sending empty messages --- front/src/core/Chat.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/front/src/core/Chat.jsx b/front/src/core/Chat.jsx index 1300871..5c31cd3 100644 --- a/front/src/core/Chat.jsx +++ b/front/src/core/Chat.jsx @@ -18,6 +18,9 @@ export default function Chat() { const onSendClick = (e) => { e.preventDefault(); + if (messageRef.current.value === "") { + return; + } if (state.messageTo_socketId === "global") { socket.emit("message", { name: state.username, From 7a7bff0b99989f6547518b2dd6d3df26c186a026 Mon Sep 17 00:00:00 2001 From: DanielPhilosoph Date: Thu, 30 Dec 2021 20:48:03 +0200 Subject: [PATCH 10/15] chat header changes base on conversation --- back/Dockerfile | 0 front/Dockerfile | 0 front/src/core/Chat.jsx | 17 ++++++++++++++++- front/src/core/ChatRoom.jsx | 3 --- front/src/core/ConnectedUser.jsx | 11 +++++++---- front/src/core/MessagesDiv.jsx | 6 +++--- front/src/helper/actionsFunctions.js | 4 ++-- front/src/helper/functions.js | 4 ++++ front/src/reducers/MainReducer.js | 7 +++++-- 9 files changed, 37 insertions(+), 15 deletions(-) create mode 100644 back/Dockerfile create mode 100644 front/Dockerfile diff --git a/back/Dockerfile b/back/Dockerfile new file mode 100644 index 0000000..e69de29 diff --git a/front/Dockerfile b/front/Dockerfile new file mode 100644 index 0000000..e69de29 diff --git a/front/src/core/Chat.jsx b/front/src/core/Chat.jsx index 5c31cd3..b8b169c 100644 --- a/front/src/core/Chat.jsx +++ b/front/src/core/Chat.jsx @@ -5,6 +5,7 @@ import ConnectedUser from "./ConnectedUser"; import { SocketContext } from "../socket/SocketContext"; import MessagesDiv from "./MessagesDiv"; import { addToPersonalMessages } from "../helper/actionsFunctions"; +import { firstLetterUppercase } from "../helper/functions"; export default function Chat() { const messageRef = useRef(); @@ -65,6 +66,20 @@ export default function Chat() { } }; + let header = ""; + switch (state.chatState.currentState) { + case "global": + header = "Chat"; + break; + case "myself": + header = `${state.chatState.sendTo} (Me)`; + break; + default: + header = state.chatState.sendTo; + break; + } + header = firstLetterUppercase(header); + return (
    @@ -91,7 +106,7 @@ export default function Chat() {
    Img - Chat + {header}
    {/* // TODO when talking to someone private - should change to load private messages diff --git a/front/src/core/ChatRoom.jsx b/front/src/core/ChatRoom.jsx index e1b9204..40840d6 100644 --- a/front/src/core/ChatRoom.jsx +++ b/front/src/core/ChatRoom.jsx @@ -45,10 +45,7 @@ export default function ChatRoom() { dispatch(removeConnectedUser(msg.id)); }); - // TODO should get a private MSG from someone - // TODO add to general state as ${name}ChatMessages. socket.on("privateMessage", (msg) => { - console.log(`I got a personal message from ${msg.from}`); dispatch( addToPersonalMessages( msg.id, diff --git a/front/src/core/ConnectedUser.jsx b/front/src/core/ConnectedUser.jsx index e517f14..d6a5b34 100644 --- a/front/src/core/ConnectedUser.jsx +++ b/front/src/core/ConnectedUser.jsx @@ -14,13 +14,16 @@ export default function ConnectedUser({ name, id, socketId, isMyself }) { const onUserClick = (e) => { e.preventDefault(); dispatch(setMessageToSocketId(socketId)); - let chatState = "person"; + let sendTo = name; + let currentState = "person"; if (isMyself) { - chatState = "myself"; + sendTo = name; + currentState = "myself"; } else if (socketId === "global") { - chatState = "global"; + sendTo = "everyone"; + currentState = "global"; } - dispatch(setChatState(chatState)); + dispatch(setChatState(sendTo, currentState)); }; const handleKeyUp = (e) => { diff --git a/front/src/core/MessagesDiv.jsx b/front/src/core/MessagesDiv.jsx index ba98421..f6a1035 100644 --- a/front/src/core/MessagesDiv.jsx +++ b/front/src/core/MessagesDiv.jsx @@ -7,7 +7,7 @@ import SpecialMessage from "./SpecialMessage"; const MessagesDiv = React.forwardRef((props, ref) => { const state = useSelector((state_) => state_); console.log(state); - if (state.chatState === "global") { + if (state.chatState.currentState === "global") { return (
    {state.messages.map((message) => { @@ -50,7 +50,7 @@ const MessagesDiv = React.forwardRef((props, ref) => {
    ); } - if (state.chatState === "person") { + if (state.chatState.currentState === "person") { return (
    {state.personalMessages.map((personalMessage) => { @@ -74,7 +74,7 @@ const MessagesDiv = React.forwardRef((props, ref) => {
    ); } - if (state.chatState === "myself") { + if (state.chatState.currentState === "myself") { return (
    {state.personalMessages.map((personalMessage) => { diff --git a/front/src/helper/actionsFunctions.js b/front/src/helper/actionsFunctions.js index 0e2ad0a..cefae57 100644 --- a/front/src/helper/actionsFunctions.js +++ b/front/src/helper/actionsFunctions.js @@ -37,10 +37,10 @@ export function setConnectedUsers(connectedUsers) { }; } -export function setChatState(chatState = "global") { +export function setChatState(sendTo, currentState) { return { type: SET_CHAT_STATE, - payload: { chatState }, + payload: { currentState, sendTo }, }; } diff --git a/front/src/helper/functions.js b/front/src/helper/functions.js index 407f11f..411eeda 100644 --- a/front/src/helper/functions.js +++ b/front/src/helper/functions.js @@ -17,3 +17,7 @@ export function formatDate(time) { const date = new Date(time); return moment(date).format("HH:mm"); } + +export function firstLetterUppercase(str) { + return str.charAt(0).toUpperCase() + str.slice(1); +} diff --git a/front/src/reducers/MainReducer.js b/front/src/reducers/MainReducer.js index e9ebaf7..00eedd7 100644 --- a/front/src/reducers/MainReducer.js +++ b/front/src/reducers/MainReducer.js @@ -5,14 +5,17 @@ const initialState = { messages: [], // {name: string, id: string , message: string, time: Date} personalMessages: [], messageTo_socketId: "global", // should contain socketId - chatState: "global", // contain the chat state (what should be displayed) + chatState: { sendTo: "everyone", currentState: "global" }, // contain the chat state (what should be displayed) }; export default function MainReducer(state = initialState, action = "") { switch (action.type) { case "SET_CHAT_STATE": return { ...state, - chatState: action.payload.chatState, + chatState: { + sendTo: action.payload.sendTo, + currentState: action.payload.currentState, + }, }; case "SET_USERNAME_AND_ID": return { From 4031d538c43bb80e019ad629cab198b4ad3296f6 Mon Sep 17 00:00:00 2001 From: DanielPhilosoph Date: Thu, 30 Dec 2021 21:14:50 +0200 Subject: [PATCH 11/15] docker files --- back/.dockerignore | 2 ++ back/Dockerfile | 13 +++++++++++++ back/app.js | 6 ++---- docker-compose.prod.yml | 9 +++++++++ front/.dockerignore | 2 ++ front/Dockerfile | 13 +++++++++++++ 6 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 back/.dockerignore create mode 100644 docker-compose.prod.yml create mode 100644 front/.dockerignore diff --git a/back/.dockerignore b/back/.dockerignore new file mode 100644 index 0000000..9ca6dc5 --- /dev/null +++ b/back/.dockerignore @@ -0,0 +1,2 @@ +node_modules +*.md \ No newline at end of file diff --git a/back/Dockerfile b/back/Dockerfile index e69de29..f124aef 100644 --- a/back/Dockerfile +++ b/back/Dockerfile @@ -0,0 +1,13 @@ +FROM node:16 + +WORKDIR /usr/back + +COPY ["package.json", "package-lock.json*", "./"] + +RUN npm install + +COPY . . + +EXPOSE 3001 + +CMD [ "node", "/" ] \ No newline at end of file diff --git a/back/app.js b/back/app.js index f139e33..d22f296 100644 --- a/back/app.js +++ b/back/app.js @@ -25,6 +25,7 @@ let connectedUsers = []; //? Socket.io Connection io.on("connection", (socket) => { console.log("a user connected"); + // ? Allows to send private messages socket.join(socket.id); socket.on("onConnect", ({ name, id }) => { @@ -58,9 +59,6 @@ io.on("connection", (socket) => { }); }); - //? ----------------------------------- - //! Understand how this works and implement!! - //? ----------------------------------- socket.on("privateMessage", (anotherSocketId, info) => { console.log("you send a private message to ", anotherSocketId); socket.to(anotherSocketId).emit("privateMessage", { @@ -77,7 +75,7 @@ io.on("connection", (socket) => { const disconnectedUser = connectedUsers.find( (user) => user.socketId === socket.id ) || { name: "someone", id: "fallback", socketId: "socketId_fallback" }; - disconnectedUser.time = new Date(); + disconnectedUser.time = new Date().toString(); io.emit("userDisconnect", disconnectedUser); connectedUsers = connectedUsers.filter( (user) => user.socketId !== socket.id diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..ea3f53d --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,9 @@ +version: '3.9' + +services: + server: + build: "./back" + ports: + - "3001:3001" + ui: + build: "./front" \ No newline at end of file diff --git a/front/.dockerignore b/front/.dockerignore new file mode 100644 index 0000000..9ca6dc5 --- /dev/null +++ b/front/.dockerignore @@ -0,0 +1,2 @@ +node_modules +*.md \ No newline at end of file diff --git a/front/Dockerfile b/front/Dockerfile index e69de29..cedffb1 100644 --- a/front/Dockerfile +++ b/front/Dockerfile @@ -0,0 +1,13 @@ +FROM node:16 + +WORKDIR /usr/front + +COPY ["package.json", "package-lock.json*", "./"] + +RUN npm install + +COPY . . + +EXPOSE 3000 + +CMD [ "node", "/" ] \ No newline at end of file From 63cff4d1a2b08abe2ac211468a03d6b0e0891453 Mon Sep 17 00:00:00 2001 From: DanielPhilosoph Date: Thu, 30 Dec 2021 22:32:27 +0200 Subject: [PATCH 12/15] responsice css --- back/Dockerfile | 2 +- docker-compose.prod.yml | 4 +- front/Dockerfile | 2 +- front/src/core/Chat.jsx | 5 +- front/src/helper/functions.js | 3 + front/src/index.css | 100 ++++++++++++++++++++++++++++++++-- 6 files changed, 103 insertions(+), 13 deletions(-) diff --git a/back/Dockerfile b/back/Dockerfile index f124aef..1fb9d71 100644 --- a/back/Dockerfile +++ b/back/Dockerfile @@ -10,4 +10,4 @@ COPY . . EXPOSE 3001 -CMD [ "node", "/" ] \ No newline at end of file +CMD [ "node", "./app.js" ] \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index ea3f53d..a4a9ba0 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,9 +1,9 @@ version: '3.9' services: - server: + server-chat: build: "./back" ports: - "3001:3001" - ui: + ui-chat: build: "./front" \ No newline at end of file diff --git a/front/Dockerfile b/front/Dockerfile index cedffb1..2b4b5c7 100644 --- a/front/Dockerfile +++ b/front/Dockerfile @@ -10,4 +10,4 @@ COPY . . EXPOSE 3000 -CMD [ "node", "/" ] \ No newline at end of file +CMD [ "npm", "start" ] \ No newline at end of file diff --git a/front/src/core/Chat.jsx b/front/src/core/Chat.jsx index b8b169c..5a0df9d 100644 --- a/front/src/core/Chat.jsx +++ b/front/src/core/Chat.jsx @@ -108,10 +108,7 @@ export default function Chat() { Img {header}
    - {/* - // TODO when talking to someone private - should change to load private messages - // TODO when to many messages, show create a scroll - */} + 25) { + return { valid: false, error: "Can't be longer then 25 characters" }; + } return { valid: true }; } diff --git a/front/src/index.css b/front/src/index.css index bd407f2..8bc8f38 100644 --- a/front/src/index.css +++ b/front/src/index.css @@ -65,7 +65,7 @@ body { .usersOnlineHeaderDiv { background-color: #315b8a; - height: 60px; + height: 8%; font-family: "Raleway", sans-serif; font-size: 30px; padding: 5px 10px; @@ -74,7 +74,7 @@ body { .chatHeaderDiv { background-color: #165eb1; - height: 60px; + height: 8%; padding: 7px 10px; color: whitesmoke; } @@ -93,6 +93,7 @@ body { font-family: "Raleway", sans-serif; font-size: 30px; padding: 0px 10px; + word-break: break-word; } .sendMassageDiv { @@ -106,7 +107,7 @@ body { } .messageInput { - width: 90%; + width: 92%; height: 30px; border-radius: 5px; border: 0; @@ -118,7 +119,7 @@ body { } .sendButton { - padding: 3px 10px; + padding: 3px 1%; margin-left: 22px; border: 0.1px solid rgb(92, 92, 92); border-radius: 50%; @@ -135,7 +136,7 @@ body { max-width: 60%; min-width: 20%; background-color: rgb(212, 212, 212); - border-radius: 5px; + border-radius: 15px; width: -moz-fit-content; width: fit-content; word-break: break-word; @@ -244,7 +245,38 @@ body { margin: auto 0; } +@media screen and (max-width: 1100px) { + .chatLogo { + border-radius: 50%; + width: 40px; + margin-top: -10px; + } + .chatHeaderText { + font-family: "Raleway", sans-serif; + font-size: 20px; + padding: 0px 10px; + word-break: break-word; + } + .chatHeaderDiv { + padding: 20px 10px; + } +} + @media screen and (max-width: 900px) { + .chatLogo { + border-radius: 50%; + width: 30px; + margin-top: -10px; + } + .chatHeaderText { + font-family: "Raleway", sans-serif; + font-size: 20px; + padding: 0px 10px; + word-break: break-word; + } + .chatHeaderDiv { + padding: 6px 10px; + } .warperChatDiv { flex-direction: column-reverse; gap: 30px; @@ -265,3 +297,61 @@ body { overflow-y: scroll; } } + +@media screen and (max-width: 1200px) { + .messageInput { + width: 91%; + } + .sendButton { + padding: 2px 7px; + margin-left: 10px; + } +} + +@media screen and (max-width: 600px) { + .messageInput { + width: 88%; + } + .sendButton { + margin-left: 13px; + } + .chatHeaderDiv { + padding: 9px 10px; + } + .chatHeaderText { + font-family: "Raleway", sans-serif; + font-size: 15px; + padding: 0px 10px; + word-break: break-word; + } +} + +@media screen and (max-width: 490px) { + .messageInput { + width: 83%; + } + .chatHeaderText { + font-size: 11px; + } + .sendButton { + margin-left: 13px; + } +} + +@media screen and (max-width: 330px) { + .messageInput { + width: 80%; + } + .chatHeaderText { + font-size: 5px; + } +} + +@media screen and (max-width: 300px) { + .messageInput { + width: 76%; + } + .chatHeaderText { + font-size: 8px; + } +} From 599d05bc4537743b21aa405dc5a075c78b023cb7 Mon Sep 17 00:00:00 2001 From: DanielPhilosoph Date: Sat, 1 Jan 2022 13:04:56 +0200 Subject: [PATCH 13/15] fixed docker compose --- docker-compose.prod.yml | 4 +++- front/src/index.css | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index a4a9ba0..e3d467f 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -6,4 +6,6 @@ services: ports: - "3001:3001" ui-chat: - build: "./front" \ No newline at end of file + build: "./front" + ports: + - "3000:3000" \ No newline at end of file diff --git a/front/src/index.css b/front/src/index.css index 8bc8f38..d7965c6 100644 --- a/front/src/index.css +++ b/front/src/index.css @@ -245,6 +245,17 @@ body { margin: auto 0; } +.connectedUserNewMessages { + float: right; + margin: auto; + background-color: red; + color: whitesmoke; + font-size: 12px; + font-weight: bold; + padding: 3px 6px; + border-radius: 10px; +} + @media screen and (max-width: 1100px) { .chatLogo { border-radius: 50%; From da3f25f3be8d557bc101311fac61f8c9cdce5847 Mon Sep 17 00:00:00 2001 From: DanielPhilosoph Date: Sat, 1 Jan 2022 13:13:18 +0200 Subject: [PATCH 14/15] fixed bug --- front/src/core/EnterNamePage.jsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/front/src/core/EnterNamePage.jsx b/front/src/core/EnterNamePage.jsx index bd251e8..55f08c2 100644 --- a/front/src/core/EnterNamePage.jsx +++ b/front/src/core/EnterNamePage.jsx @@ -30,6 +30,10 @@ export default function EnterNamePage() { } }; + document.documentElement.style.setProperty( + "--background-color", + "linear-gradient(90deg,rgba(45, 226, 255, 1) 0%,rgba(19, 50, 200, 1) 0%,rgba(0, 219, 255, 1) 100%)" + ); return (
    From 8c33e27593e89e6651a00b5c2f3c8173d1214516 Mon Sep 17 00:00:00 2001 From: DanielPhilosoph Date: Sun, 2 Jan 2022 17:04:58 +0200 Subject: [PATCH 15/15] Added heroku.yml --- heroku.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 heroku.yml diff --git a/heroku.yml b/heroku.yml new file mode 100644 index 0000000..8eec25b --- /dev/null +++ b/heroku.yml @@ -0,0 +1,3 @@ +build: + docker: + web: Dockerfile