Deploy app and fix bug in UserAccount regarding FriendList comp

This commit is contained in:
Alex Erdei 2025-05-08 14:27:54 +01:00
parent 9d109e6302
commit 4b8b7d55bf
18 changed files with 1344 additions and 645 deletions

View File

@ -1,10 +1,40 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Preact</title>
<title>fakebook-aigen</title>
<!-- Start Single Page Apps for GitHub Pages -->
<script type="text/javascript">
// Single Page Apps for GitHub Pages
// MIT License
// https://github.com/rafgraph/spa-github-pages
// This script checks to see if a redirect is present in the query string,
// converts it back into the correct url and adds it to the
// browser's history using window.history.replaceState(...),
// which won't cause the browser to attempt to load the new url.
// When the single page app is loaded further down in this file,
// the correct url will be waiting in the browser's history for
// the single page app to route accordingly.
(function (l) {
if (l.search[1] === "/") {
var decoded = l.search
.slice(1)
.split("&")
.map(function (s) {
return s.replace(/~and~/g, "&");
})
.join("?");
window.history.replaceState(
null,
null,
l.pathname.slice(0, -1) + decoded + l.hash
);
}
})(window.location);
</script>
<!-- End Single Page Apps for GitHub Pages -->
</head>
<body>
<div id="app"></div>

616
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "fakebook-ainiro",
"version": "0.0.0",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "fakebook-ainiro",
"version": "0.0.0",
"version": "0.1.0",
"dependencies": {
"@microsoft/signalr": "^8.0.7",
"@reduxjs/toolkit": "^1.8.3",
@ -17,11 +17,11 @@
"react-icons": "^4.2.0",
"react-player": "^2.12.0",
"react-redux": "^8.0.2",
"react-router-dom": "^5.2.0",
"signalr": "^2.4.3"
"react-router-dom": "^5.2.0"
},
"devDependencies": {
"@preact/preset-vite": "^2.9.3",
"gh-pages": "^6.3.0",
"vite": "^6.0.5"
}
},
@ -847,6 +847,44 @@
"ws": "^7.4.5"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
"run-parallel": "^1.1.9"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/@nodelib/fs.stat": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
}
},
"node_modules/@nodelib/fs.walk": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
"fastq": "^1.6.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
@ -1333,6 +1371,23 @@
"node": ">=6.5"
}
},
"node_modules/array-union": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/async": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
"dev": true,
"license": "MIT"
},
"node_modules/babel-plugin-transform-hook-names": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-hook-names/-/babel-plugin-transform-hook-names-1.0.2.tgz",
@ -1370,6 +1425,19 @@
"popper.js": "^1.16.1"
}
},
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"license": "MIT",
"dependencies": {
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/browserslist": {
"version": "4.24.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
@ -1430,6 +1498,23 @@
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
"license": "MIT"
},
"node_modules/commander": {
"version": "13.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
"integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
"dev": true,
"license": "MIT"
},
"node_modules/convert-source-map": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
@ -1509,6 +1594,19 @@
"node": ">=6"
}
},
"node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
"integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
"dev": true,
"license": "MIT",
"dependencies": {
"path-type": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
@ -1585,6 +1683,13 @@
"dev": true,
"license": "ISC"
},
"node_modules/email-addresses": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz",
"integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==",
"dev": true,
"license": "MIT"
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
@ -1649,6 +1754,16 @@
"node": ">=6"
}
},
"node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
@ -1674,6 +1789,33 @@
"node": ">=12.0.0"
}
},
"node_modules/fast-glob": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
"glob-parent": "^5.1.2",
"merge2": "^1.3.0",
"micromatch": "^4.0.8"
},
"engines": {
"node": ">=8.6.0"
}
},
"node_modules/fastq": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
"integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
"dev": true,
"license": "ISC",
"dependencies": {
"reusify": "^1.0.4"
}
},
"node_modules/fetch-cookie": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-2.2.0.tgz",
@ -1684,6 +1826,104 @@
"tough-cookie": "^4.0.0"
}
},
"node_modules/filename-reserved-regex": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
"integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/filenamify": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz",
"integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==",
"dev": true,
"license": "MIT",
"dependencies": {
"filename-reserved-regex": "^2.0.0",
"strip-outer": "^1.0.1",
"trim-repeated": "^1.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/find-cache-dir": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
"integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
"dev": true,
"license": "MIT",
"dependencies": {
"commondir": "^1.0.1",
"make-dir": "^3.0.2",
"pkg-dir": "^4.1.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/avajs/find-cache-dir?sponsor=1"
}
},
"node_modules/find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dev": true,
"license": "MIT",
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/fs-extra": {
"version": "11.3.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=14.14"
}
},
"node_modules/fs-extra/node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@ -1709,6 +1949,42 @@
"node": ">=6.9.0"
}
},
"node_modules/gh-pages": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-6.3.0.tgz",
"integrity": "sha512-Ot5lU6jK0Eb+sszG8pciXdjMXdBJ5wODvgjR+imihTqsUWF2K6dJ9HST55lgqcs8wWcw6o6wAsUzfcYRhJPXbA==",
"dev": true,
"license": "MIT",
"dependencies": {
"async": "^3.2.4",
"commander": "^13.0.0",
"email-addresses": "^5.0.0",
"filenamify": "^4.3.0",
"find-cache-dir": "^3.3.1",
"fs-extra": "^11.1.1",
"globby": "^11.1.0"
},
"bin": {
"gh-pages": "bin/gh-pages.js",
"gh-pages-clean": "bin/gh-pages-clean.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@ -1719,6 +1995,34 @@
"node": ">=4"
}
},
"node_modules/globby": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
"dev": true,
"license": "MIT",
"dependencies": {
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
"fast-glob": "^3.2.9",
"ignore": "^5.2.0",
"merge2": "^1.4.1",
"slash": "^3.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true,
"license": "ISC"
},
"node_modules/he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@ -1752,6 +2056,16 @@
"react-is": "^16.7.0"
}
},
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/immer": {
"version": "9.0.21",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
@ -1771,6 +2085,39 @@
"loose-envify": "^1.0.0"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
@ -1781,7 +2128,8 @@
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/js-tokens": {
"version": "4.0.0",
@ -1815,6 +2163,29 @@
"node": ">=6"
}
},
"node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/jsonfile/node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/kolorist": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz",
@ -1828,6 +2199,19 @@
"integrity": "sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==",
"license": "MIT"
},
"node_modules/locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dev": true,
"license": "MIT",
"dependencies": {
"p-locate": "^4.1.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@ -1863,12 +2247,52 @@
"node": ">=12"
}
},
"node_modules/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==",
"dev": true,
"license": "MIT",
"dependencies": {
"semver": "^6.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/memoize-one": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==",
"license": "MIT"
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
}
},
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"license": "MIT",
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@ -1955,6 +2379,55 @@
"node": ">=0.10.0"
}
},
"node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"license": "MIT",
"dependencies": {
"p-try": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dev": true,
"license": "MIT",
"dependencies": {
"p-limit": "^2.2.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/path-to-regexp": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz",
@ -1964,6 +2437,16 @@
"isarray": "0.0.1"
}
},
"node_modules/path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@ -1984,6 +2467,19 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"find-up": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/popper.js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
@ -2086,6 +2582,27 @@
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
"license": "MIT"
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/react": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
@ -2347,6 +2864,17 @@
"integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==",
"license": "MIT"
},
"node_modules/reusify": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
"dev": true,
"license": "MIT",
"engines": {
"iojs": ">=1.0.0",
"node": ">=0.10.0"
}
},
"node_modules/rollup": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.1.tgz",
@ -2386,6 +2914,30 @@
"fsevents": "~2.3.2"
}
},
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"queue-microtask": "^1.2.2"
}
},
"node_modules/scheduler": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
@ -2412,13 +2964,14 @@
"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/slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/source-map": {
@ -2451,6 +3004,19 @@
"node": ">=16"
}
},
"node_modules/strip-outer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz",
"integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==",
"dev": true,
"license": "MIT",
"dependencies": {
"escape-string-regexp": "^1.0.2"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/tiny-invariant": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
@ -2463,6 +3029,19 @@
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==",
"license": "MIT"
},
"node_modules/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==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/tough-cookie": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
@ -2484,6 +3063,19 @@
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"license": "MIT"
},
"node_modules/trim-repeated": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
"integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==",
"dev": true,
"license": "MIT",
"dependencies": {
"escape-string-regexp": "^1.0.2"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/uncontrollable": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz",

View File

@ -1,12 +1,15 @@
{
"name": "fakebook-ainiro",
"private": true,
"version": "0.0.0",
"version": "0.1.0",
"homepage": "https://alexerdei73.github.io/fakebook-aigen",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
"preview": "vite preview",
"predeploy": "npm run build",
"deploy": "gh-pages -d dist"
},
"dependencies": {
"@microsoft/signalr": "^8.0.7",
@ -18,11 +21,11 @@
"react-icons": "^4.2.0",
"react-player": "^2.12.0",
"react-redux": "^8.0.2",
"react-router-dom": "^5.2.0",
"signalr": "^2.4.3"
"react-router-dom": "^5.2.0"
},
"devDependencies": {
"@preact/preset-vite": "^2.9.3",
"gh-pages": "^6.3.0",
"vite": "^6.0.5"
}
}

50
public/404.html Normal file
View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>fakebook-aigen</title>
<script type="text/javascript">
// Single Page Apps for GitHub Pages
// MIT License
// https://github.com/rafgraph/spa-github-pages
// This script takes the current url and converts the path and query
// string into just a query string, and then redirects the browser
// to the new url with only a query string and hash fragment,
// e.g. https://www.foo.tld/one/two?a=b&c=d#qwe, becomes
// https://www.foo.tld/?/one/two&a=b~and~c=d#qwe
// Note: this 404.html file must be at least 512 bytes for it to work
// with Internet Explorer (it is currently > 512 bytes)
// If you're creating a Project Pages site and NOT using a custom domain,
// then set pathSegmentsToKeep to 1 (enterprise users may need to set it to > 1).
// This way the code will only replace the route part of the path, and not
// the real directory in which the app resides, for example:
// https://username.github.io/repo-name/one/two?a=b&c=d#qwe becomes
// https://username.github.io/repo-name/?/one/two&a=b~and~c=d#qwe
// Otherwise, leave pathSegmentsToKeep as 0.
var pathSegmentsToKeep = 1;
var l = window.location;
l.replace(
l.protocol +
"//" +
l.hostname +
(l.port ? ":" + l.port : "") +
l.pathname
.split("/")
.slice(0, 1 + pathSegmentsToKeep)
.join("/") +
"/?/" +
l.pathname
.slice(1)
.split("/")
.slice(pathSegmentsToKeep)
.join("/")
.replace(/&/g, "~and~") +
(l.search ? "&" + l.search.slice(1).replace(/&/g, "~and~") : "") +
l.hash
);
</script>
</head>
<body></body>
</html>

View File

@ -14,96 +14,101 @@ import { subscribeAuth } from "./backend/backend";
import { profileLinkSet } from "./features/accountPage/accountPageSlice";
function App() {
const user = useSelector((state) => state.user);
const dispatch = useDispatch();
const user = useSelector((state) => state.user);
const dispatch = useDispatch();
useEffect(() => {
const unsubscribe = subscribeAuth();
return unsubscribe;
}, []);
useEffect(() => {
const unsubscribe = subscribeAuth();
return unsubscribe;
}, []);
//Handle the modal
const [show, setShow] = useState(false);
//Handle the modal
const [show, setShow] = useState(false);
function handleClose() {
setShow(false);
}
function handleClose() {
setShow(false);
}
function handleShow() {
setShow(true);
}
function handleShow() {
setShow(true);
}
const handleCloseCallback = useCallback(handleClose, []);
const handleCloseCallback = useCallback(handleClose, []);
//get the first and lastName for the route of the profile
const name =
(user && user.displayName && user.displayName.trim().split(" ")) || [];
//get the first and lastName for the route of the profile
const name =
(user && user.displayName && user.displayName.trim().split(" ")) || [];
const lastName = name.pop();
const lastName = name.pop();
const firstName = name.join(" ");
const firstName = name.join(" ");
const profileLink = `/fakebook/${lastName}.${firstName}`;
const profileLink = `/${lastName}.${firstName}`;
useEffect(
() => dispatch(profileLinkSet(profileLink)),
[profileLink, dispatch]
);
useEffect(() => dispatch(profileLinkSet(profileLink)), [profileLink, dispatch]);
//handling the password reminder button
const [isModalSignup, setModalSignup] = useState(true);
//handling the password reminder button
const [isModalSignup, setModalSignup] = useState(true);
function handleClickPasswordReminderBtn() {
setModalSignup(false);
handleShow();
}
function handleClickPasswordReminderBtn() {
setModalSignup(false);
handleShow();
}
if (user.isLoading) {
return <div>...Loading</div>;
}
if (user.isLoading) {
return <div>...Loading</div>;
}
if (user.isSignedIn && !user.error) {
if (user.isEmailVerified) return <UserAccount />;
else return <></>;
} else {
return (
<Col className='bg-200 vh-100'>
<Row className='h-100 align-items-center'>
<Col
lg={{ span: 5, offset: 1 }}
className='d-flex justify-content-center'
>
<RecentLogins />
</Col>
<Col lg={5} className='bg-200 d-flex justify-content-center'>
<div className='login p-3 bg-light'>
<Login
onClickForgottenPswd={handleClickPasswordReminderBtn}
></Login>
if (user.isSignedIn && !user.error) {
if (user.isEmailVerified) return <UserAccount />;
else return <></>;
} else {
return (
<Col className="bg-200 vh-100">
<Row className="h-100 align-items-center">
<Col
lg={{ span: 5, offset: 1 }}
className="d-flex justify-content-center">
<RecentLogins />
</Col>
<Col lg={5} className="bg-200 d-flex justify-content-center">
<div className="login p-3 bg-light">
<Login
onClickForgottenPswd={handleClickPasswordReminderBtn}
></Login>
<hr />
<hr />
<Button
variant='success'
size='lg'
className='d-block w-60 mx-auto mt-4'
onClick={handleShow}
>
<b>Create New Account</b>
</Button>
</div>
</Col>
<Button
variant="success"
size="lg"
className="d-block w-60 mx-auto mt-4"
onClick={handleShow}>
<b>Create New Account</b>
</Button>
</div>
</Col>
<SignupModal
show={show && isModalSignup}
onHide={handleCloseCallback}
onExit={() => setModalSignup(true)}
></SignupModal>
<SignupModal
show={show && isModalSignup}
onHide={handleCloseCallback}
onExit={() => setModalSignup(true)}></SignupModal>
<PasswordReminderModal
show={show && !isModalSignup}
onHide={handleClose}
onExit={() => setModalSignup(true)}
/>
</Row>
</Col>
);
}
<PasswordReminderModal
show={show && !isModalSignup}
onHide={handleClose}
onExit={() => setModalSignup(true)}
/>
</Row>
</Col>
);
}
}
export default App;

View File

@ -17,50 +17,50 @@ const FriendCard = (props) => {
function handleClick() {
history.push(
user.index && user.index > 0
? `/fakebook/${user.lastname}.${user.firstname}.${user.index}`
: `/fakebook/${user.lastname}.${user.firstname}`
? `/${user.lastname}.${user.firstname}.${user.index}`
: `/${user.lastname}.${user.firstname}`
);
}
return (
<Col xs={6} className="my-3">
<Col xs={6} className='my-3'>
<OverlayTrigger
placement="auto"
placement='auto'
show={showOverlay}
overlay={
<div
className="popup-card"
className='popup-card'
onMouseEnter={() => setShowOverlay(true)}
onMouseLeave={() => setShowOverlay(false)}
onClick={handleClick}
>
<div className="m-3">
<CircularImage size="120" url={user.profilePictureURL} />
<div className='m-3'>
<CircularImage size='120' url={user.profilePictureURL} />
</div>
<h4 className="name-tag">
<h4 className='name-tag'>
<b>{userName}</b>
</h4>
</div>
}
>
<button
type="button"
type='button'
onClick={handleClick}
className="friend-btn"
tabIndex="-1"
className='friend-btn'
tabIndex='-1'
>
<StorageImage
storagePath={user.profilePictureURL}
width="90px"
height="90px"
alt=""
className="profile-picture"
width='90px'
height='90px'
alt=''
className='profile-picture'
onMouseEnter={() => setShowOverlay(true)}
onMouseLeave={() => setShowOverlay(false)}
/>
</button>
</OverlayTrigger>
<button type="button" className="ml-3 friend-btn" onClick={handleClick}>
<button type='button' className='ml-3 friend-btn' onClick={handleClick}>
<b>{userName}</b>
</button>
</Col>

View File

@ -16,10 +16,10 @@ const FriendList = (props) => {
const isModal = variant === "modal";
return (
<Col xs="auto" className="overflow-auto mh-100">
<div id="col-1" className="m-2">
<Col xs='auto' className='overflow-auto mh-100'>
<div id='col-1' className='m-2'>
{usersToUse.map((user, index) => {
let profileLink = `/fakebook/${user.lastname}.${user.firstname}`;
let profileLink = `/${user.lastname}.${user.firstname}`;
if (user.index && user.index > 0)
profileLink = profileLink + `.${user.index}`;
return (
@ -30,10 +30,10 @@ const FriendList = (props) => {
>
<ProfileLink
user={user}
fullname="true"
fullname='true'
size={isModal ? "40" : "60"}
bold="true"
className="pb-1"
bold='true'
className='pb-1'
/>
</Link>
);

View File

@ -8,46 +8,45 @@ import imgFriends from "../images/friends.jpg";
import { useDispatch } from "react-redux";
import { linkUpdated } from "../features/link/linkSlice";
const FriendsListPage = (props) => {
const FriendsListPage = () => {
const FRIENDS_LIST_PAGE_PATH = "/friends/list";
const FRIENDS_LIST_PAGE_PATH = "/fakebook/friends/list";
const location = useLocation();
const dispatch = useDispatch();
const location = useLocation();
const dispatch = useDispatch();
const isNoUser = FRIENDS_LIST_PAGE_PATH === location.pathname;
const isNoUser = FRIENDS_LIST_PAGE_PATH === location.pathname;
//we set the active link to the friends link when it renders
useEffect(() => {
dispatch(linkUpdated("friends"));
}, [dispatch]);
//we set the active link to the friends link when it renders
useEffect(() => {
dispatch(linkUpdated("friends"));
}, [dispatch]);
return window.innerWidth > 600 || isNoUser ? (
<Row className="overflow-hidden friends-list">
<FriendList />
<Col className="overflow-auto mh-100 hide-scrollbar col-two">
{isNoUser ? (
<div className="h-100 w-100 d-flex flex-column align-items-center justify-content-center">
<img
width="200px"
src={imgFriends}
alt="cartoon of fakebook friends"
className="p-4"
/>
<h5 className="text-muted">
<b>Select people's names to preview their profile.</b>
</h5>
</div>
) : (
<div className="profile-container">
<Profile />
</div>
)}
</Col>
</Row>
) : (
<Profile />
);
return window.innerWidth > 600 || isNoUser ? (
<Row className='overflow-hidden friends-list'>
<FriendList />
<Col className='overflow-auto mh-100 hide-scrollbar col-two'>
{isNoUser ? (
<div className='h-100 w-100 d-flex flex-column align-items-center justify-content-center'>
<img
width='200px'
src={imgFriends}
alt='cartoon of fakebook friends'
className='p-4'
/>
<h5 className='text-muted'>
<b>Select people's names to preview their profile.</b>
</h5>
</div>
) : (
<div className='profile-container'>
<Profile />
</div>
)}
</Col>
</Row>
) : (
<Profile />
);
};
export default FriendsListPage;

View File

@ -11,26 +11,26 @@ const LeftNavbar = (props) => {
const user = useSelector((state) => state.currentUser);
return (
<Nav className="flex-column mt-3" id="left-navbar">
<div className="navitem">
<Nav className='flex-column mt-3' id='left-navbar'>
<div className='navitem'>
<Nav.Link
as={Link}
to={props.profileLink}
className="text-dark flex-column justify-content-center"
className='text-dark flex-column justify-content-center'
>
<ProfileLink user={user} size="26" fullname="true" />
<ProfileLink user={user} size='26' fullname='true' />
</Nav.Link>
</div>
<div className="navitem">
<Nav.Link as={Link} to="/fakebook/friends/list" className="text-dark">
<FaUserFriends size="26px" className="text-info mr-2" />
<div className="d-inline">Friends</div>
<div className='navitem'>
<Nav.Link as={Link} to='/friends/list' className='text-dark'>
<FaUserFriends size='26px' className='text-info mr-2' />
<div className='d-inline'>Friends</div>
</Nav.Link>
</div>
<div className="navitem">
<Nav.Link as={Link} to="/fakebook/watch" className="text-dark">
<MdOndemandVideo size="26px" className="text-info mr-2" />
<div className="d-inline">Watch</div>
<div className='navitem'>
<Nav.Link as={Link} to='/watch' className='text-dark'>
<MdOndemandVideo size='26px' className='text-info mr-2' />
<div className='d-inline'>Watch</div>
</Nav.Link>
</div>
</Nav>

View File

@ -6,84 +6,86 @@ import ResponsiveImage from "./ResponsiveImage";
import { useSelector } from "react-redux";
const MiniFriends = (props) => {
const { user, linkHandling, ...rest } = props;
const { user, linkHandling, ...rest } = props;
const { linkRefs, linkState } = linkHandling;
const [activeLink, setActiveLink] = linkState;
const { friends: friendsLinkRef } = linkRefs;
const { linkRefs, linkState } = linkHandling;
const [activeLink, setActiveLink] = linkState;
const { friends: friendsLinkRef } = linkRefs;
const NUMBER_OF_FRIENDS = 9;
const NUMBER_OF_FRIENDS = 9;
const friendsLink =
user.index && user.index > 0
? `/fakebook/${user.lastname}.${user.firstname}.${user.index}/Friends`
: `/fakebook/${user.lastname}.${user.firstname}/Friends`;
const friendsLink =
user.index && user.index > 0
? `/${user.lastname}.${user.firstname}.${user.index}/Friends`
: `/${user.lastname}.${user.firstname}/Friends`;
function handleClick() {
handleClickLink(
{ currentTarget: friendsLinkRef.current },
activeLink,
setActiveLink
);
}
function handleClick() {
handleClickLink(
{ currentTarget: friendsLinkRef.current },
activeLink,
setActiveLink
);
}
const users = useSelector((state) => state.users);
const users = useSelector((state) => state.users);
return (
<Card {...rest}>
<Card.Body>
<Card.Title>
<Link to={friendsLink} className="text-body" onClick={handleClick}>
<b>Friends</b>
</Link>
</Card.Title>
<Card.Subtitle className="text-muted">
{users.length} friends
</Card.Subtitle>
<Row>
{users.map((user, index) => {
const userProfileURL =
user.index && user.index > 0
? `/fakebook/${user.lastname}.${user.firstname}.${user.index}`
: `/fakebook/${user.lastname}.${user.firstname}`;
const userName = `${user.firstname} ${user.lastname}`;
return (
//we render maximum 9 friends
index < NUMBER_OF_FRIENDS && (
<Col
key={index}
xs={4}
className="m-0"
style={{
paddingLeft: "3px",
paddingRight: "3px",
paddingTop: "0",
paddingBottom: "3px",
}}>
<ResponsiveImage
photo={user.profilePictureURL}
width="100%"
height="100%"
useStoragePath="true"
/>
<Link
to={userProfileURL}
className="text-body"
onClick={handleClick}>
<div className="w-100" style={{ height: "2.5em" }}>
<p style={{ fontSize: "0.9em" }}>
<b>{userName}</b>
</p>
</div>
</Link>
</Col>
)
);
})}
</Row>
</Card.Body>
</Card>
);
return (
<Card {...rest}>
<Card.Body>
<Card.Title>
<Link to={friendsLink} className='text-body' onClick={handleClick}>
<b>Friends</b>
</Link>
</Card.Title>
<Card.Subtitle className='text-muted'>
{users.length} friends
</Card.Subtitle>
<Row>
{users.map((user, index) => {
const userProfileURL =
user.index && user.index > 0
? `/${user.lastname}.${user.firstname}.${user.index}`
: `/${user.lastname}.${user.firstname}`;
const userName = `${user.firstname} ${user.lastname}`;
return (
//we render maximum 9 friends
index < NUMBER_OF_FRIENDS && (
<Col
key={index}
xs={4}
className='m-0'
style={{
paddingLeft: "3px",
paddingRight: "3px",
paddingTop: "0",
paddingBottom: "3px",
}}
>
<ResponsiveImage
photo={user.profilePictureURL}
width='100%'
height='100%'
useStoragePath='true'
/>
<Link
to={userProfileURL}
className='text-body'
onClick={handleClick}
>
<div className='w-100' style={{ height: "2.5em" }}>
<p style={{ fontSize: "0.9em" }}>
<b>{userName}</b>
</p>
</div>
</Link>
</Col>
)
);
})}
</Row>
</Card.Body>
</Card>
);
};
export default MiniFriends;

View File

@ -22,7 +22,7 @@ const MiniPhotos = (props) => {
const userSlug =
user.index && user.index > 0 ? `${slugBase}.${user.index}` : slugBase;
const photosLink = `/fakebook/${userSlug}/Photos`;
const photosLink = `/${userSlug}/Photos`;
/* ---------------------------------------------------------------- */
@ -59,7 +59,7 @@ const MiniPhotos = (props) => {
}}
>
<Link
to={`/fakebook/photo/${userID}/${index}`}
to={`/photo/${userID}/${index}`}
className='text-body'
onClick={handleClick}
tabIndex='-1'

View File

@ -23,21 +23,21 @@ const PhotoViewer = () => {
}, [location]);
const handleSelect = (selectedIndex, e) => {
history.push(`/fakebook/photo/${userID}/${selectedIndex}`);
history.push(`/photo/${userID}/${selectedIndex}`);
};
return (
<Row
className="bg-200"
className='bg-200'
style={{
position: "relative",
top: "50px",
height: "89vh",
}}
>
<Col md={9} className="h-100" style={{ backgroundColor: "black" }}>
<Col md={9} className='h-100' style={{ backgroundColor: "black" }}>
<Carousel
className="w-100 h-100"
className='w-100 h-100'
interval={null}
indicators={false}
activeIndex={activeIndex}
@ -54,7 +54,7 @@ const PhotoViewer = () => {
>
<StorageImage
storagePath={`/${userID}/${photo.fileName}`}
alt=""
alt=''
style={{
position: "absolute",
top: "50%",

View File

@ -28,15 +28,15 @@ const Photos = (props) => {
}, [activeLink, photosLinkRef, setActiveLink]);
return (
<Card variant="light" className="w-100">
<Card variant='light' className='w-100'>
<Card.Body>
<Card.Title>
<Link to={url} className="text-body">
<Link to={url} className='text-body'>
<b>Photos</b>
</Link>
{isCurrentUser && (
<Button
variant="link"
variant='link'
style={{
textDecoration: "none",
float: "right",
@ -47,14 +47,14 @@ const Photos = (props) => {
</Button>
)}
</Card.Title>
<Row className="w-100">
<Row className='w-100'>
{photos.map((photo, index) => {
return (
<Col key={index} xs={6} sm={4} md={3} lg={2} className="p-1">
<Link to={`/fakebook/photo/${userID}/${index}`}>
<Col key={index} xs={6} sm={4} md={3} lg={2} className='p-1'>
<Link to={`/photo/${userID}/${index}`}>
<ResponsiveImage
width="100%"
height="100%"
width='100%'
height='100%'
userID={userID}
photo={photo}
index={index}

View File

@ -11,213 +11,217 @@ import { useSelector } from "react-redux";
import { upload } from "../backend/backend";
const PostModal = (props) => {
const { show, onClose, setText, isYoutubeBtnPressed, placeholder } = props;
const { show, onClose, setText, isYoutubeBtnPressed, placeholder } = props;
const user = useSelector((state) => state.currentUser);
const userID = useSelector((state) => state.user.id);
const user = useSelector((state) => state.currentUser);
const userID = useSelector((state) => state.user.id);
const WELCOME_TEXT = `For adding YouTube video do the following:
const WELCOME_TEXT = `For adding YouTube video do the following:
1. copy link of the video from the addresse bar of your browser
2. press YouTube button again to upload the YouTube video to your post
3. add your text for the post
4. push the post button`;
const INIT_POST = {
userID: `${userID}`,
text: "",
isPhoto: false,
photoURL: "",
isYoutube: false,
youtubeURL: "",
likes: [],
};
const [post, setPost] = useState(INIT_POST);
const INIT_POST = {
userID: `${userID}`,
text: "",
isPhoto: false,
photoURL: "",
isYoutube: false,
youtubeURL: "",
likes: [],
};
const [post, setPost] = useState(INIT_POST);
function handleChange(e) {
const value = handleTextareaChange({
e: e,
state: post,
setState: setPost,
});
setPostBtnEnabled(value);
}
function handleChange(e) {
const value = handleTextareaChange({
e: e,
state: post,
setState: setPost,
});
setPostBtnEnabled(value);
}
function setPostBtnEnabled(value) {
if (value === "") setBtnEnabled(false);
else setBtnEnabled(true);
setText(value);
}
function setPostBtnEnabled(value) {
if (value === "") setBtnEnabled(false);
else setBtnEnabled(true);
setText(value);
}
const [isBtnEnabled, setBtnEnabled] = useState(false);
let variant, disabled;
if (isBtnEnabled) {
variant = "primary";
disabled = false;
} else {
variant = "secondary";
disabled = true;
}
const [isBtnEnabled, setBtnEnabled] = useState(false);
let variant, disabled;
if (isBtnEnabled) {
variant = "primary";
disabled = false;
} else {
variant = "secondary";
disabled = true;
}
const [showUploadPhotoDlg, setShowUploadPhotoDlg] = useState(false);
const [showUploadPhotoDlg, setShowUploadPhotoDlg] = useState(false);
function addPhotoToPost(file) {
addPhoto({
state: post,
setState: setPost,
file: file,
userID: userID,
});
setBtnEnabled(true);
}
function addPhotoToPost(file) {
addPhoto({
state: post,
setState: setPost,
file: file,
userID: userID,
});
setBtnEnabled(true);
}
function deletePhoto() {
delPhoto({
state: post,
setState: setPost,
user: user,
userID: userID,
sideEffect: setPostBtnAsSideEffect,
});
}
function deletePhoto() {
delPhoto({
state: post,
setState: setPost,
user: user,
userID: userID,
sideEffect: setPostBtnAsSideEffect,
});
}
function setPostBtnAsSideEffect() {
if (post.text === "" && !post.isYoutube) setBtnEnabled(false);
}
function setPostBtnAsSideEffect() {
if (post.text === "" && !post.isYoutube) setBtnEnabled(false);
}
function uploadPost() {
upload(post).then(() => {
setPost(INIT_POST);
setText("");
onClose();
});
}
function uploadPost() {
upload(post).then(() => {
setPost(INIT_POST);
setText("");
onClose();
});
}
function addYoutubeVideo() {
const url = post.text;
const URL_PATTERN = "https://www.youtube.com/watch?v=";
const MOBILE_URL_PATTERN = "https://m.youtube.com/watch?v=";
if (!url.startsWith(URL_PATTERN) && !url.startsWith(MOBILE_URL_PATTERN))
return;
let patternLength;
if (url.startsWith(URL_PATTERN)) patternLength = URL_PATTERN.length;
else patternLength = MOBILE_URL_PATTERN.length;
const videoID = url.slice(patternLength);
const youtubeURL = `https://www.youtube.com/embed/${videoID}`;
const newPost = { ...post };
newPost.isYoutube = true;
newPost.youtubeURL = youtubeURL;
newPost.text = "";
setPost(newPost);
setText("");
setBtnEnabled(true);
}
function addYoutubeVideo() {
const url = post.text;
const URL_PATTERN = "https://www.youtube.com/watch?v=";
const MOBILE_URL_PATTERN = "https://m.youtube.com/watch?v=";
if (!url.startsWith(URL_PATTERN) && !url.startsWith(MOBILE_URL_PATTERN))
return;
let patternLength;
if (url.startsWith(URL_PATTERN)) patternLength = URL_PATTERN.length;
else patternLength = MOBILE_URL_PATTERN.length;
const videoID = url.slice(patternLength);
const youtubeURL = `https://www.youtube.com/embed/${videoID}`;
const newPost = { ...post };
newPost.isYoutube = true;
newPost.youtubeURL = youtubeURL;
newPost.text = "";
setPost(newPost);
setText("");
setBtnEnabled(true);
}
function deleteYoutubeVideo() {
const newPost = { ...post };
newPost.isYoutube = false;
newPost.youtubeURL = "";
setPost(newPost);
if (post.text === "" && !post.isPhoto) setBtnEnabled(false);
}
function deleteYoutubeVideo() {
const newPost = { ...post };
newPost.isYoutube = false;
newPost.youtubeURL = "";
setPost(newPost);
if (post.text === "" && !post.isPhoto) setBtnEnabled(false);
}
function getPlaceholder() {
if (isYoutubeBtnPressed && !post.isYoutube) return WELCOME_TEXT;
else return placeholder;
}
function getPlaceholder() {
if (isYoutubeBtnPressed && !post.isYoutube) return WELCOME_TEXT;
else return placeholder;
}
function getRows() {
if (getPlaceholder() === WELCOME_TEXT && post.text === "") return 7;
else return 3;
}
function getRows() {
if (getPlaceholder() === WELCOME_TEXT && post.text === "") return 7;
else return 3;
}
return (
<>
<Modal show={show} onHide={onClose}>
<Modal.Header closeButton>
<div className="w-100 d-flex justify-content-center">
<Modal.Title>
<b>Create Post</b>
</Modal.Title>
</div>
</Modal.Header>
<Modal.Body>
<ProfileLink user={user} size="45" fullname="true" bold="true" />
<div className="mt-2 scrolling-container">
<textarea
type="text"
onChange={handleChange}
className="w-100 mt-2 textarea"
placeholder={getPlaceholder()}
rows={getRows()}
value={post.text}></textarea>
{post.isPhoto && (
<div className="mb-2 img-container">
<StorageImage
alt=""
storagePath={`/${post.photoURL}`}
className="w-100 img-to-post"
/>
<div className="close-btn-container">
<CloseButton onClick={deletePhoto} />
</div>
</div>
)}
{post.isYoutube && (
<div className="mb-2 video-container">
<iframe
src={post.youtubeURL}
title="YouTube video player"
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope"
allowFullScreen></iframe>
<div className="close-btn-container">
<CloseButton onClick={deleteYoutubeVideo} />
</div>
</div>
)}
</div>
<div className="w-100 my-2 add-to-post">
<b>Add to your post</b>
<Button
className="ml-2 add-photo-btn"
variant="light"
size="sm"
onClick={addYoutubeVideo}
disabled={post.isPhoto || post.isYoutube}>
<AiFillYoutube
size="26px"
className="text-danger"
aria-label="YouTube"
/>
</Button>
<Button
className="ml-2 add-photo-btn"
variant="light"
size="sm"
onClick={() => setShowUploadPhotoDlg(true)}
disabled={post.isPhoto || post.isYoutube}>
<HiOutlinePhotograph
size="26px"
className="text-success"
aria-label="photo"
/>
</Button>
</div>
<Button
variant={variant}
className="w-100 mt-3"
disabled={disabled}
onClick={uploadPost}>
<b>Post</b>
</Button>
</Modal.Body>
</Modal>
<UploadPhoto
show={showUploadPhotoDlg}
setShow={setShowUploadPhotoDlg}
updateDatabase={addPhotoToPost}
/>
</>
);
return (
<>
<Modal show={show} onHide={onClose}>
<Modal.Header closeButton>
<div className='w-100 d-flex justify-content-center'>
<Modal.Title>
<b>Create Post</b>
</Modal.Title>
</div>
</Modal.Header>
<Modal.Body>
<ProfileLink user={user} size='45' fullname='true' bold='true' />
<div className='mt-2 scrolling-container'>
<textarea
type='text'
onChange={handleChange}
className='w-100 mt-2 textarea'
placeholder={getPlaceholder()}
rows={getRows()}
value={post.text}
></textarea>
{post.isPhoto && (
<div className='mb-2 img-container'>
<StorageImage
alt=''
storagePath={`/${post.photoURL}`}
className='w-100 img-to-post'
/>
<div className='close-btn-container'>
<CloseButton onClick={deletePhoto} />
</div>
</div>
)}
{post.isYoutube && (
<div className='mb-2 video-container'>
<iframe
src={post.youtubeURL}
title='YouTube video player'
allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope'
allowFullScreen
></iframe>
<div className='close-btn-container'>
<CloseButton onClick={deleteYoutubeVideo} />
</div>
</div>
)}
</div>
<div className='w-100 my-2 add-to-post'>
<b>Add to your post</b>
<Button
className='ml-2 add-photo-btn'
variant='light'
size='sm'
onClick={addYoutubeVideo}
disabled={post.isPhoto || post.isYoutube}
>
<AiFillYoutube
size='26px'
className='text-danger'
aria-label='YouTube'
/>
</Button>
<Button
className='ml-2 add-photo-btn'
variant='light'
size='sm'
onClick={() => setShowUploadPhotoDlg(true)}
disabled={post.isPhoto || post.isYoutube}
>
<HiOutlinePhotograph
size='26px'
className='text-success'
aria-label='photo'
/>
</Button>
</div>
<Button
variant={variant}
className='w-100 mt-3'
disabled={disabled}
onClick={uploadPost}
>
<b>Post</b>
</Button>
</Modal.Body>
</Modal>
<UploadPhoto
show={showUploadPhotoDlg}
setShow={setShowUploadPhotoDlg}
updateDatabase={addPhotoToPost}
/>
</>
);
};
export default PostModal;

View File

@ -16,10 +16,10 @@ const SelectBgPhotoModal = (props) => {
{photos.map((photo, index) => {
return (
<StorageImage
className="m-1"
width="31%"
height="90px"
alt=""
className='m-1'
width='31%'
height='90px'
alt=''
key={index}
id={index}
storagePath={`/${userID}/${photo.fileName}`}

View File

@ -15,123 +15,122 @@ import { handleClickLink } from "./helper";
import { linkUpdated } from "../features/link/linkSlice";
import { friendsListPageSet } from "../features/accountPage/accountPageSlice";
const TitleBar = () => {
const refs = {
home: useRef(null),
friends: useRef(null),
watch: useRef(null),
profile: useRef(null),
};
const TitleBar = (props) => {
//Get the signed in user, active link and the profileLink
const user = useSelector((state) => state.currentUser);
const link = useSelector((state) => state.link);
const profileLink = useSelector((state) => state.accountPage.profileLink);
const refs = {
home: useRef(null),
friends: useRef(null),
watch: useRef(null),
profile: useRef(null),
};
const dispatch = useDispatch();
//Get the signed in user, active link and the profileLink
const user = useSelector((state) => state.currentUser);
const link = useSelector((state) => state.link);
const profileLink = useSelector((state) => state.accountPage.profileLink);
useEffect(() => {
handleClickLink(
{ currentTarget: refs[link.active].current },
refs[link.previous].current
);
}, [link, refs]);
const dispatch = useDispatch();
// Log out function
const handleClick = () => {
signUserOut();
};
useEffect(() => {
handleClickLink(
{ currentTarget: refs[link.active].current },
refs[link.previous].current
);
}, [link, refs]);
function closeFriendsListPage() {
dispatch(friendsListPageSet(false));
dispatch(linkUpdated("profile"));
}
// Log out function
const handleClick = () => {
signUserOut();
};
return (
<div className='titlebar bg-light'>
<Navbar bg='light' className='p-0 nav-container'>
<Navbar.Brand as={Link} to='/'>
<FaFacebook color='dodgerblue' fontSize='2em' className='mx-3' />
</Navbar.Brand>
<div style={{ width: "450px" }} className='spaceing' />
<Nav className='w-75 justify-content-start mr-5'>
<Nav.Item className='first'>
<Link to='/' className='nav-link' ref={refs.home}>
<VscHome
fontSize='2rem'
className='mx-4'
style={{ pointerEvents: "none" }}
/>
</Link>
</Nav.Item>
<Nav.Item>
<Link to='/friends/list' className='nav-link' ref={refs.friends}>
<FaUserFriends
fontSize='2rem'
className='mx-4'
style={{ pointerEvents: "none" }}
/>
</Link>
</Nav.Item>
<Nav.Item>
<Link to='/watch' className='nav-link' ref={refs.watch}>
<MdOndemandVideo
fontSize='2rem'
className='mx-4'
style={{ pointerEvents: "none" }}
/>
</Link>
</Nav.Item>
</Nav>
function closeFriendsListPage() {
dispatch(friendsListPageSet(false));
dispatch(linkUpdated("profile"));
}
return (
<div className="titlebar bg-light">
<Navbar bg="light" className="p-0 nav-container">
<Navbar.Brand as={Link} to="/fakebook">
<FaFacebook color="dodgerblue" fontSize="2em" className="mx-3" />
</Navbar.Brand>
<div style={{ width: "450px" }} className="spaceing" />
<Nav className="w-75 justify-content-start mr-5">
<Nav.Item className="first">
<Link to="/fakebook" className="nav-link" ref={refs.home}>
<VscHome
fontSize="2rem"
className="mx-4"
style={{ pointerEvents: "none" }}
/>
</Link>
</Nav.Item>
<Nav.Item>
<Link
to="/fakebook/friends/list"
className="nav-link"
ref={refs.friends}>
<FaUserFriends
fontSize="2rem"
className="mx-4"
style={{ pointerEvents: "none" }}
/>
</Link>
</Nav.Item>
<Nav.Item>
<Link to="/fakebook/watch" className="nav-link" ref={refs.watch}>
<MdOndemandVideo
fontSize="2rem"
className="mx-4"
style={{ pointerEvents: "none" }}
/>
</Link>
</Nav.Item>
</Nav>
<Nav className="w-25 justify-content-end align-self-center">
<Nav.Item className="align-self-center first">
<Link
to={profileLink}
className="nav-link profile"
id="profile"
onClick={closeFriendsListPage}
ref={refs.profile}>
<ProfileLink user={user} size="30" fullname="false" bold="true" />
</Link>
</Nav.Item>
<Nav.Item className="align-self-center">
<DropdownButton
title=""
className="mr-4 custom-drop-down-btn"
menuAlign="right">
<Dropdown.Item
as={Link}
to={profileLink}
onClick={closeFriendsListPage}>
<ProfileLink
user={user}
size="60"
fullname="true"
bold="true"
/>
</Dropdown.Item>
<Dropdown.Divider />
<Dropdown.Item
as={Link}
to="/fakebook/"
onClick={handleClick}
className="p-0">
<ImExit fontSize="1.5em" className="mx-4" />
<span>Log Out</span>
<div style={{ width: "20em" }}></div>
</Dropdown.Item>
</DropdownButton>
</Nav.Item>
</Nav>
</Navbar>
</div>
);
<Nav className='w-25 justify-content-end align-self-center'>
<Nav.Item className='align-self-center first'>
<Link
to={profileLink}
className='nav-link profile'
id='profile'
onClick={closeFriendsListPage}
ref={refs.profile}
>
<ProfileLink user={user} size='30' fullname='false' bold='true' />
</Link>
</Nav.Item>
<Nav.Item className='align-self-center'>
<DropdownButton
title=''
className='mr-4 custom-drop-down-btn'
menuAlign='right'
>
<Dropdown.Item
as={Link}
to={profileLink}
onClick={closeFriendsListPage}
>
<ProfileLink
user={user}
size='60'
fullname='true'
bold='true'
/>
</Dropdown.Item>
<Dropdown.Divider />
<Dropdown.Item
as={Link}
to='/'
onClick={handleClick}
className='p-0'
>
<ImExit fontSize='1.5em' className='mx-4' />
<span>Log Out</span>
<div style={{ width: "20em" }}></div>
</Dropdown.Item>
</DropdownButton>
</Nav.Item>
</Nav>
</Navbar>
</div>
);
};
export default TitleBar;

View File

@ -15,10 +15,10 @@ import FriendsListPage from "./FriendsListPage";
/* Router */
import {
BrowserRouter as Router,
Switch,
Route,
useLocation,
BrowserRouter as Router,
Switch,
Route,
useLocation,
} from "react-router-dom";
/* Layout */
@ -30,19 +30,18 @@ import Container from "react-bootstrap/Container";
import { useDispatch, useSelector } from "react-redux";
import {
friendsListPageSet,
profileLinkSet,
watchSet,
friendsListPageSet,
profileLinkSet,
watchSet,
} from "../features/accountPage/accountPageSlice";
/* Mock-backend helpers */
import {
currentUserOffline,
currentUserOnline,
subscribeCurrentUser,
subscribeUsers,
subscribePosts,
currentUserOnline,
subscribeCurrentUser,
subscribeUsers,
subscribePosts,
} from "../backend/backend";
/* ------------------------------------------------------------------ */
@ -52,139 +51,154 @@ import {
/* ------------------------------------------------------------------ */
const RouteStateSync = () => {
const dispatch = useDispatch();
const dispatch = useDispatch();
const location = useLocation();
const location = useLocation();
useEffect(() => {
const { pathname } = location;
const isFriendsListPage = useSelector(
(state) => state.accountPage.isFriendsListPage
);
/* friends list page */
useEffect(() => {
const { pathname } = location;
dispatch(friendsListPageSet(pathname.startsWith("/fakebook/friends/list")));
/* friends list page */
/* watch page (videos feed) */
dispatch(
friendsListPageSet(
pathname.startsWith("/friends/list") || isFriendsListPage
)
);
dispatch(watchSet(pathname.startsWith("/fakebook/watch")));
}, [location, dispatch]);
/* watch page (videos feed) */
return null; // renders nothing
dispatch(watchSet(pathname.startsWith("/watch")));
}, [location, dispatch]);
return null; // renders nothing
};
/* ------------------------------------------------------------------ */
const UserAccount = () => {
const dispatch = useDispatch();
const dispatch = useDispatch();
/* Redux selectors */
/* Redux selectors */
const profileLink = useSelector((state) => state.accountPage.profileLink);
const profileLink = useSelector((state) => state.accountPage.profileLink);
const currentUser = useSelector((state) => state.currentUser);
const currentUser = useSelector((state) => state.currentUser);
const users = useSelector((state) => state.users);
const users = useSelector((state) => state.users);
/* -------------------------------------------------- */
const isFriendsListPage = useSelector(
(state) => state.accountPage.isFriendsListPage
);
/* Firestore-like subscriptions */
/* -------------------------------------------------- */
/* -------------------------------------------------- */
/* Firestore-like subscriptions */
useEffect(() => {
const unsubCurrentUser = subscribeCurrentUser();
/* -------------------------------------------------- */
const unsubUsers = subscribeUsers();
useEffect(() => {
const unsubCurrentUser = subscribeCurrentUser();
const unsubPosts = subscribePosts();
const unsubUsers = subscribeUsers();
/* mark user online */
const unsubPosts = subscribePosts();
currentUserOnline();
/* mark user online */
/* cleanup */
currentUserOnline();
return () => {
unsubCurrentUser();
/* cleanup */
unsubUsers();
return () => {
unsubCurrentUser();
unsubPosts();
};
}, []);
unsubUsers();
/* -------------------------------------------------- */
unsubPosts();
};
}, []);
/* Build unique profile link (.index appended once) */
/* -------------------------------------------------- */
/* -------------------------------------------------- */
/* Build unique profile link (.index appended once) */
useEffect(() => {
if (!currentUser) return;
/* -------------------------------------------------- */
/* remove any existing trailing ".number" */
useEffect(() => {
if (!currentUser) return;
const base = profileLink.replace(/\.\d+$/, "");
/* remove any existing trailing ".number" */
const newLink =
currentUser.index && currentUser.index > 0
? `${base}.${currentUser.index}`
: base;
const base = profileLink.replace(/\.\d+$/, "");
dispatch(profileLinkSet(newLink));
}, [currentUser, profileLink, dispatch]);
const newLink =
currentUser.index && currentUser.index > 0
? `${base}.${currentUser.index}`
: base;
/* Loading guard */
dispatch(profileLinkSet(newLink));
}, [currentUser, profileLink, dispatch]);
if (!currentUser || users.length === 0) {
return <div>Loading</div>;
}
/* Loading guard */
/* -------------------------------------------------- */
if (!currentUser || users.length === 0) {
return <div>Loading</div>;
}
/* Render */
/* -------------------------------------------------- */
/* -------------------------------------------------- */
/* Render */
return (
<div className="bg-200 vw-100 main-container overflow-hidden">
<Container className="w-100 p-0" fluid>
<Router>
<RouteStateSync />
/* -------------------------------------------------- */
<TitleBar />
return (
<div className='bg-200 vw-100 main-container overflow-hidden'>
<Container className='w-100 p-0' fluid>
<Router basename='/fakebook-aigen'>
<RouteStateSync />
<Switch>
{/* Friends list ------------------------------------------------ */}
<TitleBar />
<Route path="/fakebook/friends/list" component={FriendsListPage} />
<Switch>
{/* Friends list ------------------------------------------------ */}
{/* Single photo ----------------------------------------------- */}
<Route path='/friends/list' component={FriendsListPage} />
<Route path="/fakebook/photo/:userID/:n" component={PhotoViewer} />
{/* Single photo ----------------------------------------------- */}
{/* Watch (video feed) ----------------------------------------- */}
<Route path='/photo/:userID/:n' component={PhotoViewer} />
<Route
path="/fakebook/watch"
render={(props) => <HomePage {...props} className="pt-5" />}
/>
{/* Watch (video feed) ----------------------------------------- */}
{/* User profile ----------------------------------------------- */}
<Route
path='/watch'
render={(props) => <HomePage {...props} className='pt-5' />}
/>
<Route path="/fakebook/:userName" component={Profile} />
{/* User profile ----------------------------------------------- */}
{/* News-feed root --------------------------------------------- */}
<Route
path='/:userName'
component={isFriendsListPage ? FriendsListPage : Profile}
/>
<Route
path="/fakebook"
exact
render={(props) => <HomePage {...props} className="pt-5" />}
/>
</Switch>
</Router>
</Container>
</div>
);
{/* News-feed root --------------------------------------------- */}
<Route
path='/'
exact
render={(props) => <HomePage {...props} className='pt-5' />}
/>
</Switch>
</Router>
</Container>
</div>
);
};
export default UserAccount;

View File

@ -1,7 +1,8 @@
import { defineConfig } from 'vite'
import preact from '@preact/preset-vite'
import { defineConfig } from "vite";
import preact from "@preact/preset-vite";
// https://vite.dev/config/
export default defineConfig({
plugins: [preact()],
})
base: "/fakebook-aigen/", // <--- ADD THIS AS THE REPO NAME
});