diff --git a/bfrontend/package-lock.json b/bfrontend/package-lock.json index af36251..d9e2126 100644 --- a/bfrontend/package-lock.json +++ b/bfrontend/package-lock.json @@ -2178,6 +2178,11 @@ "@babel/types": "^7.3.0" } }, + "@types/component-emitter": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", + "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==" + }, "@types/eslint": { "version": "7.2.6", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.6.tgz", @@ -3395,6 +3400,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=" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -3450,6 +3460,11 @@ } } }, + "base64-arraybuffer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", + "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=" + }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -5218,6 +5233,38 @@ "once": "^1.4.0" } }, + "engine.io-client": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.6.tgz", + "integrity": "sha512-5lPh8rrhxIruo5ZlgFt31KM626o5OCXrCHBweieWWuVicDtnYdz/iR93k6N9k0Xs61WrYxZKIWXzeSaJF6fpNA==", + "requires": { + "base64-arraybuffer": "0.1.4", + "component-emitter": "~1.3.0", + "debug": "~4.3.1", + "engine.io-parser": "~4.0.1", + "has-cors": "1.1.0", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "ws": "~7.4.2", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "ws": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", + "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==" + } + } + }, + "engine.io-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz", + "integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==", + "requires": { + "base64-arraybuffer": "0.1.4" + } + }, "enhanced-resolve": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz", @@ -6893,6 +6940,11 @@ "function-bind": "^1.1.1" } }, + "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", @@ -10684,6 +10736,16 @@ "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" }, + "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", @@ -13723,6 +13785,30 @@ } } }, + "socket.io-client": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-3.0.5.tgz", + "integrity": "sha512-NNnv3UH5h+aICeVDAdSHll3vSujp1OnzvDtuVz1ukUXliffr1+LTGc1W+qZAm3H7McapGsJhTI5nsBoY1r21dQ==", + "requires": { + "@types/component-emitter": "^1.2.10", + "backo2": "~1.0.2", + "component-emitter": "~1.3.0", + "debug": "~4.3.1", + "engine.io-client": "~4.0.6", + "parseuri": "0.0.6", + "socket.io-parser": "~4.0.3" + } + }, + "socket.io-parser": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.3.tgz", + "integrity": "sha512-m4ybFiP4UYVORRt7jcdqf8UWx+ywVdAqqsJyruXxAdD3Sv6MDemijWij34mOWdMJ55bEdIb9jACBhxUgNK6sxw==", + "requires": { + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", + "debug": "~4.3.1" + } + }, "sockjs": { "version": "0.3.20", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz", @@ -16494,6 +16580,11 @@ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -16548,6 +16639,11 @@ } } }, + "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/bfrontend/package.json b/bfrontend/package.json index 3e858d1..94f82a3 100644 --- a/bfrontend/package.json +++ b/bfrontend/package.json @@ -16,6 +16,7 @@ "react-scripts": "4.0.1", "redux": "^4.0.5", "sass": "^1.32.0", + "socket.io-client": "^3.0.5", "web-vitals": "^0.2.4" }, "scripts": { diff --git a/bfrontend/src/Components/Categories/CategoryButton.js b/bfrontend/src/Components/Categories/CategoryButton.js index ba4adbc..fca373b 100644 --- a/bfrontend/src/Components/Categories/CategoryButton.js +++ b/bfrontend/src/Components/Categories/CategoryButton.js @@ -1,3 +1,5 @@ +// import CategoryProfile from './CategoryProfile'; + import { useHistory } from 'react-router-dom'; export default function CategoryButton({ category }) { @@ -5,6 +7,11 @@ export default function CategoryButton({ category }) { return ( ) diff --git a/bfrontend/src/Components/Categories/CategoryProfile.js b/bfrontend/src/Components/Categories/CategoryProfile.js index e69de29..796166c 100644 --- a/bfrontend/src/Components/Categories/CategoryProfile.js +++ b/bfrontend/src/Components/Categories/CategoryProfile.js @@ -0,0 +1,24 @@ +import defaultProfile from '../../Images/defaultprofile_256px-256px.png' + +export default function CategoryProfile({ category, size }) { + let picture; + + if (!size) size = 32; + + // TODO: Make a debug error message for then the size does not exist + const pictureClass = `profile-picture profile-picture-${size}`; + + if (category.picture) { + // Not actually implemented on the server and can be unsafe, this is just futureproofing + picture = Profile + } else { + picture = Profile + } + + return ( +
+ { picture } + { category.title } +
+ ) +} \ No newline at end of file diff --git a/bfrontend/src/Components/Categories/CategoryView.js b/bfrontend/src/Components/Categories/CategoryView.js index 6be9092..5192290 100644 --- a/bfrontend/src/Components/Categories/CategoryView.js +++ b/bfrontend/src/Components/Categories/CategoryView.js @@ -1,4 +1,5 @@ import CategoryViewLoader from './CategoryViewLoader'; +import CategoryProfile from './CategoryProfile'; import { useParams } from 'react-router-dom'; import { connect } from 'react-redux' @@ -16,7 +17,7 @@ function CategoryView({ categories }) { return (
- { category.title } +
In terms of development, the first year of a cat’s life is equal to the first 15 years of a human life. After its second year, a cat is 25 in human years. And after that, each year of a cat’s life is equal to about 7 human years. diff --git a/bfrontend/src/Components/Categories/CategoryViewLoader.js b/bfrontend/src/Components/Categories/CategoryViewLoader.js index ed113af..4b61f76 100644 --- a/bfrontend/src/Components/Categories/CategoryViewLoader.js +++ b/bfrontend/src/Components/Categories/CategoryViewLoader.js @@ -5,13 +5,14 @@ export default function CategoryViewLoader(props) { - + + ) } \ No newline at end of file diff --git a/bfrontend/src/Components/Users/UserLink.js b/bfrontend/src/Components/Users/UserLink.js new file mode 100644 index 0000000..af018eb --- /dev/null +++ b/bfrontend/src/Components/Users/UserLink.js @@ -0,0 +1,3 @@ +export default function UserLink({ user }) { + return null; // TODO: implement +} \ No newline at end of file diff --git a/bfrontend/src/Config.js b/bfrontend/src/Config.js index 1c373a8..46a58a3 100644 --- a/bfrontend/src/Config.js +++ b/bfrontend/src/Config.js @@ -1,5 +1,6 @@ const config = { - apiUrl: 'http://localhost:3000' + apiUrl: 'http://localhost:3000', + gatewayUrl: '' // Leave blank for it to look for the gateway on the current page, if that makes sense }; export default config; \ No newline at end of file diff --git a/bfrontend/src/Gateway/GatewayConnection.js b/bfrontend/src/Gateway/GatewayConnection.js new file mode 100644 index 0000000..0674e45 --- /dev/null +++ b/bfrontend/src/Gateway/GatewayConnection.js @@ -0,0 +1,82 @@ +import io from 'socket.io-client'; + +class GatewayConnection { + constructor(url="") { + this.isConnected = false; + this.socket = null; + this.url = url; + + // TODO: set up proper event listening and such, not this dumb crap + this.onDisconnect = () => {} + this.onConnect = () => {} + } +} + +GatewayConnection.prototype.disconnect = function() { + this.socket?.disconnect(); + this.socket = null; + this.isConnected = false; +}; + +GatewayConnection.prototype.connect = function(token) { + console.log('[*] [gateway] [handshake] Trying to connect to gateway'); + this.socket = io(`${this.url}/gateway`, { + query: { + token + }, + transports: ['websocket'] + }); + + this.socket.on('connect', () => { + this.socket.once('hello', (debugInfo) => { + console.log('[*] [gateway] [handshake] Got hello from server, sending yoo...', debugInfo); + this.socket.emit('yoo'); + this.isConnected = true; + this.debugInfo = debugInfo; + this.onConnect('CONNECT_RECEIVED_HELLO'); + console.log('[*] [gateway] [handshake] Assuming that server received yoo and that connection is completed.'); + }); + }) + + this.socket.on('error', (e) => { + console.log('[E] [gateway] Gateway error', e); + this.isConnected = false; + this.socket = null; + this.onDisconnect('DISCONNECT_ERR', e); + }); + this.socket.on('disconnectNotification', (e) => { + console.log('[E] [gateway] Received disconnect notfication', e); + this.isConnected = false; + this.socket = null; + this.onDisconnect('DISCONNECT_NOTIF', e); + }); + this.socket.on('disconnect', (e) => { + console.log('[E] [gateway] Disconnected from gateway: ', e); + this.isConnected = false; + this.onDisconnect('DISCONNECT', e); + }); +}; + +GatewayConnection.prototype.sendMessage = function(categoryId, content) { + if (!this.isConnected) return 1; + if (content.length >= 2000) return 1; + + this.socket.emit('message', { + category: { + _id: categoryId + }, + content + }); +}; + +GatewayConnection.prototype.subscribeToCategoryChat = function(categoryId) { + if (!this.isConnected) return; + + const request = [categoryId]; + + console.log('[*] [gateway] Subscribing to channel(s)', request); + + this.socket.emit('subscribe', request); +}; + +export default GatewayConnection; \ No newline at end of file diff --git a/bfrontend/src/Gateway/globalGatewayConnection.js b/bfrontend/src/Gateway/globalGatewayConnection.js new file mode 100644 index 0000000..74ddeee --- /dev/null +++ b/bfrontend/src/Gateway/globalGatewayConnection.js @@ -0,0 +1,6 @@ +import GatewayConnection from './GatewayConnection'; +import config from '../Config'; + +const globalGatewayConnection = new GatewayConnection(config.gatewayUrl); + +export default globalGatewayConnection; \ No newline at end of file diff --git a/bfrontend/src/Styles/App.scss b/bfrontend/src/Styles/App.scss index bce94a8..fe1ecc1 100644 --- a/bfrontend/src/Styles/App.scss +++ b/bfrontend/src/Styles/App.scss @@ -142,6 +142,40 @@ body { max-width: 110px; } +.profile-picture { + border-radius: 50%; + margin: 5px; + width: 32px; + height: 32px; + + &.profile-picture-8 { + width: 8px; + height: 8px; + } + + &.profile-picture-16 { + width: 16px; + height: 16px; + } + + &.profile-picture-32 { + width: 32px; + height: 32px; + } + + &.profile-picture-64 { + width: 64px; + height: 64px; + } +} + +.category-profile { + display: flex; + flex-direction: row; + align-items: center; + justify-content: left; +} + @media only screen and (max-width: 600px) { .button.category-button { min-width: 50px;