Make SignalR connection work on users' online status
This commit is contained in:
parent
1ae6adb349
commit
2bf2c4d9dd
|
@ -8,6 +8,7 @@
|
||||||
"name": "fakebook-ainiro",
|
"name": "fakebook-ainiro",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@microsoft/signalr": "^8.0.7",
|
||||||
"@reduxjs/toolkit": "^1.8.3",
|
"@reduxjs/toolkit": "^1.8.3",
|
||||||
"bootstrap": "^4.6.0",
|
"bootstrap": "^4.6.0",
|
||||||
"preact": "^10.25.3",
|
"preact": "^10.25.3",
|
||||||
|
@ -16,7 +17,8 @@
|
||||||
"react-icons": "^4.2.0",
|
"react-icons": "^4.2.0",
|
||||||
"react-player": "^2.12.0",
|
"react-player": "^2.12.0",
|
||||||
"react-redux": "^8.0.2",
|
"react-redux": "^8.0.2",
|
||||||
"react-router-dom": "^5.2.0"
|
"react-router-dom": "^5.2.0",
|
||||||
|
"signalr": "^2.4.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@preact/preset-vite": "^2.9.3",
|
"@preact/preset-vite": "^2.9.3",
|
||||||
|
@ -832,6 +834,19 @@
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@microsoft/signalr": {
|
||||||
|
"version": "8.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-8.0.7.tgz",
|
||||||
|
"integrity": "sha512-PHcdMv8v5hJlBkRHAuKG5trGViQEkPYee36LnJQx4xHOQ5LL4X0nEWIxOp5cCtZ7tu+30quz5V3k0b1YNuc6lw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"abort-controller": "^3.0.0",
|
||||||
|
"eventsource": "^2.0.2",
|
||||||
|
"fetch-cookie": "^2.0.3",
|
||||||
|
"node-fetch": "^2.6.7",
|
||||||
|
"ws": "^7.4.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@popperjs/core": {
|
"node_modules/@popperjs/core": {
|
||||||
"version": "2.11.8",
|
"version": "2.11.8",
|
||||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||||
|
@ -1306,6 +1321,18 @@
|
||||||
"integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==",
|
"integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/abort-controller": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"event-target-shim": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/babel-plugin-transform-hook-names": {
|
"node_modules/babel-plugin-transform-hook-names": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-hook-names/-/babel-plugin-transform-hook-names-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/babel-plugin-transform-hook-names/-/babel-plugin-transform-hook-names-1.0.2.tgz",
|
||||||
|
@ -1629,6 +1656,34 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/event-target-shim": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/eventsource": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fetch-cookie": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-h9AgfjURuCgA2+2ISl8GbavpUdR+WGAM2McW/ovn4tVccegp8ZqCKWSBR8uRdM8dDNlx5WdKRWxBYUwteLDCNQ==",
|
||||||
|
"license": "Unlicense",
|
||||||
|
"dependencies": {
|
||||||
|
"set-cookie-parser": "^2.4.8",
|
||||||
|
"tough-cookie": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
|
@ -1726,8 +1781,7 @@
|
||||||
"version": "3.7.1",
|
"version": "3.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
|
||||||
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
|
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/js-tokens": {
|
"node_modules/js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
|
@ -1841,6 +1895,26 @@
|
||||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-fetch": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "4.x || >=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"encoding": "^0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"encoding": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-html-parser": {
|
"node_modules/node-html-parser": {
|
||||||
"version": "6.1.13",
|
"version": "6.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.13.tgz",
|
"resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.13.tgz",
|
||||||
|
@ -1985,6 +2059,33 @@
|
||||||
"react": ">=0.14.0"
|
"react": ">=0.14.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/psl": {
|
||||||
|
"version": "1.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
|
||||||
|
"integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": "^2.3.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/lupomontero"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/punycode": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/querystringify": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/react": {
|
"node_modules/react": {
|
||||||
"version": "17.0.2",
|
"version": "17.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
|
||||||
|
@ -2228,6 +2329,12 @@
|
||||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
|
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/requires-port": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/reselect": {
|
"node_modules/reselect": {
|
||||||
"version": "4.1.8",
|
"version": "4.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz",
|
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz",
|
||||||
|
@ -2299,6 +2406,21 @@
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/set-cookie-parser": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/signalr": {
|
||||||
|
"version": "2.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/signalr/-/signalr-2.4.3.tgz",
|
||||||
|
"integrity": "sha512-RbBKFVCZvDgyyxZDeu6Yck9T+diZO07GB0bDiKondUhBY1H8JRQSOq8R0pLkf47ddllQAssYlp7ckQAeom24mw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"jquery": ">=1.6.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/source-map": {
|
"node_modules/source-map": {
|
||||||
"version": "0.7.4",
|
"version": "0.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
|
||||||
|
@ -2341,6 +2463,27 @@
|
||||||
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==",
|
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/tough-cookie": {
|
||||||
|
"version": "4.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
|
||||||
|
"integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"psl": "^1.1.33",
|
||||||
|
"punycode": "^2.1.1",
|
||||||
|
"universalify": "^0.2.0",
|
||||||
|
"url-parse": "^1.5.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/uncontrollable": {
|
"node_modules/uncontrollable": {
|
||||||
"version": "7.2.1",
|
"version": "7.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz",
|
||||||
|
@ -2365,6 +2508,15 @@
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/universalify": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/update-browserslist-db": {
|
"node_modules/update-browserslist-db": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz",
|
||||||
|
@ -2396,6 +2548,16 @@
|
||||||
"browserslist": ">= 4.21.0"
|
"browserslist": ">= 4.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/url-parse": {
|
||||||
|
"version": "1.5.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||||
|
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"querystringify": "^2.1.1",
|
||||||
|
"requires-port": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/use-sync-external-store": {
|
"node_modules/use-sync-external-store": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
|
||||||
|
@ -2492,6 +2654,43 @@
|
||||||
"loose-envify": "^1.0.0"
|
"loose-envify": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "7.5.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||||
|
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": "^5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yallist": {
|
"node_modules/yallist": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||||
|
|
|
@ -9,15 +9,17 @@
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"preact": "^10.25.3",
|
"@microsoft/signalr": "^8.0.7",
|
||||||
"@reduxjs/toolkit": "^1.8.3",
|
"@reduxjs/toolkit": "^1.8.3",
|
||||||
"bootstrap": "^4.6.0",
|
"bootstrap": "^4.6.0",
|
||||||
|
"preact": "^10.25.3",
|
||||||
"react-bootstrap": "^1.5.2",
|
"react-bootstrap": "^1.5.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-icons": "^4.2.0",
|
"react-icons": "^4.2.0",
|
||||||
"react-player": "^2.12.0",
|
"react-player": "^2.12.0",
|
||||||
"react-redux": "^8.0.2",
|
"react-redux": "^8.0.2",
|
||||||
"react-router-dom": "^5.2.0"
|
"react-router-dom": "^5.2.0",
|
||||||
|
"signalr": "^2.4.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@preact/preset-vite": "^2.9.3",
|
"@preact/preset-vite": "^2.9.3",
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
import { configureStore } from "@reduxjs/toolkit";
|
|
||||||
import userReducer from "../features/user/userSlice";
|
|
||||||
import currentUserReducer from "../features/currentUser/currentUserSlice";
|
|
||||||
import usersReducer from "../features/users/usersSlice";
|
|
||||||
import postsReducer from "../features/posts/postsSlice";
|
|
||||||
import incomingMessagesReducer from "../features/incomingMessages/incomingMessagesSlice";
|
|
||||||
import outgoingMessagesReducer from "../features/outgoingMessages/outgoingMessagesSlice";
|
|
||||||
import imagesReducer from "../features/images/imagesSlice";
|
|
||||||
import linkReducer from "../features/link/linkSlice";
|
|
||||||
import accountPageReducer from "../features/accountPage/accountPageSlice";
|
|
||||||
|
|
||||||
export default configureStore({
|
|
||||||
reducer: {
|
|
||||||
user: userReducer,
|
|
||||||
currentUser: currentUserReducer,
|
|
||||||
users: usersReducer,
|
|
||||||
posts: postsReducer,
|
|
||||||
incomingMessages: incomingMessagesReducer,
|
|
||||||
outgoingMessages: outgoingMessagesReducer,
|
|
||||||
images: imagesReducer,
|
|
||||||
link: linkReducer,
|
|
||||||
accountPage: accountPageReducer,
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,11 +1,13 @@
|
||||||
/* =====================================================================
|
/* =====================================================================
|
||||||
|
|
||||||
|
|
||||||
Fakebook — back-end file (AUTH + mock social features)
|
Fakebook — back-end file (AUTH + mock social features)
|
||||||
|
|
||||||
|
|
||||||
===================================================================== */
|
===================================================================== */
|
||||||
|
|
||||||
|
import * as signalR from "@microsoft/signalr";
|
||||||
|
|
||||||
import store from "../app/store";
|
import store from "../app/store";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -30,6 +32,8 @@ import { outgoingMessagesUpdated } from "../features/outgoingMessages/outgoingMe
|
||||||
|
|
||||||
const API_BASE = "https://alexerdei-team.us.ainiro.io/magic/modules/fakebook";
|
const API_BASE = "https://alexerdei-team.us.ainiro.io/magic/modules/fakebook";
|
||||||
|
|
||||||
|
const SOCKETS_URL = "wss://alexerdei-team.us.ainiro.io/sockets";
|
||||||
|
|
||||||
const REGISTER_URL = `${API_BASE}/register`;
|
const REGISTER_URL = `${API_BASE}/register`;
|
||||||
|
|
||||||
const LOGIN_URL = `${API_BASE}/login`;
|
const LOGIN_URL = `${API_BASE}/login`;
|
||||||
|
@ -63,14 +67,8 @@ function setAuth(token, user_id) {
|
||||||
localStorage.setItem(LS_TOKEN, token);
|
localStorage.setItem(LS_TOKEN, token);
|
||||||
|
|
||||||
localStorage.setItem(LS_USER_ID, user_id);
|
localStorage.setItem(LS_USER_ID, user_id);
|
||||||
}
|
|
||||||
|
|
||||||
function clearAuth() {
|
openSocket();
|
||||||
authToken = authUser = null;
|
|
||||||
|
|
||||||
localStorage.removeItem(LS_TOKEN);
|
|
||||||
|
|
||||||
localStorage.removeItem(LS_USER_ID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --------------------------- Utilities -------------------------------- */
|
/* --------------------------- Utilities -------------------------------- */
|
||||||
|
@ -101,65 +99,13 @@ async function $fetch(url, opts = {}) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------------------- REST → UI mapper ------------------------------- */
|
|
||||||
|
|
||||||
function addPath(u, fileName) {
|
|
||||||
if (typeof fileName !== "string" || !fileName.length) return fileName;
|
|
||||||
|
|
||||||
if (fileName.includes("/")) return fileName; /* already has folder */
|
|
||||||
|
|
||||||
return `${u.user_id}/${fileName}`; /* prepend owner id */
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapRestUser(u) {
|
|
||||||
const photosRaw = JSON.parse(u.photos || "[]");
|
|
||||||
|
|
||||||
const photos = photosRaw.map((item) => {
|
|
||||||
if (typeof item === "string") {
|
|
||||||
/* legacy: array of strings */
|
|
||||||
|
|
||||||
return { filename: addPath(u, item) };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item && typeof item.filename === "string") {
|
|
||||||
return { ...item, filename: addPath(u, item.filename) };
|
|
||||||
}
|
|
||||||
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
userID: u.user_id,
|
|
||||||
|
|
||||||
firstname: u.firstname,
|
|
||||||
|
|
||||||
lastname: u.lastname,
|
|
||||||
|
|
||||||
/* already stored as folder/filename → leave untouched */
|
|
||||||
|
|
||||||
profilePictureURL: u.profilePictureURL,
|
|
||||||
|
|
||||||
backgroundPictureURL: u.backgroundPictureURL,
|
|
||||||
|
|
||||||
photos,
|
|
||||||
|
|
||||||
posts: JSON.parse(u.posts || "[]"),
|
|
||||||
|
|
||||||
isOnline: !!u.isOnline,
|
|
||||||
|
|
||||||
isEmailVerified: !!u.isEmailVerified,
|
|
||||||
|
|
||||||
index: u.index ?? 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===================================================================== *
|
/* ===================================================================== *
|
||||||
|
|
||||||
|
|
||||||
SECTION A — REAL AUTH WORKFLOW
|
SECTION A — REAL AUTH WORKFLOW
|
||||||
|
|
||||||
|
|
||||||
===================================================================== */
|
===================================================================== */
|
||||||
|
|
||||||
/* ---------------------- subscribeAuth -------------------------------- */
|
/* ---------------------- subscribeAuth -------------------------------- */
|
||||||
|
|
||||||
|
@ -310,6 +256,109 @@ export function sendPasswordReminder(email) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
/* SignalR hub – one connection shared across the app */
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
let hub = null;
|
||||||
|
|
||||||
|
function openSocket() {
|
||||||
|
if (hub) return; // already connected / connecting
|
||||||
|
|
||||||
|
const token = localStorage.getItem(LS_TOKEN);
|
||||||
|
|
||||||
|
if (!token) return; // not logged-in → no live updates
|
||||||
|
|
||||||
|
hub = new signalR.HubConnectionBuilder()
|
||||||
|
|
||||||
|
.withUrl(SOCKETS_URL, {
|
||||||
|
accessTokenFactory: () => token,
|
||||||
|
|
||||||
|
skipNegotiation: true, // no CORS pre-flight
|
||||||
|
|
||||||
|
transport: signalR.HttpTransportType.WebSockets, // WebSocket only
|
||||||
|
})
|
||||||
|
|
||||||
|
.withAutomaticReconnect()
|
||||||
|
|
||||||
|
.configureLogging(signalR.LogLevel.Warning)
|
||||||
|
|
||||||
|
.build();
|
||||||
|
|
||||||
|
/* ---------------------------------------------------- */
|
||||||
|
|
||||||
|
/* helper – SignalR sends a JSON-string → return POJO */
|
||||||
|
|
||||||
|
/* ---------------------------------------------------- */
|
||||||
|
|
||||||
|
const parseMsg = (raw) => (typeof raw === "string" ? JSON.parse(raw) : raw);
|
||||||
|
|
||||||
|
/* 1️⃣ Start first … */
|
||||||
|
|
||||||
|
hub
|
||||||
|
.start()
|
||||||
|
|
||||||
|
.then(() => {
|
||||||
|
console.info("[SignalR] connected, id:", hub.connectionId);
|
||||||
|
|
||||||
|
/* 2️⃣ … then register listeners ----------------------------- */
|
||||||
|
|
||||||
|
/* inside openSocket() – unchanged except the helper */
|
||||||
|
|
||||||
|
hub.on("fakebook.users.post", (raw) => {
|
||||||
|
store.dispatch(usersUpdated([parseMsg(raw)]));
|
||||||
|
});
|
||||||
|
|
||||||
|
hub.on("fakebook.users.put", (raw) => {
|
||||||
|
store.dispatch(usersUpdated([parseMsg(raw)]));
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.catch((err) => {
|
||||||
|
console.warn("[SignalR] start failed:", err);
|
||||||
|
|
||||||
|
hub = null; // let auto-reconnect retry
|
||||||
|
});
|
||||||
|
|
||||||
|
/* extra diagnostics ---------------------------------------------- */
|
||||||
|
|
||||||
|
hub.onreconnecting((err) => console.warn("[SignalR] reconnecting:", err));
|
||||||
|
|
||||||
|
hub.onreconnected((id) => console.info("[SignalR] reconnected, id:", id));
|
||||||
|
|
||||||
|
hub.onclose((err) => console.warn("[SignalR] closed:", err));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
/* Close SignalR + forget credentials */
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
function clearAuth() {
|
||||||
|
/* forget in-memory copies */
|
||||||
|
|
||||||
|
authToken = null;
|
||||||
|
|
||||||
|
authUser = null;
|
||||||
|
|
||||||
|
/* forget persisted copies */
|
||||||
|
|
||||||
|
localStorage.removeItem(LS_TOKEN); // "fakebook.jwt"
|
||||||
|
|
||||||
|
localStorage.removeItem(LS_USER_ID); // "fakebook.user_id"
|
||||||
|
|
||||||
|
/* close the hub if it exists */
|
||||||
|
|
||||||
|
if (hub) {
|
||||||
|
hub.stop(); // graceful shutdown → returns a promise we don’t await
|
||||||
|
|
||||||
|
hub = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ===================================================================== *
|
/* ===================================================================== *
|
||||||
|
|
||||||
SECTION B — IN-MEMORY MOCK (UNCHANGED) *
|
SECTION B — IN-MEMORY MOCK (UNCHANGED) *
|
||||||
|
@ -372,7 +421,7 @@ export function subscribeCurrentUser() {
|
||||||
const meRow = users.find((u) => u.user_id === user_id);
|
const meRow = users.find((u) => u.user_id === user_id);
|
||||||
|
|
||||||
if (meRow && !cancelled) {
|
if (meRow && !cancelled) {
|
||||||
store.dispatch(currentUserUpdated(mapRestUser(meRow)));
|
store.dispatch(currentUserUpdated(meRow));
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn("[subscribeCurrentUser] failed:", err.message);
|
console.warn("[subscribeCurrentUser] failed:", err.message);
|
||||||
|
@ -447,7 +496,7 @@ export function subscribeUsers() {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
store.dispatch(usersUpdated(users.map(mapRestUser)));
|
store.dispatch(usersUpdated(users));
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn("[subscribeUsers] failed:", err.message);
|
console.warn("[subscribeUsers] failed:", err.message);
|
||||||
|
|
|
@ -1,40 +1,56 @@
|
||||||
import { createSlice } from "@reduxjs/toolkit";
|
import { createSlice } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
|
import mapRestUser from "../../utils/mapRestUser"; // same helper the users slice uses
|
||||||
|
|
||||||
export const currentUserSlice = createSlice({
|
export const currentUserSlice = createSlice({
|
||||||
name: "currentUser",
|
name: "currentUser",
|
||||||
initialState: {
|
|
||||||
firstname: "",
|
initialState: null, // stays a single object, not an array
|
||||||
lastname: "",
|
|
||||||
profilePictureURL: "fakebook-avatar.jpeg",
|
reducers: {
|
||||||
backgroundPictureURL: "background-server.jpg",
|
/* ------------------------------------------------------------------
|
||||||
photos: [],
|
|
||||||
posts: [],
|
currentUserUpdated
|
||||||
isOnline: false,
|
|
||||||
},
|
– can receive either a *full* user object (first load / login)
|
||||||
reducers: {
|
|
||||||
currentUserUpdated: (state, action) => {
|
– or a *partial* patch coming from SignalR (online flag etc.)
|
||||||
const {
|
|
||||||
firstname,
|
------------------------------------------------------------------ */
|
||||||
lastname,
|
|
||||||
profilePictureURL,
|
currentUserUpdated: (state, action) => {
|
||||||
backgroundPictureURL,
|
const raw = action.payload;
|
||||||
photos,
|
|
||||||
posts,
|
/* first call or explicit full replacement ---------------------- */
|
||||||
isOnline,
|
|
||||||
index,
|
if (
|
||||||
} = action.payload;
|
state === null ||
|
||||||
state.firstname = firstname;
|
(raw.firstname !== undefined && raw.lastname !== undefined)
|
||||||
state.lastname = lastname;
|
) {
|
||||||
state.profilePictureURL = profilePictureURL;
|
return mapRestUser(raw); // normalise every field once
|
||||||
state.backgroundPictureURL = backgroundPictureURL;
|
}
|
||||||
state.isOnline = isOnline;
|
|
||||||
if (index) state.index = index;
|
/* otherwise treat it as a PATCH -------------------------------- */
|
||||||
state.photos = [];
|
|
||||||
state.posts = [];
|
const next = { ...state }; // Immer lets us mutate but explicit copy is clear
|
||||||
photos.forEach((photo) => state.photos.push(photo));
|
|
||||||
posts.forEach((post) => state.posts.push(post));
|
if (raw.isOnline !== undefined) next.isOnline = !!raw.isOnline;
|
||||||
},
|
|
||||||
},
|
if (raw.firstname !== undefined) next.firstname = raw.firstname;
|
||||||
|
|
||||||
|
if (raw.lastname !== undefined) next.lastname = raw.lastname;
|
||||||
|
|
||||||
|
if (raw.profilePictureURL !== undefined)
|
||||||
|
next.profilePictureURL = raw.profilePictureURL;
|
||||||
|
|
||||||
|
if (raw.backgroundPictureURL !== undefined)
|
||||||
|
next.backgroundPictureURL = raw.backgroundPictureURL;
|
||||||
|
|
||||||
|
/* add similar guards if more fields can arrive partially … */
|
||||||
|
|
||||||
|
return next; // Immer will take care of immutability
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { currentUserUpdated } = currentUserSlice.actions;
|
export const { currentUserUpdated } = currentUserSlice.actions;
|
||||||
|
|
|
@ -1,13 +1,52 @@
|
||||||
import { createSlice } from "@reduxjs/toolkit";
|
import { createSlice } from "@reduxjs/toolkit";
|
||||||
|
import mapRestUser from "../../utils/mapRestUser";
|
||||||
|
|
||||||
export const usersSlice = createSlice({
|
export const usersSlice = createSlice({
|
||||||
name: "users",
|
name: "users",
|
||||||
initialState: [],
|
initialState: [],
|
||||||
reducers: {
|
reducers: {
|
||||||
usersUpdated: (state, action) => {
|
usersUpdated: (state, action) => {
|
||||||
const updatedState = [];
|
/* action.payload will always be an *array* */
|
||||||
action.payload.forEach((user) => updatedState.push(user));
|
|
||||||
return updatedState;
|
const incoming = action.payload;
|
||||||
|
|
||||||
|
incoming.forEach((raw) => {
|
||||||
|
const userID = raw.user_id ?? raw.userID;
|
||||||
|
|
||||||
|
const idx = state.findIndex((u) => u.userID === userID);
|
||||||
|
|
||||||
|
if (idx === -1) {
|
||||||
|
/* -------- NEW USER ----------------------------------------- */
|
||||||
|
|
||||||
|
state.push(mapRestUser(raw)); // map every field
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------- EXISTING USER -------------------------------------- */
|
||||||
|
|
||||||
|
const cur = state[idx];
|
||||||
|
|
||||||
|
const patch = {};
|
||||||
|
|
||||||
|
/* Online flag comes as 0/1; convert to boolean if present */
|
||||||
|
|
||||||
|
if (raw.isOnline !== undefined) patch.isOnline = !!raw.isOnline;
|
||||||
|
|
||||||
|
if (raw.firstname !== undefined) patch.firstname = raw.firstname;
|
||||||
|
|
||||||
|
if (raw.lastname !== undefined) patch.lastname = raw.lastname;
|
||||||
|
|
||||||
|
if (raw.profilePictureURL !== undefined)
|
||||||
|
patch.profilePictureURL = raw.profilePictureURL;
|
||||||
|
|
||||||
|
if (raw.backgroundPictureURL !== undefined)
|
||||||
|
patch.backgroundPictureURL = raw.backgroundPictureURL;
|
||||||
|
|
||||||
|
/* add other fields you expect to arrive partially … */
|
||||||
|
|
||||||
|
state[idx] = { ...cur, ...patch }; // keep old fields
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/* utils/mapRestUser.js
|
||||||
|
|
||||||
|
Converts a “magic” REST / SignalR user row into the shape Fakebook
|
||||||
|
|
||||||
|
components already consume. */
|
||||||
|
|
||||||
|
function addPath(row, fileName) {
|
||||||
|
if (typeof fileName !== "string" || !fileName.length) return fileName;
|
||||||
|
|
||||||
|
if (fileName.includes("/")) return fileName; // already has folder
|
||||||
|
|
||||||
|
return `${row.user_id}/${fileName}`; // prepend owner id
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function mapRestUser(row) {
|
||||||
|
const photosRaw = JSON.parse(row.photos || "[]");
|
||||||
|
|
||||||
|
const photos = photosRaw.map((item) => {
|
||||||
|
if (typeof item === "string") {
|
||||||
|
return { filename: addPath(row, item) }; // legacy array
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item && typeof item.filename === "string") {
|
||||||
|
return { ...item, filename: addPath(row, item.filename) };
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
userID: row.user_id,
|
||||||
|
|
||||||
|
firstname: row.firstname,
|
||||||
|
|
||||||
|
lastname: row.lastname,
|
||||||
|
|
||||||
|
profilePictureURL: row.profilePictureURL,
|
||||||
|
|
||||||
|
backgroundPictureURL: row.backgroundPictureURL,
|
||||||
|
|
||||||
|
photos,
|
||||||
|
|
||||||
|
posts: JSON.parse(row.posts || "[]"),
|
||||||
|
|
||||||
|
isOnline: !!row.isOnline,
|
||||||
|
|
||||||
|
isEmailVerified: !!row.isEmailVerified,
|
||||||
|
|
||||||
|
index: row.index ?? 0,
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue