diff --git a/.gitignore b/.gitignore
index 34aeecc..c03feab 100755
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,7 @@
node_modules/
-config/production.json
\ No newline at end of file
+client/node_modules/
+config/production.json
+client/debug.log
+config/default.json
+package-lock.json
+
diff --git a/client/package-lock.json b/client/package-lock.json
index 6572e44..6f46875 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -1565,6 +1565,11 @@
"resolved": "https://registry.npmjs.org/address/-/address-1.0.3.tgz",
"integrity": "sha512-z55ocwKBRLryBs394Sm3ushTtBeg6VAeuku7utSoSnsJKvKcnXFIyC6vh27n3rXyxSgkJBBCAvyOn7gSUcTYjg=="
},
+ "after": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
+ "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
+ },
"ajv": {
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
@@ -1735,6 +1740,11 @@
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
},
+ "arraybuffer.slice": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
+ "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
+ },
"arrify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
@@ -2126,6 +2136,11 @@
"resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
"integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ=="
},
+ "backo2": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+ "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
+ },
"bail": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/bail/-/bail-1.0.3.tgz",
@@ -2191,6 +2206,11 @@
}
}
},
+ "base64-arraybuffer": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
+ "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
+ },
"base64-js": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
@@ -2209,6 +2229,14 @@
"tweetnacl": "^0.14.3"
}
},
+ "better-assert": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
+ "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
+ "requires": {
+ "callsite": "1.0.0"
+ }
+ },
"big.js": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
@@ -2219,6 +2247,11 @@
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
"integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw=="
},
+ "blob": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
+ "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
+ },
"bluebird": {
"version": "3.5.4",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz",
@@ -2518,6 +2551,11 @@
"caller-callsite": "^2.0.0"
}
},
+ "callsite": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
+ "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
+ },
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -2804,11 +2842,21 @@
"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.4.0.tgz",
"integrity": "sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg=="
},
+ "component-bind": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
+ "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
+ },
"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=="
},
+ "component-inherit": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
+ "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
+ },
"compressible": {
"version": "2.0.16",
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.16.tgz",
@@ -3698,6 +3746,64 @@
"once": "^1.4.0"
}
},
+ "engine.io-client": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.0.tgz",
+ "integrity": "sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA==",
+ "requires": {
+ "component-emitter": "1.2.1",
+ "component-inherit": "0.0.3",
+ "debug": "~4.1.0",
+ "engine.io-parser": "~2.2.0",
+ "has-cors": "1.1.0",
+ "indexof": "0.0.1",
+ "parseqs": "0.0.5",
+ "parseuri": "0.0.5",
+ "ws": "~6.1.0",
+ "xmlhttprequest-ssl": "~1.5.4",
+ "yeast": "0.1.2"
+ },
+ "dependencies": {
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "ws": {
+ "version": "6.1.4",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
+ "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
+ "requires": {
+ "async-limiter": "~1.0.0"
+ }
+ }
+ }
+ },
+ "engine.io-parser": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz",
+ "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==",
+ "requires": {
+ "after": "0.8.2",
+ "arraybuffer.slice": "~0.0.7",
+ "base64-arraybuffer": "0.1.5",
+ "blob": "0.0.5",
+ "has-binary2": "~1.0.2"
+ }
+ },
"enhanced-resolve": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz",
@@ -5351,6 +5457,26 @@
}
}
},
+ "has-binary2": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
+ "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
+ "requires": {
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+ }
+ }
+ },
+ "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",
@@ -7608,6 +7734,11 @@
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
+ "object-component": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
+ "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
+ },
"object-copy": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
@@ -7921,6 +8052,22 @@
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
"integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ=="
},
+ "parseqs": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
+ "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
+ "requires": {
+ "better-assert": "~1.0.0"
+ }
+ },
+ "parseuri": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
+ "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
+ "requires": {
+ "better-assert": "~1.0.0"
+ }
+ },
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -10354,6 +10501,77 @@
"kind-of": "^3.2.0"
}
},
+ "socket.io-client": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
+ "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
+ "requires": {
+ "backo2": "1.0.2",
+ "base64-arraybuffer": "0.1.5",
+ "component-bind": "1.0.0",
+ "component-emitter": "1.2.1",
+ "debug": "~4.1.0",
+ "engine.io-client": "~3.4.0",
+ "has-binary2": "~1.0.2",
+ "has-cors": "1.1.0",
+ "indexof": "0.0.1",
+ "object-component": "0.0.3",
+ "parseqs": "0.0.5",
+ "parseuri": "0.0.5",
+ "socket.io-parser": "~3.3.0",
+ "to-array": "0.1.4"
+ },
+ "dependencies": {
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "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-parser": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
+ "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==",
+ "requires": {
+ "component-emitter": "1.2.1",
+ "debug": "~3.1.0",
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
+ },
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+ }
+ }
+ },
"sockjs": {
"version": "0.3.19",
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz",
@@ -10958,6 +11176,11 @@
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz",
"integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE="
},
+ "to-array": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
+ "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
+ },
"to-arraybuffer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
@@ -12025,6 +12248,11 @@
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-1.3.1.tgz",
"integrity": "sha512-tGkGJkN8XqCod7OT+EvGYK5Z4SfDQGD30zAa58OcnAa0RRWgzUEK72tkXhsX1FZd+rgnhRxFtmO+ihkp8LHSkw=="
},
+ "xmlhttprequest-ssl": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
+ "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
+ },
"xregexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz",
@@ -12079,6 +12307,11 @@
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
+ },
+ "yeast": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
+ "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
}
}
}
diff --git a/client/package.json b/client/package.json
index 5209eb0..7b67b02 100644
--- a/client/package.json
+++ b/client/package.json
@@ -14,6 +14,7 @@
"redux": "^4.0.1",
"redux-devtools-extension": "^2.13.8",
"redux-thunk": "^2.3.0",
+ "socket.io-client": "^2.3.0",
"uuid": "^3.3.2"
},
"scripts": {
diff --git a/client/public/index.html b/client/public/index.html
index d9069be..675099b 100755
--- a/client/public/index.html
+++ b/client/public/index.html
@@ -11,6 +11,15 @@
integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf"
crossorigin="anonymous"
/>
+
+
+
Welcome To DevConnector
diff --git a/client/src/App.css b/client/src/App.css
index 0957f54..20bebb2 100755
--- a/client/src/App.css
+++ b/client/src/App.css
@@ -488,6 +488,25 @@ button {
margin-bottom: 0.5rem;
}
+/* Friends Page */
+.friend-card {
+ border: #ccc solid 1px;
+ background: var(--light-color);
+ display: grid;
+ grid-template-columns: 1fr 5fr 2fr;
+ align-items: center;
+ grid-gap: 0rem;
+ padding: 0.5rem 1rem;
+ line-height: 1.3;
+}
+
+.friend-name-card {
+ border-right: #ccc solid 1px;
+ border-left: #ccc solid 1px;
+ font-size: 1.2rem;
+ padding-left: 2rem;
+}
+
/* Mobile Styles */
@media (max-width: 700px) {
.container {
@@ -574,4 +593,150 @@ button {
.post button {
padding: 0.3rem 0.4rem;
}
+
+ /* Friends page */
+ .friend-card {
+ grid-template-columns: 1fr;
+ text-align: center;
+ align-items: left;
+ padding: 0.5rem;
+ }
+
+ .friend-card img {
+ width: 25%;
+ margin: 0.5rem auto;
+ }
+
+ .friend-name-card {
+ border: #ccc solid 1px;
+ }
+}
+
+/* Live Chat Component Styles */
+
+.chat-user-container {
+ background-color: #343a40;
+ width: 20%;
+ position: fixed;
+ top: 60px;
+ right: 0;
+ bottom: 0;
+ margin: 0;
+ padding: 0;
+}
+.chat-user-container h6 {
+ color: #0c9463;
+ font-family: 'Roboto', sans-serif;
+ font-size: 1.5rem;
+ background-color: #2d334a65;
+}
+.active.grey {
+ background-color: #454b52;
+}
+.chat-user {
+ color: white;
+ text-transform: capitalize;
+}
+
+.chat.room {
+ background-color: #851515 !important;
+}
+.chat-room .friend-list li {
+ border-bottom: 1px solid #e0e0e0;
+}
+.chat-room .friend-list li:last-of-type {
+ border-bottom: none;
+}
+
+.chat-room img.avatar {
+ height: 3rem;
+ width: 3rem;
+ border-radius: 50% !important;
+}
+
+.chat-room .text-small {
+ font-size: 0.95rem;
+}
+
+.chat-room .text-smaller {
+ font-size: 0.75rem;
+}
+
+.z-depth-1 {
+ box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12) !important;
+}
+
+.chat-body {
+ padding: 1.5rem !important;
+ width: 20rem;
+ line-height: 1.5;
+ font-family: 'Roboto', sans-serif;
+ color: #212529;
+ word-break: break-all;
+}
+
+.chat-body p {
+ padding: 0.75rem 0rem !important;
+}
+
+div.header {
+ display: flex;
+ align-items: center;
+ margin-bottom: 0.5rem;
+}
+
+.primary-font {
+ color: #212529;
+ font-weight: 700;
+ margin-right: 1rem;
+}
+
+.pull-right {
+ color: #6c757d !important;
+
+ font-weight: 300;
+}
+
+.chat-footer p {
+ color: greenyellow;
+}
+
+.user-chat-box {
+ flex-flow: row;
+}
+
+.guest-chat-box {
+ flex-flow: row-reverse;
+}
+
+.chat-input {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ background-color: whitesmoke;
+ padding: 16px 32px;
+ text-decoration: none;
+ margin: 4px;
+ border-radius: 5px;
+ width: 60%;
+ height: 60px;
+ border: 1px solid #0c7c80;
+}
+.chat-send-button {
+ position: fixed;
+ background-color: rgb(15, 112, 192);
+ width: 10%;
+ height: 60px;
+ bottom: 0;
+ right: 25%;
+ border-radius: 5px;
+ margin: 4px;
+ border: 1px solid #0c7c80;
+ color: white;
+ transition: 0.15s all ease-in;
+}
+
+.chat-send-button:hover {
+ color: white;
+ background-color: #343a40;
}
diff --git a/client/src/App.js b/client/src/App.js
index 4c20465..6fdc11c 100755
--- a/client/src/App.js
+++ b/client/src/App.js
@@ -10,6 +10,8 @@ import store from './store';
import { loadUser } from './actions/auth';
import setAuthToken from './utils/setAuthToken';
+import { socketClient } from './utils/socketClient';
+
import './App.css';
if (localStorage.token) {
@@ -18,8 +20,10 @@ if (localStorage.token) {
const App = () => {
useEffect(() => {
+ socketClient();
store.dispatch(loadUser());
- }, []);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [loadUser]);
return (
@@ -27,7 +31,7 @@ const App = () => {
-
+
diff --git a/client/src/actions/auth.js b/client/src/actions/auth.js
index 60adae0..fcf55a4 100755
--- a/client/src/actions/auth.js
+++ b/client/src/actions/auth.js
@@ -8,9 +8,10 @@ import {
LOGIN_SUCCESS,
LOGIN_FAIL,
LOGOUT,
- CLEAR_PROFILE
+ CLEAR_PROFILE,
} from './types';
import setAuthToken from '../utils/setAuthToken';
+import { socketEmit, socketActions } from '../utils/socketClient';
// Load User
export const loadUser = () => async dispatch => {
@@ -23,11 +24,12 @@ export const loadUser = () => async dispatch => {
dispatch({
type: USER_LOADED,
- payload: res.data
+ payload: res.data,
});
+ socketEmit(res);
} catch (err) {
dispatch({
- type: AUTH_ERROR
+ type: AUTH_ERROR,
});
}
};
@@ -36,8 +38,8 @@ export const loadUser = () => async dispatch => {
export const register = ({ name, email, password }) => async dispatch => {
const config = {
headers: {
- 'Content-Type': 'application/json'
- }
+ 'Content-Type': 'application/json',
+ },
};
const body = JSON.stringify({ name, email, password });
@@ -47,7 +49,7 @@ export const register = ({ name, email, password }) => async dispatch => {
dispatch({
type: REGISTER_SUCCESS,
- payload: res.data
+ payload: res.data,
});
dispatch(loadUser());
@@ -59,7 +61,7 @@ export const register = ({ name, email, password }) => async dispatch => {
}
dispatch({
- type: REGISTER_FAIL
+ type: REGISTER_FAIL,
});
}
};
@@ -68,8 +70,8 @@ export const register = ({ name, email, password }) => async dispatch => {
export const login = (email, password) => async dispatch => {
const config = {
headers: {
- 'Content-Type': 'application/json'
- }
+ 'Content-Type': 'application/json',
+ },
};
const body = JSON.stringify({ email, password });
@@ -79,7 +81,7 @@ export const login = (email, password) => async dispatch => {
dispatch({
type: LOGIN_SUCCESS,
- payload: res.data
+ payload: res.data,
});
dispatch(loadUser());
@@ -91,7 +93,7 @@ export const login = (email, password) => async dispatch => {
}
dispatch({
- type: LOGIN_FAIL
+ type: LOGIN_FAIL,
});
}
};
@@ -101,3 +103,50 @@ export const logout = () => dispatch => {
dispatch({ type: CLEAR_PROFILE });
dispatch({ type: LOGOUT });
};
+
+// Friend Request
+// Send Friend Request api/profile/friend/:id(receiver user id)
+export const sendFriendRequest = id => async dispatch => {
+ try {
+ const res = await axios.post(`/api/profile/friend/${id}`);
+
+ socketActions(res, 'sendFriendRequest');
+ dispatch(loadUser());
+ dispatch(setAlert(res.data.msg, 'success'));
+ } catch (err) {
+ dispatch(setAlert(err.response.data.msg, 'danger'));
+ }
+};
+// Accept Friend Request api/profile/friend/:senderId
+export const acceptFriendRequest = id => async dispatch => {
+ try {
+ const res = await axios.put(`/api/profile/friend/${id}`);
+ socketActions(res, 'acceptFriendRequest');
+ dispatch(loadUser());
+ dispatch(setAlert(res.data.msg, 'success'));
+ } catch (err) {
+ dispatch(setAlert(err.response.data.msg, 'danger'));
+ }
+};
+// Cancel Friend Request api/profile/friend/:senderId
+export const cancelFriendRequest = id => async dispatch => {
+ try {
+ const res = await axios.patch(`/api/profile/friend/${id}`);
+ socketActions(res, 'cancelFriendRequest');
+ dispatch(loadUser());
+ dispatch(setAlert(res.data.msg, 'success'));
+ } catch (err) {
+ dispatch(setAlert(err.response.data.msg, 'danger'));
+ }
+};
+// Remove Friend api/profile/friend/:senderId
+export const removeFriend = id => async dispatch => {
+ try {
+ const res = await axios.delete(`/api/profile/friend/${id}`);
+ socketActions(res, 'removeFriend');
+ dispatch(loadUser());
+ dispatch(setAlert(res.data.msg, 'success'));
+ } catch (err) {
+ dispatch(setAlert(err.response.data.msg, 'danger'));
+ }
+};
diff --git a/client/src/actions/chat.js b/client/src/actions/chat.js
new file mode 100644
index 0000000..359eb6d
--- /dev/null
+++ b/client/src/actions/chat.js
@@ -0,0 +1,22 @@
+import { USERLIST_ADD, USERNAME_ADD, MESSAGE_ADD } from './types';
+
+export const receiveUsername = data => {
+ return {
+ type: USERNAME_ADD,
+ payload: data,
+ };
+};
+
+export const receiveUsers = data => {
+ return {
+ type: USERLIST_ADD,
+ payload: data,
+ };
+};
+
+export const receiveMessage = data => {
+ return {
+ type: MESSAGE_ADD,
+ payload: data,
+ };
+};
diff --git a/client/src/actions/types.js b/client/src/actions/types.js
index 883c20e..a35335e 100755
--- a/client/src/actions/types.js
+++ b/client/src/actions/types.js
@@ -22,3 +22,6 @@ export const DELETE_POST = 'DELETE_POST';
export const ADD_POST = 'ADD_POST';
export const ADD_COMMENT = 'ADD_COMMENT';
export const REMOVE_COMMENT = 'REMOVE_COMMENT';
+export const USERNAME_ADD = 'USERNAME_ADD';
+export const USERLIST_ADD = 'USERLIST_ADD';
+export const MESSAGE_ADD = 'MESSAGE_ADD';
diff --git a/client/src/components/chat/App.js b/client/src/components/chat/App.js
new file mode 100644
index 0000000..5a9bb14
--- /dev/null
+++ b/client/src/components/chat/App.js
@@ -0,0 +1,32 @@
+import React, { Fragment } from 'react';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import Spinner from './../layout/Spinner';
+
+import Chat from './Chat';
+
+const App = ({ auth: { user }, chat }) => {
+ if (!user) {
+ return ;
+ }
+
+ if (!chat.userlist.length) {
+ }
+
+ const username = user.name;
+ const chatt = username ? : null;
+ return {chatt};
+};
+
+App.propTypes = {
+ auth: PropTypes.object
+};
+
+const mapStateToProps = state => ({
+ user: state.user,
+ auth: state.auth,
+ chat: state.chat
+
+});
+
+export default connect(mapStateToProps)(App);
diff --git a/client/src/components/chat/Chat.js b/client/src/components/chat/Chat.js
new file mode 100644
index 0000000..6cf839d
--- /dev/null
+++ b/client/src/components/chat/Chat.js
@@ -0,0 +1,147 @@
+import React, { useState, useEffect } from 'react';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import { receiveUsers, receiveMessage, receiveUsername } from '../../actions/chat';
+
+import UserList from './UserList';
+import Messages from './Messages';
+import Spinner from '../layout/Spinner';
+import { socket } from './../../utils/socketClient';
+const Chat = ({ user, chat, receiveMessage, receiveUsers, receiveUsername }) => {
+ const username = user.name;
+ useEffect(() => {
+ receiveUsername([username, user._id]);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [username]);
+
+ const [formData, setFormData] = useState({
+ text: '',
+ });
+ const { text } = formData;
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ const chatSocket = () => {
+ // receive userlist
+ socket.on('chat users', ({ room, users }) => {
+ receiveUsers({ room, users });
+ });
+
+ // send join message
+ socket.emit(
+ 'chat join',
+ {
+ timestamp: new Date(),
+ username,
+ userID: user._id,
+ userAvatar: user.avatar,
+ room: 'general',
+ },
+ error => {
+ if (error) {
+ return;
+ }
+ },
+ );
+
+ // receive join message
+ socket.on('chat join', msg => {
+ receiveMessage(msg);
+ });
+
+ // receive leave message
+ socket.on('chat leave', msg => {
+ receiveMessage(msg);
+ });
+
+ // receive message
+ socket.on('chat message', msg => {
+ receiveMessage(msg);
+ });
+
+ // send leave message when user leaves the page
+ window.addEventListener('beforeunload', e => {
+ e.preventDefault();
+
+ socket.emit('chat leave', {
+ timestamp: new Date(),
+ username,
+ userID: user._id,
+ room: 'general',
+ });
+ });
+ };
+ useEffect(() => {
+ chatSocket();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ // update state from input
+ const onChange = e => {
+ setFormData({ [e.target.name]: e.target.value });
+ };
+
+ const onSubmit = e => {
+ e.preventDefault();
+ // send message
+ socket.emit('chat message', {
+ timestamp: new Date(),
+ username,
+ message: text,
+ userAvatar: user.avatar,
+ });
+
+ setFormData({ text: '' });
+ };
+
+ return (
+
+
+
+
+
+
Members
+ {chat.userlist.users ? : }
+
+
+
+
+ );
+};
+
+Chat.propTypes = {
+ receiveUsers: PropTypes.func.isRequired,
+ receiveMessage: PropTypes.func.isRequired,
+ receiveUsername: PropTypes.func.isRequired,
+ auth: PropTypes.object,
+ chat: PropTypes.object,
+};
+
+const mapStateToProps = state => ({
+ chat: state.chat,
+ auth: state.auth,
+});
+
+export default connect(mapStateToProps, { receiveUsers, receiveMessage, receiveUsername })(Chat);
diff --git a/client/src/components/chat/Messages.js b/client/src/components/chat/Messages.js
new file mode 100644
index 0000000..ebca2f3
--- /dev/null
+++ b/client/src/components/chat/Messages.js
@@ -0,0 +1,47 @@
+import React, { useEffect, useRef } from 'react';
+import moment from 'moment';
+
+const Messages = ({ username, messages }) => {
+ const chat = useRef(null);
+
+ const chatMessages = messages.map((chat, key) => (
+
+
+
+
+ {chat.username}
+
+ {moment(chat.timestamp).fromNow()}
+
+
+
+
{chat.message}
+
+
+ ));
+
+ useEffect(() => {
+ if (chat.current) {
+ window.scrollTo(0, chat.current.scrollHeight);
+ }
+ }, [chatMessages]);
+
+ return (
+
+ );
+};
+
+export default Messages;
diff --git a/client/src/components/chat/UserList.js b/client/src/components/chat/UserList.js
new file mode 100644
index 0000000..e65338a
--- /dev/null
+++ b/client/src/components/chat/UserList.js
@@ -0,0 +1,37 @@
+import React from 'react';
+import { Link } from 'react-router-dom';
+
+const UserList = ({ chatData: { messages, userlist, username } }) => {
+ const countUserMessage = messages.filter(msg => {
+ return msg.username === username[0];
+ });
+
+ const users = userlist.users.map((chatUser, key) => (
+
+
+
+
+
{chatUser.username}
+
{}
+
+
+
Online
+ {username[1] === chatUser.userID && (
+
{countUserMessage.length}
+ )}
+
+
+
+ ));
+ return (
+
+ );
+};
+
+export default UserList;
diff --git a/client/src/components/dashboard/Dashboard.js b/client/src/components/dashboard/Dashboard.js
index 7a4037a..d0d45b5 100755
--- a/client/src/components/dashboard/Dashboard.js
+++ b/client/src/components/dashboard/Dashboard.js
@@ -12,7 +12,7 @@ const Dashboard = ({
getCurrentProfile,
deleteAccount,
auth: { user },
- profile: { profile, loading }
+ profile: { profile, loading },
}) => {
useEffect(() => {
getCurrentProfile();
@@ -22,9 +22,9 @@ const Dashboard = ({
) : (
- Dashboard
-
- Welcome {user && user.name}
+
Dashboard
+
+ Welcome {user && user.name}
{profile !== null ? (
@@ -32,16 +32,16 @@ const Dashboard = ({
-
-